mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Hashtag search improvements
This commit is contained in:
parent
04b25d7152
commit
4806840989
@ -13132,3 +13132,9 @@ Sorry for the inconvenience.";
|
||||
"TopApps.Info.Title" = "Top Mini Apps";
|
||||
"TopApps.Info.Text" = "This catalogue ranks mini apps based on their daily revenue, measured in Stars. To be listed, developers must set their main mini app in [@botfather]() (as described [here](https://core.telegram.org/bots/webapps#launching-the-main-mini-app)), have over **1,000** daily users, and earn a daily revenue above **1,000** Stars, based on weekly average.";
|
||||
"TopApps.Info.Done" = "Understood";
|
||||
|
||||
"Stars.Intro.Transaction.TelegramBotApi.Title" = "Paid Limit Extension";
|
||||
"Stars.Intro.Transaction.TelegramBotApi.Subtitle" = "Bot API";
|
||||
|
||||
"Stars.Transaction.TelegramBotApi.Title" = "Paid Limit Extension";
|
||||
"Stars.Transaction.TelegramBotApi.Subtitle" = "Bot API";
|
||||
|
@ -870,7 +870,7 @@ public struct ChatInputQueryCommandsResult: Equatable {
|
||||
|
||||
public enum ChatPresentationInputQueryResult: Equatable {
|
||||
case stickers([FoundStickerItem])
|
||||
case hashtags([String])
|
||||
case hashtags([String], String)
|
||||
case mentions([EnginePeer])
|
||||
case commands(ChatInputQueryCommandsResult)
|
||||
case emojis([(String, TelegramMediaFile?, String)], NSRange)
|
||||
@ -884,9 +884,9 @@ public enum ChatPresentationInputQueryResult: Equatable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .hashtags(lhsResults):
|
||||
if case let .hashtags(rhsResults) = rhs {
|
||||
return lhsResults == rhsResults
|
||||
case let .hashtags(lhsResults, lhsQuery):
|
||||
if case let .hashtags(rhsResults, rhsQuery) = rhs {
|
||||
return lhsResults == rhsResults && lhsQuery == rhsQuery
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
@ -352,7 +352,8 @@ final class BrowserAddressListComponent: Component {
|
||||
highlighting: .default,
|
||||
updateIsHighlighted: { view, _ in
|
||||
|
||||
})
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: itemFrame.size
|
||||
|
@ -2495,7 +2495,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
let firstRangeOrigin = chatListSearchResult.text.distance(from: chatListSearchResult.text.startIndex, to: firstRange.lowerBound)
|
||||
if firstRangeOrigin > 24 {
|
||||
if firstRangeOrigin > 24 && !chatListSearchResult.searchQuery.hasPrefix("#") {
|
||||
var leftOrigin: Int = 0
|
||||
(composedString.string as NSString).enumerateSubstrings(in: NSMakeRange(0, firstRangeOrigin), options: [.byWords, .reverse]) { (str, range1, _, _) in
|
||||
let distanceFromEnd = firstRangeOrigin - range1.location
|
||||
|
@ -273,6 +273,24 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func updateFrameAdditive(layer: CALayer, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if layer.frame.equalTo(frame) && !force {
|
||||
completion?(true)
|
||||
} else {
|
||||
switch self {
|
||||
case .immediate:
|
||||
layer.frame = frame
|
||||
if let completion = completion {
|
||||
completion(true)
|
||||
}
|
||||
case .animated:
|
||||
let previousFrame = layer.frame
|
||||
layer.frame = frame
|
||||
self.animatePositionAdditive(layer: layer, offset: CGPoint(x: previousFrame.minX - frame.minX, y: previousFrame.minY - frame.minY))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateFrameAdditive(node: ASDisplayNode, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if node.frame.equalTo(frame) && !force {
|
||||
completion?(true)
|
||||
|
@ -262,6 +262,9 @@ final class StarsTransactionItemNode: ListViewItemNode, ItemListItemNode {
|
||||
case .ads:
|
||||
itemTitle = item.presentationData.strings.Stars_Intro_Transaction_TelegramAds_Title
|
||||
itemSubtitle = item.presentationData.strings.Stars_Intro_Transaction_TelegramAds_Subtitle
|
||||
case .apiLimitExtension:
|
||||
itemTitle = item.presentationData.strings.Stars_Intro_Transaction_TelegramBotApi_Title
|
||||
itemSubtitle = item.presentationData.strings.Stars_Intro_Transaction_TelegramBotApi_Subtitle
|
||||
case .unsupported:
|
||||
itemTitle = item.presentationData.strings.Stars_Intro_Transaction_Unsupported_Title
|
||||
itemSubtitle = nil
|
||||
|
@ -902,8 +902,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1401868056] = { return Api.StarsSubscription.parse_starsSubscription($0) }
|
||||
dict[88173912] = { return Api.StarsSubscriptionPricing.parse_starsSubscriptionPricing($0) }
|
||||
dict[198776256] = { return Api.StarsTopupOption.parse_starsTopupOption($0) }
|
||||
dict[178185410] = { return Api.StarsTransaction.parse_starsTransaction($0) }
|
||||
dict[-1216644148] = { return Api.StarsTransaction.parse_starsTransaction($0) }
|
||||
dict[-670195363] = { return Api.StarsTransactionPeer.parse_starsTransactionPeer($0) }
|
||||
dict[-110658899] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerAPI($0) }
|
||||
dict[1617438738] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerAds($0) }
|
||||
dict[-1269320843] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerAppStore($0) }
|
||||
dict[-382740222] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerFragment($0) }
|
||||
|
@ -1010,13 +1010,13 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsTransaction: TypeConstructorDescription {
|
||||
case starsTransaction(flags: Int32, id: String, stars: Int64, date: Int32, peer: Api.StarsTransactionPeer, title: String?, description: String?, photo: Api.WebDocument?, transactionDate: Int32?, transactionUrl: String?, botPayload: Buffer?, msgId: Int32?, extendedMedia: [Api.MessageMedia]?, subscriptionPeriod: Int32?, giveawayPostId: Int32?, stargift: Api.StarGift?)
|
||||
case starsTransaction(flags: Int32, id: String, stars: Int64, date: Int32, peer: Api.StarsTransactionPeer, title: String?, description: String?, photo: Api.WebDocument?, transactionDate: Int32?, transactionUrl: String?, botPayload: Buffer?, msgId: Int32?, extendedMedia: [Api.MessageMedia]?, subscriptionPeriod: Int32?, giveawayPostId: Int32?, stargift: Api.StarGift?, floodskipDate: Int32?, floodskipNumber: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId, let stargift):
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId, let stargift, let floodskipDate, let floodskipNumber):
|
||||
if boxed {
|
||||
buffer.appendInt32(178185410)
|
||||
buffer.appendInt32(-1216644148)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
@ -1038,14 +1038,16 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 12) != 0 {serializeInt32(subscriptionPeriod!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 13) != 0 {serializeInt32(giveawayPostId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 14) != 0 {stargift!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 15) != 0 {serializeInt32(floodskipDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 15) != 0 {serializeInt32(floodskipNumber!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId, let stargift):
|
||||
return ("starsTransaction", [("flags", flags as Any), ("id", id as Any), ("stars", stars as Any), ("date", date as Any), ("peer", peer as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("transactionDate", transactionDate as Any), ("transactionUrl", transactionUrl as Any), ("botPayload", botPayload as Any), ("msgId", msgId as Any), ("extendedMedia", extendedMedia as Any), ("subscriptionPeriod", subscriptionPeriod as Any), ("giveawayPostId", giveawayPostId as Any), ("stargift", stargift as Any)])
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId, let stargift, let floodskipDate, let floodskipNumber):
|
||||
return ("starsTransaction", [("flags", flags as Any), ("id", id as Any), ("stars", stars as Any), ("date", date as Any), ("peer", peer as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("transactionDate", transactionDate as Any), ("transactionUrl", transactionUrl as Any), ("botPayload", botPayload as Any), ("msgId", msgId as Any), ("extendedMedia", extendedMedia as Any), ("subscriptionPeriod", subscriptionPeriod as Any), ("giveawayPostId", giveawayPostId as Any), ("stargift", stargift as Any), ("floodskipDate", floodskipDate as Any), ("floodskipNumber", floodskipNumber as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -1090,6 +1092,10 @@ public extension Api {
|
||||
if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() {
|
||||
_16 = Api.parse(reader, signature: signature) as? Api.StarGift
|
||||
} }
|
||||
var _17: Int32?
|
||||
if Int(_1!) & Int(1 << 15) != 0 {_17 = reader.readInt32() }
|
||||
var _18: Int32?
|
||||
if Int(_1!) & Int(1 << 15) != 0 {_18 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
@ -1106,8 +1112,10 @@ public extension Api {
|
||||
let _c14 = (Int(_1!) & Int(1 << 12) == 0) || _14 != nil
|
||||
let _c15 = (Int(_1!) & Int(1 << 13) == 0) || _15 != nil
|
||||
let _c16 = (Int(_1!) & Int(1 << 14) == 0) || _16 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 {
|
||||
return Api.StarsTransaction.starsTransaction(flags: _1!, id: _2!, stars: _3!, date: _4!, peer: _5!, title: _6, description: _7, photo: _8, transactionDate: _9, transactionUrl: _10, botPayload: _11, msgId: _12, extendedMedia: _13, subscriptionPeriod: _14, giveawayPostId: _15, stargift: _16)
|
||||
let _c17 = (Int(_1!) & Int(1 << 15) == 0) || _17 != nil
|
||||
let _c18 = (Int(_1!) & Int(1 << 15) == 0) || _18 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 {
|
||||
return Api.StarsTransaction.starsTransaction(flags: _1!, id: _2!, stars: _3!, date: _4!, peer: _5!, title: _6, description: _7, photo: _8, transactionDate: _9, transactionUrl: _10, botPayload: _11, msgId: _12, extendedMedia: _13, subscriptionPeriod: _14, giveawayPostId: _15, stargift: _16, floodskipDate: _17, floodskipNumber: _18)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -1,6 +1,7 @@
|
||||
public extension Api {
|
||||
enum StarsTransactionPeer: TypeConstructorDescription {
|
||||
case starsTransactionPeer(peer: Api.Peer)
|
||||
case starsTransactionPeerAPI
|
||||
case starsTransactionPeerAds
|
||||
case starsTransactionPeerAppStore
|
||||
case starsTransactionPeerFragment
|
||||
@ -15,6 +16,12 @@ public extension Api {
|
||||
buffer.appendInt32(-670195363)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
break
|
||||
case .starsTransactionPeerAPI:
|
||||
if boxed {
|
||||
buffer.appendInt32(-110658899)
|
||||
}
|
||||
|
||||
break
|
||||
case .starsTransactionPeerAds:
|
||||
if boxed {
|
||||
@ -59,6 +66,8 @@ public extension Api {
|
||||
switch self {
|
||||
case .starsTransactionPeer(let peer):
|
||||
return ("starsTransactionPeer", [("peer", peer as Any)])
|
||||
case .starsTransactionPeerAPI:
|
||||
return ("starsTransactionPeerAPI", [])
|
||||
case .starsTransactionPeerAds:
|
||||
return ("starsTransactionPeerAds", [])
|
||||
case .starsTransactionPeerAppStore:
|
||||
@ -87,6 +96,9 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_starsTransactionPeerAPI(_ reader: BufferReader) -> StarsTransactionPeer? {
|
||||
return Api.StarsTransactionPeer.starsTransactionPeerAPI
|
||||
}
|
||||
public static func parse_starsTransactionPeerAds(_ reader: BufferReader) -> StarsTransactionPeer? {
|
||||
return Api.StarsTransactionPeer.starsTransactionPeerAds
|
||||
}
|
||||
|
@ -490,7 +490,10 @@ private final class StarsContextImpl {
|
||||
private extension StarsContext.State.Transaction {
|
||||
init?(apiTransaction: Api.StarsTransaction, peerId: EnginePeer.Id?, transaction: Transaction) {
|
||||
switch apiTransaction {
|
||||
case let .starsTransaction(apiFlags, id, stars, date, transactionPeer, title, description, photo, transactionDate, transactionUrl, _, messageId, extendedMedia, subscriptionPeriod, giveawayPostId, starGift):
|
||||
case let .starsTransaction(apiFlags, id, stars, date, transactionPeer, title, description, photo, transactionDate, transactionUrl, _, messageId, extendedMedia, subscriptionPeriod, giveawayPostId, starGift, floodskipDate, floodskipNumber):
|
||||
let _ = floodskipDate
|
||||
let _ = floodskipNumber
|
||||
|
||||
let parsedPeer: StarsContext.State.Transaction.Peer
|
||||
var paidMessageId: MessageId?
|
||||
var giveawayMessageId: MessageId?
|
||||
@ -506,6 +509,8 @@ private extension StarsContext.State.Transaction {
|
||||
parsedPeer = .premiumBot
|
||||
case .starsTransactionPeerAds:
|
||||
parsedPeer = .ads
|
||||
case .starsTransactionPeerAPI:
|
||||
parsedPeer = .apiLimitExtension
|
||||
case .starsTransactionPeerUnsupported:
|
||||
parsedPeer = .unsupported
|
||||
case let .starsTransactionPeer(apiPeer):
|
||||
@ -595,6 +600,7 @@ public final class StarsContext {
|
||||
case fragment
|
||||
case premiumBot
|
||||
case ads
|
||||
case apiLimitExtension
|
||||
case unsupported
|
||||
case peer(EnginePeer)
|
||||
}
|
||||
|
@ -130,10 +130,10 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, cha
|
||||
case .hashtag:
|
||||
break
|
||||
default:
|
||||
signal = .single({ _ in return .hashtags([]) })
|
||||
signal = .single({ _ in return .hashtags([], query) })
|
||||
}
|
||||
} else {
|
||||
signal = .single({ _ in return .hashtags([]) })
|
||||
signal = .single({ _ in return .hashtags([], query) })
|
||||
}
|
||||
|
||||
let hashtags: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> = context.engine.messages.recentlyUsedHashtags()
|
||||
@ -145,7 +145,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, cha
|
||||
result.append(hashtag)
|
||||
}
|
||||
}
|
||||
return { _ in return .hashtags(result) }
|
||||
return { _ in return .hashtags(result, query) }
|
||||
}
|
||||
|> castError(ChatContextQueryError.self)
|
||||
|
||||
|
@ -274,7 +274,7 @@ public final class StarsAvatarComponent: Component {
|
||||
self.iconView.isHidden = false
|
||||
self.avatarNode.isHidden = true
|
||||
self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/EntityInputPremiumIcon"), color: .white)
|
||||
case .unsupported:
|
||||
case .unsupported, .apiLimitExtension:
|
||||
iconInset = 7.0
|
||||
self.backgroundView.image = generateGradientFilledCircleImage(
|
||||
diameter: size.width,
|
||||
|
@ -788,7 +788,7 @@ public final class StarsImageComponent: Component {
|
||||
direction: .mirroredDiagonal
|
||||
)
|
||||
iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/EntityInputPremiumIcon"), color: .white)
|
||||
case .peer, .unsupported:
|
||||
case .peer, .unsupported, .apiLimitExtension:
|
||||
iconInset = 15.0
|
||||
iconBackgroundView.image = generateGradientFilledCircleImage(
|
||||
diameter: imageSize.width,
|
||||
|
@ -410,6 +410,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
case .ads:
|
||||
titleText = strings.Stars_Transaction_TelegramAds_Title
|
||||
via = strings.Stars_Transaction_TelegramAds_Subtitle
|
||||
case .apiLimitExtension:
|
||||
titleText = strings.Stars_Transaction_TelegramBotApi_Title
|
||||
via = strings.Stars_Transaction_TelegramBotApi_Subtitle
|
||||
case .unsupported:
|
||||
titleText = strings.Stars_Transaction_Unsupported_Title
|
||||
}
|
||||
|
@ -344,6 +344,9 @@ final class StarsTransactionsListPanelComponent: Component {
|
||||
case .ads:
|
||||
itemTitle = environment.strings.Stars_Intro_Transaction_TelegramAds_Title
|
||||
itemSubtitle = environment.strings.Stars_Intro_Transaction_TelegramAds_Subtitle
|
||||
case .apiLimitExtension:
|
||||
itemTitle = environment.strings.Stars_Intro_Transaction_TelegramBotApi_Title
|
||||
itemSubtitle = environment.strings.Stars_Intro_Transaction_TelegramBotApi_Subtitle
|
||||
case .unsupported:
|
||||
itemTitle = environment.strings.Stars_Intro_Transaction_Unsupported_Title
|
||||
itemSubtitle = nil
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Hashtag/SuggestHashtag.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Hashtag/SuggestHashtag.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "tagrecent.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Hashtag/SuggestHashtag.imageset/tagrecent.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Hashtag/SuggestHashtag.imageset/tagrecent.pdf
vendored
Normal file
Binary file not shown.
@ -10,7 +10,7 @@ private func inputQueryResultPriority(_ result: ChatPresentationInputQueryResult
|
||||
switch result {
|
||||
case let .stickers(items):
|
||||
return (0, !items.isEmpty)
|
||||
case let .hashtags(items):
|
||||
case let .hashtags(items, _):
|
||||
return (1, !items.isEmpty)
|
||||
case let .mentions(items):
|
||||
return (2, !items.isEmpty)
|
||||
@ -98,15 +98,19 @@ func inputContextPanelForChatPresentationIntefaceState(_ chatPresentationInterfa
|
||||
return panel
|
||||
}
|
||||
}
|
||||
case let .hashtags(results):
|
||||
if !results.isEmpty {
|
||||
case let .hashtags(results, query):
|
||||
var peer: EnginePeer?
|
||||
if let chatPeer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, chatPeer.addressName != nil {
|
||||
peer = EnginePeer(chatPeer)
|
||||
}
|
||||
if !results.isEmpty || (peer != nil && query.count >= 4) {
|
||||
if let currentPanel = currentPanel as? HashtagChatInputContextPanelNode {
|
||||
currentPanel.updateResults(results)
|
||||
currentPanel.updateResults(results, query: query, peer: peer)
|
||||
return currentPanel
|
||||
} else {
|
||||
let panel = HashtagChatInputContextPanelNode(context: context, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, fontSize: chatPresentationInterfaceState.fontSize, chatPresentationContext: controllerInteraction.presentationContext)
|
||||
panel.interfaceInteraction = interfaceInteraction
|
||||
panel.updateResults(results)
|
||||
panel.updateResults(results, query: query, peer: peer)
|
||||
return panel
|
||||
}
|
||||
}
|
||||
|
@ -114,10 +114,10 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|
||||
case .hashtag:
|
||||
break
|
||||
default:
|
||||
signal = .single({ _ in return .hashtags([]) })
|
||||
signal = .single({ _ in return .hashtags([], query) })
|
||||
}
|
||||
} else {
|
||||
signal = .single({ _ in return .hashtags([]) })
|
||||
signal = .single({ _ in return .hashtags([], query) })
|
||||
}
|
||||
|
||||
let hashtags: Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, ChatContextQueryError> = context.engine.messages.recentlyUsedHashtags()
|
||||
@ -129,7 +129,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|
||||
result.append(hashtag)
|
||||
}
|
||||
}
|
||||
return { _ in return .hashtags(result) }
|
||||
return { _ in return .hashtags(result, query) }
|
||||
}
|
||||
|> castError(ChatContextQueryError.self)
|
||||
|
||||
|
@ -16,33 +16,38 @@ import ChatContextQuery
|
||||
import ChatInputContextPanelNode
|
||||
|
||||
private struct HashtagChatInputContextPanelEntryStableId: Hashable {
|
||||
let text: String
|
||||
let title: String
|
||||
}
|
||||
|
||||
private struct HashtagChatInputContextPanelEntry: Comparable, Identifiable {
|
||||
let index: Int
|
||||
let theme: PresentationTheme
|
||||
let text: String
|
||||
let peer: EnginePeer?
|
||||
let title: String
|
||||
let text: String?
|
||||
let badge: String?
|
||||
let hashtag: String
|
||||
let revealed: Bool
|
||||
let isAdditionalRecent: Bool
|
||||
|
||||
var stableId: HashtagChatInputContextPanelEntryStableId {
|
||||
return HashtagChatInputContextPanelEntryStableId(text: self.text)
|
||||
return HashtagChatInputContextPanelEntryStableId(title: self.title)
|
||||
}
|
||||
|
||||
func withUpdatedTheme(_ theme: PresentationTheme) -> HashtagChatInputContextPanelEntry {
|
||||
return HashtagChatInputContextPanelEntry(index: self.index, theme: theme, text: self.text, revealed: self.revealed)
|
||||
return HashtagChatInputContextPanelEntry(index: self.index, theme: theme, peer: peer, title: self.title, text: self.text, badge: self.badge, hashtag: self.hashtag, revealed: self.revealed, isAdditionalRecent: self.isAdditionalRecent)
|
||||
}
|
||||
|
||||
static func ==(lhs: HashtagChatInputContextPanelEntry, rhs: HashtagChatInputContextPanelEntry) -> Bool {
|
||||
return lhs.index == rhs.index && lhs.text == rhs.text && lhs.theme === rhs.theme && lhs.revealed == rhs.revealed
|
||||
return lhs.index == rhs.index && lhs.peer == rhs.peer && lhs.title == rhs.title && lhs.text == rhs.text && lhs.badge == rhs.badge && lhs.hashtag == rhs.hashtag && lhs.theme === rhs.theme && lhs.revealed == rhs.revealed && lhs.isAdditionalRecent == rhs.isAdditionalRecent
|
||||
}
|
||||
|
||||
static func <(lhs: HashtagChatInputContextPanelEntry, rhs: HashtagChatInputContextPanelEntry) -> Bool {
|
||||
return lhs.index < rhs.index
|
||||
}
|
||||
|
||||
func item(account: Account, presentationData: PresentationData, setHashtagRevealed: @escaping (String?) -> Void, hashtagSelected: @escaping (String) -> Void, removeRequested: @escaping (String) -> Void) -> ListViewItem {
|
||||
return HashtagChatInputPanelItem(presentationData: ItemListPresentationData(presentationData), text: self.text, revealed: self.revealed, setHashtagRevealed: setHashtagRevealed, hashtagSelected: hashtagSelected, removeRequested: removeRequested)
|
||||
func item(context: AccountContext, presentationData: PresentationData, setHashtagRevealed: @escaping (String?) -> Void, hashtagSelected: @escaping (String) -> Void, removeRequested: @escaping (String) -> Void) -> ListViewItem {
|
||||
return HashtagChatInputPanelItem(context: context, presentationData: ItemListPresentationData(presentationData), peer: self.peer, title: self.title, text: self.text, badge: self.badge, hashtag: self.hashtag, revealed: self.revealed, isAdditionalRecent: self.isAdditionalRecent, setHashtagRevealed: setHashtagRevealed, hashtagSelected: hashtagSelected, removeRequested: removeRequested)
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,12 +57,12 @@ private struct HashtagChatInputContextPanelTransition {
|
||||
let updates: [ListViewUpdateItem]
|
||||
}
|
||||
|
||||
private func preparedTransition(from fromEntries: [HashtagChatInputContextPanelEntry], to toEntries: [HashtagChatInputContextPanelEntry], account: Account, presentationData: PresentationData, setHashtagRevealed: @escaping (String?) -> Void, hashtagSelected: @escaping (String) -> Void, removeRequested: @escaping (String) -> Void) -> HashtagChatInputContextPanelTransition {
|
||||
private func preparedTransition(from fromEntries: [HashtagChatInputContextPanelEntry], to toEntries: [HashtagChatInputContextPanelEntry], context: AccountContext, presentationData: PresentationData, setHashtagRevealed: @escaping (String?) -> Void, hashtagSelected: @escaping (String) -> Void, removeRequested: @escaping (String) -> Void) -> HashtagChatInputContextPanelTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, setHashtagRevealed: setHashtagRevealed, hashtagSelected: hashtagSelected, removeRequested: removeRequested), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, setHashtagRevealed: setHashtagRevealed, hashtagSelected: hashtagSelected, removeRequested: removeRequested), directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, setHashtagRevealed: setHashtagRevealed, hashtagSelected: hashtagSelected, removeRequested: removeRequested), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, setHashtagRevealed: setHashtagRevealed, hashtagSelected: hashtagSelected, removeRequested: removeRequested), directionHint: nil) }
|
||||
|
||||
return HashtagChatInputContextPanelTransition(deletions: deletions, insertions: insertions, updates: updates)
|
||||
}
|
||||
@ -67,6 +72,8 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode {
|
||||
private var currentEntries: [HashtagChatInputContextPanelEntry]?
|
||||
|
||||
private var currentResults: [String] = []
|
||||
private var currentQuery: String = ""
|
||||
private var currentPeer: EnginePeer?
|
||||
private var revealedHashtag: String?
|
||||
|
||||
private var enqueuedTransitions: [(HashtagChatInputContextPanelTransition, Bool)] = []
|
||||
@ -91,14 +98,72 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode {
|
||||
self.addSubnode(self.listView)
|
||||
}
|
||||
|
||||
func updateResults(_ results: [String]) {
|
||||
func updateResults(_ results: [String], query: String, peer: EnginePeer?) {
|
||||
self.currentResults = results
|
||||
self.currentQuery = query
|
||||
self.currentPeer = peer
|
||||
|
||||
var entries: [HashtagChatInputContextPanelEntry] = []
|
||||
var index = 0
|
||||
var stableIds = Set<HashtagChatInputContextPanelEntryStableId>()
|
||||
for text in results {
|
||||
let entry = HashtagChatInputContextPanelEntry(index: index, theme: self.theme, text: text, revealed: text == self.revealedHashtag)
|
||||
|
||||
var isAdditionalRecent = false
|
||||
if let peer, let _ = peer.addressName {
|
||||
isAdditionalRecent = true
|
||||
}
|
||||
//TODO:localize
|
||||
if query.count > 3 {
|
||||
if let peer, let addressName = peer.addressName {
|
||||
let genericEntry = HashtagChatInputContextPanelEntry(
|
||||
index: 0,
|
||||
theme: self.theme,
|
||||
peer: nil,
|
||||
title: "Use #\(query)",
|
||||
text: "searches posts from all channels",
|
||||
badge: nil,
|
||||
hashtag: query,
|
||||
revealed: false,
|
||||
isAdditionalRecent: false
|
||||
)
|
||||
stableIds.insert(genericEntry.stableId)
|
||||
entries.append(genericEntry)
|
||||
|
||||
let peerEntry = HashtagChatInputContextPanelEntry(
|
||||
index: 1,
|
||||
theme: self.theme,
|
||||
peer: peer,
|
||||
title: "Use #\(query)@\(addressName)",
|
||||
text: "searches only posts from this channel",
|
||||
badge: "NEW",
|
||||
hashtag: "\(query)@\(addressName)",
|
||||
revealed: false,
|
||||
isAdditionalRecent: false
|
||||
)
|
||||
stableIds.insert(peerEntry.stableId)
|
||||
entries.append(peerEntry)
|
||||
}
|
||||
}
|
||||
|
||||
index = 2
|
||||
|
||||
for hashtag in results {
|
||||
if hashtag == query {
|
||||
continue
|
||||
}
|
||||
if !hashtag.hasPrefix(query) {
|
||||
continue
|
||||
}
|
||||
let entry = HashtagChatInputContextPanelEntry(
|
||||
index: index,
|
||||
theme: self.theme,
|
||||
peer: hashtag.contains("@") ? peer : nil,
|
||||
title: "#\(hashtag)",
|
||||
text: nil,
|
||||
badge: nil,
|
||||
hashtag: hashtag,
|
||||
revealed: hashtag == self.revealedHashtag,
|
||||
isAdditionalRecent: isAdditionalRecent && !hashtag.contains("@")
|
||||
)
|
||||
if stableIds.contains(entry.stableId) {
|
||||
continue
|
||||
}
|
||||
@ -112,10 +177,10 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode {
|
||||
private func prepareTransition(from: [HashtagChatInputContextPanelEntry]? , to: [HashtagChatInputContextPanelEntry]) {
|
||||
let firstTime = from == nil
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let transition = preparedTransition(from: from ?? [], to: to, account: self.context.account, presentationData: presentationData, setHashtagRevealed: { [weak self] text in
|
||||
let transition = preparedTransition(from: from ?? [], to: to, context: self.context, presentationData: presentationData, setHashtagRevealed: { [weak self] text in
|
||||
if let strongSelf = self {
|
||||
strongSelf.revealedHashtag = text
|
||||
strongSelf.updateResults(strongSelf.currentResults)
|
||||
strongSelf.updateResults(strongSelf.currentResults, query: strongSelf.currentQuery, peer: strongSelf.currentPeer)
|
||||
}
|
||||
}, hashtagSelected: { [weak self] text in
|
||||
if let strongSelf = self, let interfaceInteraction = strongSelf.interfaceInteraction {
|
||||
@ -131,8 +196,7 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode {
|
||||
if let range = hashtagQueryRange {
|
||||
let inputText = NSMutableAttributedString(attributedString: textInputState.inputText)
|
||||
|
||||
let replacementText = text + " "
|
||||
|
||||
let replacementText = text
|
||||
inputText.replaceCharacters(in: range, with: replacementText)
|
||||
|
||||
let selectionPosition = range.lowerBound + (replacementText as NSString).length
|
||||
@ -172,7 +236,11 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode {
|
||||
//options.insert(.LowLatency)
|
||||
} else {
|
||||
options.insert(.AnimateTopItemPosition)
|
||||
options.insert(.AnimateCrossfade)
|
||||
if transition.insertions.isEmpty && transition.deletions.isEmpty && transition.updates.count <= 2 {
|
||||
options.insert(.AnimateInsertion)
|
||||
} else {
|
||||
options.insert(.AnimateCrossfade)
|
||||
}
|
||||
}
|
||||
|
||||
var insets = UIEdgeInsets()
|
||||
|
@ -8,21 +8,35 @@ import Postbox
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import ItemListUI
|
||||
import AvatarNode
|
||||
import AccountContext
|
||||
|
||||
final class HashtagChatInputPanelItem: ListViewItem {
|
||||
fileprivate let context: AccountContext
|
||||
fileprivate let presentationData: ItemListPresentationData
|
||||
fileprivate let text: String
|
||||
fileprivate let peer: EnginePeer?
|
||||
fileprivate let title: String
|
||||
fileprivate let text: String?
|
||||
fileprivate let badge: String?
|
||||
fileprivate let hashtag: String
|
||||
fileprivate let revealed: Bool
|
||||
fileprivate let isAdditionalRecent: Bool
|
||||
fileprivate let setHashtagRevealed: (String?) -> Void
|
||||
private let hashtagSelected: (String) -> Void
|
||||
fileprivate let removeRequested: (String) -> Void
|
||||
|
||||
let selectable: Bool = true
|
||||
|
||||
public init(presentationData: ItemListPresentationData, text: String, revealed: Bool, setHashtagRevealed: @escaping (String?) -> Void, hashtagSelected: @escaping (String) -> Void, removeRequested: @escaping (String) -> Void) {
|
||||
public init(context: AccountContext, presentationData: ItemListPresentationData, peer: EnginePeer?, title: String, text: String?, badge: String? = nil, hashtag: String, revealed: Bool, isAdditionalRecent: Bool, setHashtagRevealed: @escaping (String?) -> Void, hashtagSelected: @escaping (String) -> Void, removeRequested: @escaping (String) -> Void) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.peer = peer
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.badge = badge
|
||||
self.hashtag = hashtag
|
||||
self.revealed = revealed
|
||||
self.isAdditionalRecent = isAdditionalRecent
|
||||
self.setHashtagRevealed = setHashtagRevealed
|
||||
self.hashtagSelected = hashtagSelected
|
||||
self.removeRequested = removeRequested
|
||||
@ -79,14 +93,29 @@ final class HashtagChatInputPanelItem: ListViewItem {
|
||||
if self.revealed {
|
||||
self.setHashtagRevealed(nil)
|
||||
} else {
|
||||
self.hashtagSelected(self.text)
|
||||
if self.isAdditionalRecent {
|
||||
self.hashtagSelected(self.hashtag)
|
||||
} else {
|
||||
self.hashtagSelected(self.hashtag + " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let avatarFont = avatarPlaceholderFont(size: 16.0)
|
||||
|
||||
final class HashtagChatInputPanelItemNode: ListViewItemNode {
|
||||
static let itemHeight: CGFloat = 42.0
|
||||
|
||||
private let iconBackgroundLayer = SimpleLayer()
|
||||
private let iconLayer = SimpleLayer()
|
||||
private var avatarNode: AvatarNode?
|
||||
|
||||
private let badgeBackgroundLayer = SimpleLayer()
|
||||
|
||||
private let titleNode: TextNode
|
||||
private let textNode: TextNode
|
||||
private let badgeNode: TextNode
|
||||
private let topSeparatorNode: ASDisplayNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
@ -105,7 +134,12 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode {
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
||||
|
||||
init() {
|
||||
self.iconBackgroundLayer.cornerRadius = 15.0
|
||||
self.badgeBackgroundLayer.cornerRadius = 4.0
|
||||
|
||||
self.titleNode = TextNode()
|
||||
self.textNode = TextNode()
|
||||
self.badgeNode = TextNode()
|
||||
|
||||
self.topSeparatorNode = ASDisplayNode()
|
||||
self.topSeparatorNode.isLayerBacked = true
|
||||
@ -120,9 +154,10 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode {
|
||||
self.activateAreaNode.accessibilityTraits = [.button]
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
|
||||
self.addSubnode(self.topSeparatorNode)
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
self.addSubnode(self.activateAreaNode)
|
||||
@ -131,6 +166,12 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode {
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.view.layer.addSublayer(self.iconBackgroundLayer)
|
||||
self.iconBackgroundLayer.addSublayer(self.iconLayer)
|
||||
|
||||
self.view.layer.addSublayer(self.badgeBackgroundLayer)
|
||||
self.addSubnode(self.badgeNode)
|
||||
|
||||
let recognizer = ItemListRevealOptionsGestureRecognizer(target: self, action: #selector(self.revealGesture(_:)))
|
||||
self.recognizer = recognizer
|
||||
recognizer.allowAnyDirection = false
|
||||
@ -149,16 +190,24 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: HashtagChatInputPanelItem, _ params: ListViewItemLayoutParams, _ mergedTop: Bool, _ mergedBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeTextLayout = TextNode.asyncLayout(self.textNode)
|
||||
let makeBadgeLayout = TextNode.asyncLayout(self.badgeNode)
|
||||
|
||||
return { [weak self] item, params, mergedTop, mergedBottom in
|
||||
let textFont = Font.medium(floor(item.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0))
|
||||
|
||||
let baseWidth = params.width - params.leftInset - params.rightInset
|
||||
let titleFont = Font.semibold(floor(item.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0))
|
||||
let textFont = Font.regular(floor(item.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0))
|
||||
let badgeFont = Font.medium(floor(item.presentationData.fontSize.baseDisplaySize * 10.0 / 17.0))
|
||||
|
||||
let leftInset: CGFloat = 15.0 + params.leftInset
|
||||
let textLeftInset: CGFloat = 40.0
|
||||
let baseWidth = params.width - params.leftInset - params.rightInset - textLeftInset
|
||||
|
||||
let title = "#\(item.text)"
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: textFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: baseWidth, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (badgeLayout, badgeApply) = makeBadgeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.badge ?? "", font: badgeFont, textColor: item.presentationData.theme.list.itemCheckColors.foregroundColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: baseWidth, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: baseWidth - badgeLayout.size.width, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.text ?? "", font: textFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: baseWidth, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: HashtagChatInputPanelItemNode.itemHeight), insets: UIEdgeInsets())
|
||||
|
||||
@ -166,26 +215,70 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode {
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
strongSelf.validLayout = (nodeLayout.contentSize, params.leftInset, params.rightInset)
|
||||
|
||||
|
||||
let revealOffset = strongSelf.revealOffset
|
||||
|
||||
if strongSelf.iconLayer.contents == nil {
|
||||
strongSelf.iconLayer.contents = UIImage(bundleImageName: "Chat/Hashtag/SuggestHashtag")?.cgImage
|
||||
}
|
||||
strongSelf.iconBackgroundLayer.backgroundColor = item.presentationData.theme.list.itemAccentColor.cgColor
|
||||
strongSelf.iconLayer.layerTintColor = item.presentationData.theme.list.itemCheckColors.foregroundColor.cgColor
|
||||
strongSelf.badgeBackgroundLayer.backgroundColor = item.presentationData.theme.list.itemAccentColor.cgColor
|
||||
|
||||
strongSelf.separatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor
|
||||
strongSelf.topSeparatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor
|
||||
strongSelf.backgroundColor = item.presentationData.theme.list.plainBackgroundColor
|
||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor
|
||||
|
||||
let _ = titleApply()
|
||||
let _ = textApply()
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: revealOffset + leftInset, y: floor((nodeLayout.contentSize.height - textLayout.size.height) / 2.0)), size: textLayout.size)
|
||||
let _ = badgeApply()
|
||||
|
||||
if textLayout.size.height > 0.0 {
|
||||
let combinedHeight = titleLayout.size.height + textLayout.size.height
|
||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLeftInset, y: floor((nodeLayout.contentSize.height - combinedHeight) / 2.0)), size: titleLayout.size)
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLeftInset, y: floor((nodeLayout.contentSize.height - combinedHeight) / 2.0) + combinedHeight - textLayout.size.height), size: textLayout.size)
|
||||
} else {
|
||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: revealOffset + leftInset + textLeftInset, y: floor((nodeLayout.contentSize.height - titleLayout.size.height) / 2.0)), size: titleLayout.size)
|
||||
}
|
||||
|
||||
if badgeLayout.size.height > 0.0 {
|
||||
let badgeFrame = CGRect(origin: CGPoint(x: strongSelf.titleNode.frame.maxX + 8.0, y: floorToScreenPixels(strongSelf.titleNode.frame.midY - badgeLayout.size.height / 2.0)), size: badgeLayout.size)
|
||||
let badgeBackgroundFrame = badgeFrame.insetBy(dx: -3.0, dy: -2.0)
|
||||
|
||||
strongSelf.badgeNode.frame = badgeFrame
|
||||
strongSelf.badgeBackgroundLayer.frame = badgeBackgroundFrame
|
||||
}
|
||||
|
||||
strongSelf.topSeparatorNode.isHidden = mergedTop
|
||||
strongSelf.separatorNode.isHidden = !mergedBottom
|
||||
|
||||
let iconSize = CGSize(width: 30.0, height: 30.0)
|
||||
strongSelf.iconBackgroundLayer.frame = CGRect(origin: CGPoint(x: leftInset - 3.0, y: floor((nodeLayout.contentSize.height - 30.0) / 2.0)), size: iconSize)
|
||||
strongSelf.iconLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 30.0, height: 30.0))
|
||||
|
||||
if let peer = item.peer {
|
||||
strongSelf.iconBackgroundLayer.isHidden = true
|
||||
let avatarNode: AvatarNode
|
||||
if let current = strongSelf.avatarNode {
|
||||
avatarNode = current
|
||||
} else {
|
||||
avatarNode = AvatarNode(font: avatarFont)
|
||||
strongSelf.addSubnode(avatarNode)
|
||||
strongSelf.avatarNode = avatarNode
|
||||
}
|
||||
avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer)
|
||||
avatarNode.frame = strongSelf.iconBackgroundLayer.frame
|
||||
} else {
|
||||
strongSelf.iconBackgroundLayer.isHidden = false
|
||||
}
|
||||
|
||||
strongSelf.topSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: UIScreenPixel))
|
||||
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width - leftInset, height: UIScreenPixel))
|
||||
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel))
|
||||
|
||||
strongSelf.activateAreaNode.accessibilityLabel = title
|
||||
strongSelf.activateAreaNode.accessibilityLabel = item.title
|
||||
strongSelf.activateAreaNode.frame = CGRect(origin: .zero, size: nodeLayout.size)
|
||||
|
||||
strongSelf.setRevealOptions([ItemListRevealOption(key: 0, title: item.presentationData.strings.Common_Delete, icon: .none, color: item.presentationData.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.presentationData.theme.list.itemDisclosureActions.destructive.foregroundColor)])
|
||||
@ -197,7 +290,8 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode {
|
||||
|
||||
func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
if let (_, leftInset, _) = self.validLayout {
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: CGRect(origin: CGPoint(x: min(offset, 0.0) + 15.0 + leftInset, y: self.textNode.frame.minY), size: self.textNode.frame.size))
|
||||
transition.updateFrameAdditive(layer: self.iconBackgroundLayer, frame: CGRect(origin: CGPoint(x: min(offset, 0.0) + 12.0 + leftInset, y: self.iconBackgroundLayer.frame.minY), size: self.iconBackgroundLayer.frame.size))
|
||||
transition.updateFrameAdditive(node: self.titleNode, frame: CGRect(origin: CGPoint(x: min(offset, 0.0) + 15.0 + 40.0 + leftInset, y: self.titleNode.frame.minY), size: self.titleNode.frame.size))
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,6 +370,11 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if let item = self.item {
|
||||
if let _ = item.text {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -356,7 +455,7 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
item.removeRequested(item.text)
|
||||
item.removeRequested(item.hashtag)
|
||||
}
|
||||
|
||||
private func setupAndAddRevealNode() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user