From ddf709ab5bc5980c571d9718f719ab6c0d523afe Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 24 Aug 2019 06:19:55 +0300 Subject: [PATCH] Cloud themes support --- .../Sources/AccountContext.swift | 1 + .../Sources/ThemedTextAlertController.swift | 2 +- .../Display/NavigationController.swift | 53 +-- submodules/Display/Display/UIKitUtils.swift | 20 +- .../Sources/ItemListAvatarAndNameItem.swift | 2 +- .../Sources/ItemListPeerActionItem.swift | 2 +- .../Sources/ItemListPeerItem.swift | 2 +- .../Sources/ItemListController.swift | 5 + .../Items/ItemListDisclosureItem.swift | 2 +- .../TGAttachmentCarouselItemView.h | 3 +- .../TGAttachmentCarouselItemView.m | 10 +- .../TGMediaAssetsController.h | 5 +- .../TGMediaAssetsController.m | 12 +- .../TGMediaAssetsPickerController.m | 6 +- .../LegacyComponents/TGMediaAssetsUtils.h | 6 - .../TGMediaPickerController.h | 1 + .../TGMediaPickerModernGalleryMixin.h | 4 +- .../TGMediaPickerModernGalleryMixin.m | 32 +- .../TGMediaPickerSendActionSheetController.m | 9 + .../LegacyComponents/TGPassportAttachMenu.m | 2 +- .../Sources/LegacyAttachmentMenu.swift | 11 +- .../Sources/LegacyMediaPickers.swift | 7 +- .../Sources/SecureIdAuthFormFieldNode.swift | 2 +- .../Sources/SecureIdAuthListFieldNode.swift | 2 +- .../Sources/ItemListSecretChatKeyItem.swift | 2 +- .../project.pbxproj | 16 +- .../LanguageSuggestionController.swift | 368 ------------------ .../NotificationExceptionControllerNode.swift | 44 ++- .../Themes/CreateThemeController.swift | 189 +++++++++ .../Themes/ThemePreviewController.swift | 48 ++- .../Themes/ThemePreviewControllerNode.swift | 26 +- .../Themes/ThemeSettingsAppIconItem.swift | 2 +- .../Themes/ThemeSettingsController.swift | 70 +++- .../Themes/ThemeSettingsThemeItem.swift | 73 ++-- submodules/TelegramApi/Sources/Api0.swift | 16 +- submodules/TelegramApi/Sources/Api1.swift | 182 +++++++-- submodules/TelegramApi/Sources/Api3.swift | 273 ++++++++++--- .../TelegramCore/AccountManager.swift | 2 + .../AccountStateManagementUtils.swift | 2 + .../TelegramCore/EnqueueMessage.swift | 1 - .../TelegramCore/Namespaces.swift | 2 + .../TelegramCore/TelegramCore/Theme.swift | 90 +++++ .../TelegramCore/TelegramCore/Themes.swift | 226 +++++++++++ .../TelegramCore/Wallpapers.swift | 18 +- .../project.pbxproj | 8 + .../DefaultDarkPresentationTheme.swift | 12 +- .../Sources/DefaultDayPresentationTheme.swift | 17 +- .../Sources/MakePresentationTheme.swift | 4 + .../Sources/PresentationData.swift | 2 + .../PresentationResourcesItemList.swift | 30 +- .../PresentationResourcesRootController.swift | 19 +- .../Call List/CallIcon.imageset/Contents.json | 12 + .../CallIcon.imageset/ic_addcall.pdf | Bin 0 -> 4939 bytes .../PeerMutedIcon.imageset/Contents.json | 11 +- .../DialogList_Muted@2x.png | Bin 208 -> 0 bytes .../PeerMutedIcon.imageset/ic_mutedchat.pdf | Bin 0 -> 4283 bytes .../Message/ShareIcon.imageset/Contents.json | 12 +- .../ConversationChannelInlineShareIcon@2x.png | Bin 423 -> 0 bytes .../ConversationChannelInlineShareIcon@3x.png | Bin 622 -> 0 bytes .../ShareIcon.imageset/ic_chat_share.pdf | Bin 0 -> 4140 bytes .../AddChannelIcon.imageset/Contents.json | 12 + .../AddChannelIcon.imageset/ic_addchannel.pdf | Bin 0 -> 4747 bytes .../AddExceptionIcon.imageset/Contents.json | 22 -- .../AddExceptionIcon.imageset/plus@2x.png | Bin 1133 -> 0 bytes .../AddExceptionIcon.imageset/plus@3x.png | Bin 927 -> 0 bytes .../DisclosureArrow.imageset/Contents.json | 12 + .../DisclosureArrow.imageset/ic_open.pdf | Bin 0 -> 3972 bytes .../TelegramUI/ChatController.swift | 97 ++++- .../ChatHistoryEntriesForView.swift | 2 +- .../ChatMessageBubbleItemNode.swift | 2 +- .../ChatRecentActionsControllerNode.swift | 2 + .../ChatScheduleTimeController.swift | 6 +- .../ChatScheduleTimeControllerNode.swift | 6 +- .../TelegramUI/TelegramUI/LegacyCamera.swift | 2 +- .../TelegramUI/OpenChatMessage.swift | 6 +- .../TelegramUI/OpenResolvedUrl.swift | 18 + .../TelegramUI/TelegramUI/OpenUrl.swift | 16 + .../Sources/PresentationThemeSettings.swift | 38 ++ .../UrlHandling/Sources/UrlHandling.swift | 5 + 79 files changed, 1475 insertions(+), 749 deletions(-) delete mode 100644 submodules/SettingsUI/Sources/Language Suggestion/LanguageSuggestionController.swift create mode 100644 submodules/SettingsUI/Sources/Themes/CreateThemeController.swift create mode 100644 submodules/TelegramCore/TelegramCore/Theme.swift create mode 100644 submodules/TelegramCore/TelegramCore/Themes.swift create mode 100644 submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/ic_addcall.pdf delete mode 100644 submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/DialogList_Muted@2x.png create mode 100644 submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/ic_mutedchat.pdf delete mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ConversationChannelInlineShareIcon@2x.png delete mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ConversationChannelInlineShareIcon@3x.png create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ic_chat_share.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/ic_addchannel.pdf delete mode 100644 submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/Contents.json delete mode 100644 submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/plus@2x.png delete mode 100644 submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/plus@3x.png create mode 100644 submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/ic_open.pdf diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 6b90a90d48..c3807a7048 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -166,6 +166,7 @@ public enum ResolvedUrl { case cancelAccountReset(phone: String, hash: String) case share(url: String?, text: String?, to: String?) case wallpaper(WallpaperUrlParameter) + case theme(String) } public enum NavigateToChatKeepStack { diff --git a/submodules/AlertUI/Sources/ThemedTextAlertController.swift b/submodules/AlertUI/Sources/ThemedTextAlertController.swift index 5779ac6b09..26f21b0fbd 100644 --- a/submodules/AlertUI/Sources/ThemedTextAlertController.swift +++ b/submodules/AlertUI/Sources/ThemedTextAlertController.swift @@ -6,7 +6,7 @@ import AccountContext public func textAlertController(context: AccountContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal) -> AlertController { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: title, text: text, actions: actions) + let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: title, text: text, actions: actions, actionLayout: actionLayout) let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in controller?.theme = AlertControllerTheme(presentationTheme: presentationData.theme) }) diff --git a/submodules/Display/Display/NavigationController.swift b/submodules/Display/Display/NavigationController.swift index 616e7dc6fa..e5245c5089 100644 --- a/submodules/Display/Display/NavigationController.swift +++ b/submodules/Display/Display/NavigationController.swift @@ -366,44 +366,7 @@ open class NavigationController: UINavigationController, ContainableController, } } - if let _ = self.controllerView.emptyDetailView { -// transition.updateFrame(view: navigationBackgroundView, frame: navigationBackgroundFrame) -// transition.updateFrame(view: navigationSeparatorView, frame: CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel))) -// if let image = emptyDetailView.image { -// transition.updateFrame(view: emptyDetailView, frame: CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size)) -// } - } else { - // let navigationBackgroundView = UIView() -// navigationBackgroundView.backgroundColor = self.theme.navigationBar.c -// let navigationSeparatorView = UIView() -// navigationSeparatorView.backgroundColor = self.theme.navigationBar.separatorColor -// let emptyDetailView = UIImageView() -// emptyDetailView.image = self.theme.emptyDetailIcon -// emptyDetailView.alpha = 0.0 -// -// self.controllerView.navigationBackgroundView = navigationBackgroundView -// self.controllerView.navigationSeparatorView = navigationSeparatorView -// self.controllerView.emptyDetailView = emptyDetailView -// -// self.controllerView.insertSubview(navigationBackgroundView, at: 0) -// self.controllerView.insertSubview(navigationSeparatorView, at: 1) - // self.controllerView.insertSubview(emptyDetailView, at: 0) - -// navigationBackgroundView.frame = navigationBackgroundFrame -// navigationSeparatorView.frame = CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel)) -// -// transition.animatePositionAdditive(layer: navigationBackgroundView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0)) -// transition.animatePositionAdditive(layer: navigationSeparatorView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0)) - -// if let image = emptyDetailView.image { -// emptyDetailView.frame = CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size) -// } -// -// transition.updateAlpha(layer: emptyDetailView.layer, alpha: 1.0) - } - if let blackout = self.masterDetailsBlackout { - let blackoutFrame: CGRect switch blackout { case .details: @@ -493,15 +456,15 @@ open class NavigationController: UINavigationController, ContainableController, } else if case .masterDetail = layoutConfiguration, i == 1 { controller.navigationBar?.previousItem = .close } else { - controller.navigationBar?.previousItem = .item(viewControllers[i - 1].navigationItem) + controller.navigationBar?.previousItem = .item(self.viewControllers[i - 1].navigationItem) } if i < self._viewControllers.count - 1 { - controller.updateNavigationCustomData((viewControllers[i + 1] as? ViewController)?.customData, progress: 1.0, transition: transition) + controller.updateNavigationCustomData((self.viewControllers[i + 1] as? ViewController)?.customData, progress: 1.0, transition: transition) } else { controller.updateNavigationCustomData(nil, progress: 1.0, transition: transition) } } - viewControllers[i].navigation_setNavigationController(self) + self.viewControllers[i].navigation_setNavigationController(self) if i == 0, let (_, layout) = firstControllerFrameAndLayout { controllersAndFrames.append((true, self._viewControllers[i], layout)) @@ -526,7 +489,15 @@ open class NavigationController: UINavigationController, ContainableController, frame = lastControllerFrameAndLayout.0 } let isAppearing = record.controller.view.superview == nil - (record.controller as? ViewController)?.containerLayoutUpdated(layout, transition: isAppearing ? .immediate : transition) + if let controller = record.controller as? ViewController { + let updatedLayout: ContainerViewLayout + if previousControllers.count == self.viewControllers.count + 1, previousControllers[previousControllers.count - 2].controller === controller { + updatedLayout = layout.withUpdatedInputHeight(controller.hasActiveInput ? layout.inputHeight : nil) + } else { + updatedLayout = layout + } + controller.containerLayoutUpdated(updatedLayout, transition: isAppearing ? .immediate : transition) + } if isAppearing { if isMaster { appearingMasterController = record diff --git a/submodules/Display/Display/UIKitUtils.swift b/submodules/Display/Display/UIKitUtils.swift index 3c9f5ac627..9faf783388 100644 --- a/submodules/Display/Display/UIKitUtils.swift +++ b/submodules/Display/Display/UIKitUtils.swift @@ -83,9 +83,13 @@ public extension UIColor { var red: CGFloat = 0.0 var green: CGFloat = 0.0 var blue: CGFloat = 0.0 - self.getRed(&red, green: &green, blue: &blue, alpha: nil) - - return (UInt32(red * 255.0) << 16) | (UInt32(green * 255.0) << 8) | (UInt32(blue * 255.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)) + } 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)) + } else { + return 0 + } } var argb: UInt32 { @@ -93,9 +97,13 @@ public extension UIColor { var green: CGFloat = 0.0 var blue: CGFloat = 0.0 var alpha: CGFloat = 0.0 - self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) - - return (UInt32(alpha * 255.0) << 24) | (UInt32(red * 255.0) << 16) | (UInt32(green * 255.0) << 8) | (UInt32(blue * 255.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)) + } 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)) + } else { + return 0 + } } var hsv: (CGFloat, CGFloat, CGFloat) { diff --git a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift index 432d266f93..e16ca21c30 100644 --- a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift +++ b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift @@ -927,7 +927,7 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo if strongSelf.arrowNode.supernode == nil { strongSelf.addSubnode(strongSelf.arrowNode) } - strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 15.0 - arrowImage.size.width, y: floor((layout.contentSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) + strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: floor((layout.contentSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) } else if strongSelf.arrowNode.supernode != nil { strongSelf.arrowNode.removeFromSupernode() } diff --git a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift index 4d763c81a9..e5b497cfdd 100644 --- a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift +++ b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift @@ -196,7 +196,7 @@ class ItemListPeerActionItemNode: ListViewItemNode { strongSelf.iconNode.image = item.icon if let image = item.icon { - transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(origin: CGPoint(x: params.leftInset + editingOffset + floor((leftInset - params.leftInset - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)) + transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(origin: CGPoint(x: params.leftInset + editingOffset + floor((leftInset - params.leftInset - image.size.width) / 2.0) + 3.0, y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)) } if strongSelf.backgroundNode.supernode == nil { diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index a087d6a945..f66a5dfd24 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -859,7 +859,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo if let labelArrowNode = self.labelArrowNode { if let image = labelArrowNode.image { - let labelArrowNodeFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - image.size.width, y: labelArrowNode.frame.minY), size: image.size) + let labelArrowNodeFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - image.size.width + 8.0, y: labelArrowNode.frame.minY), size: image.size) transition.updateFrame(node: labelArrowNode, frame: labelArrowNodeFrame) rightLabelInset += 19.0 } diff --git a/submodules/ItemListUI/Sources/ItemListController.swift b/submodules/ItemListUI/Sources/ItemListController.swift index 20edf03b49..81344e6c05 100644 --- a/submodules/ItemListUI/Sources/ItemListController.swift +++ b/submodules/ItemListUI/Sources/ItemListController.swift @@ -25,6 +25,7 @@ public enum ItemListNavigationButtonStyle { public enum ItemListNavigationButtonContentIcon { case search case add + case action } public enum ItemListNavigationButtonContent: Equatable { @@ -309,6 +310,8 @@ open class ItemListController: ViewController, KeyShor image = PresentationResourcesRootController.navigationCompactSearchIcon(controllerState.theme) case .add: image = PresentationResourcesRootController.navigationAddIcon(controllerState.theme) + case .action: + image = PresentationResourcesRootController.navigationShareIcon(controllerState.theme) } item = UIBarButtonItem(image: image, style: leftNavigationButton.style.barButtonItemStyle, target: strongSelf, action: #selector(strongSelf.leftNavigationButtonPressed)) } @@ -364,6 +367,8 @@ open class ItemListController: ViewController, KeyShor image = PresentationResourcesRootController.navigationCompactSearchIcon(controllerState.theme) case .add: image = PresentationResourcesRootController.navigationAddIcon(controllerState.theme) + case .action: + image = PresentationResourcesRootController.navigationShareIcon(controllerState.theme) } item = UIBarButtonItem(image: image, style: style.barButtonItemStyle, target: strongSelf, action: action) } diff --git a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift index bfcda62617..6986cf2537 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift @@ -431,7 +431,7 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode { } if let arrowImage = strongSelf.arrowNode.image { - strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 15.0 - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size) + strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size) } switch item.disclosureStyle { diff --git a/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.h b/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.h index 3f218c9302..da42f7f657 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.h +++ b/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.h @@ -32,12 +32,13 @@ @property (nonatomic) bool disableStickers; @property (nonatomic) bool hasSilentPosting; @property (nonatomic) bool hasSchedule; +@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t)); @property (nonatomic, strong) NSArray *underlyingViews; @property (nonatomic, assign) bool openEditor; @property (nonatomic, copy) void (^cameraPressed)(TGAttachmentCameraView *cameraView); -@property (nonatomic, copy) void (^sendPressed)(TGMediaAsset *currentItem, bool asFiles, TGMediaPickerGalleryCompletionMode mode); +@property (nonatomic, copy) void (^sendPressed)(TGMediaAsset *currentItem, bool asFiles, bool silentPosting, int32_t scheduleTime); @property (nonatomic, copy) void (^avatarCompletionBlock)(UIImage *image); @property (nonatomic, copy) void (^editorOpened)(void); diff --git a/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.m b/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.m index 8f78600fb8..17318f32fd 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.m +++ b/submodules/LegacyComponents/LegacyComponents/TGAttachmentCarouselItemView.m @@ -264,7 +264,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; { if (strongSelf->_selectionContext.allowGrouping) [[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"]; - strongSelf.sendPressed(nil, false, false); + strongSelf.sendPressed(nil, false, false, 0); } }]; [_sendMediaItemView setHidden:true animated:false]; @@ -276,7 +276,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; { __strong TGAttachmentCarouselItemView *strongSelf = weakSelf; if (strongSelf != nil && strongSelf.sendPressed != nil) - strongSelf.sendPressed(nil, true, false); + strongSelf.sendPressed(nil, true, false, 0); }]; _sendFileItemView.requiresDivider = false; [_sendFileItemView setHidden:true animated:false]; @@ -774,14 +774,14 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; strongSelf->_galleryMixin = nil; }; - mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, TGMediaPickerGalleryCompletionMode mode) + mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, bool silentPosting, int32_t scheduleTime) { __strong TGAttachmentCarouselItemView *strongSelf = weakSelf; if (strongSelf != nil && strongSelf.sendPressed != nil) { if (strongSelf->_selectionContext.allowGrouping) [[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"]; - strongSelf.sendPressed(item.asset, strongSelf.asFile, mode); + strongSelf.sendPressed(item.asset, strongSelf.asFile, silentPosting, scheduleTime); } }; @@ -802,7 +802,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; thumbnailImage = cell.imageView.image; TGMediaPickerModernGalleryMixin *mixin = [[TGMediaPickerModernGalleryMixin alloc] initWithContext:_context item:asset fetchResult:_fetchResult parentController:self.parentController thumbnailImage:thumbnailImage selectionContext:_selectionContext editingContext:_editingContext suggestionContext:self.suggestionContext hasCaptions:(_allowCaptions && !_forProfilePhoto) allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:self.onlyCrop inhibitDocumentCaptions:_inhibitDocumentCaptions inhibitMute:self.inhibitMute asFile:self.asFile itemsLimit:TGAttachmentDisplayedAssetLimit recipientName:self.recipientName hasSilentPosting:self.hasSilentPosting hasSchedule:self.hasSchedule]; - + mixin.presentScheduleController = self.presentScheduleController; __weak TGAttachmentCarouselItemView *weakSelf = self; mixin.thumbnailSignalForItem = ^SSignal *(id item) { diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.h b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.h index 2264bdf0f9..d60bdba4ba 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.h +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.h @@ -59,6 +59,7 @@ typedef enum @property (nonatomic, assign) bool inhibitMute; @property (nonatomic, assign) bool hasSilentPosting; @property (nonatomic, assign) bool hasSchedule; +@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t)); @property (nonatomic, assign) bool liveVideoUploadEnabled; @property (nonatomic, assign) bool shouldShowFileTipIfNeeded; @@ -67,7 +68,7 @@ typedef enum @property (nonatomic, copy) NSDictionary *(^descriptionGenerator)(id, NSString *, NSArray *, NSString *); @property (nonatomic, copy) void (^avatarCompletionBlock)(UIImage *image); -@property (nonatomic, copy) void (^completionBlock)(NSArray *signals, TGMediaPickerGalleryCompletionMode mode); +@property (nonatomic, copy) void (^completionBlock)(NSArray *signals, bool silentPosting, int32_t scheduleTime); @property (nonatomic, copy) void (^singleCompletionBlock)(id item, TGMediaEditingContext *editingContext); @property (nonatomic, copy) void (^dismissalBlock)(void); @property (nonatomic, copy) void (^selectionBlock)(TGMediaAsset *asset, UIImage *); @@ -85,7 +86,7 @@ typedef enum - (NSArray *)resultSignalsWithCurrentItem:(TGMediaAsset *)currentItem descriptionGenerator:(id (^)(id, NSString *, NSArray *, NSString *))descriptionGenerator; - (void)completeWithAvatarImage:(UIImage *)image; -- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem mode:(TGMediaPickerGalleryCompletionMode)mode; +- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem silentPosting:(bool)silentPosting scheduleTime:(int32_t)scheduleTime; - (void)dismiss; diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.m b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.m index 36417330bc..a542031965 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.m +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsController.m @@ -122,6 +122,7 @@ pickerController.onlyCrop = strongController.onlyCrop; pickerController.hasSilentPosting = strongController.hasSilentPosting; pickerController.hasSchedule = strongController.hasSchedule; + pickerController.presentScheduleController = strongController.presentScheduleController; [strongController pushViewController:pickerController animated:true]; }; [groupsController loadViewIfNeeded]; @@ -213,6 +214,11 @@ self.pickerController.hasSchedule = hasSchedule; } +- (void)setPresentScheduleController:(void (^)(void (^)(int32_t)))presentScheduleController { + _presentScheduleController = [presentScheduleController copy]; + self.pickerController.presentScheduleController = presentScheduleController; +} + - (void)setOnlyCrop:(bool)onlyCrop { _onlyCrop = onlyCrop; @@ -457,7 +463,7 @@ { __strong TGMediaAssetsController *strongSelf = weakSelf; if (strongSelf != nil) - [strongSelf completeWithCurrentItem:nil mode:TGMediaPickerGalleryCompletionModeGeneric]; + [strongSelf completeWithCurrentItem:nil silentPosting:false scheduleTime:0]; }; } @@ -538,12 +544,12 @@ self.avatarCompletionBlock(image); } -- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem mode:(TGMediaPickerGalleryCompletionMode)mode +- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem silentPosting:(bool)silentPosting scheduleTime:(int32_t)scheduleTime { if (self.completionBlock != nil) { NSArray *signals = [self resultSignalsWithCurrentItem:currentItem descriptionGenerator:self.descriptionGenerator]; - self.completionBlock(signals, mode); + self.completionBlock(signals, silentPosting, scheduleTime); } else if (self.singleCompletionBlock != nil) { diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsPickerController.m b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsPickerController.m index acad64e531..2eaa4c328c 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsPickerController.m +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsPickerController.m @@ -311,13 +311,13 @@ strongSelf->_galleryMixin = nil; }; - mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, TGMediaPickerGalleryCompletionMode mode) + mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, bool silentPosting, int32_t scheduleTime) { __strong TGMediaAssetsPickerController *strongSelf = weakSelf; if (strongSelf == nil) return; - [(TGMediaAssetsController *)strongSelf.navigationController completeWithCurrentItem:item.asset mode:mode]; + [(TGMediaAssetsController *)strongSelf.navigationController completeWithCurrentItem:item.asset silentPosting:silentPosting scheduleTime:scheduleTime]; }; } @@ -341,7 +341,7 @@ bool asFile = (_intent == TGMediaAssetsControllerSendFileIntent); TGMediaPickerModernGalleryMixin *mixin = [self _galleryMixinForContext:_context item:asset thumbnailImage:thumbnailImage selectionContext:self.selectionContext editingContext:self.editingContext suggestionContext:self.suggestionContext hasCaptions:self.captionsEnabled allowCaptionEntities:self.allowCaptionEntities inhibitDocumentCaptions:self.inhibitDocumentCaptions asFile:asFile]; - + mixin.presentScheduleController = self.presentScheduleController; __weak TGMediaAssetsPickerController *weakSelf = self; mixin.thumbnailSignalForItem = ^SSignal *(id item) { diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsUtils.h b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsUtils.h index 993fedd8a3..b4d5ed7c8e 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsUtils.h +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaAssetsUtils.h @@ -4,12 +4,6 @@ @class TGMediaAsset; @class TGMediaSelectionContext; -typedef NS_ENUM(NSUInteger, TGMediaPickerGalleryCompletionMode) { - TGMediaPickerGalleryCompletionModeGeneric, - TGMediaPickerGalleryCompletionModeSilent, - TGMediaPickerGalleryCompletionModeSchedule -}; - @interface TGMediaAssetsPreheatMixin : NSObject @property (nonatomic, copy) NSInteger (^assetCount)(void); diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerController.h b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerController.h index cf734aa4f2..b78e565560 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerController.h +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerController.h @@ -29,6 +29,7 @@ @property (nonatomic, strong) NSString *recipientName; @property (nonatomic, assign) bool hasSilentPosting; @property (nonatomic, assign) bool hasSchedule; +@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t)); @property (nonatomic, strong) TGMediaAssetsPallete *pallete; diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.h b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.h index 8e4401c5c8..d3deb03362 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.h +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.h @@ -22,11 +22,13 @@ @property (nonatomic, copy) void (^didTransitionOut)(); @property (nonatomic, copy) UIView *(^referenceViewForItem)(TGMediaPickerGalleryItem *); -@property (nonatomic, copy) void (^completeWithItem)(TGMediaPickerGalleryItem *item, TGMediaPickerGalleryCompletionMode mode); +@property (nonatomic, copy) void (^completeWithItem)(TGMediaPickerGalleryItem *item, bool silentPosting, int32_t scheduleTime); @property (nonatomic, copy) void (^editorOpened)(void); @property (nonatomic, copy) void (^editorClosed)(void); +@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t)); + - (instancetype)initWithContext:(id)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule; - (instancetype)initWithContext:(id)context item:(id)item momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule; diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.m b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.m index 29960e53d3..1ce2b7b2e8 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.m +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerModernGalleryMixin.m @@ -136,7 +136,7 @@ strongSelf->_galleryModel.dismiss(true, false); if (strongSelf.completeWithItem != nil) - strongSelf.completeWithItem(item, false); + strongSelf.completeWithItem(item, false, 0); }; model.interfaceView.doneLongPressed = ^(TGMediaPickerGalleryItem *item) { @@ -156,27 +156,33 @@ strongSelf->_galleryModel.dismiss(true, false); if (strongSelf.completeWithItem != nil) - strongSelf.completeWithItem(item, TGMediaPickerGalleryCompletionModeGeneric); + strongSelf.completeWithItem(item, false, 0); }; controller.sendSilently = ^{ __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; if (strongSelf == nil) return; - strongSelf->_galleryModel.dismiss(true, TGMediaPickerGalleryCompletionModeSilent); - - if (strongSelf.completeWithItem != nil) - strongSelf.completeWithItem(item, true); - }; - controller.schedule = ^{ - __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; - if (strongSelf == nil) - return; - strongSelf->_galleryModel.dismiss(true, false); if (strongSelf.completeWithItem != nil) - strongSelf.completeWithItem(item, TGMediaPickerGalleryCompletionModeSchedule); + strongSelf.completeWithItem(item, true, 0); + }; + controller.schedule = ^{ + __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + strongSelf.presentScheduleController(^(int32_t time) { + __strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + strongSelf->_galleryModel.dismiss(true, false); + + if (strongSelf.completeWithItem != nil) + strongSelf.completeWithItem(item, false, time); + }); }; TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:[strongSelf->_context makeOverlayWindowManager] parentController:strongSelf->_parentController contentController:controller]; diff --git a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.m b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.m index d27edeb0c4..9d4365a90e 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.m +++ b/submodules/LegacyComponents/LegacyComponents/TGMediaPickerSendActionSheetController.m @@ -115,12 +115,21 @@ _containerView.layer.cornerRadius = 12.0; [self.view addSubview:_containerView]; + __weak TGMediaPickerSendActionSheetController *weakSelf = self; if (_canSendSilently) { _sendSilentlyButton = [[TGMediaPickerSendActionSheetItemView alloc] initWithTitle:TGLocalized(@"Conversation.SendMessage.SendSilently") icon:TGComponentsImageNamed(@"MediaMute")]; + _sendSilentlyButton.pressed = ^{ + __strong TGMediaPickerSendActionSheetController *strongSelf = weakSelf; + [strongSelf sendSilentlyPressed]; + }; [_containerView addSubview:_sendSilentlyButton]; } _scheduleButton = [[TGMediaPickerSendActionSheetItemView alloc] initWithTitle:TGLocalized(@"Conversation.SendMessage.ScheduleMessage") icon:TGComponentsImageNamed(@"MediaSchedule")]; + _scheduleButton.pressed = ^{ + __strong TGMediaPickerSendActionSheetController *strongSelf = weakSelf; + [strongSelf schedulePressed]; + }; [_containerView addSubview:_scheduleButton]; TGMediaAssetsPallete *pallete = nil; diff --git a/submodules/LegacyComponents/LegacyComponents/TGPassportAttachMenu.m b/submodules/LegacyComponents/LegacyComponents/TGPassportAttachMenu.m index 606e7184c4..12d35852dc 100644 --- a/submodules/LegacyComponents/LegacyComponents/TGPassportAttachMenu.m +++ b/submodules/LegacyComponents/LegacyComponents/TGPassportAttachMenu.m @@ -67,7 +67,7 @@ [TGPassportAttachMenu _displayCameraWithView:cameraView menuController:strongController parentController:strongParentController context:context intent:intent uploadAction:uploadAction]; }; - carouselItem.sendPressed = ^(TGMediaAsset *currentItem, __unused bool asFiles, __unused TGMediaPickerGalleryCompletionMode mode) + carouselItem.sendPressed = ^(TGMediaAsset *currentItem, __unused bool asFiles, __unused bool silentPosting, __unused int32_t scheduleTime) { __strong TGMenuSheetController *strongController = weakController; if (strongController == nil) diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift index fce72c2ae6..f2a833798e 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift @@ -20,7 +20,7 @@ public struct LegacyAttachmentMenuMediaEditing: OptionSet { public static let imageOrVideo = LegacyAttachmentMenuMediaEditing(rawValue: 1 << 0) } -public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, theme: PresentationTheme, strings: PresentationStrings, parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: String, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void) -> TGMenuSheetController { +public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, theme: PresentationTheme, strings: PresentationStrings, parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: String, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, presentScheduleController: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void) -> TGMenuSheetController { let isSecretChat = peer.id.namespace == Namespaces.Peer.SecretChat let controller = TGMenuSheetController(context: parentController.context, dark: false)! @@ -77,7 +77,12 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaO carouselItem.hasSilentPosting = !isSecretChat } carouselItem.hasSchedule = !isSecretChat - carouselItem.sendPressed = { [weak controller, weak carouselItem] currentItem, asFiles, mode in + carouselItem.presentScheduleController = { done in + presentScheduleController { time in + done?(time) + } + } + carouselItem.sendPressed = { [weak controller, weak carouselItem] currentItem, asFiles, silentPosting, scheduleTime in if let controller = controller, let carouselItem = carouselItem { let intent: TGMediaAssetsControllerIntent = asFiles ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent let signals = TGMediaAssetsController.resultSignals(for: carouselItem.selectionContext, editingContext: carouselItem.editingContext, intent: intent, currentItem: currentItem, storeAssets: true, useMediaCache: false, descriptionGenerator: legacyAssetPickerItemGenerator(), saveEditedPhotos: saveEditedPhotos) @@ -85,7 +90,7 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaO presentCantSendMultipleFiles() } else { controller.dismiss(animated: true) - sendMessagesWithSignals(signals, false) + sendMessagesWithSignals(signals, silentPosting, scheduleTime) } } }; diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift index 1bef9419c7..a1ed9bffc6 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift @@ -17,7 +17,7 @@ public func guessMimeTypeByFileExtension(_ ext: String) -> String { return TGMimeTypeMap.mimeType(forExtension: ext) ?? "application/binary" } -public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context: AccountContext, peer: Peer, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false, initialCaption: String, presentWebSearch: (() -> Void)?, presentSelectionLimitExceeded: @escaping () -> Void) { +public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context: AccountContext, peer: Peer, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false, initialCaption: String, presentWebSearch: (() -> Void)?, presentSelectionLimitExceeded: @escaping () -> Void, presentScheduleController: @escaping (@escaping (Int32) -> Void) -> Void) { let isSecretChat = peer.id.namespace == Namespaces.Peer.SecretChat controller.captionsEnabled = captionsEnabled @@ -30,6 +30,11 @@ public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, co controller.hasSilentPosting = !isSecretChat } controller.hasSchedule = !isSecretChat + controller.presentScheduleController = { done in + presentScheduleController { time in + done?(time) + } + } controller.dismissalBlock = { } controller.selectionLimitExceeded = { diff --git a/submodules/PassportUI/Sources/SecureIdAuthFormFieldNode.swift b/submodules/PassportUI/Sources/SecureIdAuthFormFieldNode.swift index 0290391218..438f8eb2c9 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthFormFieldNode.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthFormFieldNode.swift @@ -1005,7 +1005,7 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode { transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -(hasPrevious ? UIScreenPixel : 0.0)), size: CGSize(width: width, height: height + (hasPrevious ? UIScreenPixel : 0.0)))) if let image = self.disclosureNode.image { - self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 15.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size) + self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 7.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size) } if let image = self.checkNode.image { diff --git a/submodules/PassportUI/Sources/SecureIdAuthListFieldNode.swift b/submodules/PassportUI/Sources/SecureIdAuthListFieldNode.swift index 5e7005e5c9..fff18c5755 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthListFieldNode.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthListFieldNode.swift @@ -240,7 +240,7 @@ final class SecureIdAuthListFieldNode: ASDisplayNode { transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -(hasPrevious ? UIScreenPixel : 0.0)), size: CGSize(width: width, height: height + (hasPrevious ? UIScreenPixel : 0.0)))) if let image = self.disclosureNode.image { - self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 15.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size) + self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 7.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size) } return height diff --git a/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift b/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift index 0eec849879..e39bc62ac5 100644 --- a/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift +++ b/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift @@ -273,7 +273,7 @@ class ItemListSecretChatKeyItemNode: ListViewItemNode { } if let arrowImage = strongSelf.arrowNode.image { - strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 15.0 - arrowImage.size.width, y: 15.0), size: arrowImage.size) + strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: 15.0), size: arrowImage.size) } switch item.disclosureStyle { diff --git a/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj b/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj index cc24ab68cd..dd27f05c9a 100644 --- a/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj +++ b/submodules/SettingsUI/SettingsUI_Xcode.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 09B4A9B823102B7A005C2E08 /* CreateThemeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B723102B7A005C2E08 /* CreateThemeController.swift */; }; D03E465223075D930049C28B /* SettingsUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E465023075D930049C28B /* SettingsUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03E466823075E660049C28B /* TabBarAccountSwitchControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E465C23075E630049C28B /* TabBarAccountSwitchControllerNode.swift */; }; D03E466923075E660049C28B /* LogoutOptionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E465D23075E630049C28B /* LogoutOptionsController.swift */; }; @@ -25,7 +26,6 @@ D03E467F23075EE90049C28B /* NotificationExceptionSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E467C23075EE90049C28B /* NotificationExceptionSettingsController.swift */; }; D03E468023075EE90049C28B /* NotificationExceptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E467D23075EE90049C28B /* NotificationExceptions.swift */; }; D03E468123075EE90049C28B /* NotificationExceptionControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E467E23075EE90049C28B /* NotificationExceptionControllerNode.swift */; }; - D03E468423075EF60049C28B /* LanguageSuggestionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468323075EF60049C28B /* LanguageSuggestionController.swift */; }; D03E468A23075F010049C28B /* SettingsSearchRecentQueries.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468523075EFF0049C28B /* SettingsSearchRecentQueries.swift */; }; D03E468B23075F010049C28B /* SettingsSearchItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468623075F000049C28B /* SettingsSearchItem.swift */; }; D03E468C23075F010049C28B /* SettingsSearchableItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468723075F000049C28B /* SettingsSearchableItems.swift */; }; @@ -191,6 +191,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 09B4A9B723102B7A005C2E08 /* CreateThemeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateThemeController.swift; sourceTree = ""; }; D03E464D23075D930049C28B /* SettingsUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SettingsUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D03E465023075D930049C28B /* SettingsUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsUI.h; sourceTree = ""; }; D03E465123075D930049C28B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -211,7 +212,6 @@ D03E467C23075EE90049C28B /* NotificationExceptionSettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExceptionSettingsController.swift; sourceTree = ""; }; D03E467D23075EE90049C28B /* NotificationExceptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExceptions.swift; sourceTree = ""; }; D03E467E23075EE90049C28B /* NotificationExceptionControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExceptionControllerNode.swift; sourceTree = ""; }; - D03E468323075EF60049C28B /* LanguageSuggestionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LanguageSuggestionController.swift; sourceTree = ""; }; D03E468523075EFF0049C28B /* SettingsSearchRecentQueries.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsSearchRecentQueries.swift; sourceTree = ""; }; D03E468623075F000049C28B /* SettingsSearchItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsSearchItem.swift; sourceTree = ""; }; D03E468723075F000049C28B /* SettingsSearchableItems.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsSearchableItems.swift; sourceTree = ""; }; @@ -484,7 +484,6 @@ isa = PBXGroup; children = ( D03E467423075E6C0049C28B /* Search */, - D03E467523075E920049C28B /* Language Suggestion */, D03E467623075EA90049C28B /* Notifications */, D03E468223075EEB0049C28B /* Privacy and Security */, D03E46B423075F610049C28B /* Data and Storage */, @@ -527,14 +526,6 @@ path = Search; sourceTree = ""; }; - D03E467523075E920049C28B /* Language Suggestion */ = { - isa = PBXGroup; - children = ( - D03E468323075EF60049C28B /* LanguageSuggestionController.swift */, - ); - path = "Language Suggestion"; - sourceTree = ""; - }; D03E467623075EA90049C28B /* Notifications */ = { isa = PBXGroup; children = ( @@ -658,6 +649,7 @@ D03E46E523075FD40049C28B /* WallpaperGalleryToolbarNode.swift */, D03E46EB23075FD60049C28B /* WallpaperPatternPanelNode.swift */, D03E46EE23075FD60049C28B /* WallpaperSearchRecentQueries.swift */, + 09B4A9B723102B7A005C2E08 /* CreateThemeController.swift */, ); path = Themes; sourceTree = ""; @@ -939,7 +931,7 @@ D03E468E23075F010049C28B /* SettingsSearchRecentItem.swift in Sources */, D03E467223075E660049C28B /* ChangePhoneNumberCodeController.swift in Sources */, D03E466823075E660049C28B /* TabBarAccountSwitchControllerNode.swift in Sources */, - D03E468423075EF60049C28B /* LanguageSuggestionController.swift in Sources */, + 09B4A9B823102B7A005C2E08 /* CreateThemeController.swift in Sources */, D03E471623075FE40049C28B /* WallpaperColorPanelNode.swift in Sources */, D03E470023075FE40049C28B /* ThemeColorsGridControllerItem.swift in Sources */, D03E468B23075F010049C28B /* SettingsSearchItem.swift in Sources */, diff --git a/submodules/SettingsUI/Sources/Language Suggestion/LanguageSuggestionController.swift b/submodules/SettingsUI/Sources/Language Suggestion/LanguageSuggestionController.swift deleted file mode 100644 index e77eb77318..0000000000 --- a/submodules/SettingsUI/Sources/Language Suggestion/LanguageSuggestionController.swift +++ /dev/null @@ -1,368 +0,0 @@ -import Foundation -import UIKit -import SwiftSignalKit -import AsyncDisplayKit -import Display -import TelegramCore -import TelegramPresentationData -import ActivityIndicator -import AccountContext - -struct LanguageSuggestionControllerStrings { - let ChooseLanguage: String - let Other: String - let English: String - - init(localization: SuggestedLocalizationInfo) { - var chooseLanguage = "Choose Your Language" - var other = "Other" - var english = "English" - - for entry in localization.extractedEntries { - switch entry { - case let .string(key, value): - switch key { - case "Localization.ChooseLanguage": - chooseLanguage = value - case "Localization.LanguageOther": - other = value - case "Localization.EnglishLanguageName": - english = value - default: - break - } - default: - break - } - } - - self.ChooseLanguage = chooseLanguage - self.Other = other - self.English = english - } - - init(bundle: Bundle?) { - var chooseLanguage = "Choose Your Language" - var other = "Other" - var english = "English" - - if let bundle = bundle { - for key in LanguageSuggestionControllerStrings.keys { - let value = bundle.localizedString(forKey: key, value: nil, table: nil) - if value != key { - switch key { - case "Localization.ChooseLanguage": - chooseLanguage = value - case "Localization.LanguageOther": - other = value - case "Localization.EnglishLanguageName": - english = value - default: - break - } - } - } - } - - self.ChooseLanguage = chooseLanguage - self.Other = other - self.English = english - } - - static let keys: [String] = ["Localization.ChooseLanguage", - "Localization.LanguageOther", - "Localization.EnglishLanguageName"] -} - -private enum LanguageSuggestionItemType { - case localization(String) - case disclosure - case action -} - -private struct LanguageSuggestionItem { - public let type: LanguageSuggestionItemType - public let title: String - public let subtitle: String? - public let action: () -> Void - - public init(type: LanguageSuggestionItemType, title: String, subtitle: String?, action: @escaping () -> Void) { - self.type = type - self.title = title - self.subtitle = subtitle - self.action = action - } -} - -private final class LanguageSuggestionItemNode: HighlightableButtonNode { - private let backgroundNode: ASDisplayNode - private let separatorNode: ASDisplayNode - private let subtitleNode: ASTextNode - private let iconNode: ASImageNode - - let item: LanguageSuggestionItem - - override var isSelected: Bool { - didSet { - if case .localization = self.item.type { - self.iconNode.isHidden = !self.isSelected - } - } - } - - init(theme: PresentationTheme, item: LanguageSuggestionItem) { - self.item = item - - self.backgroundNode = ASDisplayNode() - self.backgroundNode.isLayerBacked = true - self.backgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor - self.backgroundNode.alpha = 0.0 - - self.separatorNode = ASDisplayNode() - self.separatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor - - self.subtitleNode = ASTextNode() - - self.iconNode = ASImageNode() - - super.init() - - self.addSubnode(self.subtitleNode) - self.addSubnode(self.separatorNode) - self.addSubnode(self.iconNode) - - var color: UIColor = theme.actionSheet.primaryTextColor - var alignment: ASHorizontalAlignment = .left - var inset: CGFloat = 19.0 - var icon: UIImage? - switch item.type { - case .action: - alignment = .middle - color = theme.actionSheet.controlAccentColor - inset = 0.0 - case .disclosure: - icon = PresentationResourcesItemList.disclosureArrowImage(theme) - case .localization: - icon = PresentationResourcesItemList.checkIconImage(theme) - } - - self.iconNode.image = icon - self.contentHorizontalAlignment = alignment - self.setTitle(item.title, with: Font.regular(17.0), with: color, for: []) - - var titleVerticalOffset: CGFloat = 0.0 - if let subtitle = item.subtitle { - self.subtitleNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(14.0), textColor: theme.actionSheet.secondaryTextColor) - titleVerticalOffset = 20.0 - } - self.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: inset, bottom: titleVerticalOffset, right: 0.0) - - self.highligthedChanged = { [weak self] value in - if let strongSelf = self { - if value { - if strongSelf.backgroundNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) - } - strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity") - strongSelf.backgroundNode.alpha = 1.0 - } else if !strongSelf.backgroundNode.alpha.isZero { - strongSelf.backgroundNode.alpha = 0.0 - strongSelf.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25) - } - } - } - } - - override func didLoad() { - super.didLoad() - - self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) - } - - @objc func pressed() { - self.item.action() - } - - public func updateLayout(_ constrainedSize: CGSize) -> CGSize { - let bounds = CGRect(origin: CGPoint(), size: CGSize(width: constrainedSize.width, height: self.item.subtitle != nil ? 58.0 : 44.0)) - self.backgroundNode.frame = bounds - - let subtitleSize = self.subtitleNode.measure(bounds.size) - self.subtitleNode.frame = CGRect(origin: CGPoint(x: 19.0, y: 31.0), size: subtitleSize) - self.separatorNode.frame = CGRect(x: 0.0, y: bounds.height - UIScreenPixel, width: bounds.width, height: UIScreenPixel) - if let icon = self.iconNode.image { - self.iconNode.frame = CGRect(origin: CGPoint(x: bounds.width - icon.size.width - 19.0, y: floorToScreenPixels((bounds.height - icon.size.height) / 2.0)), size: icon.size) - } - return bounds.size - } -} - -private final class LanguageSuggestionAlertContentNode: AlertContentNode { - private var validLayout: CGSize? - - private let titleNode: ASTextNode - private let subtitleNode: ASTextNode - private let titleSeparatorNode: ASDisplayNode - private let activityIndicator: ActivityIndicator - - private var nodes: [LanguageSuggestionItemNode] - - private let disposable = MetaDisposable() - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(theme: PresentationTheme, strings: LanguageSuggestionControllerStrings, englishStrings: LanguageSuggestionControllerStrings, suggestedLocalization: LocalizationInfo, openSelection: @escaping () -> Void, applyLocalization: @escaping (String, () -> Void) -> Void, dismiss: @escaping () -> Void) { - let selectedLocalization = ValuePromise(suggestedLocalization.languageCode, ignoreRepeated: true) - - self.titleNode = ASTextNode() - self.titleNode.attributedText = NSAttributedString(string: strings.ChooseLanguage, font: Font.bold(17.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center) - self.titleNode.maximumNumberOfLines = 2 - - self.subtitleNode = ASTextNode() - self.subtitleNode.attributedText = NSAttributedString(string: englishStrings.ChooseLanguage, font: Font.regular(14.0), textColor: theme.actionSheet.secondaryTextColor, paragraphAlignment: .center) - self.subtitleNode.maximumNumberOfLines = 2 - - self.titleSeparatorNode = ASDisplayNode() - self.titleSeparatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor - - self.activityIndicator = ActivityIndicator(type: .custom(theme.actionSheet.controlAccentColor, 22.0, 1.0, false)) - self.activityIndicator.isHidden = true - - var items: [LanguageSuggestionItem] = [] - items.append(LanguageSuggestionItem(type: .localization(suggestedLocalization.languageCode), title: suggestedLocalization.localizedTitle, subtitle: suggestedLocalization.title, action: { - selectedLocalization.set(suggestedLocalization.languageCode) - })) - items.append(LanguageSuggestionItem(type: .localization("en"), title: strings.English, subtitle: englishStrings.English, action: { - selectedLocalization.set("en") - })) - items.append(LanguageSuggestionItem(type: .disclosure, title: strings.Other, subtitle: englishStrings.Other != strings.Other ? englishStrings.Other : nil, action: { - openSelection() - })) - - var applyImpl: (() -> Void)? - items.append(LanguageSuggestionItem(type: .action, title: "OK", subtitle: nil, action: { - applyImpl?() - })) - - self.nodes = items.map { LanguageSuggestionItemNode(theme: theme, item: $0) } - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.subtitleNode) - self.addSubnode(self.titleSeparatorNode) - self.addSubnode(self.activityIndicator) - for node in self.nodes { - self.addSubnode(node) - } - - self.disposable.set(selectedLocalization.get().start(next: { [weak self] selectedCode in - if let strongSelf = self { - for node in strongSelf.nodes { - if case let .localization(code) = node.item.type { - node.isSelected = code == selectedCode - } - } - } - })) - - applyImpl = { [weak self] in - if let strongSelf = self { - strongSelf.isUserInteractionEnabled = false - - _ = (selectedLocalization.get() - |> take(1)).start(next: { selectedCode in - applyLocalization(selectedCode, { [weak self] in - if let strongSelf = self { - strongSelf.activityIndicator.isHidden = false - if let lastNode = strongSelf.nodes.last { - lastNode.isHidden = true - } - } - }) - }) - } - } - } - - deinit { - self.disposable.dispose() - } - - 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.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 + 3.0 - - let subtitleSize = self.subtitleNode.measure(size) - transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - subtitleSize.width) / 2.0), y: origin.y), size: subtitleSize)) - origin.y += subtitleSize.height + 17.0 - transition.updateFrame(node: self.titleSeparatorNode, frame: CGRect(x: 0.0, y: origin.y - UIScreenPixel, width: size.width, height: UIScreenPixel)) - - var lastNodeSize: CGSize? - for node in self.nodes { - let size = node.updateLayout(size) - transition.updateFrame(node: node, frame: CGRect(origin: origin, size: size)) - origin.y += size.height - lastNodeSize = size - } - - if let lastSize = lastNodeSize { - let indicatorSize = self.activityIndicator.measure(CGSize(width: 100.0, height: 100.0)) - transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - indicatorSize.width) / 2.0), y: origin.y - lastSize.height + floorToScreenPixels((lastSize.height - indicatorSize.height) / 2.0)), size: indicatorSize)) - } - - return CGSize(width: size.width, height: origin.y - UIScreenPixel) - } -} - -func languageSuggestionController(context: AccountContext, suggestedLocalization: SuggestedLocalizationInfo, currentLanguageCode: String, openSelection: @escaping () -> Void) -> AlertController? { - guard let localization = suggestedLocalization.availableLocalizations.filter({ $0.languageCode == suggestedLocalization.languageCode }).first else { - return nil - } - - let theme = context.sharedContext.currentPresentationData.with { $0 }.theme - let strings = LanguageSuggestionControllerStrings(localization: suggestedLocalization) - guard let mainPath = Bundle.main.path(forResource: "en", ofType: "lproj") else { - return nil - } - let englishStrings = LanguageSuggestionControllerStrings(bundle: Bundle(path: mainPath)) - - let disposable = MetaDisposable() - - var dismissImpl: ((Bool) -> Void)? - let contentNode = LanguageSuggestionAlertContentNode(theme: theme, strings: strings, englishStrings: englishStrings, suggestedLocalization: localization, openSelection: { - dismissImpl?(true) - openSelection() - }, applyLocalization: { languageCode, startActivity in - if languageCode == currentLanguageCode { - dismissImpl?(true) - } else { - startActivity() - disposable.set((downloadAndApplyLocalization(accountManager: context.sharedContext.accountManager, postbox: context.account.postbox, network: context.account.network, languageCode: languageCode) - |> deliverOnMainQueue).start(completed: { - dismissImpl?(true) - })) - } - }, dismiss: { - dismissImpl?(true) - }) - let controller = AlertController(theme: AlertControllerTheme(presentationTheme: theme), contentNode: contentNode) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller -} diff --git a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift index a9f4d0df0f..e24985854f 100644 --- a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift +++ b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift @@ -20,7 +20,7 @@ import ItemListPeerActionItem import TelegramStringFormatting private final class NotificationExceptionState : Equatable { - let mode:NotificationExceptionMode + let mode: NotificationExceptionMode let isSearchMode: Bool let revealedPeerId: PeerId? let editing: Bool @@ -93,7 +93,15 @@ public struct NotificationExceptionWrapper : Equatable { } } + + public enum NotificationExceptionMode : Equatable { + fileprivate enum Mode { + case users + case groups + case channels + } + public static func == (lhs: NotificationExceptionMode, rhs: NotificationExceptionMode) -> Bool { switch lhs { case let .users(lhsValue): @@ -117,6 +125,17 @@ public enum NotificationExceptionMode : Equatable { } } + fileprivate var mode: Mode { + switch self { + case .users: + return .users + case .groups: + return .groups + case .channels: + return .channels + } + } + var isEmpty: Bool { switch self { case let .users(value), let .groups(value), let .channels(value): @@ -271,7 +290,7 @@ private func notificationsExceptionEntries(presentationData: PresentationData, s var entries: [NotificationExceptionEntry] = [] if !state.isSearchMode { - entries.append(.addException(presentationData.theme, presentationData.strings, state.editing)) + entries.append(.addException(presentationData.theme, presentationData.strings, state.mode.mode, state.editing)) } var existingPeerIds = Set() @@ -486,7 +505,7 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { case search(PresentationTheme, PresentationStrings) case peer(index: Int, peer: Peer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, description: String, notificationSettings: TelegramPeerNotificationSettings, revealed: Bool, editing: Bool, isSearching: Bool) case addPeer(index: Int, peer: Peer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) - case addException(PresentationTheme, PresentationStrings, Bool) + case addException(PresentationTheme, PresentationStrings, NotificationExceptionMode.Mode, Bool) case removeAll(PresentationTheme, PresentationStrings) func item(_ arguments: NotificationExceptionArguments) -> ListViewItem { @@ -495,8 +514,17 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { return NotificationSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { arguments.activateSearch() }) - case let .addException(theme, strings, editing): - return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.addExceptionIcon(theme), title: strings.Notification_Exceptions_AddException, alwaysPlain: true, sectionId: self.section, editing: editing, action: { + case let .addException(theme, strings, mode, editing): + let icon: UIImage? + switch mode { + case .users: + icon = PresentationResourcesItemList.addPersonIcon(theme) + case .groups: + icon = PresentationResourcesItemList.createGroupIcon(theme) + case .channels: + icon = PresentationResourcesItemList.addChannelIcon(theme) + } + return ItemListPeerActionItem(theme: theme, icon: icon, title: strings.Notification_Exceptions_AddException, alwaysPlain: true, sectionId: self.section, editing: editing, action: { arguments.selectPeer() }) case let .peer(_, peer, theme, strings, dateTimeFormat, nameDisplayOrder, value, _, revealed, editing, isSearching): @@ -543,10 +571,10 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { default: return false } - case let .addException(lhsTheme, lhsStrings, lhsEditing): + case let .addException(lhsTheme, lhsStrings, lhsMode, lhsEditing): switch rhs { - case let .addException(rhsTheme, rhsStrings, rhsEditing): - return lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsEditing == rhsEditing + case let .addException(rhsTheme, rhsStrings, rhsMode, rhsEditing): + return lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsMode == rhsMode && lhsEditing == rhsEditing default: return false } diff --git a/submodules/SettingsUI/Sources/Themes/CreateThemeController.swift b/submodules/SettingsUI/Sources/Themes/CreateThemeController.swift new file mode 100644 index 0000000000..06c9b2d5c8 --- /dev/null +++ b/submodules/SettingsUI/Sources/Themes/CreateThemeController.swift @@ -0,0 +1,189 @@ +import Foundation +import UIKit +import Display +import SwiftSignalKit +import Postbox +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import ItemListUI +import AlertUI +import AccountContext + +private final class CreateThemeControllerArguments { + let context: AccountContext + let updateState: ((CreateThemeControllerState) -> CreateThemeControllerState) -> Void + + init(context: AccountContext, updateState: @escaping ((CreateThemeControllerState) -> CreateThemeControllerState) -> Void) { + self.context = context + self.updateState = updateState + } +} + +private enum CreateThemeControllerSection: Int32 { + case chatPreview + case info +} + +private enum CreateThemeControllerEntry: ItemListNodeEntry { + case chatPreviewHeader(PresentationTheme, String) + case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder) + case title(PresentationTheme, PresentationStrings, String, String) + case slug(PresentationTheme, PresentationStrings, String, String, Bool) + + var section: ItemListSectionId { + switch self { + case .chatPreviewHeader, .chatPreview: + return CreateThemeControllerSection.chatPreview.rawValue + case .title, .slug: + return CreateThemeControllerSection.info.rawValue + } + } + + var stableId: Int32 { + switch self { + case .chatPreviewHeader: + return 0 + case .chatPreview: + return 1 + case .title: + return 2 + case .slug: + return 3 + } + } + + static func ==(lhs: CreateThemeControllerEntry, rhs: CreateThemeControllerEntry) -> Bool { + switch lhs { + case let .chatPreviewHeader(lhsTheme, lhsText): + if case let .chatPreviewHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder): + if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder { + return true + } else { + return false + } + case let .title(lhsTheme, lhsStrings, lhsTitle, lhsValue): + if case let .title(rhsTheme, rhsStrings, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsTitle == rhsTitle, lhsValue == rhsValue { + return true + } else { + return false + } + case let .slug(lhsTheme, lhsStrings, lhsTitle, lhsValue, lhsEnabled): + if case let .slug(rhsTheme, rhsStrings, rhsTitle, rhsValue, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsTitle == rhsTitle, lhsValue == rhsValue, lhsEnabled == rhsEnabled { + return true + } else { + return false + } + } + } + + static func <(lhs: CreateThemeControllerEntry, rhs: CreateThemeControllerEntry) -> Bool { + return lhs.stableId < rhs.stableId + } + + func item(_ arguments: CreateThemeControllerArguments) -> ListViewItem { + switch self { + case let .chatPreviewHeader(theme, text): + return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) + case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder): + return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder) + case let .title(theme, strings, title, text): + return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(), text: text, placeholder: title, sectionId: self.section, textUpdated: { value in + arguments.updateState { current in + var state = current + state.title = value + return state + } + }, action: {}) + case let .slug(theme, strings, title, text, enabled): + return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: "t.me/addtheme/", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "Short Name", type: .username, enabled: enabled, sectionId: self.section, textUpdated: { value in + arguments.updateState { current in + var state = current + state.slug = value + return state + } + }, action: {}) + } + } +} + +private enum CreateThemeControllerMode { + case create + case update +} + +private struct CreateThemeControllerState: Equatable { + var mode: CreateThemeControllerMode + var title: String + var slug: String + + var isComplete: Bool { + if self.title.isEmpty || self.slug.isEmpty { + return false + } + return true + } +} + +private func createThemeControllerEntries(presentationData: PresentationData, theme: PresentationTheme, state: CreateThemeControllerState) -> [CreateThemeControllerEntry] { + var entries: [CreateThemeControllerEntry] = [] + + entries.append(.chatPreviewHeader(presentationData.theme, "Preview".uppercased())) + entries.append(.chatPreview(presentationData.theme, theme, theme.chat.defaultWallpaper, presentationData.fontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder)) + + entries.append(.title(presentationData.theme, presentationData.strings, "Title", state.title)) + entries.append(.slug(presentationData.theme, presentationData.strings, "Slug", state.slug, true)) + + return entries +} + +public func createThemeController(context: AccountContext, theme: PresentationTheme, resource: MediaResource) -> ViewController { + let initialState = CreateThemeControllerState(mode: .create, title: "", slug: "") + let statePromise = ValuePromise(initialState, ignoreRepeated: true) + let stateValue = Atomic(value: initialState) + let updateState: ((CreateThemeControllerState) -> CreateThemeControllerState) -> Void = { f in + statePromise.set(stateValue.modify { f($0) }) + } + + var pushControllerImpl: ((ViewController) -> Void)? + var presentControllerImpl: ((ViewController, Any?) -> Void)? + var dismissImpl: (() -> Void)? + + let arguments = CreateThemeControllerArguments(context: context, updateState: { f in + updateState(f) + }) + + let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get()) + |> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, CreateThemeControllerEntry.ItemGenerationArguments)) in + let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { + dismissImpl?() + }) + let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: state.isComplete, action: { + let _ = (createTheme(account: context.account, resource: resource, title: state.title, slug: state.slug) |> deliverOnMainQueue).start(completed: { + dismissImpl?() + }) + }) + + let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Publish Theme"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) + let listState = ItemListNodeState(entries: createThemeControllerEntries(presentationData: presentationData, theme: theme, state: state), style: .blocks, emptyStateItem: nil, animateChanges: false) + + return (controllerState, (listState, arguments)) + } + + let controller = ItemListController(context: context, state: signal) + pushControllerImpl = { [weak controller] c in + (controller?.navigationController as? NavigationController)?.pushViewController(c) + } + presentControllerImpl = { [weak controller] c, a in + controller?.present(c, in: .window(.root), with: a) + } + dismissImpl = { [weak controller] in + let _ = controller?.dismiss() + } + return controller +} diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift index 41199e1961..99b8ec862f 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift @@ -11,10 +11,15 @@ import AccountContext import ShareController import CounterContollerTitleView +public enum ThemePreviewSource { + case theme(TelegramTheme) + case media(AnyMediaReference) +} + public final class ThemePreviewController: ViewController { private let context: AccountContext private let previewTheme: PresentationTheme - private let media: AnyMediaReference + private let source: ThemePreviewSource private var controllerNode: ThemePreviewControllerNode { return self.displayNode as! ThemePreviewControllerNode @@ -25,21 +30,28 @@ public final class ThemePreviewController: ViewController { private var presentationData: PresentationData private var presentationDataDisposable: Disposable? - public init(context: AccountContext, previewTheme: PresentationTheme, media: AnyMediaReference) { + public init(context: AccountContext, previewTheme: PresentationTheme, source: ThemePreviewSource) { self.context = context self.previewTheme = previewTheme - self.media = media + self.source = source self.presentationData = context.sharedContext.currentPresentationData.with { $0 } super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.previewTheme, presentationStrings: self.presentationData.strings)) + let themeName: String + if case let .theme(theme) = source { + themeName = theme.title + } else { + themeName = previewTheme.name.string + } + if let author = previewTheme.author { let titleView = CounterContollerTitleView(theme: self.previewTheme) - titleView.title = CounterContollerTitle(title: self.previewTheme.name.string, counter: author) + titleView.title = CounterContollerTitle(title: themeName, counter: author) self.navigationItem.titleView = titleView } else { - self.title = previewTheme.name.string + self.title = themeName } self.statusBar.statusBarStyle = self.previewTheme.rootController.statusBarStyle.style self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) @@ -83,6 +95,13 @@ public final class ThemePreviewController: ViewController { } }, apply: { [weak self] in if let strongSelf = self { + let theme: PresentationThemeReference + if case let .theme(info) = strongSelf.source { + theme = .cloud(info) + } else { + theme = .builtin(.day) + } + let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in let current: PresentationThemeSettings @@ -92,7 +111,7 @@ public final class ThemePreviewController: ViewController { current = PresentationThemeSettings.defaultSettings } - return PresentationThemeSettings(chatWallpaper: .color(0xffffff), theme: .builtin(.day), themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) + return PresentationThemeSettings(chatWallpaper: .color(0xffffff), theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations) }) }).start(completed: { [weak self] in if let strongSelf = self { @@ -122,7 +141,22 @@ public final class ThemePreviewController: ViewController { } @objc private func actionPressed() { - let controller = ShareController(context: self.context, subject: .media(self.media)) + let subject: ShareControllerSubject + let preferredAction: ShareControllerPreferredAction + switch self.source { + case let .theme(theme): + subject = .url("https://t.me/addtheme/\(theme.slug)") + preferredAction = .default + case let .media(media): + subject = .media(media) + preferredAction = .custom(action: ShareControllerAction(title: "Publish", action: { [weak self] in + if let strongSelf = self, let file = media.media as? TelegramMediaFile { + let controller = createThemeController(context: strongSelf.context, theme: strongSelf.previewTheme, resource: file.resource) + strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet), blockInteraction: true) + } + })) + } + let controller = ShareController(context: self.context, subject: subject, preferredAction: preferredAction) self.present(controller, in: .window(.root), blockInteraction: true) } } diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 1911169468..40ad17ddcb 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -195,22 +195,30 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) - messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) + messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_ReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - //let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: self.previewTheme, wallpaper: self.previewTheme.chat.defaultWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: false, largeEmoji: false) + let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: "", attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) + let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) + let message3 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 14, title: nil, performer: nil, waveform: MemoryBuffer())] let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: nil, attributes: voiceAttributes) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.playing), fetchStatus: .Local))) + let message4 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []) - items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.playing), fetchStatus: .Local))) + + //items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.playing), fetchStatus: .Local)), controllerInteraction: controllerInteraction, content: .message(message: , read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false)) + + let message5 = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []) + items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message5, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil)) let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right) if let messageNodes = self.messageNodes { @@ -260,8 +268,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.scrollNode.frame = bounds let toolbarHeight = 49.0 + layout.intrinsicInsets.bottom - self.chatListBackgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height) - self.chatBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height) + self.chatListBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height) + self.chatBackgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height) self.scrollNode.view.contentSize = CGSize(width: bounds.width * 2.0, height: bounds.height) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift index dbd03cc362..142baa02f8 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift @@ -20,7 +20,7 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec let lineWidth: CGFloat if selected { var accentColor = theme.list.itemAccentColor - if accentColor.rgb == UIColor.white.rgb { + if accentColor.rgb == 0xffffff { accentColor = UIColor(rgb: 0x999999) } context.setStrokeColor(accentColor.cgColor) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index f75e3d12e4..85f5b1fc3d 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -7,6 +7,7 @@ import TelegramCore import TelegramPresentationData import TelegramUIPreferences import ItemListUI +import AlertUI import AccountContext private final class ThemeSettingsControllerArguments { @@ -294,16 +295,15 @@ private struct ThemeSettingsState: Equatable { } } -private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] { +private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], availableThemes: [PresentationThemeReference], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] { var entries: [ThemeSettingsControllerEntry] = [] entries.append(.themeListHeader(presentationData.theme, strings.Appearance_ColorTheme.uppercased())) entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder)) - let availableThemes: [PresentationThemeReference] = [.builtin(.dayClassic), .builtin(.day), .builtin(.night), .builtin(.nightAccent)] entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, themeSpecificAccentColors, themeSpecificAccentColors[themeReference.index])) - if theme.name != .builtin(.dayClassic) { + if case let .builtin(theme) = themeReference, theme != .dayClassic { entries.append(.accentColor(presentationData.theme, themeReference, strings.Appearance_AccentColor, themeSpecificAccentColors[themeReference.index])) } @@ -349,6 +349,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The var pushControllerImpl: ((ViewController) -> Void)? var presentControllerImpl: ((ViewController, Any?) -> Void)? + var moreImpl: (() -> Void)? + let _ = telegramWallpapers(postbox: context.account.postbox, network: context.account.network).start() let currentAppIcon: PresentationAppIcon? @@ -426,9 +428,14 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The context.sharedContext.applicationBindings.requestSetAlternateIconName(name, { _ in }) }) - - let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]), availableAppIcons, currentAppIconName.get(), statePromise.get()) - |> map { presentationData, sharedData, availableAppIcons, currentAppIconName, state -> (ItemListControllerState, (ItemListNodeState, ThemeSettingsControllerEntry.ItemGenerationArguments)) in + + let savedThemes = telegramThemes(postbox: context.account.postbox, network: context.account.network) + |> map { themes -> [PresentationThemeReference] in + return themes.map { .cloud($0) } + } + + let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]), savedThemes, availableAppIcons, currentAppIconName.get(), statePromise.get()) + |> map { presentationData, sharedData, savedThemes, availableAppIcons, currentAppIconName, state -> (ItemListControllerState, (ItemListNodeState, ThemeSettingsControllerEntry.ItemGenerationArguments)) in let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings let fontSize = settings.fontSize @@ -446,8 +453,15 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The wallpaper = settings.chatWallpaper } - let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) - let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeReference: settings.theme, themeSpecificAccentColors: settings.themeSpecificAccentColors, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false) + let rightNavigationButton = ItemListNavigationButton(content: .icon(.action), style: .regular, enabled: true, action: { + moreImpl?() + }) + + let defaultThemes: [PresentationThemeReference] = [.builtin(.dayClassic), .builtin(.day), .builtin(.night), .builtin(.nightAccent)] + let availableThemes = defaultThemes + savedThemes + + let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) + let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeReference: settings.theme, themeSpecificAccentColors: settings.themeSpecificAccentColors, availableThemes: availableThemes, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false) return (controllerState, (listState, arguments)) } @@ -460,6 +474,46 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The presentControllerImpl = { [weak controller] c, a in controller?.present(c, in: .window(.root), with: a) } + moreImpl = { [weak controller] in + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) + var items: [ActionSheetItem] = [] + items.append(ActionSheetButtonItem(title: "Create New Theme", color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + var randomId: Int64 = 0 + arc4random_buf(&randomId, 8) + let path = NSTemporaryDirectory() + "\(randomId)" + + let encoder = PresentationThemeEncoder() + let theme = presentationData.theme + guard let strings = try? encoder.encode(theme), let _ = try? strings.write(toFile: path, atomically: true, encoding: .utf8) else { + return + } + + let id = arc4random64() + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/x-tgtheme-ios", size: nil, attributes: [.FileName(fileName: "\(theme.name.string).tgios-theme")]) + let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + + let _ = enqueueMessages(account: context.account, peerId: context.account.peerId, messages: [message]).start() + + presentControllerImpl?(textAlertController(context: context, title: nil, text: "A new theme template has been added to your Saved Messages.", actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_SavedMessages, action: { + if let controller = controller, let navigationController = controller.navigationController as? NavigationController { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(context.account.peerId))) + } + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})], actionLayout: .vertical), nil) + })) + items.append(ActionSheetButtonItem(title: "Upload Theme", color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + })) + actionSheet.setItemGroups([ActionSheetItemGroup(items:items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + presentControllerImpl?(actionSheet, nil) + } return controller } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift index 40b13f8db3..b5845a82ae 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsThemeItem.swift @@ -21,7 +21,7 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec let lineWidth: CGFloat if selected { var accentColor = theme.list.itemAccentColor - if accentColor.rgb == UIColor.white.rgb { + if accentColor.rgb == 0xffffff { accentColor = UIColor(rgb: 0x999999) } context.setStrokeColor(accentColor.cgColor) @@ -40,32 +40,40 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec private func generateThemeIconImage(theme: PresentationThemeReference, accentColor: UIColor?) -> UIImage { return generateImage(CGSize(width: 98.0, height: 62.0), rotatedContext: { size, context in - guard case let .builtin(theme) = theme else { - return - } let bounds = CGRect(origin: CGPoint(), size: size) let background: UIColor let incomingFill: UIColor let outgoingFill: UIColor switch theme { - case .dayClassic: + case let .builtin(theme): + switch theme { + case .dayClassic: + background = UIColor(rgb: 0xd6e2ee) + incomingFill = UIColor(rgb: 0xffffff) + outgoingFill = UIColor(rgb: 0xe1ffc7) + case .day: + background = .white + incomingFill = UIColor(rgb: 0xd5dde6) + outgoingFill = accentColor ?? UIColor(rgb: 0x007aff) + case .night: + background = UIColor(rgb: 0x000000) + incomingFill = UIColor(rgb: 0x1f1f1f) + outgoingFill = accentColor ?? UIColor(rgb: 0x313131) + case .nightAccent: + let accentColor = accentColor ?? UIColor(rgb: 0x007aff) + background = accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18) + incomingFill = accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25) + outgoingFill = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59) + } + case let .cloud(theme): + background = UIColor(rgb: 0xd6e2ee) + incomingFill = UIColor(rgb: 0xffffff) + outgoingFill = UIColor(rgb: 0xe1ffc7) + default: background = UIColor(rgb: 0xd6e2ee) incomingFill = UIColor(rgb: 0xffffff) outgoingFill = UIColor(rgb: 0xe1ffc7) - case .day: - background = .white - incomingFill = UIColor(rgb: 0xd5dde6) - outgoingFill = accentColor ?? UIColor(rgb: 0x007aff) - case .night: - background = UIColor(rgb: 0x000000) - incomingFill = UIColor(rgb: 0x1f1f1f) - outgoingFill = accentColor ?? UIColor(rgb: 0x313131) - case .nightAccent: - let accentColor = accentColor ?? UIColor(rgb: 0x007aff) - background = accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18) - incomingFill = accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25) - outgoingFill = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59) } context.setFillColor(background.cgColor) @@ -336,19 +344,22 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { } let name: String? - if case let .builtin(theme) = theme { - switch theme { - case .dayClassic: - name = item.strings.Appearance_ThemeCarouselClassic - case .day: - name = item.strings.Appearance_ThemeCarouselDay - case .night: - name = item.strings.Appearance_ThemeCarouselNewNight - case .nightAccent: - name = item.strings.Appearance_ThemeCarouselTintedNight - } - } else { - name = nil + switch theme { + case let .builtin(theme): + switch theme { + case .dayClassic: + name = item.strings.Appearance_ThemeCarouselClassic + case .day: + name = item.strings.Appearance_ThemeCarouselDay + case .night: + name = item.strings.Appearance_ThemeCarouselNewNight + case .nightAccent: + name = item.strings.Appearance_ThemeCarouselTintedNight + } + case let .cloud(theme): + name = theme.title + default: + name = nil } if let name = name { diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index dafa6f5ca6..d8ef0a30d7 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -238,6 +238,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[967122427] = { return Api.Update.parse_updateNewScheduledMessage($0) } dict[-1870238482] = { return Api.Update.parse_updateDeleteScheduledMessages($0) } dict[357013699] = { return Api.Update.parse_updateMessageReactions($0) } + dict[-2112423005] = { return Api.Update.parse_updateTheme($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } dict[367766557] = { return Api.ChannelParticipant.parse_channelParticipant($0) } @@ -263,6 +264,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-748155807] = { return Api.ContactStatus.parse_contactStatus($0) } dict[1679398724] = { return Api.SecureFile.parse_secureFileEmpty($0) } dict[-534283678] = { return Api.SecureFile.parse_secureFile($0) } + dict[-199313886] = { return Api.account.Themes.parse_themesNotModified($0) } + dict[2137482273] = { return Api.account.Themes.parse_themes($0) } dict[236446268] = { return Api.PhotoSize.parse_photoSizeEmpty($0) } dict[2009052699] = { return Api.PhotoSize.parse_photoSize($0) } dict[-374917894] = { return Api.PhotoSize.parse_photoCachedSize($0) } @@ -274,6 +277,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1359533640] = { return Api.messages.FoundStickerSets.parse_foundStickerSets($0) } dict[471437699] = { return Api.account.WallPapers.parse_wallPapersNotModified($0) } dict[1881892265] = { return Api.account.WallPapers.parse_wallPapers($0) } + dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) } + dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) } dict[1158290442] = { return Api.messages.FoundGifs.parse_foundGifs($0) } dict[-1199954735] = { return Api.MessageReactions.parse_messageReactions($0) } dict[-1132476723] = { return Api.FileLocation.parse_fileLocationToBeDeprecated($0) } @@ -420,7 +425,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-2128640689] = { return Api.account.SentEmailCode.parse_sentEmailCode($0) } dict[-1038136962] = { return Api.EncryptedFile.parse_encryptedFileEmpty($0) } dict[1248893260] = { return Api.EncryptedFile.parse_encryptedFile($0) } - dict[-557924733] = { return Api.CodeSettings.parse_codeSettings($0) } dict[-391902247] = { return Api.SecureValueError.parse_secureValueErrorData($0) } dict[12467706] = { return Api.SecureValueError.parse_secureValueErrorFrontSide($0) } dict[-2037765467] = { return Api.SecureValueError.parse_secureValueErrorReverseSide($0) } @@ -715,6 +719,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1363483106] = { return Api.DialogPeer.parse_dialogPeerFolder($0) } dict[-104284986] = { return Api.WebDocument.parse_webDocumentNoProxy($0) } dict[475467473] = { return Api.WebDocument.parse_webDocument($0) } + dict[1211967244] = { return Api.Theme.parse_themeDocumentNotModified($0) } + dict[1464749545] = { return Api.Theme.parse_theme($0) } dict[-1290580579] = { return Api.contacts.Found.parse_found($0) } dict[-368018716] = { return Api.ChannelAdminLogEventsFilter.parse_channelAdminLogEventsFilter($0) } dict[1889961234] = { return Api.PeerNotifySettings.parse_peerNotifySettingsEmpty($0) } @@ -944,6 +950,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.SecureFile: _1.serialize(buffer, boxed) + case let _1 as Api.account.Themes: + _1.serialize(buffer, boxed) case let _1 as Api.PhotoSize: _1.serialize(buffer, boxed) case let _1 as Api.messages.Stickers: @@ -954,6 +962,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.account.WallPapers: _1.serialize(buffer, boxed) + case let _1 as Api.InputTheme: + _1.serialize(buffer, boxed) case let _1 as Api.messages.FoundGifs: _1.serialize(buffer, boxed) case let _1 as Api.MessageReactions: @@ -1068,8 +1078,6 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.EncryptedFile: _1.serialize(buffer, boxed) - case let _1 as Api.CodeSettings: - _1.serialize(buffer, boxed) case let _1 as Api.SecureValueError: _1.serialize(buffer, boxed) case let _1 as Api.NotifyPeer: @@ -1302,6 +1310,8 @@ public struct Api { _1.serialize(buffer, boxed) case let _1 as Api.WebDocument: _1.serialize(buffer, boxed) + case let _1 as Api.Theme: + _1.serialize(buffer, boxed) case let _1 as Api.contacts.Found: _1.serialize(buffer, boxed) case let _1 as Api.ChannelAdminLogEventsFilter: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 6ed0942678..7575cc1f7c 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -4022,6 +4022,7 @@ public extension Api { case updateNewScheduledMessage(message: Api.Message) case updateDeleteScheduledMessages(peer: Api.Peer, messages: [Int32]) case updateMessageReactions(peer: Api.Peer, msgId: Int32, reactions: Api.MessageReactions) + case updateTheme(theme: Api.Theme) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -4648,6 +4649,12 @@ public extension Api { serializeInt32(msgId, buffer: buffer, boxed: false) reactions.serialize(buffer, true) break + case .updateTheme(let theme): + if boxed { + buffer.appendInt32(-2112423005) + } + theme.serialize(buffer, true) + break } } @@ -4801,6 +4808,8 @@ public extension Api { return ("updateDeleteScheduledMessages", [("peer", peer), ("messages", messages)]) case .updateMessageReactions(let peer, let msgId, let reactions): return ("updateMessageReactions", [("peer", peer), ("msgId", msgId), ("reactions", reactions)]) + case .updateTheme(let theme): + return ("updateTheme", [("theme", theme)]) } } @@ -6067,6 +6076,19 @@ public extension Api { return nil } } + public static func parse_updateTheme(_ reader: BufferReader) -> Update? { + var _1: Api.Theme? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Theme + } + let _c1 = _1 != nil + if _c1 { + return Api.Update.updateTheme(theme: _1!) + } + else { + return nil + } + } } public enum PopularContact: TypeConstructorDescription { @@ -6964,6 +6986,64 @@ public extension Api { } } + } + public enum InputTheme: TypeConstructorDescription { + case inputTheme(id: Int64, accessHash: Int64) + case inputThemeSlug(slug: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputTheme(let id, let accessHash): + if boxed { + buffer.appendInt32(1012306921) + } + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + break + case .inputThemeSlug(let slug): + if boxed { + buffer.appendInt32(-175567375) + } + serializeString(slug, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inputTheme(let id, let accessHash): + return ("inputTheme", [("id", id), ("accessHash", accessHash)]) + case .inputThemeSlug(let slug): + return ("inputThemeSlug", [("slug", slug)]) + } + } + + public static func parse_inputTheme(_ reader: BufferReader) -> InputTheme? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _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 + } + } + 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 + } + } + } public enum MessageReactions: TypeConstructorDescription { case messageReactions(flags: Int32, results: [Api.ReactionCount]) @@ -10430,40 +10510,6 @@ public extension Api { } } - } - public enum CodeSettings: TypeConstructorDescription { - case codeSettings(flags: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .codeSettings(let flags): - if boxed { - buffer.appendInt32(-557924733) - } - serializeInt32(flags, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .codeSettings(let flags): - return ("codeSettings", [("flags", flags)]) - } - } - - public static func parse_codeSettings(_ reader: BufferReader) -> CodeSettings? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.CodeSettings.codeSettings(flags: _1!) - } - else { - return nil - } - } - } public enum SecureValueError: TypeConstructorDescription { case secureValueErrorData(type: Api.SecureValueType, dataHash: Buffer, field: String, text: String) @@ -18178,6 +18224,74 @@ public extension Api { } } + } + public enum Theme: TypeConstructorDescription { + case themeDocumentNotModified + case theme(flags: Int32, id: Int64, accessHash: Int64, slug: String, title: String, document: Api.Document) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .themeDocumentNotModified: + if boxed { + buffer.appendInt32(1211967244) + } + + break + case .theme(let flags, let id, let accessHash, let slug, let title, let document): + if boxed { + buffer.appendInt32(1464749545) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + serializeString(slug, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + document.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .themeDocumentNotModified: + return ("themeDocumentNotModified", []) + case .theme(let flags, let id, let accessHash, let slug, let title, let document): + return ("theme", [("flags", flags), ("id", id), ("accessHash", accessHash), ("slug", slug), ("title", title), ("document", document)]) + } + } + + public static func parse_themeDocumentNotModified(_ reader: BufferReader) -> Theme? { + return Api.Theme.themeDocumentNotModified + } + public static func parse_theme(_ reader: BufferReader) -> Theme? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: String? + _4 = parseString(reader) + var _5: String? + _5 = parseString(reader) + var _6: Api.Document? + if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.Document + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.Theme.theme(flags: _1!, id: _2!, accessHash: _3!, slug: _4!, title: _5!, document: _6!) + } + else { + return nil + } + } + } public enum ChannelAdminLogEventsFilter: TypeConstructorDescription { case channelAdminLogEventsFilter(flags: Int32) diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index a5b0363284..16344d8e88 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -415,6 +415,62 @@ public struct account { } } + } + public enum Themes: TypeConstructorDescription { + case themesNotModified + case themes(hash: Int32, themes: [Api.Theme]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .themesNotModified: + if boxed { + buffer.appendInt32(-199313886) + } + + break + case .themes(let hash, let themes): + if boxed { + buffer.appendInt32(2137482273) + } + serializeInt32(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(themes.count)) + for item in themes { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .themesNotModified: + return ("themesNotModified", []) + case .themes(let hash, let themes): + return ("themes", [("hash", hash), ("themes", themes)]) + } + } + + public static func parse_themesNotModified(_ reader: BufferReader) -> Themes? { + return Api.account.Themes.themesNotModified + } + public static func parse_themes(_ reader: BufferReader) -> Themes? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.Theme]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Theme.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.account.Themes.themes(hash: _1!, themes: _2!) + } + else { + return nil + } + } + } public enum WallPapers: TypeConstructorDescription { case wallPapersNotModified @@ -2868,6 +2924,33 @@ public extension Api { }) } + public static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-637606386) + serializeInt32(flags, buffer: buffer, boxed: false) + fromPeer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(id.count)) + for item in id { + serializeInt32(item, buffer: buffer, boxed: false) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(randomId.count)) + for item in randomId { + serializeInt64(item, buffer: buffer, boxed: false) + } + toPeer.serialize(buffer, true) + if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", flags), ("fromPeer", fromPeer), ("id", id), ("randomId", randomId), ("toPeer", toPeer), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } + public static func sendInlineBotResult(flags: Int32, peer: Api.InputPeer, replyToMsgId: Int32?, randomId: Int64, queryId: Int64, id: String, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(570955184) @@ -2888,6 +2971,31 @@ public extension Api { }) } + public static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1224152952) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(entities!.count)) + for item in entities! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 15) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.editMessage", parameters: [("flags", flags), ("peer", peer), ("id", id), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } + public static func sendMultiMedia(flags: Int32, peer: Api.InputPeer, replyToMsgId: Int32?, multiMedia: [Api.InputSingleMedia], scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(-872345397) @@ -2910,33 +3018,6 @@ public extension Api { }) } - public static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-637606386) - serializeInt32(flags, buffer: buffer, boxed: false) - fromPeer.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(id.count)) - for item in id { - serializeInt32(item, buffer: buffer, boxed: false) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(randomId.count)) - for item in randomId { - serializeInt64(item, buffer: buffer, boxed: false) - } - toPeer.serialize(buffer, true) - if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", flags), ("fromPeer", fromPeer), ("id", id), ("randomId", randomId), ("toPeer", toPeer), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } - public static func getScheduledHistory(peer: Api.InputPeer, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(-490575781) @@ -3009,31 +3090,6 @@ public extension Api { }) } - public static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(1224152952) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(entities!.count)) - for item in entities! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 15) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "messages.editMessage", parameters: [("flags", flags), ("peer", peer), ("id", id), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } - public static func sendReaction(flags: Int32, peer: Api.InputPeer, msgId: Int32, reaction: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(627641572) @@ -5628,6 +5684,119 @@ public extension Api { return result }) } + + public static func uploadTheme(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, fileName: String, mimeType: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(473805619) + serializeInt32(flags, buffer: buffer, boxed: false) + file.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {thumb!.serialize(buffer, true)} + serializeString(fileName, buffer: buffer, boxed: false) + serializeString(mimeType, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.uploadTheme", parameters: [("flags", flags), ("file", file), ("thumb", thumb), ("fileName", fileName), ("mimeType", mimeType)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Document? in + let reader = BufferReader(buffer) + var result: Api.Document? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Document + } + return result + }) + } + + public static func createTheme(slug: String, title: String, document: Api.InputDocument) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(729808255) + serializeString(slug, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + document.serialize(buffer, true) + return (FunctionDescription(name: "account.createTheme", parameters: [("slug", slug), ("title", title), ("document", document)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in + let reader = BufferReader(buffer) + var result: Api.Theme? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Theme + } + return result + }) + } + + public static func updateTheme(flags: Int32, theme: Api.InputTheme, slug: String?, title: String?, document: Api.InputDocument?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-124161674) + serializeInt32(flags, buffer: buffer, boxed: false) + theme.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(slug!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {document!.serialize(buffer, true)} + return (FunctionDescription(name: "account.updateTheme", parameters: [("flags", flags), ("theme", theme), ("slug", slug), ("title", title), ("document", document)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in + let reader = BufferReader(buffer) + var result: Api.Theme? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Theme + } + return result + }) + } + + public static func saveTheme(theme: Api.InputTheme, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-229175188) + theme.serialize(buffer, true) + unsave.serialize(buffer, true) + return (FunctionDescription(name: "account.saveTheme", parameters: [("theme", theme), ("unsave", unsave)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } + + public static func installTheme(format: String, theme: Api.InputTheme) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-520600676) + serializeString(format, buffer: buffer, boxed: false) + theme.serialize(buffer, true) + return (FunctionDescription(name: "account.installTheme", parameters: [("format", format), ("theme", theme)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } + + public static func getTheme(format: String, theme: Api.InputTheme, documentId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1919060949) + serializeString(format, buffer: buffer, boxed: false) + theme.serialize(buffer, true) + serializeInt64(documentId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getTheme", parameters: [("format", format), ("theme", theme), ("documentId", documentId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in + let reader = BufferReader(buffer) + var result: Api.Theme? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Theme + } + return result + }) + } + + public static func getThemes(format: String, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(676939512) + serializeString(format, buffer: buffer, boxed: false) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getThemes", parameters: [("format", format), ("hash", hash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Themes? in + let reader = BufferReader(buffer) + var result: Api.account.Themes? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.account.Themes + } + return result + }) + } } public struct langpack { public static func getLangPack(langPack: String, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { diff --git a/submodules/TelegramCore/TelegramCore/AccountManager.swift b/submodules/TelegramCore/TelegramCore/AccountManager.swift index e7053b31fe..2f37b84baf 100644 --- a/submodules/TelegramCore/TelegramCore/AccountManager.swift +++ b/submodules/TelegramCore/TelegramCore/AccountManager.swift @@ -122,12 +122,14 @@ private var declaredEncodables: Void = { declareEncodable(CachedLocalizationInfos.self, f: { CachedLocalizationInfos(decoder: $0) }) declareEncodable(CachedSecureIdConfiguration.self, f: { CachedSecureIdConfiguration(decoder: $0) }) declareEncodable(CachedWallpapersConfiguration.self, f: { CachedWallpapersConfiguration(decoder: $0) }) + declareEncodable(CachedThemesConfiguration.self, f: { CachedThemesConfiguration(decoder: $0) }) declareEncodable(SynchronizeGroupedPeersOperation.self, f: { SynchronizeGroupedPeersOperation(decoder: $0) }) declareEncodable(ContentPrivacySettings.self, f: { ContentPrivacySettings(decoder: $0) }) declareEncodable(TelegramDeviceContactImportedData.self, f: { TelegramDeviceContactImportedData(decoder: $0) }) declareEncodable(SecureFileMediaResource.self, f: { SecureFileMediaResource(decoder: $0) }) declareEncodable(CachedStickerQueryResult.self, f: { CachedStickerQueryResult(decoder: $0) }) declareEncodable(TelegramWallpaper.self, f: { TelegramWallpaper(decoder: $0) }) + declareEncodable(TelegramTheme.self, f: { TelegramTheme(decoder: $0) }) declareEncodable(SynchronizeMarkAllUnseenPersonalMessagesOperation.self, f: { SynchronizeMarkAllUnseenPersonalMessagesOperation(decoder: $0) }) declareEncodable(SynchronizeAppLogEventsOperation.self, f: { SynchronizeAppLogEventsOperation(decoder: $0) }) declareEncodable(CachedRecentPeers.self, f: { CachedRecentPeers(decoder: $0) }) diff --git a/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift b/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift index 2539e788bb..9b79249143 100644 --- a/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/TelegramCore/AccountStateManagementUtils.swift @@ -1314,6 +1314,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo messageIds.append(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.ScheduledCloud, id: message)) } updatedState.deleteMessages(messageIds) + case let .updateTheme(theme): + break default: break } diff --git a/submodules/TelegramCore/TelegramCore/EnqueueMessage.swift b/submodules/TelegramCore/TelegramCore/EnqueueMessage.swift index bddb8cf8f4..5d6bb1e239 100644 --- a/submodules/TelegramCore/TelegramCore/EnqueueMessage.swift +++ b/submodules/TelegramCore/TelegramCore/EnqueueMessage.swift @@ -398,7 +398,6 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, } } - storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: localTags, forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: mediaList)) case let .forward(source, grouping, requestedAttributes): let sourceMessage = transaction.getMessage(source) diff --git a/submodules/TelegramCore/TelegramCore/Namespaces.swift b/submodules/TelegramCore/TelegramCore/Namespaces.swift index 1b6fc51ff3..65af9b9e27 100644 --- a/submodules/TelegramCore/TelegramCore/Namespaces.swift +++ b/submodules/TelegramCore/TelegramCore/Namespaces.swift @@ -59,6 +59,7 @@ public struct Namespaces { public static let CloudWallpapers: Int32 = 6 public static let CloudSavedStickers: Int32 = 7 public static let RecentlyUsedHashtags: Int32 = 8 + public static let CloudThemes: Int32 = 9 } struct CachedItemCollection { @@ -70,6 +71,7 @@ public struct Namespaces { public static let cachedStickerQueryResults: Int8 = 5 public static let cachedSecureIdConfiguration: Int8 = 6 public static let cachedWallpapersConfiguration: Int8 = 7 + public static let cachedThemesConfiguration: Int8 = 7 } struct UnorderedItemList { diff --git a/submodules/TelegramCore/TelegramCore/Theme.swift b/submodules/TelegramCore/TelegramCore/Theme.swift new file mode 100644 index 0000000000..96e936ede8 --- /dev/null +++ b/submodules/TelegramCore/TelegramCore/Theme.swift @@ -0,0 +1,90 @@ +import Foundation +#if os(macOS) +import PostboxMac +import SwiftSignalKitMac +import TelegramApiMac +#else +import Postbox +import SwiftSignalKit +import TelegramApi +#endif + +public final class TelegramTheme: OrderedItemListEntryContents, Equatable { + public let id: Int64 + public let accessHash: Int64 + public let slug: String + public let title: String + public let file: TelegramMediaFile + public let isCreator: Bool + public let isDefault: Bool + + public init(id: Int64, accessHash: Int64, slug: String, title: String, file: TelegramMediaFile, isCreator: Bool, isDefault: Bool) { + self.id = id + self.accessHash = accessHash + self.slug = slug + self.title = title + self.file = file + self.isCreator = isCreator + self.isDefault = isDefault + } + + public init(decoder: PostboxDecoder) { + self.id = decoder.decodeInt64ForKey("id", orElse: 0) + self.accessHash = decoder.decodeInt64ForKey("accessHash", orElse: 0) + self.slug = decoder.decodeStringForKey("slug", orElse: "") + self.title = decoder.decodeStringForKey("title", orElse: "") + self.file = decoder.decodeObjectForKey("file", decoder: { TelegramMediaFile(decoder: $0) }) as! TelegramMediaFile + self.isCreator = decoder.decodeInt32ForKey("isCreator", orElse: 0) != 0 + self.isDefault = decoder.decodeInt32ForKey("isDefault", orElse: 0) != 0 + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt64(self.id, forKey: "id") + encoder.encodeInt64(self.accessHash, forKey: "accessHash") + encoder.encodeString(self.slug, forKey: "slug") + encoder.encodeString(self.title, forKey: "title") + encoder.encodeObject(self.file, forKey: "file") + encoder.encodeInt32(self.isCreator ? 1 : 0, forKey: "isCreator") + encoder.encodeInt32(self.isDefault ? 1 : 0, forKey: "isDefault") + } + + public static func ==(lhs: TelegramTheme, rhs: TelegramTheme) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.accessHash != rhs.accessHash { + return false + } + if lhs.slug != rhs.slug { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.file.id != rhs.file.id { + return false + } + if lhs.isCreator != rhs.isCreator { + return false + } + if lhs.isDefault != rhs.isDefault { + return false + } + return true + } +} + +extension TelegramTheme { + convenience init?(apiTheme: Api.Theme) { + switch apiTheme { + case let .theme(flags, id, accessHash, slug, title, document): + if let file = telegramMediaFileFromApiDocument(document) { + self.init(id: id, accessHash: accessHash, slug: slug, title: title, file: file, isCreator: (flags & 1 << 0) != 0, isDefault: (flags & 1 << 1) != 0) + } else { + return nil + } + default: + return nil + } + } +} diff --git a/submodules/TelegramCore/TelegramCore/Themes.swift b/submodules/TelegramCore/TelegramCore/Themes.swift new file mode 100644 index 0000000000..bfbc5c46fb --- /dev/null +++ b/submodules/TelegramCore/TelegramCore/Themes.swift @@ -0,0 +1,226 @@ +import Foundation +#if os(macOS) +import PostboxMac +import SwiftSignalKitMac +import TelegramApiMac +#else +import Postbox +import SwiftSignalKit +import TelegramApi +#endif + +final class CachedThemesConfiguration: PostboxCoding { + let hash: Int32 + + init(hash: Int32) { + self.hash = hash + } + + init(decoder: PostboxDecoder) { + self.hash = decoder.decodeInt32ForKey("hash", orElse: 0) + } + + func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.hash, forKey: "hash") + } +} + +#if os(macOS) +private let themeFormat = "macos" +private let themeFileExtension = "palette" +#else +private let themeFormat = "ios" +private let themeFileExtension = "tgios-theme" +#endif + +public func telegramThemes(postbox: Postbox, network: Network, forceUpdate: Bool = false) -> Signal<[TelegramTheme], NoError> { + let fetch: ([TelegramTheme]?, Int32?) -> Signal<[TelegramTheme], NoError> = { current, hash in + network.request(Api.functions.account.getThemes(format: themeFormat, hash: hash ?? 0)) + |> retryRequest + |> mapToSignal { result -> Signal<([TelegramTheme], Int32), NoError> in + switch result { + case let .themes(hash, themes): + let result = themes.compactMap { TelegramTheme(apiTheme: $0) } + if result == current { + return .complete() + } else { + return .single((result, hash)) + } + case .themesNotModified: + return .complete() + } + } + |> mapToSignal { items, hash -> Signal<[TelegramTheme], NoError> in + return postbox.transaction { transaction -> [TelegramTheme] in + var entries: [OrderedItemListEntry] = [] + for item in items { + var intValue = Int32(entries.count) + let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4)) + entries.append(OrderedItemListEntry(id: id, contents: item)) + } + transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: entries) + transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedThemesConfiguration, key: ValueBoxKey(length: 0)), entry: CachedThemesConfiguration(hash: hash), collectionSpec: ItemCacheCollectionSpec(lowWaterItemCount: 1, highWaterItemCount: 1)) + return items + } + } + } + + if forceUpdate { + return fetch(nil, nil) + } else { + return postbox.transaction { transaction -> ([TelegramTheme], Int32?) in + let configuration = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedThemesConfiguration, key: ValueBoxKey(length: 0))) as? CachedThemesConfiguration + let items = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes) + return (items.map { $0.contents as! TelegramTheme }, configuration?.hash) + } + |> mapToSignal { current, hash -> Signal<[TelegramTheme], NoError> in + return .single(current) + |> then(fetch(current, hash)) + } + } +} + +public enum GetThemeError { + case generic +} + +public func getTheme(account: Account, slug: String) -> Signal { + return account.network.request(Api.functions.account.getTheme(format: themeFormat, theme: .inputThemeSlug(slug: slug), documentId: 0)) + |> mapError { _ -> GetThemeError in return .generic } + |> mapToSignal { theme -> Signal in + if let theme = TelegramTheme(apiTheme: theme) { + return .single(theme) + } else { + return .fail(.generic) + } + } +} + +public func saveTheme(account: Account, theme: TelegramTheme) -> Signal { + return saveUnsaveTheme(account: account, theme: theme, unsave: false) +} + +public func deleteTheme(account: Account, theme: TelegramTheme) -> Signal { + return saveUnsaveTheme(account: account, theme: theme, unsave: true) +} + +private func saveUnsaveTheme(account: Account, theme: TelegramTheme, unsave: Bool) -> Signal { + return account.network.request(Api.functions.account.saveTheme(theme: Api.InputTheme.inputTheme(id: theme.id, accessHash: theme.accessHash), unsave: unsave ? Api.Bool.boolTrue : Api.Bool.boolFalse)) + |> `catch` { _ -> Signal in + return .complete() + } + |> mapToSignal { _ -> Signal in + return .single(Void()) + } +} + +public func installTheme(account: Account, theme: TelegramTheme) -> Signal { + return account.network.request(Api.functions.account.installTheme(format: themeFormat, theme: Api.InputTheme.inputTheme(id: theme.id, accessHash: theme.accessHash))) + |> `catch` { _ -> Signal in + return .complete() + } + |> mapToSignal { _ -> Signal in + return .complete() + } +} + +public enum UploadThemeStatus { + case progress(Float) + case complete(TelegramMediaFile) +} + +public enum UploadThemeError { + case generic +} + +private struct UploadedThemeData { + fileprivate let resource: MediaResource + fileprivate let content: UploadedThemeDataContent +} + +private enum UploadedThemeDataContent { + case result(MultipartUploadResult) + case error +} + +private func uploadedTheme(postbox: Postbox, network: Network, resource: MediaResource) -> Signal { + return multipartUpload(network: network, postbox: postbox, source: .resource(.standalone(resource: resource)), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .file), hintFileSize: nil, hintFileIsLarge: false) + |> map { result -> UploadedThemeData in + return UploadedThemeData(resource: resource, content: .result(result)) + } + |> `catch` { _ -> Signal in + return .single(UploadedThemeData(resource: resource, content: .error)) + } +} + +private func uploadTheme(account: Account, resource: MediaResource) -> Signal { + let fileName = "theme.\(themeFileExtension)" + let mimeType = "application/x-tgtheme-\(themeFormat)" + + return uploadedTheme(postbox: account.postbox, network: account.network, resource: resource) + |> mapError { _ -> UploadThemeError in return .generic } + |> mapToSignal { result -> Signal<(UploadThemeStatus, MediaResource?), UploadThemeError> in + switch result.content { + case .error: + return .fail(.generic) + case let .result(resultData): + switch resultData { + case let .progress(progress): + return .single((.progress(progress), result.resource)) + case let .inputFile(file): + return account.network.request(Api.functions.account.uploadTheme(flags: 0, file: file, thumb: nil, fileName: fileName, mimeType: mimeType)) + |> mapError { _ in return UploadThemeError.generic } + |> mapToSignal { document -> Signal<(UploadThemeStatus, MediaResource?), UploadThemeError> in + if let file = telegramMediaFileFromApiDocument(document) { + return .single((.complete(file), result.resource)) + } else { + return .fail(.generic) + } + } + default: + return .fail(.generic) + } + } + } + |> map { result, _ -> UploadThemeStatus in + return result + } +} + +public enum CreateThemeError { + case generic +} + +public func createTheme(account: Account, resource: MediaResource, title: String, slug: String) -> Signal { + return uploadTheme(account: account, resource: resource) + |> mapError { _ in return CreateThemeError.generic } + |> mapToSignal { status -> Signal in + switch status { + case let .complete(file): + if let resource = file.resource as? CloudDocumentMediaResource { + return account.network.request(Api.functions.account.createTheme(slug: slug, title: title, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)))) + |> mapError { _ in return CreateThemeError.generic } + |> mapToSignal { apiTheme -> Signal in + if let theme = TelegramTheme(apiTheme: apiTheme) { + return .single(theme) + } else { + return .fail(.generic) + } + } + } + else { + return .fail(.generic) + } + default: + return .complete() + } + } +} + +public func updateTheme(account: Account, theme: TelegramTheme, title: String?, resource: MediaResource?) -> Signal { + guard title != nil || resource != nil else { + return .complete() + } + + return .never() +} diff --git a/submodules/TelegramCore/TelegramCore/Wallpapers.swift b/submodules/TelegramCore/TelegramCore/Wallpapers.swift index a84b61d740..565dce34e7 100644 --- a/submodules/TelegramCore/TelegramCore/Wallpapers.swift +++ b/submodules/TelegramCore/TelegramCore/Wallpapers.swift @@ -26,7 +26,7 @@ final class CachedWallpapersConfiguration: PostboxCoding { } public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate: Bool = false) -> Signal<[TelegramWallpaper], NoError> { - let fetch: ([TelegramWallpaper]?, Int32?) -> Signal<[TelegramWallpaper], NoError> = { list, hash in + let fetch: ([TelegramWallpaper]?, Int32?) -> Signal<[TelegramWallpaper], NoError> = { current, hash in network.request(Api.functions.account.getWallPapers(hash: hash ?? 0)) |> retryRequest |> mapToSignal { result -> Signal<([TelegramWallpaper], Int32), NoError> in @@ -49,7 +49,7 @@ public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate: items.append(.builtin(WallpaperSettings())) } - if items == list { + if items == current { return .complete() } else { return .single((items, hash)) @@ -85,9 +85,9 @@ public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate: return (items.map { $0.contents as! TelegramWallpaper }, configuration?.hash) } } - |> mapToSignal { list, hash -> Signal<[TelegramWallpaper], NoError> in - return .single(list) - |> then(fetch(list, hash)) + |> mapToSignal { current, hash -> Signal<[TelegramWallpaper], NoError> in + return .single(current) + |> then(fetch(current, hash)) } } } @@ -101,7 +101,7 @@ public enum UploadWallpaperError { case generic } -public struct UploadedWallpaperData { +private struct UploadedWallpaperData { fileprivate let resource: MediaResource fileprivate let content: UploadedWallpaperDataContent } @@ -134,9 +134,9 @@ public func uploadWallpaper(account: Account, resource: MediaResource, mimeType: return .single((.progress(progress), result.resource)) case let .inputFile(file): return account.network.request(Api.functions.account.uploadWallPaper(file: file, mimeType: mimeType, settings: apiWallpaperSettings(settings))) - |> mapError {_ in return UploadWallpaperError.generic} - |> mapToSignal { wallpaper -> Signal<(UploadWallpaperStatus, MediaResource?), UploadWallpaperError> in - return .single((.complete(TelegramWallpaper(apiWallpaper: wallpaper)), result.resource)) + |> mapError { _ in return UploadWallpaperError.generic } + |> map { wallpaper -> (UploadWallpaperStatus, MediaResource?) in + return (.complete(TelegramWallpaper(apiWallpaper: wallpaper)), result.resource) } default: return .fail(.generic) diff --git a/submodules/TelegramCore/TelegramCore_Xcode.xcodeproj/project.pbxproj b/submodules/TelegramCore/TelegramCore_Xcode.xcodeproj/project.pbxproj index 93fa992764..3f4ff9c444 100644 --- a/submodules/TelegramCore/TelegramCore_Xcode.xcodeproj/project.pbxproj +++ b/submodules/TelegramCore/TelegramCore_Xcode.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 0962E66F21B6147600245FD9 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E66E21B6147600245FD9 /* AppConfiguration.swift */; }; 0962E67521B6437600245FD9 /* SplitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E67421B6437600245FD9 /* SplitTest.swift */; }; 0962E68121BAA20E00245FD9 /* SearchBotsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E68021BAA20E00245FD9 /* SearchBotsConfiguration.swift */; }; + 09B4A9B4230FB70B005C2E08 /* Themes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B3230FB70B005C2E08 /* Themes.swift */; }; + 09B4A9B6230FBB2B005C2E08 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B5230FBB2B005C2E08 /* Theme.swift */; }; 09EC0DE922C6825D00E7185B /* AppUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EC0DE822C6825D00E7185B /* AppUpdate.swift */; }; 09EDAD382213120C0012A50B /* AutodownloadSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EDAD372213120C0012A50B /* AutodownloadSettings.swift */; }; 09EDAD3A22131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EDAD3922131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift */; }; @@ -808,6 +810,8 @@ 0962E66E21B6147600245FD9 /* AppConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfiguration.swift; sourceTree = ""; }; 0962E67421B6437600245FD9 /* SplitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitTest.swift; sourceTree = ""; }; 0962E68021BAA20E00245FD9 /* SearchBotsConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBotsConfiguration.swift; sourceTree = ""; }; + 09B4A9B3230FB70B005C2E08 /* Themes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Themes.swift; sourceTree = ""; }; + 09B4A9B5230FBB2B005C2E08 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; 09EC0DE822C6825D00E7185B /* AppUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdate.swift; sourceTree = ""; }; 09EDAD372213120C0012A50B /* AutodownloadSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutodownloadSettings.swift; sourceTree = ""; }; 09EDAD3922131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedAutodownloadSettingsUpdates.swift; sourceTree = ""; }; @@ -1270,6 +1274,7 @@ 0962E66E21B6147600245FD9 /* AppConfiguration.swift */, 0962E68021BAA20E00245FD9 /* SearchBotsConfiguration.swift */, 09EDAD372213120C0012A50B /* AutodownloadSettings.swift */, + 09B4A9B5230FBB2B005C2E08 /* Theme.swift */, ); name = Settings; sourceTree = ""; @@ -1667,6 +1672,7 @@ D099E221229420D600561B75 /* BlockedPeersContext.swift */, D098907E22942E3B0053F151 /* ActiveSessionsContext.swift */, D069257022D8B526002FC021 /* SecretChatSettings.swift */, + 09B4A9B3230FB70B005C2E08 /* Themes.swift */, ); name = Settings; sourceTree = ""; @@ -2148,6 +2154,7 @@ D03B0CE01D62249100955575 /* StoreMessage_Telegram.swift in Sources */, D03E416C2304D5B30049C28B /* ValidateAddressNameInteractive.swift in Sources */, D08774FE1E3E3A3500A97350 /* GlobalNotificationSettings.swift in Sources */, + 09B4A9B6230FBB2B005C2E08 /* Theme.swift in Sources */, D023E67821540624008C27D1 /* UpdateMessageMedia.swift in Sources */, D0EE7FC420986C5300981319 /* SecureIdPassportRegistrationValue.swift in Sources */, D03B0CB91D62233400955575 /* Either.swift in Sources */, @@ -2155,6 +2162,7 @@ 0962E66D21B5C56F00245FD9 /* JSON.swift in Sources */, D0338740223BD48B007A2CE4 /* ContactsSettings.swift in Sources */, D03B0CBD1D62234300955575 /* Regex.swift in Sources */, + 09B4A9B4230FB70B005C2E08 /* Themes.swift in Sources */, D00BDA191EE593D600C64C5E /* TelegramChannelAdminRights.swift in Sources */, 09EDAD3A22131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift in Sources */, D0EA188220D3D2B1001AEE19 /* RemoteStorageConfiguration.swift in Sources */, diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift index 2bac77cd0c..d369fb0062 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift @@ -9,6 +9,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta let badgeFillColor: UIColor let badgeTextColor: UIColor + let secondaryBadgeTextColor: UIColor let outgoingBubbleFillColor: UIColor let outgoingBubbleHighlightedFillColor: UIColor let outgoingScamColor: UIColor @@ -23,6 +24,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta if accentColor.rgb == UIColor.white.rgb { badgeFillColor = .white badgeTextColor = .black + secondaryBadgeTextColor = .black outgoingBubbleFillColor = UIColor(rgb: 0x313131) outgoingBubbleHighlightedFillColor = UIColor(rgb: 0x464646) outgoingScamColor = destructiveColor @@ -41,6 +43,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta if lightness > 0.7 { outgoingScamColor = .black + secondaryBadgeTextColor = .black outgoingPrimaryTextColor = .black outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.5) outgoingLinkTextColor = .black @@ -48,6 +51,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta } else { outgoingScamColor = .white + secondaryBadgeTextColor = .white outgoingPrimaryTextColor = .white outgoingSecondaryTextColor = UIColor(rgb: 0xffffff, alpha: 0.5) outgoingLinkTextColor = .white @@ -153,7 +157,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta itemCheckColors: PresentationThemeFillStrokeForeground( fillColor: accentColor, strokeColor: UIColor(rgb: 0xffffff, alpha: 0.5), - foregroundColor: badgeTextColor + foregroundColor: secondaryBadgeTextColor ), controlSecondaryColor: UIColor(rgb: 0xffffff, alpha: 0.5), freeInputField: PresentationInputFieldTheme( @@ -188,7 +192,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta failedForegroundColor: .white, muteIconColor: UIColor(rgb: 0x8e8e92), unreadBadgeActiveBackgroundColor: accentColor, - unreadBadgeActiveTextColor: badgeTextColor, + unreadBadgeActiveTextColor: secondaryBadgeTextColor, unreadBadgeInactiveBackgroundColor: UIColor(rgb: 0x666666), unreadBadgeInactiveTextColor:UIColor(rgb: 0x000000), pinnedBadgeColor: UIColor(rgb: 0x767677), @@ -207,7 +211,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta let message = PresentationThemeChatMessage( incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.4), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0x000000), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff))), - outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.3), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: .white, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: .white, highlight: UIColor(white: 1.0, alpha: 0.12), separator: UIColor(white: 1.0, alpha: 0.3), bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff))), + outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff))), freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f))), infoPrimaryTextColor: .white, infoLinkTextColor: accentColor, @@ -320,7 +324,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta inputPlaceholderColor: UIColor(rgb: 0x8f8f8f), inputTextColor: .white, inputClearButtonColor: UIColor(rgb: 0x8f8f8f), - checkContentColor: .white + checkContentColor: secondaryBadgeTextColor ) let contextMenu = PresentationThemeContextMenu( diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index 92adff2cb6..5f62d281b1 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -16,16 +16,22 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr let outgoingCheckColor: UIColor let outgoingControlColor: UIColor let outgoingBubbleFillColor: UIColor - if accentColor.lightness > 0.7 { + let outgoingBubbleStrokeColor: UIColor + if accentColor.lightness > 0.705 { outgoingPrimaryTextColor = .black - outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.65) + outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.55) outgoingLinkTextColor = .black outgoingCheckColor = .black outgoingControlColor = outgoingPrimaryTextColor outgoingBubbleFillColor = accentColor + if outgoingBubbleFillColor.rgb == 0xffffff { + outgoingBubbleStrokeColor = UIColor(rgb: 0xc8c7cc) + } else { + outgoingBubbleStrokeColor = outgoingBubbleFillColor + } let hsv = accentColor.hsv - accentColor = UIColor(hue: hsv.0, saturation: hsv.1 * 1.1, brightness: min(hsv.2, 0.7), alpha: 1.0) + accentColor = UIColor(hue: hsv.0, saturation: min(1.0, hsv.1 * 1.1), brightness: min(hsv.2, 0.6), alpha: 1.0) } else { outgoingPrimaryTextColor = .white outgoingSecondaryTextColor = UIColor(rgb: 0xffffff, alpha: 0.65) @@ -33,6 +39,7 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr outgoingCheckColor = .white outgoingControlColor = outgoingPrimaryTextColor outgoingBubbleFillColor = accentColor + outgoingBubbleStrokeColor = outgoingBubbleFillColor } let rootTabBar = PresentationThemeRootTabBar( @@ -201,8 +208,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr ) let messageDay = PresentationThemeChatMessage( - incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: UIColor(rgb: 0x0b8bed), fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor)), - outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor)), + incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor)), + outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleStrokeColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor)), freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xDADADE), stroke: UIColor(rgb: 0xE5E5EA)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xE5E5EA))), infoPrimaryTextColor: UIColor(rgb: 0x000000), infoLinkTextColor: UIColor(rgb: 0x004bad), diff --git a/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift b/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift index b1d7061085..fb65ef16a7 100644 --- a/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/MakePresentationTheme.swift @@ -16,6 +16,10 @@ public func makePresentationTheme(themeReference: PresentationThemeReference, ac case .day: theme = makeDefaultDayPresentationTheme(accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: true, preview: preview) } + case let .local(resource): + theme = makeDefaultDayPresentationTheme(serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: false, preview: preview) + case let .cloud(info): + theme = makeDefaultDayPresentationTheme(serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: false, preview: preview) } return theme } diff --git a/submodules/TelegramPresentationData/Sources/PresentationData.swift b/submodules/TelegramPresentationData/Sources/PresentationData.swift index ae7fa81dfa..bee81a651c 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationData.swift @@ -338,6 +338,8 @@ private func automaticThemeShouldSwitchNow(_ parameters: AutomaticThemeSwitchPar default: break } + default: + return false } switch parameters.trigger { case .none: diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift index 10be928c5c..84a91cad0f 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift @@ -2,19 +2,6 @@ import Foundation import UIKit import Display -private func generateArrowImage(_ theme: PresentationTheme) -> UIImage? { - return generateImage(CGSize(width: 7.0, height: 13.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.setStrokeColor(theme.list.disclosureArrowColor.cgColor) - context.setLineWidth(1.98) - context.setLineCap(.round) - context.setLineJoin(.round) - context.translateBy(x: 1.0, y: 1.0) - - let _ = try? drawSvgPath(context, path: "M0,0 L4.79819816,5.27801798 L4.79819816,5.27801798 C4.91262453,5.40388698 4.91262453,5.59611302 4.79819816,5.72198202 L0,11 S ") - }) -} - public func generateItemListCheckIcon(color: UIColor) -> UIImage? { return generateImage(CGSize(width: 12.0, height: 10.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) @@ -29,19 +16,14 @@ public func generateItemListCheckIcon(color: UIColor) -> UIImage? { } public func generateItemListPlusIcon(_ color: UIColor) -> UIImage? { - return generateImage(CGSize(width: 18.0, height: 18.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.setFillColor(color.cgColor) - context.setLineCap(.round) - let lineWidth: CGFloat = 2.0 - context.fill(CGRect(x: floorToScreenPixels((18.0 - lineWidth) / 2.0), y: 0.0, width: lineWidth, height: 18.0)) - context.fill(CGRect(x: 0.0, y: floorToScreenPixels((18.0 - lineWidth) / 2.0), width: 18.0, height: lineWidth)) - }) + return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddIcon"), color: color) } public struct PresentationResourcesItemList { public static func disclosureArrowImage(_ theme: PresentationTheme) -> UIImage? { - return theme.image(PresentationResourceKey.itemListDisclosureArrow.rawValue, generateArrowImage) + return theme.image(PresentationResourceKey.itemListDisclosureArrow.rawValue, { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: theme.list.disclosureArrowColor) + }) } public static func checkIconImage(_ theme: PresentationTheme) -> UIImage? { @@ -113,9 +95,9 @@ public struct PresentationResourcesItemList { }) } - public static func addExceptionIcon(_ theme: PresentationTheme) -> UIImage? { + public static func addChannelIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.itemListAddExceptionIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Item List/AddExceptionIcon"), color: theme.list.itemAccentColor) + return generateTintedImage(image: UIImage(bundleImageName: "Item List/AddChannelIcon"), color: theme.list.itemAccentColor) }) } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift index 2ef6886592..ba4958757d 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift @@ -48,7 +48,7 @@ public struct PresentationResourcesRootController { public static func navigationCallIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.navigationCallIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Info/CallButton"), color: theme.rootController.navigationBar.accentTextColor) + return generateTintedImage(image: UIImage(bundleImageName: "Call List/CallIcon"), color: theme.rootController.navigationBar.accentTextColor) }) } @@ -66,14 +66,15 @@ 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).flatMap({ image in - let factor: CGFloat = 0.8 - let size = CGSize(width: floor(image.size.width * factor), height: floor(image.size.height * factor)) - return generateImage(size, contextGenerator: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) - }) - }) + return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.rootController.navigationBar.accentTextColor) +// return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.rootController.navigationBar.accentTextColor).flatMap({ image in +// let factor: CGFloat = 0.8 +// let size = CGSize(width: floor(image.size.width * factor), height: floor(image.size.height * factor)) +// return generateImage(size, contextGenerator: { size, context in +// context.clear(CGRect(origin: CGPoint(), size: size)) +// context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) +// }) +// }) }) } diff --git a/submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/Contents.json new file mode 100644 index 0000000000..679a5bed44 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_addcall.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/ic_addcall.pdf b/submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/ic_addcall.pdf new file mode 100644 index 0000000000000000000000000000000000000000..266479298e68f4bd80a02c33985f67953dd7d412 GIT binary patch literal 4939 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 R1%XSzU?5&zH63-({{d)Bvi<-7 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/Contents.json index 0300ffdc7f..65904a6348 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "DialogList_Muted@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "ic_mutedchat.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/DialogList_Muted@2x.png b/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/DialogList_Muted@2x.png deleted file mode 100644 index 06f297b781957d1b4773242e1ca2601212dabe76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqEBNeutY4NNL8^S!d@G~E_u~a{;Q+l zT9yfcb$@ui^)J3=RH0w>(U<-Fr|aQv>LF4s`w~>IFqQ9QJ6q$}#_k4mB7>)^pUXO@ GgeCy1I7}A+ diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/ic_mutedchat.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/ic_mutedchat.pdf new file mode 100644 index 0000000000000000000000000000000000000000..830c7b25335dca902e26c29d70decebb184d88c0 GIT binary patch literal 4283 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 RRmuPW002ovPDHLkV1mMN!>s@S diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ConversationChannelInlineShareIcon@3x.png b/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ConversationChannelInlineShareIcon@3x.png deleted file mode 100644 index 600f3a10be6565dee609203f7f8c6b52dd1cb11e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 622 zcmV-!0+IcRP)Px%C`m*?R9Fe+mOU>7Q51%E5i}wZA|zP7_=r{^LZMJltZbq43v{9re?cWt`3(vo z@ll9MA{rsFB%(se5?Rk#vy+|KYv#_*oomBM-ktk-&NFAu-I-MCKUuL@%%O~ldu}k{ z#OFi64ov_i0Vk{jCIFLyGXj_-Tx`Ik;bH;WfU6nM7F6v<#j2=)buzw> zMpW~b#*UPk18x_9SxXSAFr~~@O#rrxe$&)72a){SU!t}uYC8kpwdTAr{O{JadI6JM zxHU)Cdg$Mx7MZYYo{W`aS7vU7!MXtxz*6SOS|ja8)Fp!<_QCu)Iz?CL#gU9lt>(}o z(lKSmJR5$J|rtbK!QIoL1Hy@iso`ZlMjnxgNYzv3lVd zE4$}5?-jRt#+ZCKMl$wJQ9*0Y)M3;$BNVtN;K207*qo IM6N<$f)XthHUIzs diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ic_chat_share.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ic_chat_share.pdf new file mode 100644 index 0000000000000000000000000000000000000000..156646235388cd03e19f93b68db80891aa41f491 GIT binary patch literal 4140 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/Contents.json new file mode 100644 index 0000000000..8033bd89cd --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_addchannel.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/ic_addchannel.pdf b/submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/ic_addchannel.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0eb106737a906a74754ee7d31d74c31073932ba7 GIT binary patch literal 4747 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 Z{wYFNH*#tG{2&N896~QBsG_b)|35u6RYCv& literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/Contents.json deleted file mode 100644 index ebc5665c79..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "plus@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "plus@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/plus@2x.png b/submodules/TelegramUI/Images.xcassets/Item List/AddExceptionIcon.imageset/plus@2x.png deleted file mode 100644 index 7fd6bbc381fc5f2bc63fb72465283add508cfcfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1133 zcmV-z1d{uSP) z1F&pI5IwPN+bG1gokDEePG;uYF2pFrP9e5!JDFa6+xPFO?YjH84|_4EYTkRR+4FmP z`gEUf(mP0OhGFk$z zcr2XU4=W`YAN6QinNeNO!4%HHkjhHP1g>~Ito+cLjQkEOBS-{J4u>o52sdSQxbli{ zm960l2g1qea0LS+pH|nx__I76Cs*U1XsNUa?+GjK#b3zdH*yC__I$6(`Y?sdVEqTJ z_H)Hk5k-q3F_2&!B`L%BtCETkbH4Qn%9C)iJemS8k@b&hL>5%nAm(H`cSbIRk;(cG z_!9jmY=mCHmDh!p&y@5O^eG&JwBcl182_tAz6e*?y%&*I(!6-CusyQEpvkMj`qwn_ z72nd zVY=w)Ux&^R*J)OdKtP4os84{Kv}(tSsC7D#k~etbDq7N-hxKpN$QOpRzYKK|xGJG9 z`-nXWWj}#3xhqqcjT_vA4O^?1%5GwcX#IcO6HYcp5^(ufHSJs2Az0!}XI@b5*{`wOSBADj`ptB4dA5^KC< zUM)KROQZMk#3?X6ApVmQm%03-+X_b+ry}w*Nh~&WPEPE>82POd_Y?UCr|}+Kk`Kf<);?$ps7xOTC2=!n(x`eNq-ICMD#0fg>JX5MvD5u7g=?(jC4$z zJ-sU(iXMTcdfjmRo(5zVIj!lTq>JbG-YuG;8R4&_d{Na$(QHewwYRc6nUt!akwmk}C9gWe|_GlL2@NUt`X)2O_!l?~@`M zCQZBSK?9Or9&Is_Y%tAF%kgcKKlE=V9|N8j=$%o<$I`0@-nm6e_IwL zbmwx?cN^jGcOkZyl`O^05d(=~98Cj0j&p#8tEvssIQfyv3!#WAFUaqSJ( zBCSAy)`!i%xFkyN_a~>n!fXDN^b7A9OSCtczF}Pa(P8-?ss4t0 z0cL8!31-(VKT8_SeWCH4DT1%$UIM4i9e3xj4fkFc%{E=rcf?0*!`v4^lXZ2}HeJf& zy1iGge1Gf90I{iZYt(lgOX0PB&3}*8Y}Yxq7g7H<9tgS~#Z*~anQ<^@K{jvNg;g(t zMcs;H^X-~e*{Nh3T+=-k;QcC=tBUz%WlhzTxsLon@>6v=YHwCOi`@20pk{qtVFrH+ z_k^{M(vNen&}c9UnEcYn5d*5%^GN8e47EK4vxEOqYkx6OaM59a(ofB5&jhhN{GnD_AQ*V^+R y3jUru99kB;|FgmU{Sxn+-h2=8U$}g8^-BB8@zFD^&o!2Sa+;^BpUXO@geCy@)~mq) diff --git a/submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/Contents.json new file mode 100644 index 0000000000..73d58c942a --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_open.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/ic_open.pdf b/submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/ic_open.pdf new file mode 100644 index 0000000000000000000000000000000000000000..058efa69b64243ee50dd31f06698fddc3d94c7dd GIT binary patch literal 3972 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 100.0 { + print() + } + self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop) }) @@ -4554,11 +4558,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.chatDisplayNode.updateChatPresentationInterfaceState(updatedChatPresentationInterfaceState, transition: transition, interactive: interactive) } - if let selectionState = self.presentationInterfaceState.interfaceState.selectionState, !selectionState.selectedIds.isEmpty { - self.chatTitleView?.titleContent = .custom(self.presentationData.strings.Conversation_SelectedMessages(Int32(selectionState.selectedIds.count))) - } else { - - } +// if let selectionState = self.presentationInterfaceState.interfaceState.selectionState, !selectionState.selectedIds.isEmpty { +// self.chatTitleView?.titleContent = .custom(self.presentationData.strings.Conversation_SelectedMessages(Int32(selectionState.selectedIds.count))) +// } else { +// +// } if let button = leftNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: self.leftNavigationButton, target: self, selector: #selector(self.leftNavigationButtonAction)) { if self.leftNavigationButton != button { @@ -4892,14 +4896,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return result } let controller = legacyAttachmentMenu(context: strongSelf.context, peer: peer, editMediaOptions: menuEditMediaOptions, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, parentController: legacyController, recentlyUsedInlineBots: strongSelf.recentlyUsedInlineBotsValue, initialCaption: inputText.string, openGallery: { - self?.presentMediaPicker(fileMode: false, editingMedia: editMediaOptions != nil, completion: { signals, silentPosting in + self?.presentMediaPicker(fileMode: false, editingMedia: editMediaOptions != nil, completion: { signals, silentPosting, scheduleTime in if !inputText.string.isEmpty { //strongSelf.clearInputText() } if editMediaOptions != nil { self?.editMessageMediaWithLegacySignals(signals) } else { - self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting) + self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil) } }) }, openCamera: { [weak self] cameraView, menuController in @@ -4919,6 +4923,26 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self, let (host, port, username, password, secret) = parseProxyUrl(code) { strongSelf.openResolved(ResolvedUrl.proxy(host: host, port: port, username: username, password: password, secret: secret)) } + }, presentScheduleController: { [weak self] done in + guard let strongSelf = self else { + return + } + let mode: ChatScheduleTimeControllerMode + if case let .peer(peerId) = strongSelf.presentationInterfaceState.chatLocation, peerId == strongSelf.context.account.peerId { + mode = .reminders + } else { + mode = .scheduledMessages + } + let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, completion: { [weak self] time in + if let strongSelf = self { + done(time) + if !strongSelf.presentationInterfaceState.isScheduledMessages { + let controller = ChatControllerImpl(context: strongSelf.context, chatLocation: strongSelf.chatLocation, subject: .scheduledMessages) + (strongSelf.navigationController as? NavigationController)?.pushViewController(controller) + } + } + }) + strongSelf.present(controller, in: .window(.root)) }) } }, openFileGallery: { @@ -4941,16 +4965,34 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentMultipleFilesDisabled, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - }, sendMessagesWithSignals: { [weak self] signals, mode in + }, presentScheduleController: { [weak self] done in + guard let strongSelf = self else { + return + } + let mode: ChatScheduleTimeControllerMode + if case let .peer(peerId) = strongSelf.presentationInterfaceState.chatLocation, peerId == strongSelf.context.account.peerId { + mode = .reminders + } else { + mode = .scheduledMessages + } + let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, completion: { [weak self] time in + if let strongSelf = self { + done(time) + if !strongSelf.presentationInterfaceState.isScheduledMessages { + let controller = ChatControllerImpl(context: strongSelf.context, chatLocation: strongSelf.chatLocation, subject: .scheduledMessages) + (strongSelf.navigationController as? NavigationController)?.pushViewController(controller) + } + } + }) + strongSelf.present(controller, in: .window(.root)) + }, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in if !inputText.string.isEmpty { //strongSelf.clearInputText() } if editMediaOptions != nil { self?.editMessageMediaWithLegacySignals(signals!) } else { - var silentPosting = false - //if mode == - self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting) + self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil) } }, selectRecentlyUsedInlineBot: { [weak self] peer in if let strongSelf = self, let addressName = peer.addressName { @@ -4986,11 +5028,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G ActionSheetButtonItem(title: self.presentationData.strings.Conversation_FilePhotoOrVideo, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { - strongSelf.presentMediaPicker(fileMode: true, editingMedia: editingMessage, completion: { signals, silentPosting in + strongSelf.presentMediaPicker(fileMode: true, editingMedia: editingMessage, completion: { signals, silentPosting, scheduleTime in if editingMessage { self?.editMessageMediaWithLegacySignals(signals) } else { - self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting) + self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil) } }) } @@ -5054,7 +5096,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.present(actionSheet, in: .window(.root)) } - private func presentMediaPicker(fileMode: Bool, editingMedia: Bool, completion: @escaping ([Any], Bool) -> Void) { + private func presentMediaPicker(fileMode: Bool, editingMedia: Bool, completion: @escaping ([Any], Bool, Int32) -> Void) { let postbox = self.context.account.postbox let _ = (self.context.sharedContext.accountManager.transaction { transaction -> Signal<(GeneratedMediaStoreSettings, SearchBotsConfiguration), NoError> in let entry = transaction.getSharedData(ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings) as? GeneratedMediaStoreSettings @@ -5108,13 +5150,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentLimitReached, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + }, presentScheduleController: { [weak self] done in + guard let strongSelf = self else { + return + } + let mode: ChatScheduleTimeControllerMode + if case let .peer(peerId) = strongSelf.presentationInterfaceState.chatLocation, peerId == strongSelf.context.account.peerId { + mode = .reminders + } else { + mode = .scheduledMessages + } + let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, completion: { [weak self] time in + if let strongSelf = self { + done(time) + if !strongSelf.presentationInterfaceState.isScheduledMessages { + let controller = ChatControllerImpl(context: strongSelf.context, chatLocation: strongSelf.chatLocation, subject: .scheduledMessages) + (strongSelf.navigationController as? NavigationController)?.pushViewController(controller) + } + } + }) + strongSelf.present(controller, in: .window(.root)) }) controller.descriptionGenerator = legacyAssetPickerItemGenerator() - controller.completionBlock = { [weak legacyController] signals, mode in + controller.completionBlock = { [weak legacyController] signals, silentPosting, scheduleTime in if let legacyController = legacyController { legacyController.dismiss() - var silentPosting = false - completion(signals!, silentPosting) + completion(signals!, silentPosting, scheduleTime) } } controller.dismissalBlock = { [weak legacyController] in @@ -5381,7 +5442,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else { mode = .scheduledMessages } - let controller = ChatScheduleTimeController(context: self.context, mode: mode, completion: { [weak self] time in + let controller = ChatScheduleTimeController(context: self.context, mode: mode, dismissByTapOutside: false, completion: { [weak self] time in if let strongSelf = self { strongSelf.sendMessages(strongSelf.transformEnqueueMessages(messages, silentPosting: false, scheduleTime: time), commit: true) } diff --git a/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift index 35adb0d555..259e46dd23 100644 --- a/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/TelegramUI/ChatHistoryEntriesForView.swift @@ -20,7 +20,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, } } } - + var groupBucket: [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes)] = [] loop: for entry in view.entries { for media in entry.message.media { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift index ec7696a61a..a9dc1f3898 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift @@ -400,7 +400,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode let reactionRecognizer = ReactionSwipeGestureRecognizer(target: nil, action: nil) self.reactionRecognizer = reactionRecognizer reactionRecognizer.availableReactions = { [weak self] in - guard let strongSelf = self, let item = strongSelf.item, !Namespaces.Message.allScheduled.contains(item.message.id.namespace) else { + guard let strongSelf = self, let item = strongSelf.item, !item.presentationData.isPreview && !Namespaces.Message.allScheduled.contains(item.message.id.namespace) else { return [] } if strongSelf.selectionNode != nil { diff --git a/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift index 0e377b5f14..3cdbbaf05d 100644 --- a/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatRecentActionsControllerNode.swift @@ -817,6 +817,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }) case .wallpaper: break + case .theme: + break } } })) diff --git a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift index 06bff51447..852511b40d 100644 --- a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeController.swift @@ -22,14 +22,16 @@ final class ChatScheduleTimeController: ViewController { private let context: AccountContext private let mode: ChatScheduleTimeControllerMode private let currentTime: Int32? + private let dismissByTapOutside: Bool private let completion: (Int32) -> Void private var presentationDataDisposable: Disposable? - init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32? = nil, completion: @escaping (Int32) -> Void) { + init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) { self.context = context self.mode = mode self.currentTime = currentTime + self.dismissByTapOutside = dismissByTapOutside self.completion = completion super.init(navigationBarPresentationData: nil) @@ -53,7 +55,7 @@ final class ChatScheduleTimeController: ViewController { } override public func loadDisplayNode() { - self.displayNode = ChatScheduleTimeControllerNode(context: self.context, mode: self.mode, currentTime: self.currentTime) + self.displayNode = ChatScheduleTimeControllerNode(context: self.context, mode: self.mode, currentTime: self.currentTime, dismissByTapOutside: self.dismissByTapOutside) self.controllerNode.completion = { [weak self] time in self?.completion(time + 5) self?.dismiss() diff --git a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift index 2f1edf11ca..ee96d48d71 100644 --- a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift @@ -14,6 +14,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel private let context: AccountContext private let mode: ChatScheduleTimeControllerMode private var presentationData: PresentationData + private let dismissByTapOutside: Bool private let dimNode: ASDisplayNode private let wrappingScrollNode: ASScrollNode @@ -32,10 +33,11 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel var dismiss: (() -> Void)? var cancel: (() -> Void)? - init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32?) { + init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32?, dismissByTapOutside: Bool) { self.context = context self.mode = mode self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + self.dismissByTapOutside = dismissByTapOutside self.wrappingScrollNode = ASScrollNode() self.wrappingScrollNode.view.alwaysBounceVertical = true @@ -208,7 +210,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel } @objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { + if self.dismissByTapOutside, case .ended = recognizer.state { self.cancelButtonPressed() } } diff --git a/submodules/TelegramUI/TelegramUI/LegacyCamera.swift b/submodules/TelegramUI/TelegramUI/LegacyCamera.swift index 0dc3f57023..9becf57ef4 100644 --- a/submodules/TelegramUI/TelegramUI/LegacyCamera.swift +++ b/submodules/TelegramUI/TelegramUI/LegacyCamera.swift @@ -10,7 +10,7 @@ import ShareController import LegacyUI import LegacyMediaPickerUI -func presentedLegacyCamera(context: AccountContext, peer: Peer, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, sendMessagesWithSignals: @escaping ([Any]?) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }) { +func presentedLegacyCamera(context: AccountContext, peer: Peer, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, sendMessagesWithSignals: @escaping ([Any]?) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentScheduleController: @escaping (@escaping (Int32) -> Void) -> Void) { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme) legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait) diff --git a/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift b/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift index 83f94dad37..0a16d86dd7 100644 --- a/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift +++ b/submodules/TelegramUI/TelegramUI/OpenChatMessage.swift @@ -138,7 +138,9 @@ private func chatMessageGalleryControllerData(context: AccountContext, message: if let file = galleryMedia as? TelegramMediaFile { if let fileName = file.fileName { let ext = (fileName as NSString).pathExtension.lowercased() - if ext == "wav" || ext == "opus" { + if ext == "tgios-theme" { + return .theme(file) + } else if ext == "wav" || ext == "opus" { return .audio(file) } else if ext == "json", let fileSize = file.size, fileSize < 1024 * 1024 { if let path = context.account.postbox.mediaBox.completedResourcePath(file.resource), let _ = LOTComposition(filePath: path) { @@ -365,7 +367,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { return nil })) case let .theme(media): - let controller = ThemePreviewController(context: params.context, previewTheme: makeDefaultDayPresentationTheme(accentColor: nil, serviceBackgroundColor: .black, baseColor: nil, day: true, preview: false), media: .message(message: MessageReference(params.message), media: media)) + let controller = ThemePreviewController(context: params.context, previewTheme: makeDefaultDayPresentationTheme(accentColor: nil, serviceBackgroundColor: .black, baseColor: nil, day: true, preview: false), source: .media(.message(message: MessageReference(params.message), media: media))) params.present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } } diff --git a/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift b/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift index c5e09200be..30bc71d58a 100644 --- a/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/TelegramUI/OpenResolvedUrl.swift @@ -5,6 +5,7 @@ import Postbox import Display import SwiftSignalKit import TelegramUIPreferences +import TelegramPresentationData import AccountContext import OverlayStatusController import AlertUI @@ -236,5 +237,22 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur controller?.dismiss() }) dismissInput() + case let .theme(slug): + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + let signal = getTheme(account: context.account, slug: slug) + let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil)) + present(controller, nil) + + let _ = (signal + |> deliverOnMainQueue).start(next: { [weak controller] theme in + controller?.dismiss() + let previewTheme = makePresentationTheme(themeReference: .cloud(theme), accentColor: nil, serviceBackgroundColor: .black, baseColor: nil) + let previewController = ThemePreviewController(context: context, previewTheme: previewTheme, source: .theme(theme)) + present(previewController, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + }, error: { [weak controller] error in + controller?.dismiss() + }) + dismissInput() } } diff --git a/submodules/TelegramUI/TelegramUI/OpenUrl.swift b/submodules/TelegramUI/TelegramUI/OpenUrl.swift index 0aba6f1aa2..cee562f1d5 100644 --- a/submodules/TelegramUI/TelegramUI/OpenUrl.swift +++ b/submodules/TelegramUI/TelegramUI/OpenUrl.swift @@ -538,6 +538,22 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur convertedUrl = "https://t.me/bg/\(parameter)\(mode)" } } + } else if parsedUrl.host == "addtheme" { + if let components = URLComponents(string: "/?" + query) { + var parameter: String? + if let queryItems = components.queryItems { + for queryItem in queryItems { + if let value = queryItem.value { + if queryItem.name == "slug" { + parameter = value + } + } + } + } + if let parameter = parameter { + convertedUrl = "https://t.me/addtheme/\(parameter)" + } + } } if parsedUrl.host == "resolve" { diff --git a/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift b/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift index 482392b2dc..764864694d 100644 --- a/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift +++ b/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift @@ -28,11 +28,17 @@ public struct WallpaperPresentationOptions: OptionSet { public enum PresentationThemeReference: PostboxCoding, Equatable { case builtin(PresentationBuiltinThemeReference) + case local(LocalFileMediaResource) + case cloud(TelegramTheme) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("v", orElse: 0) { case 0: self = .builtin(PresentationBuiltinThemeReference(rawValue: decoder.decodeInt32ForKey("t", orElse: 0))!) + case 1: + self = .local(decoder.decodeObjectForKey("resource", decoder: { LocalFileMediaResource(decoder: $0) }) as! LocalFileMediaResource) + case 2: + self = .cloud(decoder.decodeObjectForKey("theme", decoder: { TelegramTheme(decoder: $0) }) as! TelegramTheme) default: assertionFailure() self = .builtin(.dayClassic) @@ -44,6 +50,12 @@ public enum PresentationThemeReference: PostboxCoding, Equatable { case let .builtin(reference): encoder.encodeInt32(0, forKey: "v") encoder.encodeInt32(reference.rawValue, forKey: "t") + case let .local(resource): + encoder.encodeInt32(1, forKey: "v") + encoder.encodeObject(resource, forKey: "resource") + case let .cloud(theme): + encoder.encodeInt32(2, forKey: "v") + encoder.encodeObject(theme, forKey: "theme") } } @@ -55,6 +67,18 @@ public enum PresentationThemeReference: PostboxCoding, Equatable { } else { return false } + case let .local(lhsResource): + if case let .local(rhsResource) = rhs, lhsResource.isEqual(to: rhsResource) { + return true + } else { + return false + } + case let .cloud(lhsTheme): + if case let .cloud(rhsTheme) = rhs, lhsTheme == rhsTheme { + return true + } else { + return false + } } } @@ -65,6 +89,12 @@ public enum PresentationThemeReference: PostboxCoding, Equatable { case let .builtin(reference): namespace = 0 id = reference.rawValue + case let .local(resource): + namespace = 1 + id = 1//1reference.rawValue + case let .cloud(theme): + namespace = 2 + id = Int32(clamping: theme.id) } return (Int64(namespace) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: id))) @@ -302,6 +332,14 @@ public struct PresentationThemeSettings: PreferencesEntry { for (_, chatWallpaper) in self.themeSpecificChatWallpapers { resources.append(contentsOf: wallpaperResources(chatWallpaper)) } + switch self.theme { + case .builtin: + break + case let .local(resource): + resources.append(resource.id) + case let .cloud(theme): + resources.append(theme.file.resource.id) + } return resources } diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 439601002e..e73c413b63 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -31,6 +31,7 @@ public enum ParsedInternalUrl { case cancelAccountReset(phone: String, hash: String) case share(url: String?, text: String?, to: String?) case wallpaper(WallpaperUrlParameter) + case theme(String) } private enum ParsedUrl { @@ -202,6 +203,8 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { parameter = .slug(component, options, color, intensity) } return .wallpaper(parameter) + } else if pathComponents[0] == "addtheme" { + return .theme(pathComponents[1]) } else if pathComponents.count == 3 && pathComponents[0] == "c" { if let channelId = Int32(pathComponents[1]), let messageId = Int32(pathComponents[2]) { return .privateMessage(MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: messageId)) @@ -305,6 +308,8 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig return .single(.share(url: url, text: text, to: to)) case let .wallpaper(parameter): return .single(.wallpaper(parameter)) + case let .theme(slug): + return .single(.theme(slug)) } }