Group boosts

This commit is contained in:
Ilya Laktyushin 2024-02-09 15:17:45 +04:00
parent cd7c9ea73c
commit 971b4d6986
7 changed files with 186 additions and 57 deletions

View File

@ -24,8 +24,9 @@ private final class ChannelAdminsControllerArguments {
let addAdmin: () -> Void let addAdmin: () -> Void
let openAdmin: (ChannelParticipant) -> Void let openAdmin: (ChannelParticipant) -> Void
let updateAntiSpamEnabled: (Bool) -> Void let updateAntiSpamEnabled: (Bool) -> Void
let updateSignMessagesEnabled: (Bool) -> Void
init(context: AccountContext, openRecentActions: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removeAdmin: @escaping (EnginePeer.Id) -> Void, addAdmin: @escaping () -> Void, openAdmin: @escaping (ChannelParticipant) -> Void, updateAntiSpamEnabled: @escaping (Bool) -> Void) { init(context: AccountContext, openRecentActions: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removeAdmin: @escaping (EnginePeer.Id) -> Void, addAdmin: @escaping () -> Void, openAdmin: @escaping (ChannelParticipant) -> Void, updateAntiSpamEnabled: @escaping (Bool) -> Void, updateSignMessagesEnabled: @escaping (Bool) -> Void) {
self.context = context self.context = context
self.openRecentActions = openRecentActions self.openRecentActions = openRecentActions
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
@ -33,12 +34,14 @@ private final class ChannelAdminsControllerArguments {
self.addAdmin = addAdmin self.addAdmin = addAdmin
self.openAdmin = openAdmin self.openAdmin = openAdmin
self.updateAntiSpamEnabled = updateAntiSpamEnabled self.updateAntiSpamEnabled = updateAntiSpamEnabled
self.updateSignMessagesEnabled = updateSignMessagesEnabled
} }
} }
private enum ChannelAdminsSection: Int32 { private enum ChannelAdminsSection: Int32 {
case administration case administration
case admins case admins
case signMessages
} }
private enum ChannelAdminsEntryStableId: Hashable { private enum ChannelAdminsEntryStableId: Hashable {
@ -56,31 +59,40 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
case addAdmin(PresentationTheme, String, Bool) case addAdmin(PresentationTheme, String, Bool)
case adminsInfo(PresentationTheme, String) case adminsInfo(PresentationTheme, String)
case signMessages(PresentationTheme, String, Bool)
case signMessagesInfo(PresentationTheme, String)
var section: ItemListSectionId { var section: ItemListSectionId {
switch self { switch self {
case .recentActions, .antiSpam, .antiSpamInfo: case .recentActions, .antiSpam, .antiSpamInfo:
return ChannelAdminsSection.administration.rawValue return ChannelAdminsSection.administration.rawValue
case .adminsHeader, .adminPeerItem, .addAdmin, .adminsInfo: case .adminsHeader, .adminPeerItem, .addAdmin, .adminsInfo:
return ChannelAdminsSection.admins.rawValue return ChannelAdminsSection.admins.rawValue
case .signMessages, .signMessagesInfo:
return ChannelAdminsSection.signMessages.rawValue
} }
} }
var stableId: ChannelAdminsEntryStableId { var stableId: ChannelAdminsEntryStableId {
switch self { switch self {
case .recentActions: case .recentActions:
return .index(0) return .index(0)
case .antiSpam: case .antiSpam:
return .index(1) return .index(1)
case .antiSpamInfo: case .antiSpamInfo:
return .index(2) return .index(2)
case .adminsHeader: case .adminsHeader:
return .index(3) return .index(3)
case .addAdmin: case .addAdmin:
return .index(4) return .index(4)
case .adminsInfo: case .adminsInfo:
return .index(5) return .index(5)
case let .adminPeerItem(_, _, _, _, _, _, participant, _, _, _): case let .adminPeerItem(_, _, _, _, _, _, participant, _, _, _):
return .peer(participant.peer.id) return .peer(participant.peer.id)
case .signMessages:
return .index(6)
case .signMessagesInfo:
return .index(7)
} }
} }
@ -158,6 +170,18 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .signMessages(lhsTheme, lhsText, lhsValue):
if case let .signMessages(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .signMessagesInfo(lhsTheme, lhsText):
if case let .signMessagesInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
} }
} }
@ -203,7 +227,26 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
return true return true
} }
case .adminsInfo: case .adminsInfo:
return false switch rhs {
case .recentActions, .antiSpam, .antiSpamInfo, .adminsHeader, .addAdmin, .adminsInfo:
return false
default:
return true
}
case .signMessages:
switch rhs {
case .recentActions, .antiSpam, .antiSpamInfo, .adminsHeader, .addAdmin, .adminsInfo, .signMessages:
return false
default:
return true
}
case .signMessagesInfo:
switch rhs {
case .recentActions, .antiSpam, .antiSpamInfo, .adminsHeader, .addAdmin, .adminsInfo, .signMessages, .signMessagesInfo:
return false
default:
return true
}
} }
} }
@ -259,6 +302,12 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
}) })
case let .adminsInfo(_, text): case let .adminsInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .signMessages(_, text, value):
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSignMessagesEnabled(value)
})
case let .signMessagesInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
} }
} }
} }
@ -337,7 +386,7 @@ private struct ChannelAdminsControllerState: Equatable {
} }
} }
private func channelAdminsControllerEntries(presentationData: PresentationData, accountPeerId: EnginePeer.Id, peer: EnginePeer?, state: ChannelAdminsControllerState, participants: [RenderedChannelParticipant]?, antiSpamAvailable: Bool, antiSpamEnabled: Bool) -> [ChannelAdminsEntry] { private func channelAdminsControllerEntries(presentationData: PresentationData, accountPeerId: EnginePeer.Id, peer: EnginePeer?, state: ChannelAdminsControllerState, participants: [RenderedChannelParticipant]?, antiSpamAvailable: Bool, antiSpamEnabled: Bool, signMessagesEnabled: Bool) -> [ChannelAdminsEntry] {
if participants == nil || participants?.count == nil { if participants == nil || participants?.count == nil {
return [] return []
} }
@ -429,6 +478,11 @@ private func channelAdminsControllerEntries(presentationData: PresentationData,
let info = isGroup ? presentationData.strings.Group_Management_AddModeratorHelp : presentationData.strings.Channel_Management_AddModeratorHelp let info = isGroup ? presentationData.strings.Group_Management_AddModeratorHelp : presentationData.strings.Channel_Management_AddModeratorHelp
entries.append(.adminsInfo(presentationData.theme, info)) entries.append(.adminsInfo(presentationData.theme, info))
} }
if !isGroup && peer.hasPermission(.sendSomething) {
entries.append(.signMessages(presentationData.theme, presentationData.strings.Channel_SignMessages, signMessagesEnabled))
entries.append(.signMessagesInfo(presentationData.theme, presentationData.strings.Channel_SignMessages_Help))
}
} }
} else if case let .legacyGroup(peer) = peer { } else if case let .legacyGroup(peer) = peer {
let isGroup = true let isGroup = true
@ -536,6 +590,9 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
let updateAntiSpamDisposable = MetaDisposable() let updateAntiSpamDisposable = MetaDisposable()
actionsDisposable.add(updateAntiSpamDisposable) actionsDisposable.add(updateAntiSpamDisposable)
let updateSignMessagesDisposable = MetaDisposable()
actionsDisposable.add(updateSignMessagesDisposable)
let adminsPromise = Promise<[RenderedChannelParticipant]?>(nil) let adminsPromise = Promise<[RenderedChannelParticipant]?>(nil)
let antiSpamConfiguration = AntiSpamBotConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) let antiSpamConfiguration = AntiSpamBotConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
@ -732,6 +789,12 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|> deliverOnMainQueue).start(next: { peerId in |> deliverOnMainQueue).start(next: { peerId in
updateAntiSpamDisposable.set(context.engine.peers.toggleAntiSpamProtection(peerId: peerId, enabled: value).start()) updateAntiSpamDisposable.set(context.engine.peers.toggleAntiSpamProtection(peerId: peerId, enabled: value).start())
}) })
}, updateSignMessagesEnabled: { value in
let _ = (currentPeerId.get()
|> take(1)
|> deliverOnMainQueue).start(next: { peerId in
updateSignMessagesDisposable.set(context.engine.peers.toggleShouldChannelMessagesSignatures(peerId: peerId, enabled: value).start())
})
}) })
let membersAndLoadMoreControlValue = Atomic<(Disposable, PeerChannelMemberCategoryControl?)?>(value: nil) let membersAndLoadMoreControlValue = Atomic<(Disposable, PeerChannelMemberCategoryControl?)?>(value: nil)
@ -852,6 +915,11 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
antiSpamAvailable = true antiSpamAvailable = true
} }
var signMessagesEnabled = false
if case let .channel(channel) = view.peer, case let .broadcast(info) = channel.info {
signMessagesEnabled = info.flags.contains(.messagesShouldHaveSignatures)
}
var rightNavigationButton: ItemListNavigationButton? var rightNavigationButton: ItemListNavigationButton?
var secondaryRightNavigationButton: ItemListNavigationButton? var secondaryRightNavigationButton: ItemListNavigationButton?
if let admins = admins, admins.count > 1 { if let admins = admins, admins.count > 1 {
@ -923,7 +991,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
} }
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(isGroup ? presentationData.strings.ChatAdmins_Title : presentationData.strings.Channel_Management_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(isGroup ? presentationData.strings.ChatAdmins_Title : presentationData.strings.Channel_Management_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: context.account.peerId, peer: view.peer, state: state, participants: admins, antiSpamAvailable: antiSpamAvailable, antiSpamEnabled: antiSpamEnabled), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: context.account.peerId, peer: view.peer, state: state, participants: admins, antiSpamAvailable: antiSpamAvailable, antiSpamEnabled: antiSpamEnabled, signMessagesEnabled: signMessagesEnabled), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} |> afterDisposed { } |> afterDisposed {

View File

@ -59,6 +59,7 @@ public enum PresentationResourceKey: Int32 {
case itemListVerifiedPeerIcon case itemListVerifiedPeerIcon
case itemListCloudFetchIcon case itemListCloudFetchIcon
case itemListCloseIconImage case itemListCloseIconImage
case itemListRemoveIconImage
case itemListMakeVisibleIcon case itemListMakeVisibleIcon
case itemListMakeInvisibleIcon case itemListMakeInvisibleIcon
case itemListEditThemeIcon case itemListEditThemeIcon

View File

@ -211,6 +211,24 @@ public struct PresentationResourcesItemList {
}) })
} }
public static func itemListRemoveIconImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.itemListRemoveIconImage.rawValue, { theme in
return generateImage(CGSize(width: 15.0, height: 15.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.copy)
context.setStrokeColor(theme.list.disclosureArrowColor.cgColor)
context.setLineWidth(2.0)
context.setLineCap(.round)
context.move(to: CGPoint(x: 1.0, y: 1.0))
context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0))
context.strokePath()
context.move(to: CGPoint(x: size.width - 1.0, y: 1.0))
context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0))
context.strokePath()
})
})
}
public static func makeVisibleIcon(_ theme: PresentationTheme) -> UIImage? { public static func makeVisibleIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.itemListMakeVisibleIcon.rawValue, { theme in return theme.image(PresentationResourceKey.itemListMakeVisibleIcon.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Contact List/MakeVisibleIcon"), color: theme.list.itemAccentColor) return generateTintedImage(image: UIImage(bundleImageName: "Contact List/MakeVisibleIcon"), color: theme.list.itemAccentColor)

View File

@ -180,6 +180,7 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
self.addSubnode(self.notFoundNode) self.addSubnode(self.notFoundNode)
self.addSubnode(self.activityIndicator) self.addSubnode(self.activityIndicator)
self.addSubnode(self.removeButtonIcon)
self.addSubnode(self.removeButton) self.addSubnode(self.removeButton)
var firstTime = true var firstTime = true
@ -195,12 +196,29 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
} }
firstTime = false firstTime = false
} }
self.removeButton.addTarget(self, action: #selector(self.removeButtonPressed), forControlEvents: .touchUpInside)
self.removeButton.highligthedChanged = { [weak self] highlighted in
if let self {
if highlighted {
self.removeButtonIcon.layer.removeAnimation(forKey: "opacity")
self.removeButtonIcon.alpha = 0.4
} else {
self.removeButtonIcon.alpha = 1.0
self.removeButtonIcon.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
}
}
} }
deinit { deinit {
self.fetchDisposable.dispose() self.fetchDisposable.dispose()
} }
@objc private func removeButtonPressed() {
self.item?.remove?()
}
private func removePlaceholder(animated: Bool) { private func removePlaceholder(animated: Bool) {
if let placeholderNode = self.placeholderNode { if let placeholderNode = self.placeholderNode {
self.placeholderNode = nil self.placeholderNode = nil
@ -330,6 +348,14 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
strongSelf.activityIndicator.isHidden = true strongSelf.activityIndicator.isHidden = true
} }
if case .found = item.content {
strongSelf.removeButtonIcon.isHidden = false
strongSelf.removeButton.isHidden = false
} else {
strongSelf.removeButtonIcon.isHidden = true
strongSelf.removeButton.isHidden = true
}
let revealOffset = strongSelf.revealOffset let revealOffset = strongSelf.revealOffset
let transition: ContainedViewLayoutTransition = .immediate let transition: ContainedViewLayoutTransition = .immediate
@ -377,6 +403,7 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
} }
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.removeButtonIcon.image = PresentationResourcesItemList.itemListRemoveIconImage(item.theme)
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
@ -403,17 +430,16 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode {
transition.updateFrame(node: strongSelf.notFoundNode, frame: CGRect(origin: CGPoint(x: params.leftInset + 15.0 + floor((boundingSize.width - image.size.width) / 2.0), y: 13.0 + floor((boundingSize.height - image.size.height) / 2.0)), size: image.size)) transition.updateFrame(node: strongSelf.notFoundNode, frame: CGRect(origin: CGPoint(x: params.leftInset + 15.0 + floor((boundingSize.width - image.size.width) / 2.0), y: 13.0 + floor((boundingSize.height - image.size.height) / 2.0)), size: image.size))
} }
if let updatedImageSignal = updatedImageSignal { if let updatedImageSignal = updatedImageSignal {
strongSelf.imageNode.setSignal(updatedImageSignal) strongSelf.imageNode.setSignal(updatedImageSignal)
} }
let imageFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0 + floor((boundingSize.width - imageSize.width) / 2.0), y: 11.0 + floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize) let imageFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0 + floor((boundingSize.width - imageSize.width) / 2.0), y: 11.0 + floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
transition.updateFrame(node: strongSelf.imageNode, frame: imageFrame) transition.updateFrame(node: strongSelf.imageNode, frame: imageFrame)
strongSelf.removeButton.frame = CGRect(origin: CGPoint(x: layoutSize.width - params.rightInset - layoutSize.height, y: 0.0), size: CGSize(width: layoutSize.height, height: layoutSize.height))
if let icon = strongSelf.removeButtonIcon.image {
strongSelf.removeButtonIcon.frame = CGRect(origin: CGPoint(x: layoutSize.width - params.rightInset - icon.size.width - 18.0, y: floor((layoutSize.height - icon.size.height) / 2.0)), size: icon.size)
}
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: contentSize.height + UIScreenPixel + UIScreenPixel)) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: contentSize.height + UIScreenPixel + UIScreenPixel))

View File

@ -1588,18 +1588,16 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
case .broadcast: case .broadcast:
let ItemUsername = 1 let ItemUsername = 1
let ItemPeerColor = 2 let ItemPeerColor = 2
let ItemInviteLinks = 4 let ItemInviteLinks = 3
let ItemDiscussionGroup = 5 let ItemDiscussionGroup = 4
let ItemSignMessages = 6 let ItemDeleteChannel = 5
let ItemSignMessagesHelp = 7 let ItemReactions = 6
let ItemDeleteChannel = 8 let ItemAdmins = 7
let ItemReactions = 9 let ItemMembers = 8
let ItemAdmins = 10 let ItemMemberRequests = 9
let ItemMembers = 11 let ItemStats = 10
let ItemMemberRequests = 12 let ItemBanned = 11
let ItemStats = 13 let ItemRecentActions = 12
let ItemBanned = 14
let ItemRecentActions = 15
let isCreator = channel.flags.contains(.isCreator) let isCreator = channel.flags.contains(.isCreator)
@ -1708,19 +1706,19 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
})) }))
} }
if isCreator || (channel.adminRights != nil && channel.hasPermission(.sendSomething)) { // if isCreator || (channel.adminRights != nil && channel.hasPermission(.sendSomething)) {
let messagesShouldHaveSignatures: Bool // let messagesShouldHaveSignatures: Bool
switch channel.info { // switch channel.info {
case let .broadcast(info): // case let .broadcast(info):
messagesShouldHaveSignatures = info.flags.contains(.messagesShouldHaveSignatures) // messagesShouldHaveSignatures = info.flags.contains(.messagesShouldHaveSignatures)
default: // default:
messagesShouldHaveSignatures = false // messagesShouldHaveSignatures = false
} // }
items[.peerSettings]!.append(PeerInfoScreenSwitchItem(id: ItemSignMessages, text: presentationData.strings.Channel_SignMessages, value: messagesShouldHaveSignatures, icon: UIImage(bundleImageName: "Chat/Info/GroupSignIcon"), toggled: { value in // items[.peerSettings]!.append(PeerInfoScreenSwitchItem(id: ItemSignMessages, text: presentationData.strings.Channel_SignMessages, value: messagesShouldHaveSignatures, icon: UIImage(bundleImageName: "Chat/Info/GroupSignIcon"), toggled: { value in
interaction.editingToggleMessageSignatures(value) // interaction.editingToggleMessageSignatures(value)
})) // }))
items[.peerSettings]!.append(PeerInfoScreenCommentItem(id: ItemSignMessagesHelp, text: presentationData.strings.Channel_SignMessages_Help)) // items[.peerSettings]!.append(PeerInfoScreenCommentItem(id: ItemSignMessagesHelp, text: presentationData.strings.Channel_SignMessages_Help))
} // }
var canEditMembers = false var canEditMembers = false
if channel.hasPermission(.banMembers) && (channel.adminRights != nil || channel.flags.contains(.isCreator)) { if channel.hasPermission(.banMembers) && (channel.adminRights != nil || channel.flags.contains(.isCreator)) {

View File

@ -10800,8 +10800,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
items.append(.custom(ChatSendAsPeerTitleContextItem(text: strongSelf.presentationInterfaceState.strings.Conversation_SendMesageAs.uppercased()), false)) items.append(.custom(ChatSendAsPeerTitleContextItem(text: strongSelf.presentationInterfaceState.strings.Conversation_SendMesageAs.uppercased()), false))
items.append(.custom(ChatSendAsPeerListContextItem(context: strongSelf.context, chatPeerId: peerId, peers: peers, selectedPeerId: myPeerId, isPremium: isPremium, presentToast: { [weak self] peer in items.append(.custom(ChatSendAsPeerListContextItem(context: strongSelf.context, chatPeerId: peerId, peers: peers, selectedPeerId: myPeerId, isPremium: isPremium, presentToast: { [weak self] peer in
if let strongSelf = self { if let strongSelf = self {
let hapticFeedback = HapticFeedback() HapticFeedback().impact()
hapticFeedback.impact()
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.Conversation_SendMesageAsPremiumInfo, action: strongSelf.presentationData.strings.EmojiInput_PremiumEmojiToast_Action, duration: 3), elevatedLayout: false, action: { [weak self] action in strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.Conversation_SendMesageAsPremiumInfo, action: strongSelf.presentationData.strings.EmojiInput_PremiumEmojiToast_Action, duration: 3), elevatedLayout: false, action: { [weak self] action in
guard let strongSelf = self else { guard let strongSelf = self else {
@ -11075,6 +11074,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let self, let peerId = self.chatLocation.peerId, let cachedData = self.peerView?.cachedData as? CachedChannelData, let boostToUnrestrict = cachedData.boostsToUnrestrict else { guard let self, let peerId = self.chatLocation.peerId, let cachedData = self.peerView?.cachedData as? CachedChannelData, let boostToUnrestrict = cachedData.boostsToUnrestrict else {
return return
} }
HapticFeedback().impact()
let _ = combineLatest(queue: Queue.mainQueue(), let _ = combineLatest(queue: Queue.mainQueue(),
context.engine.peers.getChannelBoostStatus(peerId: peerId), context.engine.peers.getChannelBoostStatus(peerId: peerId),
context.engine.peers.getMyBoostStatus() context.engine.peers.getMyBoostStatus()

View File

@ -4703,6 +4703,7 @@ private func generateClearImage(color: UIColor) -> UIImage? {
private final class BoostSlowModeButton: HighlightTrackingButtonNode { private final class BoostSlowModeButton: HighlightTrackingButtonNode {
let containerNode: ASDisplayNode
let backgroundNode: ASImageNode let backgroundNode: ASImageNode
let textNode: ImmediateAnimatedCountLabelNode let textNode: ImmediateAnimatedCountLabelNode
let iconNode: ASImageNode let iconNode: ASImageNode
@ -4712,6 +4713,8 @@ private final class BoostSlowModeButton: HighlightTrackingButtonNode {
var requestUpdate: () -> Void = {} var requestUpdate: () -> Void = {}
override init(pointerStyle: PointerStyle? = nil) { override init(pointerStyle: PointerStyle? = nil) {
self.containerNode = ASDisplayNode()
self.backgroundNode = ASImageNode() self.backgroundNode = ASImageNode()
self.backgroundNode.displaysAsynchronously = false self.backgroundNode.displaysAsynchronously = false
self.backgroundNode.clipsToBounds = true self.backgroundNode.clipsToBounds = true
@ -4727,9 +4730,20 @@ private final class BoostSlowModeButton: HighlightTrackingButtonNode {
super.init(pointerStyle: pointerStyle) super.init(pointerStyle: pointerStyle)
self.addSubnode(self.backgroundNode) self.addSubnode(self.containerNode)
self.addSubnode(self.iconNode) self.containerNode.addSubnode(self.backgroundNode)
self.addSubnode(self.textNode) self.containerNode.addSubnode(self.iconNode)
self.containerNode.addSubnode(self.textNode)
self.highligthedChanged = { [weak self] highlighted in
if let self {
if highlighted {
self.containerNode.layer.animateScale(from: 1.0, to: 0.75, duration: 0.4, removeOnCompletion: false)
} else if let presentationLayer = self.containerNode.layer.presentation() {
self.containerNode.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.25, removeOnCompletion: false)
}
}
}
} }
func update(size: CGSize, interfaceState: ChatPresentationInterfaceState) -> CGSize { func update(size: CGSize, interfaceState: ChatPresentationInterfaceState) -> CGSize {
@ -4778,6 +4792,8 @@ private final class BoostSlowModeButton: HighlightTrackingButtonNode {
let textSize = self.textNode.updateLayout(size: CGSize(width: 200.0, height: 100.0), animated: true) let textSize = self.textNode.updateLayout(size: CGSize(width: 200.0, height: 100.0), animated: true)
let totalSize = CGSize(width: textSize.width > 0.0 ? textSize.width + 38.0 : 33.0, height: 33.0) let totalSize = CGSize(width: textSize.width > 0.0 ? textSize.width + 38.0 : 33.0, height: 33.0)
self.containerNode.bounds = CGRect(origin: .zero, size: totalSize)
self.containerNode.position = CGPoint(x: totalSize.width / 2.0, y: totalSize.height / 2.0)
self.backgroundNode.frame = CGRect(origin: .zero, size: totalSize) self.backgroundNode.frame = CGRect(origin: .zero, size: totalSize)
self.backgroundNode.cornerRadius = totalSize.height / 2.0 self.backgroundNode.cornerRadius = totalSize.height / 2.0
self.textNode.frame = CGRect(origin: CGPoint(x: 9.0, y: floorToScreenPixels((totalSize.height - textSize.height) / 2.0)), size: textSize) self.textNode.frame = CGRect(origin: CGPoint(x: 9.0, y: floorToScreenPixels((totalSize.height - textSize.height) / 2.0)), size: textSize)