diff --git a/submodules/ActivityIndicator/Sources/ActivityIndicator.swift b/submodules/ActivityIndicator/Sources/ActivityIndicator.swift index da9e973855..8a91a27ab3 100644 --- a/submodules/ActivityIndicator/Sources/ActivityIndicator.swift +++ b/submodules/ActivityIndicator/Sources/ActivityIndicator.swift @@ -97,7 +97,7 @@ public final class ActivityIndicator: ASDisplayNode { super.init() if case let .custom(_, _, _, forceCustom) = self.type, forceCustom { - self.isLayerBacked = true + //self.isLayerBacked = true } switch type { diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 5fa2cb54b5..c1ea7b5ad8 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2705,6 +2705,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self?.push(c) }, presentController: { [weak self] c in self?.present(c, in: .window(.root)) + }, completed: { }, linkUpdated: { _ in }) } @@ -2816,33 +2817,38 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - let previewScreen = ChatFolderLinkPreviewScreen( - context: self.context, - subject: .remove(folderId: id, defaultSelectedPeerIds: defaultSelectedPeerIds), - contents: ChatFolderLinkContents( - localFilterId: id, - title: title, - peers: peers.compactMap { $0 }.filter { peer in - if case .channel = peer { - return true - } else { - return false - } - }, - alreadyMemberPeerIds: Set(), - memberCounts: memberCounts - ), - completion: { [weak self] in - guard let self else { - return - } - if self.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id == id { - self.chatListDisplayNode.mainContainerNode.switchToFilter(id: .all, completion: { - }) - } + let filteredPeers = peers.compactMap { $0 }.filter { peer in + if case .channel = peer { + return true + } else { + return false } - ) - self.push(previewScreen) + } + if filteredPeers.isEmpty { + apply() + } else { + let previewScreen = ChatFolderLinkPreviewScreen( + context: self.context, + subject: .remove(folderId: id, defaultSelectedPeerIds: defaultSelectedPeerIds), + contents: ChatFolderLinkContents( + localFilterId: id, + title: title, + peers: filteredPeers, + alreadyMemberPeerIds: Set(), + memberCounts: memberCounts + ), + completion: { [weak self] in + guard let self else { + return + } + if self.chatListDisplayNode.mainContainerNode.currentItemNode.chatListFilter?.id == id { + self.chatListDisplayNode.mainContainerNode.switchToFilter(id: .all, completion: { + }) + } + } + ) + self.push(previewScreen) + } } if hasLinks { diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index fede201ed7..adea419845 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -1084,7 +1084,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat var dismissImpl: (() -> Void)? var focusOnNameImpl: (() -> Void)? var clearFocusImpl: (() -> Void)? - var applyImpl: ((@escaping () -> Void) -> Void)? + var applyImpl: ((Bool, @escaping () -> Void) -> Void)? let sharedLinks = Promise<[ExportedChatFolderLink]?>(nil) if let currentPreset { @@ -1297,11 +1297,16 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat }, createLink: { if currentPreset == nil { + //TODO:localize let presentationData = context.sharedContext.currentPresentationData.with { $0 } let text = "Please finish creating this folder to share it." presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) } else { - applyImpl?({ + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let statusController = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) + presentControllerImpl?(statusController, nil) + + applyImpl?(true, { [weak statusController] in let state = stateValue.with({ $0 }) if let currentPreset, let data = currentPreset.data { @@ -1315,17 +1320,24 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat unavailableText = "You can’t share folders which have chat types or excluded chats." } if let unavailableText { + statusController?.dismiss() + let presentationData = context.sharedContext.currentPresentationData.with { $0 } presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: unavailableText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) return } + var statusController = statusController + var previousLink: ExportedChatFolderLink? openCreateChatListFolderLink(context: context, folderId: currentPreset.id, checkIfExists: false, title: currentPreset.title, peerIds: state.additionallyIncludePeers, pushController: { c in pushControllerImpl?(c) }, presentController: { c in presentControllerImpl?(c, nil) + }, completed: { + statusController?.dismiss() + statusController = nil }, linkUpdated: { updatedLink in let previousLinkValue = previousLink previousLink = updatedLink @@ -1347,12 +1359,14 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat sharedLinks.set(.single(links)) }) }) + } else { + statusController?.dismiss() } }) } }, openLink: { link in if let currentPreset, let _ = currentPreset.data { - applyImpl?({ + applyImpl?(false, { let state = stateValue.with({ $0 }) pushControllerImpl?(folderInviteLinkListController(context: context, filterId: currentPreset.id, title: currentPreset.title, allPeerIds: state.additionallyIncludePeers, currentInvitation: link, linkUpdated: { updatedLink in if updatedLink != link { @@ -1394,14 +1408,13 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat ) var attemptNavigationImpl: (() -> Bool)? - applyImpl = { completed in + applyImpl = { waitForSync, completed in let state = stateValue.with { $0 } var includePeers = ChatListFilterIncludePeers() includePeers.setPeers(state.additionallyIncludePeers) let _ = (context.engine.peers.updateChatListFiltersInteractively { filters in - var filterId = currentPreset?.id ?? -1 if currentPreset == nil { filterId = context.engine.peers.generateNewChatListFilterId(filters: filters) @@ -1440,7 +1453,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat |> deliverOnMainQueue).start(next: { filters in updated(filters) - if let currentPreset { + if let currentPreset, waitForSync { let _ = (context.engine.peers.updatedChatListFilters() |> filter { filters -> Bool in for filter in filters { @@ -1490,7 +1503,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat } }) let rightNavigationButton = ItemListNavigationButton(content: .text(currentPreset == nil ? presentationData.strings.Common_Create : presentationData.strings.Common_Done), style: .bold, enabled: state.isComplete, action: { - applyImpl?({ + applyImpl?(false, { dismissImpl?() }) }) @@ -1559,7 +1572,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat }), //TODO:localize TextAlertAction(type: .defaultAction, title: "Save", action: { - applyImpl?({ + applyImpl?(false, { dismissImpl?() }) })]), nil) @@ -1594,8 +1607,9 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat return controller } -func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, checkIfExists: Bool, title: String, peerIds: [EnginePeer.Id], pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController) -> Void, linkUpdated: @escaping (ExportedChatFolderLink?) -> Void) { +func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, checkIfExists: Bool, title: String, peerIds: [EnginePeer.Id], pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController) -> Void, completed: @escaping () -> Void, linkUpdated: @escaping (ExportedChatFolderLink?) -> Void) { if peerIds.isEmpty { + completed() return } @@ -1610,6 +1624,9 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec |> map { result, peers -> ExportedChatFolderLink? in var enabledPeerIds: [EnginePeer.Id] = [] for peer in peers { + if case let .legacyGroup(group) = peer, group.migrationReference != nil { + continue + } if let peer, canShareLinkToPeer(peer: peer) { enabledPeerIds.append(peer.id) } @@ -1634,6 +1651,7 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec let _ = (existingLink |> deliverOnMainQueue).start(next: { existingLink in if let existingLink { + completed() pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peerIds, currentInvitation: existingLink, linkUpdated: linkUpdated)) return @@ -1643,9 +1661,18 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec EngineDataList(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) ) |> deliverOnMainQueue).start(next: { peers in - let peers = peers.compactMap({ $0 }) + let peers = peers.compactMap({ peer -> EnginePeer? in + guard let peer else { + return nil + } + if case let .legacyGroup(group) = peer, group.migrationReference != nil { + return nil + } + return peer + }) if peers.allSatisfy({ !canShareLinkToPeer(peer: $0) }) { - pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peerIds, currentInvitation: nil, linkUpdated: linkUpdated)) + completed() + pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peers.map(\.id), currentInvitation: nil, linkUpdated: linkUpdated)) } else { var enabledPeerIds: [EnginePeer.Id] = [] for peer in peers { @@ -1656,10 +1683,12 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec let _ = (context.engine.peers.exportChatFolder(filterId: folderId, title: "", peerIds: enabledPeerIds) |> deliverOnMainQueue).start(next: { link in + completed() linkUpdated(link) - pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peerIds, currentInvitation: link, linkUpdated: linkUpdated)) + pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peers.map(\.id), currentInvitation: link, linkUpdated: linkUpdated)) }, error: { error in + completed() //TODO:localize let text: String switch error { diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift index 52eff9e601..b8440a7aa9 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift @@ -416,24 +416,37 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch } let confirmDeleteFolder: () -> Void = { - let previewScreen = ChatFolderLinkPreviewScreen( - context: context, - subject: .remove(folderId: id, defaultSelectedPeerIds: defaultSelectedPeerIds), - contents: ChatFolderLinkContents( - localFilterId: id, - title: title, - peers: peers.compactMap { $0 }.filter { peer in - if case .channel = peer { - return true - } else { - return false - } - }, - alreadyMemberPeerIds: Set(), - memberCounts: memberCounts + let filteredPeers = peers.compactMap { $0 }.filter { peer in + if case .channel = peer { + return true + } else { + return false + } + } + + if filteredPeers.isEmpty { + let _ = (context.engine.peers.updateChatListFiltersInteractively { filters in + var filters = filters + if let index = filters.firstIndex(where: { $0.id == id }) { + filters.remove(at: index) + } + return filters + } + |> deliverOnMainQueue).start() + } else { + let previewScreen = ChatFolderLinkPreviewScreen( + context: context, + subject: .remove(folderId: id, defaultSelectedPeerIds: defaultSelectedPeerIds), + contents: ChatFolderLinkContents( + localFilterId: id, + title: title, + peers: filteredPeers, + alreadyMemberPeerIds: Set(), + memberCounts: memberCounts + ) ) - ) - pushControllerImpl?(previewScreen) + pushControllerImpl?(previewScreen) + } } if hasLinks { diff --git a/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift b/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift index 0bd2beb65e..f7c0dc6cab 100644 --- a/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift +++ b/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift @@ -521,7 +521,15 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese text = "You can't share private chats" } } else { - text = "You don't have the admin rights to share invite links to this group chat." + var isGroup = true + if case let .channel(channel) = peer, case .broadcast = channel.info { + isGroup = false + } + if isGroup { + text = "You don't have the admin rights to share invite links to this group chat." + } else { + text = "You don't have the admin rights to share invite links to this channel." + } } dismissTooltipsImpl?() displayTooltipImpl?(.peers(context: context, peers: [peer], title: nil, text: text, customUndoText: nil)) @@ -557,6 +565,17 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese let allPeers = context.engine.data.subscribe( EngineDataList(combinedPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) ) + |> map { peers -> [EnginePeer] in + return peers.compactMap({ peer -> EnginePeer? in + guard let peer else { + return nil + } + if case let .legacyGroup(group) = peer, group.migrationReference != nil { + return nil + } + return peer + }) + } let applyChangesImpl: (() -> Void)? = { let state = stateValue.with({ $0 }) @@ -608,9 +627,9 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese state.selectedPeerIds.insert(peerId) } } else { - for peerId in allPeerIds { - if let peer = peers.first(where: { $0?.id == peerId }), let peerValue = peer { - if canShareLinkToPeer(peer: peerValue) { + for peerId in peers.map(\.id) { + if let peer = peers.first(where: { $0.id == peerId }) { + if canShareLinkToPeer(peer: peer) { state.selectedPeerIds.insert(peerId) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift index 1274b09b2f..de68152bfe 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift @@ -366,12 +366,10 @@ func _internal_checkChatFolderLink(account: Account, slug: String) -> Signal public let isEnabled: Bool + public let displaysProgress: Bool public let action: () -> Void public init( background: Background, content: AnyComponentWithIdentity, isEnabled: Bool, + displaysProgress: Bool, action: @escaping () -> Void ) { self.background = background self.content = content self.isEnabled = isEnabled + self.displaysProgress = displaysProgress self.action = action } @@ -308,6 +315,9 @@ public final class ButtonComponent: Component { if lhs.isEnabled != rhs.isEnabled { return false } + if lhs.displaysProgress != rhs.displaysProgress { + return false + } return true } @@ -326,6 +336,8 @@ public final class ButtonComponent: Component { private var contentItem: ContentItem? + private var activityIndicator: ActivityIndicator? + override init(frame: CGRect) { super.init(frame: frame) @@ -363,10 +375,17 @@ public final class ButtonComponent: Component { self.component = component self.componentState = state - self.isEnabled = component.isEnabled + self.isEnabled = component.isEnabled && !component.displaysProgress transition.setBackgroundColor(view: self, color: component.background.color) transition.setCornerRadius(layer: self.layer, cornerRadius: component.background.cornerRadius) + + var contentAlpha: CGFloat = 1.0 + if component.displaysProgress { + contentAlpha = 0.0 + } else if !component.isEnabled { + contentAlpha = 0.7 + } var previousContentItem: ContentItem? let contentItem: ContentItem @@ -398,11 +417,11 @@ public final class ButtonComponent: Component { let contentFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - contentSize.width) * 0.5), y: floor((availableSize.height - contentSize.height) * 0.5)), size: contentSize) contentTransition.setFrame(view: contentView, frame: contentFrame) - contentTransition.setAlpha(view: contentView, alpha: component.isEnabled ? 1.0 : 0.7) + contentTransition.setAlpha(view: contentView, alpha: contentAlpha) if animateIn && previousContentItem != nil && !transition.animation.isImmediate { contentView.layer.animateScale(from: 0.4, to: 1.0, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring) - contentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + contentView.layer.animateAlpha(from: 0.0, to: contentAlpha, duration: 0.1) contentView.layer.animatePosition(from: CGPoint(x: 0.0, y: -availableSize.height * 0.15), to: CGPoint(), duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring, additive: true) } } @@ -410,7 +429,7 @@ public final class ButtonComponent: Component { if let previousContentItem, let previousContentView = previousContentItem.view.view { if !transition.animation.isImmediate { previousContentView.layer.animateScale(from: 1.0, to: 0.0, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) - previousContentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousContentView] _ in + previousContentView.layer.animateAlpha(from: contentAlpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousContentView] _ in previousContentView?.removeFromSuperview() }) previousContentView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: availableSize.height * 0.35), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) @@ -419,6 +438,30 @@ public final class ButtonComponent: Component { } } + if component.displaysProgress { + let activityIndicator: ActivityIndicator + var activityIndicatorTransition = transition + if let current = self.activityIndicator { + activityIndicator = current + } else { + activityIndicatorTransition = .immediate + activityIndicator = ActivityIndicator(type: .custom(component.background.foreground, 22.0, 2.0, true)) + activityIndicator.view.alpha = 0.0 + self.activityIndicator = activityIndicator + self.addSubview(activityIndicator.view) + } + let indicatorSize = CGSize(width: 22.0, height: 22.0) + transition.setAlpha(view: activityIndicator.view, alpha: 1.0) + activityIndicatorTransition.setFrame(view: activityIndicator.view, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - indicatorSize.width) / 2.0), y: floor((availableSize.height - indicatorSize.height) / 2.0)), size: indicatorSize)) + } else { + if let activityIndicator = self.activityIndicator { + self.activityIndicator = nil + transition.setAlpha(view: activityIndicator.view, alpha: 0.0, completion: { [weak activityIndicator] _ in + activityIndicator?.view.removeFromSuperview() + }) + } + } + return availableSize } } diff --git a/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift b/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift index 8be463e077..26577c7e7b 100644 --- a/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift +++ b/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift @@ -716,15 +716,19 @@ private final class ChatFolderLinkPreviewScreenComponent: Component { initialContentHeight += 24.0 let actionButtonTitle: String + var actionButtonBadge: Int = 0 if case .remove = component.subject { + actionButtonBadge = self.selectedItems.count if self.selectedItems.isEmpty { actionButtonTitle = "Remove Folder" } else { actionButtonTitle = "Remove Folder and Chats" } } else if allChatsAdded { + actionButtonBadge = 0 actionButtonTitle = "OK" } else if let linkContents = component.linkContents { + actionButtonBadge = self.selectedItems.count if linkContents.localFilterId != nil { if self.selectedItems.isEmpty { actionButtonTitle = "Do Not Join Any Chats" @@ -743,19 +747,21 @@ private final class ChatFolderLinkPreviewScreenComponent: Component { component: AnyComponent(ButtonComponent( background: ButtonComponent.Background( color: environment.theme.list.itemCheckColors.fillColor, + foreground: environment.theme.list.itemCheckColors.foregroundColor, pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) ), content: AnyComponentWithIdentity( id: actionButtonTitle, component: AnyComponent(ButtonTextContentComponent( text: actionButtonTitle, - badge: self.selectedItems.count, + badge: actionButtonBadge, textColor: environment.theme.list.itemCheckColors.foregroundColor, badgeBackground: environment.theme.list.itemCheckColors.foregroundColor, badgeForeground: environment.theme.list.itemCheckColors.fillColor )) ), isEnabled: !self.selectedItems.isEmpty || component.linkContents?.localFilterId != nil, + displaysProgress: self.inProgress, action: { [weak self] in guard let self, let component = self.component, let controller = self.environment?.controller() else { return diff --git a/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift index 01f5b7b444..982390da9c 100644 --- a/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -321,7 +321,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { case "telegram_botapp": title = item.presentationData.strings.Conversation_BotApp actionTitle = item.presentationData.strings.Conversation_OpenBotApp - case "telegram_community": + case "telegram_chatlist": actionTitle = item.presentationData.strings.Conversation_OpenChatFolder default: break