mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
[WIP] Polls
This commit is contained in:
parent
676f46d44d
commit
f12fb93a3e
10 changed files with 184 additions and 66 deletions
|
|
@ -260,7 +260,7 @@ public extension TelegramEngine {
|
|||
return _internal_addPollOption(account: self.account, messageId: messageId, text: text, entities: entities, mediaReference: mediaReference)
|
||||
}
|
||||
|
||||
public func deletePollOption(messageId: MessageId, opaqueIdentifier: Data, mediaReference: AnyMediaReference? = nil) -> Signal<Never, DeletePollOptionError> {
|
||||
public func deletePollOption(messageId: MessageId, opaqueIdentifier: Data) -> Signal<Never, DeletePollOptionError> {
|
||||
return _internal_deletePollOption(account: self.account, messageId: messageId, opaqueIdentifier: opaqueIdentifier)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,42 @@ private func customEmojiAttributes(primaryTextColor: UIColor, emoji: ChatTextInp
|
|||
return MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: emoji])
|
||||
}
|
||||
|
||||
private func serviceMessageArgumentRange(index: Int, value: String, in stringWithRanges: (String, [(Int, NSRange)])) -> NSRange? {
|
||||
if let range = stringWithRanges.1.first(where: { $0.0 == index })?.1 {
|
||||
return range
|
||||
}
|
||||
|
||||
let string = stringWithRanges.0 as NSString
|
||||
return stringWithRanges.1.map { $0.1 }.first(where: { range in
|
||||
NSMaxRange(range) <= string.length && string.substring(with: range) == value
|
||||
})
|
||||
}
|
||||
|
||||
private func addServiceMessageTextEntities(_ entities: [MessageTextEntity], to attributedString: NSMutableAttributedString, text: String, range: NSRange, associatedMedia: [MediaId: Media]) {
|
||||
let textLength = min((text as NSString).length, range.length)
|
||||
|
||||
for entity in entities {
|
||||
if entity.range.lowerBound >= textLength {
|
||||
continue
|
||||
}
|
||||
|
||||
let length = min(entity.range.count, textLength - entity.range.lowerBound)
|
||||
if length <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
let entityRange = NSRange(location: range.location + entity.range.lowerBound, length: length)
|
||||
switch entity.type {
|
||||
case .Spoiler:
|
||||
attributedString.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler), value: true, range: entityRange)
|
||||
case let .CustomEmoji(_, fileId):
|
||||
attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: fileId, file: associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile), range: entityRange)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func peerMentionAttributes(primaryTextColor: UIColor, peerId: EnginePeer.Id) -> MarkdownAttributeSet {
|
||||
return MarkdownAttributeSet(font: titleBoldFont, textColor: primaryTextColor, additionalAttributes: [TelegramTextAttributes.PeerMention: TelegramPeerMention(peerId: peerId, mention: "")])
|
||||
}
|
||||
|
|
@ -1796,13 +1832,26 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
if optionTitle.count > 20 {
|
||||
optionTitle = optionTitle.prefix(20) + "…"
|
||||
}
|
||||
let optionEntities = option.entities.filter { entity in
|
||||
switch entity.type {
|
||||
case .Spoiler, .CustomEmoji:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
if message.author?.id == accountPeerId {
|
||||
var optionText = option.text
|
||||
if optionText.count > 20 {
|
||||
optionText = optionText.prefix(20) + "…"
|
||||
}
|
||||
let resultString = strings.Notification_PollAddedOptionYou(optionText)
|
||||
attributedString = addAttributesToStringWithRanges(resultString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes, 1: boldAttributes])
|
||||
let stringWithRanges = resultString._tuple
|
||||
let resultAttributedString = NSMutableAttributedString(attributedString: addAttributesToStringWithRanges(stringWithRanges, body: bodyAttributes, argumentAttributes: [0: boldAttributes, 1: boldAttributes]))
|
||||
if let optionRange = serviceMessageArgumentRange(index: 0, value: optionText, in: stringWithRanges) {
|
||||
addServiceMessageTextEntities(optionEntities, to: resultAttributedString, text: optionText, range: optionRange, associatedMedia: message.associatedMedia)
|
||||
}
|
||||
attributedString = resultAttributedString
|
||||
} else {
|
||||
let peerName = message.author?.compactDisplayTitle ?? ""
|
||||
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)])
|
||||
|
|
@ -1813,7 +1862,12 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
optionText = optionText.prefix(20) + "…"
|
||||
}
|
||||
let resultString = strings.Notification_PollAddedOption(peerName, optionText)
|
||||
attributedString = addAttributesToStringWithRanges(resultString._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
let stringWithRanges = resultString._tuple
|
||||
let resultAttributedString = NSMutableAttributedString(attributedString: addAttributesToStringWithRanges(stringWithRanges, body: bodyAttributes, argumentAttributes: attributes))
|
||||
if let optionRange = serviceMessageArgumentRange(index: 1, value: optionText, in: stringWithRanges) {
|
||||
addServiceMessageTextEntities(optionEntities, to: resultAttributedString, text: optionText, range: optionRange, associatedMedia: message.associatedMedia)
|
||||
}
|
||||
attributedString = resultAttributedString
|
||||
}
|
||||
case .unknown:
|
||||
attributedString = nil
|
||||
|
|
|
|||
|
|
@ -728,7 +728,7 @@ private final class ChatMessagePollOptionNode: ASDisplayNode {
|
|||
return
|
||||
}
|
||||
|
||||
guard self.forceSelected == nil else {
|
||||
guard self.forceSelected == nil && self.currentResult == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -1359,6 +1359,8 @@ private final class ChatMessagePollAddOptionNode: ASDisplayNode {
|
|||
private var currentStrings: PresentationStrings?
|
||||
private var currentTheme: PresentationTheme?
|
||||
private var currentIncoming = false
|
||||
private var currentFocusedTextInputIsMedia = false
|
||||
private var currentModeSelectorAnimationName: String?
|
||||
|
||||
var textUpdated: ((NSAttributedString) -> Void)?
|
||||
var heightUpdated: (() -> Void)?
|
||||
|
|
@ -1444,7 +1446,7 @@ private final class ChatMessagePollAddOptionNode: ASDisplayNode {
|
|||
textColor: currentTextColor,
|
||||
accentColor: currentTintColor,
|
||||
insets: UIEdgeInsets(top: ChatMessagePollAddOptionNode.verticalInset, left: 0.0, bottom: ChatMessagePollAddOptionNode.verticalInset, right: 0.0),
|
||||
hideKeyboard: false,
|
||||
hideKeyboard: self.currentFocusedTextInputIsMedia,
|
||||
customInputView: nil,
|
||||
placeholder: NSAttributedString(string: strings.CreatePoll_AddOption, font: font, textColor: currentPlaceholderColor),
|
||||
resetText: nil,
|
||||
|
|
@ -1530,8 +1532,8 @@ private final class ChatMessagePollAddOptionNode: ASDisplayNode {
|
|||
return max(ChatMessagePollAddOptionNode.minHeight, measureSize.height + ChatMessagePollAddOptionNode.verticalInset * 2.0)
|
||||
}
|
||||
|
||||
static func asyncLayout(_ maybeNode: ChatMessagePollAddOptionNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ strings: PresentationStrings, _ incoming: Bool, _ text: NSAttributedString, _ attachment: Attachment?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool) -> ChatMessagePollAddOptionNode))) {
|
||||
return { context, presentationData, strings, incoming, text, attachment, constrainedWidth in
|
||||
static func asyncLayout(_ maybeNode: ChatMessagePollAddOptionNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ strings: PresentationStrings, _ incoming: Bool, _ focusedTextInputIsMedia: Bool, _ text: NSAttributedString, _ attachment: Attachment?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool) -> ChatMessagePollAddOptionNode))) {
|
||||
return { context, presentationData, strings, incoming, focusedTextInputIsMedia, text, attachment, constrainedWidth in
|
||||
let font = presentationData.messageFont
|
||||
let textColor = incoming ? presentationData.theme.theme.chat.message.incoming.primaryTextColor : presentationData.theme.theme.chat.message.outgoing.primaryTextColor
|
||||
let secondaryTextColor = incoming ? presentationData.theme.theme.chat.message.incoming.secondaryTextColor : presentationData.theme.theme.chat.message.outgoing.secondaryTextColor
|
||||
|
|
@ -1565,6 +1567,7 @@ private final class ChatMessagePollAddOptionNode: ASDisplayNode {
|
|||
node.currentContext = context
|
||||
node.currentTheme = presentationData.theme.theme
|
||||
node.currentIncoming = incoming
|
||||
node.currentFocusedTextInputIsMedia = focusedTextInputIsMedia
|
||||
let textFieldSize = node.updateTextFieldLayout(size: size, forceUpdate: false)
|
||||
node.currentMeasuredHeight = max(ChatMessagePollAddOptionNode.minHeight, textFieldSize.height)
|
||||
node.currentTextValue = node._textFieldExternalState.text
|
||||
|
|
@ -1607,8 +1610,19 @@ private final class ChatMessagePollAddOptionNode: ASDisplayNode {
|
|||
self.addIconNode.isHidden = displaySelector
|
||||
|
||||
if displaySelector {
|
||||
var playAnimation = false
|
||||
|
||||
let modeSelectorSize = CGSize(width: 32.0, height: 32.0)
|
||||
let modeSelectorFrame = CGRect(origin: CGPoint(x: floor((ChatMessagePollAddOptionNode.leftInset - modeSelectorSize.width) * 0.5) - 2.0, y: floor((size.height - modeSelectorSize.height) * 0.5)), size: modeSelectorSize)
|
||||
var modeSelectorFrame = CGRect(origin: CGPoint(x: floor((ChatMessagePollAddOptionNode.leftInset - modeSelectorSize.width) * 0.5) - 2.0, y: floor((size.height - modeSelectorSize.height) * 0.5)), size: modeSelectorSize)
|
||||
let animationName = self.currentFocusedTextInputIsMedia ? "input_anim_smileToKey" : "input_anim_keyToSmile"
|
||||
if let currentModeSelectorAnimationName = self.currentModeSelectorAnimationName, currentModeSelectorAnimationName != animationName {
|
||||
playAnimation = true
|
||||
}
|
||||
self.currentModeSelectorAnimationName = animationName
|
||||
|
||||
if self.currentFocusedTextInputIsMedia {
|
||||
modeSelectorFrame = modeSelectorFrame.offsetBy(dx: 3.0, dy: 0.0)
|
||||
}
|
||||
|
||||
let modeSelector: ComponentView<Empty>
|
||||
if let current = self.modeSelector {
|
||||
|
|
@ -1622,7 +1636,7 @@ private final class ChatMessagePollAddOptionNode: ASDisplayNode {
|
|||
transition: animated ? .easeInOut(duration: 0.2) : .immediate,
|
||||
component: AnyComponent(PlainButtonComponent(
|
||||
content: AnyComponent(LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: "input_anim_keyToSmile"),
|
||||
content: LottieComponent.AppBundleContent(name: animationName),
|
||||
color: secondaryTextColor,
|
||||
size: modeSelectorSize
|
||||
)),
|
||||
|
|
@ -1640,9 +1654,20 @@ private final class ChatMessagePollAddOptionNode: ASDisplayNode {
|
|||
if modeSelectorView.superview == nil {
|
||||
self.view.addSubview(modeSelectorView)
|
||||
}
|
||||
modeSelectorView.frame = modeSelectorFrame
|
||||
if playAnimation {
|
||||
let transition = ComponentTransition(animation: .curve(duration: animationName == "input_anim_smileToKey" ? 0.32 : 0.26, curve: .easeInOut))
|
||||
transition.setFrame(view: modeSelectorView, frame: modeSelectorFrame)
|
||||
} else {
|
||||
modeSelectorView.frame = modeSelectorFrame
|
||||
}
|
||||
modeSelectorView.alpha = 1.0
|
||||
modeSelectorView.transform = .identity
|
||||
|
||||
if let animationView = modeSelectorView.contentView as? LottieComponent.View {
|
||||
if playAnimation {
|
||||
animationView.playOnce()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let modeSelector = self.modeSelector {
|
||||
self.modeSelector = nil
|
||||
|
|
@ -2090,6 +2115,9 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
return
|
||||
}
|
||||
self.newOptionIsFocused = focus
|
||||
if !focus {
|
||||
item.controllerInteraction.focusedTextInputIsMedia = false
|
||||
}
|
||||
item.controllerInteraction.updatePresentationState { state in
|
||||
if focus {
|
||||
if state.focusedPollAddOptionMessageId == item.message.id {
|
||||
|
|
@ -2122,15 +2150,21 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
item.controllerInteraction.updatePresentationState { state in
|
||||
return state.updatedInputMode({ inputMode in
|
||||
item.controllerInteraction.updatePresentationState { [weak item] state in
|
||||
var focusedTextInputIsMedia = false
|
||||
let updatedState = state.updatedInputMode({ inputMode in
|
||||
if case .media = inputMode {
|
||||
return .text
|
||||
} else {
|
||||
focusedTextInputIsMedia = true
|
||||
return .media(mode: .other, expanded: .none, focused: true)
|
||||
}
|
||||
})
|
||||
item?.controllerInteraction.focusedTextInputIsMedia = focusedTextInputIsMedia
|
||||
|
||||
return updatedState
|
||||
}
|
||||
self.requestNewOptionLayoutUpdate()
|
||||
}
|
||||
|
||||
private func openNewOptionAttachment() {
|
||||
|
|
@ -2707,7 +2741,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
|
||||
let displayAddOption = poll.openAnswers && !isClosed && !hasVoted && poll.pollId.namespace == Namespaces.Media.CloudPoll
|
||||
if displayAddOption {
|
||||
let addOptionResult = makeAddOptionLayout(item.context, item.presentationData, item.presentationData.strings, incoming, currentNewOptionText, currentNewOptionAttachment, constrainedSize.width - layoutConstants.bubble.borderInset * 2.0)
|
||||
let addOptionResult = makeAddOptionLayout(item.context, item.presentationData, item.presentationData.strings, incoming, item.controllerInteraction.focusedTextInputIsMedia, currentNewOptionText, currentNewOptionAttachment, constrainedSize.width - layoutConstants.bubble.borderInset * 2.0)
|
||||
boundingSize.width = max(boundingSize.width, addOptionResult.minimumWidth + layoutConstants.bubble.borderInset * 2.0)
|
||||
addOptionFinalizeLayout = addOptionResult.layout
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||
public var chatIsRotated: Bool = true
|
||||
public var canReadHistory: Bool = false
|
||||
public var summarizedMessageIds: Set<MessageId> = Set()
|
||||
|
||||
public var focusedTextInputIsMedia: Bool = false
|
||||
|
||||
private var isOpeningMediaValue: Bool = false
|
||||
public var isOpeningMedia: Bool {
|
||||
|
|
|
|||
|
|
@ -370,6 +370,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||
private var hasRecentGifsDisposable: Disposable?
|
||||
private let opaqueTopPanelBackground: Bool
|
||||
private let useOpaqueTheme: Bool
|
||||
private let displayBottomPanel: Bool
|
||||
|
||||
private struct EmojiSearchResult {
|
||||
var groups: [EmojiPagerContentComponent.ItemGroup]
|
||||
|
|
@ -484,13 +485,14 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||
|> distinctUntilChanged
|
||||
}
|
||||
|
||||
public init(context: AccountContext, currentInputData: InputData, updatedInputData: Signal<InputData, NoError>, defaultToEmojiTab: Bool, opaqueTopPanelBackground: Bool = false, useOpaqueTheme: Bool = false, interaction: ChatEntityKeyboardInputNode.Interaction?, chatPeerId: PeerId?, stateContext: StateContext?, forceHasPremium: Bool = false) {
|
||||
public init(context: AccountContext, currentInputData: InputData, updatedInputData: Signal<InputData, NoError>, defaultToEmojiTab: Bool, opaqueTopPanelBackground: Bool = false, useOpaqueTheme: Bool = false, interaction: ChatEntityKeyboardInputNode.Interaction?, chatPeerId: PeerId?, stateContext: StateContext?, forceHasPremium: Bool = false, displayBottomPanel: Bool = true) {
|
||||
self.context = context
|
||||
self.currentInputData = currentInputData
|
||||
self.defaultToEmojiTab = defaultToEmojiTab
|
||||
self.opaqueTopPanelBackground = opaqueTopPanelBackground
|
||||
self.useOpaqueTheme = useOpaqueTheme
|
||||
self.stateContext = stateContext
|
||||
self.displayBottomPanel = displayBottomPanel
|
||||
|
||||
self.interaction = interaction
|
||||
|
||||
|
|
@ -2162,10 +2164,10 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||
emoji: inputData.emoji.flatMap { emoji in
|
||||
return emoji.withUpdatedItemGroups(panelItemGroups: self.processStableItemGroupList(category: .emoji, itemGroups: emoji.panelItemGroups), contentItemGroups: self.processStableItemGroupList(category: .emoji, itemGroups: emoji.contentItemGroups), itemContentUniqueId: emoji.itemContentUniqueId, emptySearchResults: emoji.emptySearchResults, searchState: emoji.searchState)
|
||||
},
|
||||
stickers: inputData.stickers.flatMap { stickers in
|
||||
stickers: !self.displayBottomPanel ? nil : inputData.stickers.flatMap { stickers in
|
||||
return stickers.withUpdatedItemGroups(panelItemGroups: self.processStableItemGroupList(category: .stickers, itemGroups: stickers.panelItemGroups), contentItemGroups: self.processStableItemGroupList(category: .stickers, itemGroups: stickers.contentItemGroups), itemContentUniqueId: stickers.itemContentUniqueId, emptySearchResults: nil, searchState: stickers.searchState)
|
||||
},
|
||||
gifs: inputData.gifs,
|
||||
gifs: !self.displayBottomPanel ? nil : inputData.gifs,
|
||||
availableGifSearchEmojies: inputData.availableGifSearchEmojies
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1503,11 +1503,12 @@ public final class TextFieldComponent: Component {
|
|||
environment: {},
|
||||
containerSize: textFrame.size
|
||||
)
|
||||
let placeholderFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: textFrame.minY + floor((textFrame.height - placeholderSize.height) * 0.5) - 1.0), size: placeholderSize)
|
||||
let placeholderFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((textFrame.height - placeholderSize.height) * 0.5) - 1.0), size: placeholderSize)
|
||||
if let placeholderView = placeholder.view {
|
||||
if placeholderView.superview == nil {
|
||||
placeholderView.isUserInteractionEnabled = false
|
||||
placeholderView.layer.anchorPoint = CGPoint()
|
||||
self.insertSubview(placeholderView, belowSubview: self.textView)
|
||||
self.textView.insertSubview(placeholderView, at: 0)
|
||||
}
|
||||
placeholderTransition.setPosition(view: placeholderView, position: placeholderFrame.origin)
|
||||
placeholderView.bounds = CGRect(origin: CGPoint(), size: placeholderFrame.size)
|
||||
|
|
|
|||
|
|
@ -4130,16 +4130,13 @@ extension ChatControllerImpl {
|
|||
guard let strongSelf = self, let interfaceInteraction = strongSelf.interfaceInteraction else {
|
||||
return
|
||||
}
|
||||
|
||||
if let textFieldView = strongSelf.chatDisplayNode.chatPresentationInterfaceStateTextFieldView(strongSelf.presentationInterfaceState) {
|
||||
textFieldView.insertText(text)
|
||||
return
|
||||
}
|
||||
|
||||
if !strongSelf.chatDisplayNode.isTextInputPanelActive {
|
||||
return
|
||||
}
|
||||
|
||||
interfaceInteraction.updateTextInputStateAndMode { textInputState, inputMode in
|
||||
let inputText = NSMutableAttributedString(attributedString: textInputState.inputText)
|
||||
|
||||
|
|
@ -4163,6 +4160,10 @@ extension ChatControllerImpl {
|
|||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let textFieldView = strongSelf.chatDisplayNode.chatPresentationInterfaceStateTextFieldView(strongSelf.presentationInterfaceState) {
|
||||
textFieldView.deleteBackward()
|
||||
return
|
||||
}
|
||||
if !strongSelf.chatDisplayNode.isTextInputPanelActive {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,15 @@ extension ChatControllerImpl {
|
|||
|
||||
let pollOption = poll.options[pollOptionIndex]
|
||||
|
||||
var selectedOptions: [Data] = []
|
||||
if let voters = poll.results.voters {
|
||||
for voter in voters {
|
||||
if voter.selected {
|
||||
selectedOptions.append(voter.opaqueIdentifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = (contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: self.presentationInterfaceState, context: self.context, messages: [message], controllerInteraction: self.controllerInteraction, selectAll: false, interfaceInteraction: self.interfaceInteraction, messageNode: params.messageNode as? ChatMessageItemView)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] actions in
|
||||
guard let self else {
|
||||
|
|
@ -35,36 +44,34 @@ extension ChatControllerImpl {
|
|||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
// if canMark {
|
||||
// items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Todo_ContextMenu_CheckTask, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
|
||||
// guard let self else {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if !self.context.isPremium {
|
||||
// f(.default)
|
||||
// let controller = UndoOverlayController(
|
||||
// presentationData: self.presentationData,
|
||||
// content: .premiumPaywall(title: nil, text: self.presentationData.strings.Chat_Todo_PremiumRequired, customUndoText: nil, timeout: nil, linkAction: nil),
|
||||
// action: { [weak self] action in
|
||||
// guard let self else {
|
||||
// return false
|
||||
// }
|
||||
// if case .info = action {
|
||||
// let controller = self.context.sharedContext.makePremiumIntroController(context: context, source: .presence, forceDark: false, dismissed: nil)
|
||||
// self.push(controller)
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
// )
|
||||
// self.present(controller, in: .current)
|
||||
// } else {
|
||||
// c?.dismiss(completion: {
|
||||
// let _ = self.context.engine.messages.requestUpdateTodoMessageItems(messageId: message.id, completedIds: [todoItemId], incompletedIds: []).start()
|
||||
// })
|
||||
// }
|
||||
// })))
|
||||
// }
|
||||
if !poll.isClosed && (selectedOptions.isEmpty || !poll.revotingDisabled) {
|
||||
if selectedOptions.contains(pollOption.opaqueIdentifier) {
|
||||
items.append(.action(ContextMenuActionItem(text: "Retract Vote", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unvote"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
c?.dismiss(result: .default, completion: {
|
||||
var updatedOptions = selectedOptions
|
||||
updatedOptions.removeAll(where: { $0 == pollOption.opaqueIdentifier } )
|
||||
self.controllerInteraction?.requestSelectMessagePollOptions(message.id, updatedOptions)
|
||||
})
|
||||
})))
|
||||
} else {
|
||||
items.append(.action(ContextMenuActionItem(text: "Vote", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/StopPoll"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
c?.dismiss(result: .default, completion: {
|
||||
var updatedOptions = selectedOptions
|
||||
if !poll.kind.multipleAnswers {
|
||||
updatedOptions = []
|
||||
}
|
||||
updatedOptions.append(pollOption.opaqueIdentifier)
|
||||
self.controllerInteraction?.requestSelectMessagePollOptions(message.id, updatedOptions)
|
||||
})
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
if canReplyInChat(self.presentationInterfaceState, accountPeerId: self.context.account.peerId) {
|
||||
|
|
@ -139,6 +146,18 @@ extension ChatControllerImpl {
|
|||
})))
|
||||
}
|
||||
|
||||
if pollOption.date != nil {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Remove", textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self.context.engine.messages.deletePollOption(messageId: message.id, opaqueIdentifier: pollOption.opaqueIdentifier).start()
|
||||
})))
|
||||
}
|
||||
|
||||
self.canReadHistory.set(false)
|
||||
|
||||
//TODO:localize
|
||||
|
|
|
|||
|
|
@ -3733,7 +3733,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
//TODO:localize
|
||||
let controller = UndoOverlayController(
|
||||
presentationData: strongSelf.presentationData,
|
||||
content: .universal(animation: "anim_timer", scale: 0.066, colors: [:], title: nil, text: "Results will appear after the poll ends", customUndoText: nil, timeout: nil),
|
||||
content: .universal(animation: "anim_timer", scale: 0.06, colors: [:], title: nil, text: "Results will appear after the poll ends", customUndoText: nil, timeout: nil),
|
||||
action: { _ in return true }
|
||||
)
|
||||
strongSelf.present(controller, in: .current)
|
||||
|
|
|
|||
|
|
@ -3681,18 +3681,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||
}
|
||||
|
||||
var waitForKeyboardLayout = false
|
||||
var effectiveTextView: UITextView?
|
||||
let customTextView = self.chatPresentationInterfaceStateTextFieldView(chatPresentationInterfaceState)
|
||||
if let customTextView {
|
||||
effectiveTextView = customTextView.inputTextView
|
||||
} else if let mainTextView = self.textInputPanelNode?.textInputNode?.textView {
|
||||
effectiveTextView = mainTextView
|
||||
}
|
||||
if let textView = effectiveTextView {
|
||||
if let textView = self.textInputPanelNode?.textInputNode?.textView {
|
||||
let updatedInputView = self.chatPresentationInterfaceStateInputView(chatPresentationInterfaceState)
|
||||
if textView.inputView !== updatedInputView {
|
||||
textView.inputView = updatedInputView
|
||||
if textView.isFirstResponder {
|
||||
if let customTextView {
|
||||
if customTextView.isActive {
|
||||
if self.chatPresentationInterfaceStateRequiresInputFocus(chatPresentationInterfaceState), let validLayout = self.validLayout {
|
||||
if case .compact = validLayout.0.metrics.widthClass {
|
||||
waitForKeyboardLayout = true
|
||||
|
|
@ -3700,7 +3693,20 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||
waitForKeyboardLayout = true
|
||||
}
|
||||
}
|
||||
textView.reloadInputViews()
|
||||
}
|
||||
} else {
|
||||
if textView.inputView !== updatedInputView {
|
||||
textView.inputView = updatedInputView
|
||||
if textView.isFirstResponder {
|
||||
if self.chatPresentationInterfaceStateRequiresInputFocus(chatPresentationInterfaceState), let validLayout = self.validLayout {
|
||||
if case .compact = validLayout.0.metrics.widthClass {
|
||||
waitForKeyboardLayout = true
|
||||
} else if let inputHeight = validLayout.0.inputHeight, inputHeight > 100.0 {
|
||||
waitForKeyboardLayout = true
|
||||
}
|
||||
}
|
||||
textView.reloadInputViews()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3888,10 +3894,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||
context: self.context,
|
||||
currentInputData: inputMediaNodeData,
|
||||
updatedInputData: self.inputMediaNodeDataPromise.get(),
|
||||
defaultToEmojiTab: !self.chatPresentationInterfaceState.interfaceState.effectiveInputState.inputText.string.isEmpty || self.chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil || self.openStickersBeginWithEmoji,
|
||||
defaultToEmojiTab: !self.chatPresentationInterfaceState.interfaceState.effectiveInputState.inputText.string.isEmpty || self.chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil || self.openStickersBeginWithEmoji || self.chatPresentationInterfaceState.focusedPollAddOptionMessageId != nil,
|
||||
interaction: ChatEntityKeyboardInputNode.Interaction(chatControllerInteraction: self.controllerInteraction, panelInteraction: interfaceInteraction),
|
||||
chatPeerId: peerId,
|
||||
stateContext: self.inputMediaNodeStateContext
|
||||
stateContext: self.inputMediaNodeStateContext,
|
||||
displayBottomPanel: self.chatPresentationInterfaceState.focusedPollAddOptionMessageId == nil
|
||||
)
|
||||
self.openStickersBeginWithEmoji = false
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue