Poll UI improvements

This commit is contained in:
Ali 2020-01-13 00:37:27 +04:00
parent 2a6f1f811b
commit 6d5bfe1f1e
12 changed files with 142 additions and 28 deletions

View File

@ -396,7 +396,7 @@ private func createPollControllerEntries(presentationData: PresentationData, pee
} }
var canBePublic = true var canBePublic = true
if let channel = peer as? TelegramChannel, case .broadcast = channel.info, let username = channel.username, !username.isEmpty { if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
canBePublic = false canBePublic = false
} }

View File

@ -3049,7 +3049,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight)) stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight))
case .topEdge: case .topEdge:
headerFrame = CGRect(origin: CGPoint(x: 0.0, y: min(max(upperDisplayBound, upperBoundEdge - itemHeaderHeight), lowerBound - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight)) headerFrame = CGRect(origin: CGPoint(x: 0.0, y: min(max(upperDisplayBound, upperBoundEdge - itemHeaderHeight), lowerBound - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight))
stickLocationDistance = headerFrame.maxY - upperBoundEdge - itemHeaderHeight stickLocationDistance = headerFrame.minY - upperBoundEdge + itemHeaderHeight
stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight)) stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight))
case .bottom: case .bottom:
headerFrame = CGRect(origin: CGPoint(x: 0.0, y: max(upperBound, min(lowerBound, lowerDisplayBound) - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight)) headerFrame = CGRect(origin: CGPoint(x: 0.0, y: max(upperBound, min(lowerBound, lowerDisplayBound) - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight))

View File

@ -66,7 +66,7 @@ private final class ShimmerEffectNode: ASDisplayNode {
self.currentBackgroundColor = backgroundColor self.currentBackgroundColor = backgroundColor
self.currentForegroundColor = foregroundColor self.currentForegroundColor = foregroundColor
self.imageNode.image = generateImage(CGSize(width: 8.0, height: 100.0), opaque: true, scale: 1.0, rotatedContext: { size, context in self.imageNode.image = generateImage(CGSize(width: 4.0, height: 320.0), opaque: true, scale: 1.0, rotatedContext: { size, context in
context.setFillColor(backgroundColor.cgColor) context.setFillColor(backgroundColor.cgColor)
context.fill(CGRect(origin: CGPoint(), size: size)) context.fill(CGRect(origin: CGPoint(), size: size))
@ -1288,7 +1288,7 @@ public final class ItemListPeerItemHeaderNode: ListViewItemHeaderNode {
private let actionTextNode: ImmediateTextNode private let actionTextNode: ImmediateTextNode
private let actionButton: HighlightableButtonNode private let actionButton: HighlightableButtonNode
private var stickDistanceFactor: CGFloat = 0.0 private var stickDistanceFactor: CGFloat?
public init(theme: PresentationTheme, strings: PresentationStrings, text: String, actionTitle: String?, action: (() -> Void)?) { public init(theme: PresentationTheme, strings: PresentationStrings, text: String, actionTitle: String?, action: (() -> Void)?) {
self.theme = theme self.theme = theme
@ -1305,6 +1305,7 @@ public final class ItemListPeerItemHeaderNode: ListViewItemHeaderNode {
self.separatorNode = ASDisplayNode() self.separatorNode = ASDisplayNode()
self.separatorNode.backgroundColor = theme.list.itemBlocksSeparatorColor self.separatorNode.backgroundColor = theme.list.itemBlocksSeparatorColor
self.separatorNode.alpha = 0.0
let titleFont = Font.regular(13.0) let titleFont = Font.regular(13.0)
@ -1391,6 +1392,20 @@ public final class ItemListPeerItemHeaderNode: ListViewItemHeaderNode {
} }
override public func updateStickDistanceFactor(_ factor: CGFloat, transition: ContainedViewLayoutTransition) { override public func updateStickDistanceFactor(_ factor: CGFloat, transition: ContainedViewLayoutTransition) {
transition.updateAlpha(node: self.snappedBackgroundNode, alpha: (1.0 - factor) * 0.0 + factor * 1.0) if self.stickDistanceFactor == factor {
return
}
self.stickDistanceFactor = factor
if let (size, leftInset, _) = self.validLayout {
if leftInset.isZero {
transition.updateAlpha(node: self.separatorNode, alpha: 1.0)
transition.updateAlpha(node: self.snappedBackgroundNode, alpha: (1.0 - factor) * 0.0 + factor * 1.0)
} else {
let distance = factor * size.height
let alpha = abs(distance) / 16.0
transition.updateAlpha(node: self.separatorNode, alpha: max(0.0, min(1.0, alpha)))
transition.updateAlpha(node: self.snappedBackgroundNode, alpha: 0.0)
}
}
} }
} }

View File

@ -116,8 +116,7 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode {
var bottomInset: CGFloat = 7.0 var bottomInset: CGFloat = 7.0
let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseHeaderFontSize) let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseHeaderFontSize)
let largeTitleFont = Font.semibold(floor(item.presentationData.fontSize.itemListBaseHeaderFontSize * 2.0)) let largeTitleFont = Font.semibold(floor(item.presentationData.fontSize.itemListBaseFontSize))
let semiLargeTitleFont = Font.semibold(floor(item.presentationData.fontSize.itemListBaseHeaderFontSize * 1.2))
let titleBoldFont = Font.semibold(item.presentationData.fontSize.itemListBaseHeaderFontSize) let titleBoldFont = Font.semibold(item.presentationData.fontSize.itemListBaseHeaderFontSize)
let attributedText: NSAttributedString let attributedText: NSAttributedString
@ -125,13 +124,7 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode {
case let .plain(text): case let .plain(text):
attributedText = NSAttributedString(string: text, font: titleFont, textColor: item.presentationData.theme.list.freeTextColor) attributedText = NSAttributedString(string: text, font: titleFont, textColor: item.presentationData.theme.list.freeTextColor)
case let .large(text): case let .large(text):
let font: UIFont attributedText = NSAttributedString(string: text, font: largeTitleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
if params.width >= 330.0 {
font = largeTitleFont
} else {
font = semiLargeTitleFont
}
attributedText = NSAttributedString(string: text, font: font, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
case let .markdown(text): case let .markdown(text):
attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.presentationData.theme.list.freeTextColor), bold: MarkdownAttributeSet(font: titleBoldFont, textColor: item.presentationData.theme.list.freeTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.presentationData.theme.list.itemAccentColor), linkAttribute: { contents in attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.presentationData.theme.list.freeTextColor), bold: MarkdownAttributeSet(font: titleBoldFont, textColor: item.presentationData.theme.list.freeTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.presentationData.theme.list.itemAccentColor), linkAttribute: { contents in
return (TelegramTextAttributes.URL, contents) return (TelegramTextAttributes.URL, contents)

View File

@ -38,6 +38,13 @@ public struct ValueBoxKey: Equatable, Hashable, CustomStringConvertible, Compara
memcpy(self.memory, buffer.memory, buffer.length) memcpy(self.memory, buffer.memory, buffer.length)
} }
public func setData(_ offset: Int, value: Data) {
let valueLength = value.count
value.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
memcpy(self.memory + offset, bytes, valueLength)
}
}
public func setInt32(_ offset: Int, value: Int32) { public func setInt32(_ offset: Int, value: Int32) {
var bigEndianValue = Int32(bigEndian: value) var bigEndianValue = Int32(bigEndian: value)
memcpy(self.memory + offset, &bigEndianValue, 4) memcpy(self.memory + offset, &bigEndianValue, 4)

View File

@ -67,7 +67,8 @@ public struct Namespaces {
public static let cachedStickerQueryResults: Int8 = 5 public static let cachedStickerQueryResults: Int8 = 5
public static let cachedSecureIdConfiguration: Int8 = 6 public static let cachedSecureIdConfiguration: Int8 = 6
public static let cachedWallpapersConfiguration: Int8 = 7 public static let cachedWallpapersConfiguration: Int8 = 7
public static let cachedThemesConfiguration: Int8 = 7 public static let cachedThemesConfiguration: Int8 = 8
public static let cachedPollResults: Int8 = 9
} }
public struct UnorderedItemList { public struct UnorderedItemList {

View File

@ -151,6 +151,7 @@ private var declaredEncodables: Void = {
declareEncodable(WalletCollection.self, f: { WalletCollection(decoder: $0) }) declareEncodable(WalletCollection.self, f: { WalletCollection(decoder: $0) })
declareEncodable(EmbeddedMediaStickersMessageAttribute.self, f: { EmbeddedMediaStickersMessageAttribute(decoder: $0) }) declareEncodable(EmbeddedMediaStickersMessageAttribute.self, f: { EmbeddedMediaStickersMessageAttribute(decoder: $0) })
declareEncodable(TelegramMediaWebpageAttribute.self, f: { TelegramMediaWebpageAttribute(decoder: $0) }) declareEncodable(TelegramMediaWebpageAttribute.self, f: { TelegramMediaWebpageAttribute(decoder: $0) })
declareEncodable(CachedPollOptionResult.self, f: { CachedPollOptionResult(decoder: $0) })
return return
}() }()

View File

@ -124,9 +124,40 @@ public func requestClosePoll(postbox: Postbox, network: Network, stateManager: A
} }
} }
private let cachedPollResultsCollectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 20, highWaterItemCount: 40)
final class CachedPollOptionResult: PostboxCoding {
let peerIds: [PeerId]
let count: Int32
public static func key(pollId: MediaId, optionOpaqueIdentifier: Data) -> ValueBoxKey {
let key = ValueBoxKey(length: 4 + 8 + optionOpaqueIdentifier.count)
key.setInt32(0, value: pollId.namespace)
key.setInt64(4, value: pollId.id)
key.setData(4 + 8, value: optionOpaqueIdentifier)
return key
}
public init(peerIds: [PeerId], count: Int32) {
self.peerIds = peerIds
self.count = count
}
public init(decoder: PostboxDecoder) {
self.peerIds = decoder.decodeInt64ArrayForKey("peerIds").map(PeerId.init)
self.count = decoder.decodeInt32ForKey("count", orElse: 0)
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt64Array(self.peerIds.map { $0.toInt64() }, forKey: "peerIds")
encoder.encodeInt32(self.count, forKey: "count")
}
}
private final class PollResultsOptionContext { private final class PollResultsOptionContext {
private let queue: Queue private let queue: Queue
private let account: Account private let account: Account
private let pollId: MediaId
private let messageId: MessageId private let messageId: MessageId
private let opaqueIdentifier: Data private let opaqueIdentifier: Data
private let disposable = MetaDisposable() private let disposable = MetaDisposable()
@ -136,15 +167,46 @@ private final class PollResultsOptionContext {
private var nextOffset: String? private var nextOffset: String?
private var results: [RenderedPeer] = [] private var results: [RenderedPeer] = []
private var count: Int private var count: Int
private var populateCache: Bool = true
let state = Promise<PollResultsOptionState>() let state = Promise<PollResultsOptionState>()
init(queue: Queue, account: Account, messageId: MessageId, opaqueIdentifier: Data, count: Int) { init(queue: Queue, account: Account, pollId: MediaId, messageId: MessageId, opaqueIdentifier: Data, count: Int) {
self.queue = queue self.queue = queue
self.account = account self.account = account
self.pollId = pollId
self.messageId = messageId self.messageId = messageId
self.opaqueIdentifier = opaqueIdentifier self.opaqueIdentifier = opaqueIdentifier
self.count = count self.count = count
self.isLoadingMore = true
self.disposable.set((account.postbox.transaction { transaction -> [RenderedPeer]? in
let cachedResult = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPollResults, key: CachedPollOptionResult.key(pollId: pollId, optionOpaqueIdentifier: opaqueIdentifier))) as? CachedPollOptionResult
if let cachedResult = cachedResult, Int(cachedResult.count) == count {
var result: [RenderedPeer] = []
for peerId in cachedResult.peerIds {
if let peer = transaction.getPeer(peerId) {
result.append(RenderedPeer(peer: peer))
} else {
return nil
}
}
return result
} else {
return nil
}
}
|> deliverOn(self.queue)).start(next: { [weak self] cachedPeers in
guard let strongSelf = self else {
return
}
strongSelf.isLoadingMore = false
if let cachedPeers = cachedPeers {
strongSelf.results = cachedPeers
strongSelf.hasLoadedOnce = true
}
strongSelf.loadMore()
}))
} }
deinit { deinit {
@ -156,10 +218,12 @@ private final class PollResultsOptionContext {
return return
} }
self.isLoadingMore = true self.isLoadingMore = true
let pollId = self.pollId
let messageId = self.messageId let messageId = self.messageId
let opaqueIdentifier = self.opaqueIdentifier let opaqueIdentifier = self.opaqueIdentifier
let account = self.account let account = self.account
let nextOffset = self.nextOffset let nextOffset = self.nextOffset
let populateCache = self.populateCache
self.disposable.set((self.account.postbox.transaction { transaction -> Api.InputPeer? in self.disposable.set((self.account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
} }
@ -193,12 +257,15 @@ private final class PollResultsOptionContext {
} }
} }
} }
if populateCache {
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPollResults, key: CachedPollOptionResult.key(pollId: pollId, optionOpaqueIdentifier: opaqueIdentifier)), entry: CachedPollOptionResult(peerIds: resultPeers.map { $0.peerId }, count: count), collectionSpec: cachedPollResultsCollectionSpec)
}
return (resultPeers, Int(count), nextOffset) return (resultPeers, Int(count), nextOffset)
} }
} }
} }
#if DEBUG #if DEBUG
return signal |> delay(4.0, queue: .concurrentDefaultQueue()) //return signal |> delay(4.0, queue: .concurrentDefaultQueue())
#endif #endif
return signal return signal
} else { } else {
@ -209,6 +276,10 @@ private final class PollResultsOptionContext {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
if strongSelf.populateCache {
strongSelf.populateCache = false
strongSelf.results.removeAll()
}
var existingIds = Set(strongSelf.results.map { $0.peerId }) var existingIds = Set(strongSelf.results.map { $0.peerId })
for peer in peers { for peer in peers {
if !existingIds.contains(peer.peerId) { if !existingIds.contains(peer.peerId) {
@ -266,7 +337,7 @@ private final class PollResultsContextImpl {
} }
} }
} }
self.optionContexts[option.opaqueIdentifier] = PollResultsOptionContext(queue: self.queue, account: account, messageId: messageId, opaqueIdentifier: option.opaqueIdentifier, count: count) self.optionContexts[option.opaqueIdentifier] = PollResultsOptionContext(queue: self.queue, account: account, pollId: poll.pollId, messageId: messageId, opaqueIdentifier: option.opaqueIdentifier, count: count)
} }
self.state.set(combineLatest(queue: self.queue, self.optionContexts.map { (opaqueIdentifier, context) -> Signal<(Data, PollResultsOptionState), NoError> in self.state.set(combineLatest(queue: self.queue, self.optionContexts.map { (opaqueIdentifier, context) -> Signal<(Data, PollResultsOptionState), NoError> in

View File

@ -343,7 +343,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
primaryColor: UIColor(rgb: 0xffffff), primaryColor: UIColor(rgb: 0xffffff),
controlColor: UIColor(rgb: 0x4d4d4d) controlColor: UIColor(rgb: 0x4d4d4d)
), ),
mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.1), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.05),
scrollIndicatorColor: UIColor(rgb: 0xffffff, alpha: 0.3), scrollIndicatorColor: UIColor(rgb: 0xffffff, alpha: 0.3),
pageIndicatorInactiveColor: UIColor(white: 1.0, alpha: 0.3), pageIndicatorInactiveColor: UIColor(white: 1.0, alpha: 0.3),
inputClearButtonColor: UIColor(rgb: 0x8b9197), inputClearButtonColor: UIColor(rgb: 0x8b9197),

View File

@ -644,7 +644,7 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres
let message = PresentationThemeChatMessage( let message = PresentationThemeChatMessage(
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: UIColor(rgb: 0xff6767), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, accentControlDisabledColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), mediaControlInnerBackgroundColor: mainBackgroundColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor, barIconForeground: .white, barPositive: UIColor(rgb: 0x00A700), barNegative: UIColor(rgb: 0xFE3824)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor), incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: UIColor(rgb: 0xff6767), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, accentControlDisabledColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), mediaControlInnerBackgroundColor: mainBackgroundColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor, barIconForeground: .white, barPositive: UIColor(rgb: 0x00A700), barNegative: UIColor(rgb: 0xFE3824)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, accentControlDisabledColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: outgoingPrimaryTextColor, barIconForeground: .white, barPositive: outgoingPrimaryTextColor, barNegative: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white), outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, gradientFill: outgoingBubbleFillGradientColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, accentControlDisabledColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: outgoingPrimaryTextColor, barIconForeground: .clear, barPositive: outgoingPrimaryTextColor, barNegative: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)),
infoPrimaryTextColor: UIColor(rgb: 0xffffff), infoPrimaryTextColor: UIColor(rgb: 0xffffff),
infoLinkTextColor: accentColor, infoLinkTextColor: accentColor,

