Poll improvements

This commit is contained in:
Ilya Laktyushin 2026-04-30 15:36:13 +02:00
parent fce36108da
commit e8baaa5a22
11 changed files with 107 additions and 52 deletions

View file

@ -189,7 +189,10 @@ public final class BrowserBookmarksScreen: ViewController {
}, requestToggleTodoMessageItem: { _, _, _ in
}, displayTodoToggleUnavailable: { _ in
}, openStarsPurchase: { _ in
}, openRankInfo: { _, _, _ in }, openSetPeerAvatar: {}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
}, openRankInfo: { _, _, _ in
}, openSetPeerAvatar: {
}, displayPollRestrictedToast: { _ in
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
let tagMask: MessageTags = .webPage

View file

@ -31,6 +31,7 @@ func pollCloudMediaToInputMedia(_ media: Media) -> Api.InputMedia? {
public enum RequestMessageSelectPollOptionError {
case generic
case restrictedToSubscribers
}
func _internal_requestMessageSelectPollOption(account: Account, messageId: MessageId, opaqueIdentifiers: [Data]) -> Signal<TelegramMediaPoll?, RequestMessageSelectPollOptionError> {
@ -40,7 +41,10 @@ func _internal_requestMessageSelectPollOption(account: Account, messageId: Messa
|> mapToSignal { peer in
if let inputPeer = apiInputPeer(peer) {
return account.network.request(Api.functions.messages.sendVote(peer: inputPeer, msgId: messageId.id, options: opaqueIdentifiers.map { Buffer(data: $0) }))
|> mapError { _ -> RequestMessageSelectPollOptionError in
|> mapError { error -> RequestMessageSelectPollOptionError in
if error.errorDescription == "POLL_MEMBER_RESTRICTED" {
return .restrictedToSubscribers
}
return .generic
}
|> mapToSignal { result -> Signal<TelegramMediaPoll?, RequestMessageSelectPollOptionError> in

View file

@ -2918,49 +2918,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
if case .public = poll.publicity {
item.controllerInteraction.openMessagePollResults(item.message.id, option.opaqueIdentifier)
} else if isRestricted {
let text: String
let peerName = item.message.peers[item.message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? ""
if !poll.countries.isEmpty {
let locale = localeWithStrings(item.presentationData.strings)
let countryNames = poll.countries.map { id in
if id == "FT" {
return "Fragment"
} else if let countryName = locale.localizedString(forRegionCode: id) {
return countryName
} else {
return id
}
}
var countries: String = ""
if countryNames.count == 1, let country = countryNames.first {
countries = "**\(country)**"
} else {
for i in 0 ..< countryNames.count {
countries.append("**\(countryNames[i])**")
if i == countryNames.count - 2 {
countries.append(item.presentationData.strings.Chat_Poll_Restriction_Country_CountriesLastDelimiter)
} else if i < countryNames.count - 2 {
countries.append(item.presentationData.strings.Chat_Poll_Restriction_Country_CountriesDelimiter)
}
}
}
if poll.restrictToSubscribers {
text = item.presentationData.strings.Chat_Poll_Restriction_SubscribersCountry(peerName, countries).string
} else {
text = item.presentationData.strings.Chat_Poll_Restriction_Country(countries).string
}
} else {
text = item.presentationData.strings.Chat_Poll_Restriction_Subscribers(peerName).string
}
let controller = UndoOverlayController(
presentationData: item.context.sharedContext.currentPresentationData.with { $0 },
content: .banned(text: text),
elevatedLayout: true,
position: .bottom,
action: { _ in return true }
)
item.controllerInteraction.presentController(controller, nil)
item.controllerInteraction.displayPollRestrictedToast(item.message.id)
}
}
}

View file

@ -666,7 +666,10 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, requestToggleTodoMessageItem: { _, _, _ in
}, displayTodoToggleUnavailable: { _ in
}, openStarsPurchase: { _ in
}, openRankInfo: { _, _, _ in }, openSetPeerAvatar: {}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
}, openRankInfo: { _, _, _ in
}, openSetPeerAvatar: {
}, displayPollRestrictedToast: { _ in
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: self.backgroundNode))
self.controllerInteraction = controllerInteraction

View file

@ -513,7 +513,10 @@ public final class ChatSendGroupMediaMessageContextPreview: UIView, ChatSendMess
}, requestToggleTodoMessageItem: { _, _, _ in
}, displayTodoToggleUnavailable: { _ in
}, openStarsPurchase: { _ in
}, openRankInfo: { _, _, _ in }, openSetPeerAvatar: {}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
}, openRankInfo: { _, _, _ in
}, openSetPeerAvatar: {
}, displayPollRestrictedToast: { _ in
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: self.context, backgroundNode: self.wallpaperBackgroundNode))
let associatedData = ChatMessageItemAssociatedData(

View file

@ -303,6 +303,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
public let openStarsPurchase: (Int64?) -> Void
public let openRankInfo: (EnginePeer, ChatRankInfoScreenRole, String) -> Void
public let openSetPeerAvatar: () -> Void
public let displayPollRestrictedToast: (EngineMessage.Id) -> Void
public var canPlayMedia: Bool = false
public var hiddenMedia: [MessageId: [Media]] = [:]
@ -480,6 +481,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
openStarsPurchase: @escaping (Int64?) -> Void,
openRankInfo: @escaping (EnginePeer, ChatRankInfoScreenRole, String) -> Void,
openSetPeerAvatar: @escaping () -> Void,
displayPollRestrictedToast: @escaping (EngineMessage.Id) -> Void,
automaticMediaDownloadSettings: MediaAutoDownloadSettings,
pollActionState: ChatInterfacePollActionState,
stickerSettings: ChatInterfaceStickerSettings,
@ -610,6 +612,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
self.openStarsPurchase = openStarsPurchase
self.openRankInfo = openRankInfo
self.openSetPeerAvatar = openSetPeerAvatar
self.displayPollRestrictedToast = displayPollRestrictedToast
self.automaticMediaDownloadSettings = automaticMediaDownloadSettings

View file

@ -1284,7 +1284,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}, requestToggleTodoMessageItem: { _, _, _ in
}, displayTodoToggleUnavailable: { _ in
}, openStarsPurchase: { _ in
}, openRankInfo: { _, _, _ in }, openSetPeerAvatar: {}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
}, openRankInfo: { _, _, _ in
}, openSetPeerAvatar: {
}, displayPollRestrictedToast: { _ in
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
self.hiddenMediaDisposable = context.sharedContext.mediaManager.galleryHiddenMediaManager.hiddenIds().startStrict(next: { [weak self] ids in
guard let strongSelf = self else {

View file

@ -8,6 +8,7 @@ import Display
import UndoUI
import AccountContext
import ChatControllerInteraction
import TelegramStringFormatting
extension ChatControllerImpl {
func displaySendReactionRestrictedToast() {
@ -20,6 +21,66 @@ extension ChatControllerImpl {
action: { _ in return true }
), nil)
}
func displayPollRestrictedToast(messageId: EngineMessage.Id) {
let _ = (self.context.engine.data.get(
TelegramEngine.EngineData.Item.Messages.Message(id: messageId)
)
|> deliverOnMainQueue).startStandalone(next: { [weak self] message in
guard let self, let message else {
return
}
let peerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? ""
guard let poll = message.media.first(where: { $0 is TelegramMediaPoll }) as? TelegramMediaPoll else {
return
}
var accountCountry = ""
if let data = self.context.currentAppConfiguration.with({ $0 }).data, let country = data["phone_country_iso2"] as? String {
accountCountry = (country)
}
var text = ""
if !poll.countries.isEmpty && !poll.countries.contains(accountCountry) {
let locale = localeWithStrings(self.presentationData.strings)
let countryNames = poll.countries.map { id in
if id == "FT" {
return "Fragment"
} else if let countryName = locale.localizedString(forRegionCode: id) {
return countryName
} else {
return id
}
}
var countries: String = ""
if countryNames.count == 1, let country = countryNames.first {
countries = "**\(country)**"
} else {
for i in 0 ..< countryNames.count {
countries.append("**\(countryNames[i])**")
if i == countryNames.count - 2 {
countries.append(self.presentationData.strings.Chat_Poll_Restriction_Country_CountriesLastDelimiter)
} else if i < countryNames.count - 2 {
countries.append(self.presentationData.strings.Chat_Poll_Restriction_Country_CountriesDelimiter)
}
}
}
if poll.restrictToSubscribers {
text = self.presentationData.strings.Chat_Poll_Restriction_SubscribersCountry(peerName, countries).string
} else {
text = self.presentationData.strings.Chat_Poll_Restriction_Country(countries).string
}
} else {
text = self.presentationData.strings.Chat_Poll_Restriction_Subscribers_TimeLimit
}
let controller = UndoOverlayController(
presentationData: self.presentationData,
content: .banned(text: text),
position: .bottom,
action: { _ in return true }
)
self.controllerInteraction?.presentControllerInCurrent(controller, nil)
})
}
func displayPostedScheduledMessagesToast(ids: [EngineMessage.Id]) {
let timestamp = CFAbsoluteTimeGetCurrent()

View file

@ -3896,20 +3896,26 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
)
strongSelf.present(controller, in: .current)
}
}, error: { _ in
}, error: { error in
guard let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction else {
return
}
if controllerInteraction.pollActionState.pollMessageIdsInProgress.removeValue(forKey: id) != nil {
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id)
}
switch error {
case .restrictedToSubscribers:
strongSelf.displayPollRestrictedToast(messageId: id)
default:
break
}
}, completed: {
guard let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction else {
return
}
if controllerInteraction.pollActionState.pollMessageIdsInProgress.removeValue(forKey: id) != nil {
Queue.mainQueue().after(1.0, {
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id)
})
}
@ -5664,6 +5670,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
self.interfaceInteraction?.openSetPeerAvatar()
}, displayPollRestrictedToast: { [weak self] messageId in
guard let self else {
return
}
self.displayPollRestrictedToast(messageId: messageId)
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings, presentationContext: ChatPresentationContext(context: context, backgroundNode: self.chatBackgroundNode))
controllerInteraction.enableFullTranslucency = context.sharedContext.energyUsageSettings.fullTranslucency

View file

@ -258,7 +258,10 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
}, requestToggleTodoMessageItem: { _, _, _ in
}, displayTodoToggleUnavailable: { _ in
}, openStarsPurchase: { _ in
}, openRankInfo: { _, _, _ in }, openSetPeerAvatar: {}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
}, openRankInfo: { _, _, _ in
}, openSetPeerAvatar: {
}, displayPollRestrictedToast: { _ in
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
self.dimNode = ASDisplayNode()
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)

View file

@ -2581,7 +2581,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
openStarsPurchase: { _ in
},
openRankInfo: { _, _, _ in
}, openSetPeerAvatar: {
},
openSetPeerAvatar: {
},
displayPollRestrictedToast: { _ in
},
automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
pollActionState: ChatInterfacePollActionState(),