mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Poll UI improvements
This commit is contained in:
parent
2a6f1f811b
commit
6d5bfe1f1e
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}()
|
}()
|
||||||
|
@ -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
|
||||||
|
@ -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),
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user