View File

@ -1550,12 +1550,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode { itemNode in strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode { itemNode in
if !found, let itemNode = itemNode as? ChatMessageBubbleItemNode, itemNode.item?.message.id == id { if !found, let itemNode = itemNode as? ChatMessageBubbleItemNode, itemNode.item?.message.id == id {
found = true found = true
if strongSelf.selectPollOptionFeedback == nil {
strongSelf.selectPollOptionFeedback = HapticFeedback()
}
strongSelf.selectPollOptionFeedback?.error()
itemNode.animateQuizInvalidOptionSelected() itemNode.animateQuizInvalidOptionSelected()
} }
} }
return; return;
} }
if false { if false {
if strongSelf.selectPollOptionFeedback == nil {
strongSelf.selectPollOptionFeedback = HapticFeedback()
}
strongSelf.selectPollOptionFeedback?.success()
strongSelf.chatDisplayNode.animateQuizCorrectOptionSelected() strongSelf.chatDisplayNode.animateQuizCorrectOptionSelected()
return; return;
} }
@ -1582,18 +1590,31 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
switch resultPoll.kind { switch resultPoll.kind {
case .poll: case .poll:
break if strongSelf.selectPollOptionFeedback == nil {
strongSelf.selectPollOptionFeedback = HapticFeedback()
}
strongSelf.selectPollOptionFeedback?.success()
case .quiz: case .quiz:
if let voters = resultPoll.results.voters { if let voters = resultPoll.results.voters {
for voter in voters { for voter in voters {
if voter.selected { if voter.selected {
if voter.isCorrect { if voter.isCorrect {
if strongSelf.selectPollOptionFeedback == nil {
strongSelf.selectPollOptionFeedback = HapticFeedback()
}
strongSelf.selectPollOptionFeedback?.success()
strongSelf.chatDisplayNode.animateQuizCorrectOptionSelected() strongSelf.chatDisplayNode.animateQuizCorrectOptionSelected()
} else { } else {
var found = false var found = false
strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode { itemNode in strongSelf.chatDisplayNode.historyNode.forEachVisibleItemNode { itemNode in
if !found, let itemNode = itemNode as? ChatMessageBubbleItemNode, itemNode.item?.message.id == id { if !found, let itemNode = itemNode as? ChatMessageBubbleItemNode, itemNode.item?.message.id == id {
found = true found = true
if strongSelf.selectPollOptionFeedback == nil {
strongSelf.selectPollOptionFeedback = HapticFeedback()
}
strongSelf.selectPollOptionFeedback?.error()
itemNode.animateQuizInvalidOptionSelected() itemNode.animateQuizInvalidOptionSelected()
} }
} }
@ -1617,10 +1638,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if controllerInteraction.pollActionState.pollMessageIdsInProgress.removeValue(forKey: id) != nil { if controllerInteraction.pollActionState.pollMessageIdsInProgress.removeValue(forKey: id) != nil {
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id) strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id)
} }
if strongSelf.selectPollOptionFeedback == nil {
strongSelf.selectPollOptionFeedback = HapticFeedback()
}
strongSelf.selectPollOptionFeedback?.success()
}), forKey: id) }), forKey: id)
} }
}, requestOpenMessagePollResults: { [weak self] messageId, pollId in }, requestOpenMessagePollResults: { [weak self] messageId, pollId in

View File

@ -1260,9 +1260,18 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
} }
var hasResults = false var hasResults = false
if let totalVoters = poll.results.totalVoters, totalVoters != 0 { if poll.isClosed {
if let _ = poll.results.voters { hasResults = true
hasResults = true } else {
if let totalVoters = poll.results.totalVoters, totalVoters != 0 {
if let voters = poll.results.voters {
for voter in voters {
if voter.selected {
hasResults = true
break
}
}
}
} }
} }