mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Update API [skip ci]
This commit is contained in:
parent
76a24167d9
commit
f1b98f6dd8
@ -607,7 +607,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[1192749220] = { return Api.MessageAction.parse_messageActionStarGift($0) }
|
dict[1192749220] = { return Api.MessageAction.parse_messageActionStarGift($0) }
|
||||||
dict[775611918] = { return Api.MessageAction.parse_messageActionStarGiftUnique($0) }
|
dict[775611918] = { return Api.MessageAction.parse_messageActionStarGiftUnique($0) }
|
||||||
dict[1474192222] = { return Api.MessageAction.parse_messageActionSuggestProfilePhoto($0) }
|
dict[1474192222] = { return Api.MessageAction.parse_messageActionSuggestProfilePhoto($0) }
|
||||||
dict[866770590] = { return Api.MessageAction.parse_messageActionSuggestedPostApproval($0) }
|
dict[-1354584535] = { return Api.MessageAction.parse_messageActionSuggestedPostApproval($0) }
|
||||||
dict[-940721021] = { return Api.MessageAction.parse_messageActionTodoAppendTasks($0) }
|
dict[-940721021] = { return Api.MessageAction.parse_messageActionTodoAppendTasks($0) }
|
||||||
dict[-864265079] = { return Api.MessageAction.parse_messageActionTodoCompletions($0) }
|
dict[-864265079] = { return Api.MessageAction.parse_messageActionTodoCompletions($0) }
|
||||||
dict[228168278] = { return Api.MessageAction.parse_messageActionTopicCreate($0) }
|
dict[228168278] = { return Api.MessageAction.parse_messageActionTopicCreate($0) }
|
||||||
|
|||||||
@ -395,7 +395,7 @@ public extension Api {
|
|||||||
case messageActionStarGift(flags: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, convertStars: Int64?, upgradeMsgId: Int32?, upgradeStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?)
|
case messageActionStarGift(flags: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, convertStars: Int64?, upgradeMsgId: Int32?, upgradeStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?)
|
||||||
case messageActionStarGiftUnique(flags: Int32, gift: Api.StarGift, canExportAt: Int32?, transferStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?, resaleStars: Int64?, canTransferAt: Int32?, canResellAt: Int32?)
|
case messageActionStarGiftUnique(flags: Int32, gift: Api.StarGift, canExportAt: Int32?, transferStars: Int64?, fromId: Api.Peer?, peer: Api.Peer?, savedId: Int64?, resaleStars: Int64?, canTransferAt: Int32?, canResellAt: Int32?)
|
||||||
case messageActionSuggestProfilePhoto(photo: Api.Photo)
|
case messageActionSuggestProfilePhoto(photo: Api.Photo)
|
||||||
case messageActionSuggestedPostApproval(flags: Int32)
|
case messageActionSuggestedPostApproval(flags: Int32, rejectComment: String?, scheduleDate: Int32?, starsAmount: Int64?)
|
||||||
case messageActionTodoAppendTasks(list: [Api.TodoItem])
|
case messageActionTodoAppendTasks(list: [Api.TodoItem])
|
||||||
case messageActionTodoCompletions(completed: [Int32], incompleted: [Int32])
|
case messageActionTodoCompletions(completed: [Int32], incompleted: [Int32])
|
||||||
case messageActionTopicCreate(flags: Int32, title: String, iconColor: Int32, iconEmojiId: Int64?)
|
case messageActionTopicCreate(flags: Int32, title: String, iconColor: Int32, iconEmojiId: Int64?)
|
||||||
@ -804,11 +804,14 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
photo.serialize(buffer, true)
|
photo.serialize(buffer, true)
|
||||||
break
|
break
|
||||||
case .messageActionSuggestedPostApproval(let flags):
|
case .messageActionSuggestedPostApproval(let flags, let rejectComment, let scheduleDate, let starsAmount):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(866770590)
|
buffer.appendInt32(-1354584535)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
|
if Int(flags) & Int(1 << 2) != 0 {serializeString(rejectComment!, buffer: buffer, boxed: false)}
|
||||||
|
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
||||||
|
if Int(flags) & Int(1 << 4) != 0 {serializeInt64(starsAmount!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .messageActionTodoAppendTasks(let list):
|
case .messageActionTodoAppendTasks(let list):
|
||||||
if boxed {
|
if boxed {
|
||||||
@ -966,8 +969,8 @@ public extension Api {
|
|||||||
return ("messageActionStarGiftUnique", [("flags", flags as Any), ("gift", gift as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any), ("resaleStars", resaleStars as Any), ("canTransferAt", canTransferAt as Any), ("canResellAt", canResellAt as Any)])
|
return ("messageActionStarGiftUnique", [("flags", flags as Any), ("gift", gift as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any), ("fromId", fromId as Any), ("peer", peer as Any), ("savedId", savedId as Any), ("resaleStars", resaleStars as Any), ("canTransferAt", canTransferAt as Any), ("canResellAt", canResellAt as Any)])
|
||||||
case .messageActionSuggestProfilePhoto(let photo):
|
case .messageActionSuggestProfilePhoto(let photo):
|
||||||
return ("messageActionSuggestProfilePhoto", [("photo", photo as Any)])
|
return ("messageActionSuggestProfilePhoto", [("photo", photo as Any)])
|
||||||
case .messageActionSuggestedPostApproval(let flags):
|
case .messageActionSuggestedPostApproval(let flags, let rejectComment, let scheduleDate, let starsAmount):
|
||||||
return ("messageActionSuggestedPostApproval", [("flags", flags as Any)])
|
return ("messageActionSuggestedPostApproval", [("flags", flags as Any), ("rejectComment", rejectComment as Any), ("scheduleDate", scheduleDate as Any), ("starsAmount", starsAmount as Any)])
|
||||||
case .messageActionTodoAppendTasks(let list):
|
case .messageActionTodoAppendTasks(let list):
|
||||||
return ("messageActionTodoAppendTasks", [("list", list as Any)])
|
return ("messageActionTodoAppendTasks", [("list", list as Any)])
|
||||||
case .messageActionTodoCompletions(let completed, let incompleted):
|
case .messageActionTodoCompletions(let completed, let incompleted):
|
||||||
@ -1770,9 +1773,18 @@ public extension Api {
|
|||||||
public static func parse_messageActionSuggestedPostApproval(_ reader: BufferReader) -> MessageAction? {
|
public static func parse_messageActionSuggestedPostApproval(_ reader: BufferReader) -> MessageAction? {
|
||||||
var _1: Int32?
|
var _1: Int32?
|
||||||
_1 = reader.readInt32()
|
_1 = reader.readInt32()
|
||||||
|
var _2: String?
|
||||||
|
if Int(_1!) & Int(1 << 2) != 0 {_2 = parseString(reader) }
|
||||||
|
var _3: Int32?
|
||||||
|
if Int(_1!) & Int(1 << 3) != 0 {_3 = reader.readInt32() }
|
||||||
|
var _4: Int64?
|
||||||
|
if Int(_1!) & Int(1 << 4) != 0 {_4 = reader.readInt64() }
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
if _c1 {
|
let _c2 = (Int(_1!) & Int(1 << 2) == 0) || _2 != nil
|
||||||
return Api.MessageAction.messageActionSuggestedPostApproval(flags: _1!)
|
let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil
|
||||||
|
let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil
|
||||||
|
if _c1 && _c2 && _c3 && _c4 {
|
||||||
|
return Api.MessageAction.messageActionSuggestedPostApproval(flags: _1!, rejectComment: _2, scheduleDate: _3, starsAmount: _4)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -8909,14 +8909,15 @@ public extension Api.functions.messages {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.functions.messages {
|
public extension Api.functions.messages {
|
||||||
static func toggleSuggestedPostApproval(flags: Int32, peer: Api.InputPeer, msgId: Int32, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
static func toggleSuggestedPostApproval(flags: Int32, peer: Api.InputPeer, msgId: Int32, scheduleDate: Int32?, rejectComment: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(806598987)
|
buffer.appendInt32(-2130229924)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
peer.serialize(buffer, true)
|
peer.serialize(buffer, true)
|
||||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
||||||
return (FunctionDescription(name: "messages.toggleSuggestedPostApproval", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("scheduleDate", String(describing: scheduleDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
if Int(flags) & Int(1 << 2) != 0 {serializeString(rejectComment!, buffer: buffer, boxed: false)}
|
||||||
|
return (FunctionDescription(name: "messages.toggleSuggestedPostApproval", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("scheduleDate", String(describing: scheduleDate)), ("rejectComment", String(describing: rejectComment))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||||
let reader = BufferReader(buffer)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Updates?
|
var result: Api.Updates?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
|
|||||||
@ -228,7 +228,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||||||
return TelegramMediaAction(action: .todoCompletions(completed: completed, incompleted: incompleted))
|
return TelegramMediaAction(action: .todoCompletions(completed: completed, incompleted: incompleted))
|
||||||
case let .messageActionTodoAppendTasks(list):
|
case let .messageActionTodoAppendTasks(list):
|
||||||
return TelegramMediaAction(action: .todoAppendTasks(list.map { TelegramMediaTodo.Item(apiItem: $0) }))
|
return TelegramMediaAction(action: .todoAppendTasks(list.map { TelegramMediaTodo.Item(apiItem: $0) }))
|
||||||
case let .messageActionSuggestedPostApproval(flags):
|
case let .messageActionSuggestedPostApproval(flags, rejectComment, scheduleDate, starsAmount):
|
||||||
let status: TelegramMediaActionType.SuggestedPostApprovalStatus
|
let status: TelegramMediaActionType.SuggestedPostApprovalStatus
|
||||||
if (flags & (1 << 0)) != 0 {
|
if (flags & (1 << 0)) != 0 {
|
||||||
let reason: TelegramMediaActionType.SuggestedPostApprovalStatus.RejectionReason
|
let reason: TelegramMediaActionType.SuggestedPostApprovalStatus.RejectionReason
|
||||||
@ -237,9 +237,9 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||||||
} else {
|
} else {
|
||||||
reason = .generic
|
reason = .generic
|
||||||
}
|
}
|
||||||
status = .rejected(reason: reason)
|
status = .rejected(reason: reason, comment: rejectComment)
|
||||||
} else {
|
} else {
|
||||||
status = .approved
|
status = .approved(timestamp: scheduleDate, amount: starsAmount ?? 0)
|
||||||
}
|
}
|
||||||
return TelegramMediaAction(action: .suggestedPostApprovalStatus(status: status))
|
return TelegramMediaAction(action: .suggestedPostApprovalStatus(status: status))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -117,13 +117,16 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
case lowBalance
|
case lowBalance
|
||||||
}
|
}
|
||||||
|
|
||||||
case approved
|
case approved(timestamp: Int32?, amount: Int64)
|
||||||
case rejected(reason: RejectionReason)
|
case rejected(reason: RejectionReason, comment: String?)
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
switch decoder.decodeInt32ForKey("_t", orElse: 0) {
|
switch decoder.decodeInt32ForKey("_t", orElse: 0) {
|
||||||
case 0:
|
case 0:
|
||||||
self = .approved
|
self = .approved(
|
||||||
|
timestamp: decoder.decodeOptionalInt32ForKey("ts"),
|
||||||
|
amount: decoder.decodeInt64ForKey("am", orElse: 0)
|
||||||
|
)
|
||||||
case 1:
|
case 1:
|
||||||
let reason: RejectionReason
|
let reason: RejectionReason
|
||||||
switch decoder.decodeInt32ForKey("rs", orElse: 0) {
|
switch decoder.decodeInt32ForKey("rs", orElse: 0) {
|
||||||
@ -135,18 +138,24 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
assertionFailure()
|
assertionFailure()
|
||||||
reason = .generic
|
reason = .generic
|
||||||
}
|
}
|
||||||
self = .rejected(reason: reason)
|
self = .rejected(reason: reason, comment: decoder.decodeOptionalStringForKey("com"))
|
||||||
default:
|
default:
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
self = .rejected(reason: .generic)
|
self = .rejected(reason: .generic, comment: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
switch self {
|
switch self {
|
||||||
case .approved:
|
case let .approved(timestamp, amount):
|
||||||
encoder.encodeInt32(0, forKey: "_t")
|
encoder.encodeInt32(0, forKey: "_t")
|
||||||
case let .rejected(reason):
|
if let timestamp {
|
||||||
|
encoder.encodeInt32(timestamp, forKey: "ts")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "ts")
|
||||||
|
}
|
||||||
|
encoder.encodeInt64(amount, forKey: "am")
|
||||||
|
case let .rejected(reason, comment):
|
||||||
encoder.encodeInt32(1, forKey: "_t")
|
encoder.encodeInt32(1, forKey: "_t")
|
||||||
switch reason {
|
switch reason {
|
||||||
case .generic:
|
case .generic:
|
||||||
@ -154,6 +163,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
case .lowBalance:
|
case .lowBalance:
|
||||||
encoder.encodeInt32(1, forKey: "rs")
|
encoder.encodeInt32(1, forKey: "rs")
|
||||||
}
|
}
|
||||||
|
if let comment {
|
||||||
|
encoder.encodeString(comment, forKey: "com")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "com")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -356,7 +370,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
)
|
)
|
||||||
case 51:
|
case 51:
|
||||||
let status: SuggestedPostApprovalStatus? = decoder.decodeObjectForKey("st", decoder: { SuggestedPostApprovalStatus(decoder: $0) }) as? SuggestedPostApprovalStatus
|
let status: SuggestedPostApprovalStatus? = decoder.decodeObjectForKey("st", decoder: { SuggestedPostApprovalStatus(decoder: $0) }) as? SuggestedPostApprovalStatus
|
||||||
self = .suggestedPostApprovalStatus(status: status ?? .rejected(reason: .generic))
|
self = .suggestedPostApprovalStatus(status: status ?? .rejected(reason: .generic, comment: nil))
|
||||||
default:
|
default:
|
||||||
self = .unknown
|
self = .unknown
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1588,13 +1588,18 @@ public extension TelegramEngine {
|
|||||||
return _internal_requestMessageAuthor(account: self.account, id: id)
|
return _internal_requestMessageAuthor(account: self.account, id: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func monoforumPerformSuggestedPostAction(id: EngineMessage.Id, approve: Bool, timestamp: Int32?) -> Signal<Never, NoError> {
|
public enum MonoforumSuggestedPostAction {
|
||||||
return _internal_monoforumPerformSuggestedPostAction(account: self.account, id: id, approve: approve, timestamp: timestamp)
|
case approve(timestamp: Int32?)
|
||||||
|
case reject(comment: String?)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func monoforumPerformSuggestedPostAction(id: EngineMessage.Id, action: MonoforumSuggestedPostAction) -> Signal<Never, NoError> {
|
||||||
|
return _internal_monoforumPerformSuggestedPostAction(account: self.account, id: id, action: action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_monoforumPerformSuggestedPostAction(account: Account, id: EngineMessage.Id, approve: Bool, timestamp: Int32?) -> Signal<Never, NoError> {
|
func _internal_monoforumPerformSuggestedPostAction(account: Account, id: EngineMessage.Id, action: TelegramEngine.Messages.MonoforumSuggestedPostAction) -> Signal<Never, NoError> {
|
||||||
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
return transaction.getPeer(id.peerId).flatMap(apiInputPeer)
|
return transaction.getPeer(id.peerId).flatMap(apiInputPeer)
|
||||||
}
|
}
|
||||||
@ -1607,13 +1612,22 @@ func _internal_monoforumPerformSuggestedPostAction(account: Account, id: EngineM
|
|||||||
}
|
}
|
||||||
|
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
if !approve {
|
var timestamp: Int32?
|
||||||
flags |= 1 << 1
|
var rejectComment: String?
|
||||||
}
|
switch action {
|
||||||
|
case let .approve(timestampValue):
|
||||||
|
timestamp = timestampValue
|
||||||
if timestamp != nil {
|
if timestamp != nil {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
}
|
}
|
||||||
return account.network.request(Api.functions.messages.toggleSuggestedPostApproval(flags: flags, peer: inputPeer, msgId: id.id, scheduleDate: timestamp))
|
case let .reject(commentValue):
|
||||||
|
flags |= 1 << 1
|
||||||
|
rejectComment = commentValue
|
||||||
|
if rejectComment != nil {
|
||||||
|
flags |= 1 << 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return account.network.request(Api.functions.messages.toggleSuggestedPostApproval(flags: flags, peer: inputPeer, msgId: id.id, scheduleDate: timestamp, rejectComment: rejectComment))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
|
|||||||
@ -31,6 +31,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/TextNodeWithEntities",
|
"//submodules/TelegramUI/Components/TextNodeWithEntities",
|
||||||
"//submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode",
|
"//submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode",
|
||||||
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
|
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
|
||||||
|
"//submodules/Markdown",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import InvisibleInkDustNode
|
|||||||
import TextNodeWithEntities
|
import TextNodeWithEntities
|
||||||
import ChatMessageBubbleContentNode
|
import ChatMessageBubbleContentNode
|
||||||
import ChatMessageItemCommon
|
import ChatMessageItemCommon
|
||||||
|
import Markdown
|
||||||
|
|
||||||
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: Message, messageCount: Int? = nil, accountPeerId: PeerId, forForumOverview: Bool) -> NSAttributedString? {
|
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: Message, messageCount: Int? = nil, accountPeerId: PeerId, forForumOverview: Bool) -> NSAttributedString? {
|
||||||
return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: EngineMessage(message), messageCount: messageCount, accountPeerId: accountPeerId, forChatList: false, forForumOverview: forForumOverview)
|
return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: EngineMessage(message), messageCount: messageCount, accountPeerId: accountPeerId, forChatList: false, forForumOverview: forForumOverview)
|
||||||
@ -29,6 +30,7 @@ private func attributedServiceMessageString(theme: ChatPresentationThemeData, st
|
|||||||
public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||||
public var expandHighlightingNode: LinkHighlightingNode?
|
public var expandHighlightingNode: LinkHighlightingNode?
|
||||||
|
|
||||||
|
public var titleNode: TextNode?
|
||||||
public let labelNode: TextNodeWithEntities
|
public let labelNode: TextNodeWithEntities
|
||||||
private var dustNode: InvisibleInkDustNode?
|
private var dustNode: InvisibleInkDustNode?
|
||||||
public var backgroundNode: WallpaperBubbleBackgroundNode?
|
public var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||||
@ -155,6 +157,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
|
override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
|
||||||
|
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||||
let makeLabelLayout = TextNodeWithEntities.asyncLayout(self.labelNode)
|
let makeLabelLayout = TextNodeWithEntities.asyncLayout(self.labelNode)
|
||||||
|
|
||||||
let cachedMaskBackgroundImage = self.cachedMaskBackgroundImage
|
let cachedMaskBackgroundImage = self.cachedMaskBackgroundImage
|
||||||
@ -184,11 +187,14 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
var image: TelegramMediaImage?
|
var image: TelegramMediaImage?
|
||||||
var story: TelegramMediaStory?
|
var story: TelegramMediaStory?
|
||||||
|
var suggestedPost: TelegramMediaActionType.SuggestedPostApprovalStatus?
|
||||||
for media in item.message.media {
|
for media in item.message.media {
|
||||||
if let action = media as? TelegramMediaAction {
|
if let action = media as? TelegramMediaAction {
|
||||||
switch action.action {
|
switch action.action {
|
||||||
case let .photoUpdated(img):
|
case let .photoUpdated(img):
|
||||||
image = img
|
image = img
|
||||||
|
case let .suggestedPostApprovalStatus(status):
|
||||||
|
suggestedPost = status
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -206,7 +212,94 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
updatedAttributedString = mutableString
|
updatedAttributedString = mutableString
|
||||||
}
|
}
|
||||||
|
|
||||||
let (labelLayout, apply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: updatedAttributedString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
var textAlignment: NSTextAlignment = .center
|
||||||
|
|
||||||
|
let primaryTextColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).primaryText
|
||||||
|
|
||||||
|
if let suggestedPost {
|
||||||
|
textAlignment = .left
|
||||||
|
|
||||||
|
let channelName: String
|
||||||
|
if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, peer.isMonoForum, let linkedMonoforumId = peer.linkedMonoforumId, let mainChannel = item.message.peers[linkedMonoforumId] as? TelegramChannel {
|
||||||
|
channelName = EnginePeer(mainChannel).compactDisplayTitle
|
||||||
|
} else {
|
||||||
|
channelName = " "
|
||||||
|
}
|
||||||
|
|
||||||
|
switch suggestedPost {
|
||||||
|
case let .approved(timestamp, amount):
|
||||||
|
//TODO:localize
|
||||||
|
let timeString = humanReadableStringForTimestamp(strings: item.presentationData.strings, dateTimeFormat: item.presentationData.dateTimeFormat, timestamp: timestamp ?? 0, alwaysShowTime: true, allowYesterday: false, format: HumanReadableStringFormat(
|
||||||
|
dateFormatString: { value in
|
||||||
|
return PresentationStrings.FormattedString(string: item.presentationData.strings.SuggestPost_SetTimeFormat_Date(value).string, ranges: [])
|
||||||
|
},
|
||||||
|
tomorrowFormatString: { value in
|
||||||
|
return PresentationStrings.FormattedString(string: item.presentationData.strings.SuggestPost_SetTimeFormat_TomorrowAt(value).string, ranges: [])
|
||||||
|
},
|
||||||
|
todayFormatString: { value in
|
||||||
|
return PresentationStrings.FormattedString(string: item.presentationData.strings.SuggestPost_SetTimeFormat_TodayAt(value).string, ranges: [])
|
||||||
|
},
|
||||||
|
yesterdayFormatString: { value in
|
||||||
|
return PresentationStrings.FormattedString(string: item.presentationData.strings.SuggestPost_SetTimeFormat_TodayAt(value).string, ranges: [])
|
||||||
|
}
|
||||||
|
)).string
|
||||||
|
|
||||||
|
let amountString = amount == 1 ? "\(amount) Star" : "\(amount) Stars"
|
||||||
|
|
||||||
|
let rawString = "📅 Your post will be automatically published on **\(channelName)** **\(timeString)**.\n\n💰 You have been charged \(amountString).\n\n⌛ **\(channelName)** will receive your Stars once the post has been live for 24 hours.\n\n🔄 If **\(channelName)** removes the post before it has been live for 24 hours, your Stars will be refunded."
|
||||||
|
updatedAttributedString = parseMarkdownIntoAttributedString(rawString, attributes: MarkdownAttributes(
|
||||||
|
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: primaryTextColor),
|
||||||
|
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: primaryTextColor),
|
||||||
|
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: primaryTextColor),
|
||||||
|
linkAttribute: { url in
|
||||||
|
return ("URL", url)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
case let .rejected(reason, comment):
|
||||||
|
let rawString: String
|
||||||
|
switch reason {
|
||||||
|
case .generic:
|
||||||
|
if let comment {
|
||||||
|
rawString = "**\(channelName)** declined your post with the following comment:\n\n" + comment
|
||||||
|
} else {
|
||||||
|
rawString = "**\(channelName)** declined your post."
|
||||||
|
}
|
||||||
|
case .lowBalance:
|
||||||
|
rawString = "**\(channelName)** was unable to post your message, because you did not have enough Stars."
|
||||||
|
}
|
||||||
|
updatedAttributedString = parseMarkdownIntoAttributedString(rawString, attributes: MarkdownAttributes(
|
||||||
|
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: primaryTextColor),
|
||||||
|
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: primaryTextColor),
|
||||||
|
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: primaryTextColor),
|
||||||
|
linkAttribute: { url in
|
||||||
|
return ("URL", url)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var titleLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
||||||
|
if let suggestedPost {
|
||||||
|
let rawString: String
|
||||||
|
switch suggestedPost {
|
||||||
|
case .approved:
|
||||||
|
rawString = "🤝 Agreement Reached!"
|
||||||
|
case .rejected:
|
||||||
|
rawString = "Declined"
|
||||||
|
}
|
||||||
|
let titleString = parseMarkdownIntoAttributedString(rawString, attributes: MarkdownAttributes(
|
||||||
|
body: MarkdownAttributeSet(font: Font.semibold(15.0), textColor: primaryTextColor),
|
||||||
|
bold: MarkdownAttributeSet(font: Font.bold(15.0), textColor: primaryTextColor),
|
||||||
|
link: MarkdownAttributeSet(font: Font.semibold(15.0), textColor: primaryTextColor),
|
||||||
|
linkAttribute: { url in
|
||||||
|
return ("URL", url)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
titleLayoutAndApply = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: textAlignment, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
}
|
||||||
|
|
||||||
|
let (labelLayout, apply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: updatedAttributedString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: textAlignment, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
var labelRects = labelLayout.linesRects()
|
var labelRects = labelLayout.linesRects()
|
||||||
if labelRects.count > 1 {
|
if labelRects.count > 1 {
|
||||||
@ -231,20 +324,47 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
let backgroundMaskImage: (CGPoint, UIImage)?
|
let backgroundMaskImage: (CGPoint, UIImage)?
|
||||||
var backgroundMaskUpdated = false
|
var backgroundMaskUpdated = false
|
||||||
|
if suggestedPost != nil {
|
||||||
|
backgroundMaskImage = nil
|
||||||
|
if cachedMaskBackgroundImage != nil {
|
||||||
|
backgroundMaskUpdated = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if let (currentOffset, currentImage, currentRects) = cachedMaskBackgroundImage, currentRects == labelRects {
|
if let (currentOffset, currentImage, currentRects) = cachedMaskBackgroundImage, currentRects == labelRects {
|
||||||
backgroundMaskImage = (currentOffset, currentImage)
|
backgroundMaskImage = (currentOffset, currentImage)
|
||||||
} else {
|
} else {
|
||||||
backgroundMaskImage = LinkHighlightingNode.generateImage(color: .white, inset: 0.0, innerRadius: 10.0, outerRadius: 10.0, rects: labelRects, useModernPathCalculation: false)
|
backgroundMaskImage = LinkHighlightingNode.generateImage(color: .white, inset: 0.0, innerRadius: 10.0, outerRadius: 10.0, rects: labelRects, useModernPathCalculation: false)
|
||||||
backgroundMaskUpdated = true
|
backgroundMaskUpdated = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var backgroundSize = CGSize(width: labelLayout.size.width + 8.0 + 8.0, height: labelLayout.size.height + 4.0)
|
var backgroundSize = CGSize(width: labelLayout.size.width, height: labelLayout.size.height)
|
||||||
if let _ = image {
|
if let _ = image {
|
||||||
backgroundSize.width = imageSize.width + 2.0
|
backgroundSize.width = imageSize.width + 2.0
|
||||||
backgroundSize.height += imageSize.height + 10.0
|
backgroundSize.height += imageSize.height + 10.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let titleSpacing: CGFloat = 18.0
|
||||||
|
|
||||||
|
var contentInsets = UIEdgeInsets()
|
||||||
|
var contentOuterInsets = UIEdgeInsets()
|
||||||
|
|
||||||
|
if let titleLayoutAndApply {
|
||||||
|
backgroundSize.width = max(backgroundSize.width, titleLayoutAndApply.0.size.width)
|
||||||
|
backgroundSize.height += titleSpacing + titleLayoutAndApply.0.size.height
|
||||||
|
|
||||||
|
contentInsets = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0)
|
||||||
|
contentOuterInsets = UIEdgeInsets(top: 8.0, left: 0.0, bottom: 8.0, right: 0.0)
|
||||||
|
|
||||||
|
backgroundSize.width += contentInsets.left + contentInsets.right
|
||||||
|
backgroundSize.height += contentInsets.top + contentInsets.bottom
|
||||||
|
} else {
|
||||||
|
backgroundSize.width += 8.0 + 8.0
|
||||||
|
backgroundSize.height += 4.0
|
||||||
|
}
|
||||||
|
|
||||||
return (backgroundSize.width, { boundingWidth in
|
return (backgroundSize.width, { boundingWidth in
|
||||||
return (CGSize(width: boundingWidth, height: backgroundSize.height), { [weak self] animation, synchronousLoads, _ in
|
return (CGSize(width: boundingWidth, height: backgroundSize.height + contentOuterInsets.top + contentOuterInsets.bottom), { [weak self] animation, synchronousLoads, _ in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
|
|
||||||
@ -330,7 +450,31 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
attemptSynchronous: synchronousLoads
|
attemptSynchronous: synchronousLoads
|
||||||
))
|
))
|
||||||
|
|
||||||
let labelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((boundingWidth - labelLayout.size.width) / 2.0) - 1.0, y: image != nil ? 2.0 : floorToScreenPixels((backgroundSize.height - labelLayout.size.height) / 2.0) - 1.0), size: labelLayout.size)
|
let labelFrame: CGRect
|
||||||
|
let contentFrame: CGRect
|
||||||
|
|
||||||
|
if let (titleLayout, titleApply) = titleLayoutAndApply {
|
||||||
|
contentFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((boundingWidth - backgroundSize.width) * 0.5), y: contentOuterInsets.top), size: backgroundSize)
|
||||||
|
|
||||||
|
let titleFrame = CGRect(origin: CGPoint(x: contentFrame.minX + floor((contentFrame.width - titleLayout.size.width) * 0.5), y: contentFrame.minY + contentInsets.top), size: titleLayout.size)
|
||||||
|
labelFrame = CGRect(origin: CGPoint(x: contentFrame.minX + contentInsets.left, y: titleFrame.maxY + titleSpacing), size: labelLayout.size)
|
||||||
|
|
||||||
|
let titleNode = titleApply()
|
||||||
|
if strongSelf.titleNode !== titleNode {
|
||||||
|
strongSelf.titleNode?.removeFromSupernode()
|
||||||
|
strongSelf.titleNode = titleNode
|
||||||
|
strongSelf.addSubnode(titleNode)
|
||||||
|
titleNode.anchorPoint = CGPoint()
|
||||||
|
|
||||||
|
titleNode.frame = titleFrame
|
||||||
|
} else {
|
||||||
|
animation.animator.updatePosition(layer: titleNode.layer, position: titleFrame.origin, completion: nil)
|
||||||
|
titleNode.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
labelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((boundingWidth - labelLayout.size.width) / 2.0) - 1.0, y: image != nil ? 2.0 : floorToScreenPixels((backgroundSize.height - labelLayout.size.height) / 2.0) - 1.0), size: labelLayout.size)
|
||||||
|
contentFrame = labelFrame
|
||||||
|
}
|
||||||
|
|
||||||
if story != nil {
|
if story != nil {
|
||||||
let leadingIconView: UIImageView
|
let leadingIconView: UIImageView
|
||||||
@ -395,7 +539,59 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
strongSelf.expandHighlightingNode = nil
|
strongSelf.expandHighlightingNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (offset, image) = backgroundMaskImage {
|
if suggestedPost != nil {
|
||||||
|
let backgroundFrame = contentFrame
|
||||||
|
|
||||||
|
if item.context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||||
|
if strongSelf.backgroundNode == nil {
|
||||||
|
if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||||
|
strongSelf.backgroundNode = backgroundNode
|
||||||
|
backgroundNode.addSubnode(strongSelf.backgroundColorNode)
|
||||||
|
strongSelf.insertSubnode(backgroundNode, at: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strongSelf.backgroundColorNode.isHidden = true
|
||||||
|
} else {
|
||||||
|
if strongSelf.backgroundMaskNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.backgroundMaskNode, at: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let backgroundNode = strongSelf.backgroundNode {
|
||||||
|
backgroundNode.clipsToBounds = true
|
||||||
|
backgroundNode.cornerRadius = min(backgroundFrame.height * 0.5, 22.0)
|
||||||
|
backgroundNode.view.mask = nil
|
||||||
|
|
||||||
|
animation.animator.updateFrame(layer: backgroundNode.layer, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: backgroundFrame.size), completion: nil)
|
||||||
|
|
||||||
|
if let (rect, size) = strongSelf.absoluteRect {
|
||||||
|
strongSelf.updateAbsoluteRect(rect, within: size)
|
||||||
|
}
|
||||||
|
strongSelf.backgroundMaskNode.frame = CGRect(origin: CGPoint(), size: backgroundFrame.size)
|
||||||
|
strongSelf.backgroundMaskNode.layer.layerTintColor = nil
|
||||||
|
} else {
|
||||||
|
animation.animator.updateFrame(layer: strongSelf.backgroundMaskNode.layer, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: backgroundFrame.size), completion: nil)
|
||||||
|
strongSelf.backgroundMaskNode.layer.layerTintColor = selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper).cgColor
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.backgroundMaskNode.image = nil
|
||||||
|
|
||||||
|
animation.animator.updateFrame(layer: strongSelf.backgroundColorNode.layer, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size), completion: nil)
|
||||||
|
|
||||||
|
strongSelf.cachedMaskBackgroundImage = nil
|
||||||
|
|
||||||
|
switch strongSelf.visibility {
|
||||||
|
case .none:
|
||||||
|
strongSelf.labelNode.visibilityRect = nil
|
||||||
|
//strongSelf.spoilerTextNode?.visibilityRect = nil
|
||||||
|
case let .visible(_, subRect):
|
||||||
|
var subRect = subRect
|
||||||
|
subRect.origin.x = 0.0
|
||||||
|
subRect.size.width = 10000.0
|
||||||
|
strongSelf.labelNode.visibilityRect = subRect
|
||||||
|
//strongSelf.spoilerTextNode?.visibilityRect = subRect
|
||||||
|
}
|
||||||
|
} else if let (offset, image) = backgroundMaskImage {
|
||||||
if item.context.sharedContext.energyUsageSettings.fullTranslucency {
|
if item.context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||||
if strongSelf.backgroundNode == nil {
|
if strongSelf.backgroundNode == nil {
|
||||||
if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||||
@ -415,7 +611,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
if let backgroundNode = strongSelf.backgroundNode {
|
if let backgroundNode = strongSelf.backgroundNode {
|
||||||
if labelRects.count == 1 {
|
if labelRects.count == 1 {
|
||||||
backgroundNode.clipsToBounds = true
|
backgroundNode.clipsToBounds = true
|
||||||
backgroundNode.cornerRadius = labelRects[0].height / 2.0
|
backgroundNode.cornerRadius = min(32.0, labelRects[0].height / 2.0)
|
||||||
backgroundNode.view.mask = nil
|
backgroundNode.view.mask = nil
|
||||||
} else {
|
} else {
|
||||||
backgroundNode.clipsToBounds = false
|
backgroundNode.clipsToBounds = false
|
||||||
|
|||||||
@ -91,6 +91,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/LottieMetal",
|
"//submodules/TelegramUI/Components/LottieMetal",
|
||||||
"//submodules/TelegramStringFormatting",
|
"//submodules/TelegramStringFormatting",
|
||||||
"//submodules/AvatarNode",
|
"//submodules/AvatarNode",
|
||||||
|
"//submodules/TelegramUI/Components/Chat/ChatMessageSuggestedPostInfoNode",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -80,6 +80,7 @@ import AnimatedStickerNode
|
|||||||
import TelegramAnimatedStickerNode
|
import TelegramAnimatedStickerNode
|
||||||
import LottieMetal
|
import LottieMetal
|
||||||
import AvatarNode
|
import AvatarNode
|
||||||
|
import ChatMessageSuggestedPostInfoNode
|
||||||
|
|
||||||
private struct BubbleItemAttributes {
|
private struct BubbleItemAttributes {
|
||||||
var index: Int?
|
var index: Int?
|
||||||
@ -625,6 +626,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
private let shadowNode: ChatMessageShadowNode
|
private let shadowNode: ChatMessageShadowNode
|
||||||
private var clippingNode: ChatMessageBubbleClippingNode
|
private var clippingNode: ChatMessageBubbleClippingNode
|
||||||
|
|
||||||
|
private var suggestedPostInfoNode: ChatMessageSuggestedPostInfoNode?
|
||||||
|
|
||||||
override public var extractedBackgroundNode: ASDisplayNode? {
|
override public var extractedBackgroundNode: ASDisplayNode? {
|
||||||
return self.shadowNode
|
return self.shadowNode
|
||||||
}
|
}
|
||||||
@ -1432,6 +1435,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
|
|
||||||
let weakSelf = Weak(self)
|
let weakSelf = Weak(self)
|
||||||
|
|
||||||
|
let makeSuggestedPostInfoNodeLayout: ChatMessageSuggestedPostInfoNode.AsyncLayout = ChatMessageSuggestedPostInfoNode.asyncLayout(self.suggestedPostInfoNode)
|
||||||
|
|
||||||
return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in
|
return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in
|
||||||
let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params, presentationData: item.presentationData)
|
let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params, presentationData: item.presentationData)
|
||||||
return ChatMessageBubbleItemNode.beginLayout(
|
return ChatMessageBubbleItemNode.beginLayout(
|
||||||
@ -1454,6 +1459,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
unlockButtonLayout: unlockButtonLayout,
|
unlockButtonLayout: unlockButtonLayout,
|
||||||
mediaInfoLayout: mediaInfoLayout,
|
mediaInfoLayout: mediaInfoLayout,
|
||||||
mosaicStatusLayout: mosaicStatusLayout,
|
mosaicStatusLayout: mosaicStatusLayout,
|
||||||
|
makeSuggestedPostInfoNodeLayout: makeSuggestedPostInfoNodeLayout,
|
||||||
layoutConstants: layoutConstants,
|
layoutConstants: layoutConstants,
|
||||||
currentItem: currentItem,
|
currentItem: currentItem,
|
||||||
currentForwardInfo: currentForwardInfo,
|
currentForwardInfo: currentForwardInfo,
|
||||||
@ -1482,6 +1488,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
unlockButtonLayout: (ChatMessageUnlockMediaNode.Arguments) -> (CGSize, (Bool) -> ChatMessageUnlockMediaNode),
|
unlockButtonLayout: (ChatMessageUnlockMediaNode.Arguments) -> (CGSize, (Bool) -> ChatMessageUnlockMediaNode),
|
||||||
mediaInfoLayout: (ChatMessageStarsMediaInfoNode.Arguments) -> (CGSize, (Bool) -> ChatMessageStarsMediaInfoNode),
|
mediaInfoLayout: (ChatMessageStarsMediaInfoNode.Arguments) -> (CGSize, (Bool) -> ChatMessageStarsMediaInfoNode),
|
||||||
mosaicStatusLayout: (ChatMessageDateAndStatusNode.Arguments) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> ChatMessageDateAndStatusNode)),
|
mosaicStatusLayout: (ChatMessageDateAndStatusNode.Arguments) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> ChatMessageDateAndStatusNode)),
|
||||||
|
makeSuggestedPostInfoNodeLayout: ChatMessageSuggestedPostInfoNode.AsyncLayout,
|
||||||
layoutConstants: ChatMessageItemLayoutConstants,
|
layoutConstants: ChatMessageItemLayoutConstants,
|
||||||
currentItem: ChatMessageItem?,
|
currentItem: ChatMessageItem?,
|
||||||
currentForwardInfo: (Peer?, String?)?,
|
currentForwardInfo: (Peer?, String?)?,
|
||||||
@ -3039,7 +3046,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
var totalContentNodesHeight: CGFloat = 0.0
|
var totalContentNodesHeight: CGFloat = 0.0
|
||||||
var currentContainerGroupOverlap: CGFloat = 0.0
|
var currentContainerGroupOverlap: CGFloat = 0.0
|
||||||
var detachedContentNodesHeight: CGFloat = 0.0
|
var detachedContentNodesHeight: CGFloat = 0.0
|
||||||
let additionalTopHeight: CGFloat = 0.0
|
var additionalTopHeight: CGFloat = 0.0
|
||||||
|
|
||||||
var mosaicStatusOrigin: CGPoint?
|
var mosaicStatusOrigin: CGPoint?
|
||||||
var unlockButtonPosition: CGPoint?
|
var unlockButtonPosition: CGPoint?
|
||||||
@ -3216,6 +3223,18 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
reactionButtonsSizeAndApply = reactionButtonsFinalize(maxContentWidth)
|
reactionButtonsSizeAndApply = reactionButtonsFinalize(maxContentWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var suggestedPostInfoNodeLayout: (CGSize, () -> ChatMessageSuggestedPostInfoNode)?
|
||||||
|
for attribute in item.message.attributes {
|
||||||
|
if let _ = attribute as? SuggestedPostMessageAttribute {
|
||||||
|
let suggestedPostInfoNodeLayoutValue = makeSuggestedPostInfoNodeLayout(item, baseWidth)
|
||||||
|
suggestedPostInfoNodeLayout = suggestedPostInfoNodeLayoutValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let suggestedPostInfoNodeLayout {
|
||||||
|
additionalTopHeight += 4.0 + suggestedPostInfoNodeLayout.0.height + 8.0
|
||||||
|
}
|
||||||
|
|
||||||
let minimalContentSize: CGSize
|
let minimalContentSize: CGSize
|
||||||
if hideBackground {
|
if hideBackground {
|
||||||
minimalContentSize = CGSize(width: 1.0, height: 1.0)
|
minimalContentSize = CGSize(width: 1.0, height: 1.0)
|
||||||
@ -3352,6 +3371,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
avatarOffset: avatarOffset,
|
avatarOffset: avatarOffset,
|
||||||
hidesHeaders: hidesHeaders,
|
hidesHeaders: hidesHeaders,
|
||||||
disablesComments: disablesComments,
|
disablesComments: disablesComments,
|
||||||
|
suggestedPostInfoNodeLayout: suggestedPostInfoNodeLayout,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
isSidePanelOpen: isSidePanelOpen
|
isSidePanelOpen: isSidePanelOpen
|
||||||
)
|
)
|
||||||
@ -3415,6 +3435,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
avatarOffset: CGFloat?,
|
avatarOffset: CGFloat?,
|
||||||
hidesHeaders: Bool,
|
hidesHeaders: Bool,
|
||||||
disablesComments: Bool,
|
disablesComments: Bool,
|
||||||
|
suggestedPostInfoNodeLayout: (CGSize, () -> ChatMessageSuggestedPostInfoNode)?,
|
||||||
alignment: ChatMessageBubbleContentAlignment,
|
alignment: ChatMessageBubbleContentAlignment,
|
||||||
isSidePanelOpen: Bool
|
isSidePanelOpen: Bool
|
||||||
) -> Void {
|
) -> Void {
|
||||||
@ -3499,6 +3520,22 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
let previousBackgroundFrame = strongSelf.backgroundNode.backgroundFrame
|
let previousBackgroundFrame = strongSelf.backgroundNode.backgroundFrame
|
||||||
strongSelf.backgroundNode.backgroundFrame = backgroundFrame
|
strongSelf.backgroundNode.backgroundFrame = backgroundFrame
|
||||||
|
|
||||||
|
if let (suggestedPostInfoSize, suggestedPostInfoApply) = suggestedPostInfoNodeLayout {
|
||||||
|
let suggestedPostInfoNode = suggestedPostInfoApply()
|
||||||
|
if suggestedPostInfoNode !== strongSelf.suggestedPostInfoNode {
|
||||||
|
strongSelf.suggestedPostInfoNode?.removeFromSupernode()
|
||||||
|
strongSelf.suggestedPostInfoNode = suggestedPostInfoNode
|
||||||
|
strongSelf.mainContextSourceNode.contentNode.addSubnode(suggestedPostInfoNode)
|
||||||
|
|
||||||
|
let suggestedPostInfoFrame = CGRect(origin: CGPoint(x: floor((params.width - suggestedPostInfoSize.width) * 0.5), y: 4.0), size: suggestedPostInfoSize)
|
||||||
|
suggestedPostInfoNode.frame = suggestedPostInfoFrame
|
||||||
|
//animation.animator.updateFrame(layer: suggestedPostInfoNode.layer, frame: suggestedPostInfoFrame, completion: nil)
|
||||||
|
}
|
||||||
|
} else if let suggestedPostInfoNode = strongSelf.suggestedPostInfoNode {
|
||||||
|
strongSelf.suggestedPostInfoNode = nil
|
||||||
|
suggestedPostInfoNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
|
||||||
if let avatarOffset {
|
if let avatarOffset {
|
||||||
strongSelf.updateAttachedAvatarNodeOffset(offset: avatarOffset, transition: .animated(duration: 0.3, curve: .spring))
|
strongSelf.updateAttachedAvatarNodeOffset(offset: avatarOffset, transition: .animated(duration: 0.3, curve: .spring))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "ChatMessageSuggestedPostInfoNode",
|
||||||
|
module_name = "ChatMessageSuggestedPostInfoNode",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-warnings-as-errors",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//submodules/AsyncDisplayKit",
|
||||||
|
"//submodules/Display",
|
||||||
|
"//submodules/SSignalKit/SwiftSignalKit",
|
||||||
|
"//submodules/Postbox",
|
||||||
|
"//submodules/TelegramCore",
|
||||||
|
"//submodules/TelegramPresentationData",
|
||||||
|
"//submodules/TelegramUIPreferences",
|
||||||
|
"//submodules/TextFormat",
|
||||||
|
"//submodules/AccountContext",
|
||||||
|
"//submodules/WallpaperBackgroundNode",
|
||||||
|
"//submodules/TelegramUI/Components/Chat/ChatMessageItem",
|
||||||
|
"//submodules/TelegramStringFormatting",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
||||||
@ -0,0 +1,171 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import TextFormat
|
||||||
|
import AccountContext
|
||||||
|
import WallpaperBackgroundNode
|
||||||
|
import ChatMessageItem
|
||||||
|
import TelegramStringFormatting
|
||||||
|
|
||||||
|
public final class ChatMessageSuggestedPostInfoNode: ASDisplayNode {
|
||||||
|
private var titleNode: TextNode?
|
||||||
|
private var priceLabelNode: TextNode?
|
||||||
|
private var priceValueNode: TextNode?
|
||||||
|
private var timeLabelNode: TextNode?
|
||||||
|
private var timeValueNode: TextNode?
|
||||||
|
|
||||||
|
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||||
|
|
||||||
|
override public init() {
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
public typealias AsyncLayout = (ChatMessageItem, CGFloat) -> (CGSize, () -> ChatMessageSuggestedPostInfoNode)
|
||||||
|
|
||||||
|
public static func asyncLayout(_ node: ChatMessageSuggestedPostInfoNode?) -> (ChatMessageItem, CGFloat) -> (CGSize, () -> ChatMessageSuggestedPostInfoNode) {
|
||||||
|
let makeTitleLayout = TextNode.asyncLayout(node?.titleNode)
|
||||||
|
let makePriceLabelLayout = TextNode.asyncLayout(node?.priceLabelNode)
|
||||||
|
let makePriceValueLayout = TextNode.asyncLayout(node?.priceValueNode)
|
||||||
|
let makeTimeLabelLayout = TextNode.asyncLayout(node?.timeLabelNode)
|
||||||
|
let makeTimeValueLayout = TextNode.asyncLayout(node?.timeValueNode)
|
||||||
|
|
||||||
|
return { item, maxWidth in
|
||||||
|
let insets = UIEdgeInsets(
|
||||||
|
top: 12.0,
|
||||||
|
left: 12.0,
|
||||||
|
bottom: 12.0,
|
||||||
|
right: 12.0
|
||||||
|
)
|
||||||
|
|
||||||
|
let titleSpacing: CGFloat = 8.0
|
||||||
|
let labelSpacing: CGFloat = 8.0
|
||||||
|
let valuesVerticalSpacing: CGFloat = 2.0
|
||||||
|
|
||||||
|
var amount: Int64 = 0
|
||||||
|
var timestamp: Int32?
|
||||||
|
|
||||||
|
for attribute in item.message.attributes {
|
||||||
|
if let attribute = attribute as? SuggestedPostMessageAttribute {
|
||||||
|
amount = attribute.amount
|
||||||
|
timestamp = attribute.timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
let amountString: String
|
||||||
|
if amount == 0 {
|
||||||
|
amountString = "Free"
|
||||||
|
} else if amount == 1 {
|
||||||
|
amountString = "1 Star"
|
||||||
|
} else {
|
||||||
|
amountString = "\(amount) Stars"
|
||||||
|
}
|
||||||
|
|
||||||
|
var timestampString: String
|
||||||
|
if let timestamp {
|
||||||
|
timestampString = humanReadableStringForTimestamp(strings: item.presentationData.strings, dateTimeFormat: PresentationDateTimeFormat(), timestamp: timestamp, alwaysShowTime: true).string
|
||||||
|
if timestampString.count > 1 {
|
||||||
|
timestampString = String(timestampString[timestampString.startIndex]).capitalized + timestampString[timestampString.index(after: timestampString.startIndex)...]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timestampString = "Anytime"
|
||||||
|
}
|
||||||
|
|
||||||
|
let serviceColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
let titleText: String
|
||||||
|
if !item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||||
|
titleText = "You suggest to post\nthis message."
|
||||||
|
} else {
|
||||||
|
titleText = "\(item.message.author.flatMap(EnginePeer.init)?.compactDisplayTitle ?? " ") suggests to post\nthis message."
|
||||||
|
}
|
||||||
|
|
||||||
|
let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleText, font: Font.regular(13.0), textColor: serviceColor.primaryText), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maxWidth - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
let priceLabelLayout = makePriceLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Price", font: Font.regular(13.0), textColor: serviceColor.primaryText.withMultipliedAlpha(0.5)), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maxWidth - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
let timeLabelLayout = makeTimeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Time", font: Font.regular(13.0), textColor: serviceColor.primaryText.withMultipliedAlpha(0.5)), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maxWidth - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
let priceValueLayout = makePriceValueLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: amountString, font: Font.semibold(13.0), textColor: serviceColor.primaryText), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maxWidth - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
let timeValueLayout = makeTimeValueLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: timestampString, font: Font.semibold(13.0), textColor: serviceColor.primaryText), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maxWidth - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
var maxContentWidth: CGFloat = 0.0
|
||||||
|
var contentHeight: CGFloat = 0.0
|
||||||
|
|
||||||
|
maxContentWidth = max(maxContentWidth, titleLayout.0.size.width)
|
||||||
|
|
||||||
|
contentHeight += titleLayout.0.size.height
|
||||||
|
contentHeight += titleSpacing
|
||||||
|
|
||||||
|
maxContentWidth = max(maxContentWidth, priceLabelLayout.0.size.width + labelSpacing + priceValueLayout.0.size.width)
|
||||||
|
contentHeight += priceLabelLayout.0.size.height + valuesVerticalSpacing
|
||||||
|
|
||||||
|
maxContentWidth = max(maxContentWidth, timeLabelLayout.0.size.width + labelSpacing + timeValueLayout.0.size.width)
|
||||||
|
contentHeight += timeLabelLayout.0.size.height
|
||||||
|
|
||||||
|
let size = CGSize(width: insets.left + insets.right + maxContentWidth, height: insets.top + insets.bottom + contentHeight)
|
||||||
|
|
||||||
|
return (size, {
|
||||||
|
let node = node ?? ChatMessageSuggestedPostInfoNode()
|
||||||
|
|
||||||
|
if node.backgroundNode == nil {
|
||||||
|
if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||||
|
node.backgroundNode = backgroundNode
|
||||||
|
backgroundNode.layer.masksToBounds = true
|
||||||
|
backgroundNode.layer.cornerRadius = 15.0
|
||||||
|
node.insertSubnode(backgroundNode, at: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let backgroundNode = node.backgroundNode {
|
||||||
|
backgroundNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
let titleNode = titleLayout.1()
|
||||||
|
if node.titleNode !== titleNode {
|
||||||
|
node.titleNode = titleNode
|
||||||
|
node.addSubnode(titleNode)
|
||||||
|
}
|
||||||
|
let priceLabelNode = priceLabelLayout.1()
|
||||||
|
if node.priceLabelNode !== priceLabelNode {
|
||||||
|
node.priceLabelNode = priceLabelNode
|
||||||
|
node.addSubnode(priceLabelNode)
|
||||||
|
}
|
||||||
|
let priceValueNode = priceValueLayout.1()
|
||||||
|
if node.priceValueNode !== priceValueNode {
|
||||||
|
node.priceValueNode = priceValueNode
|
||||||
|
node.addSubnode(priceValueNode)
|
||||||
|
}
|
||||||
|
let timeLabelNode = timeLabelLayout.1()
|
||||||
|
if node.timeLabelNode !== timeLabelNode {
|
||||||
|
node.timeLabelNode = timeLabelNode
|
||||||
|
node.addSubnode(timeLabelNode)
|
||||||
|
}
|
||||||
|
let timeValueNode = timeValueLayout.1()
|
||||||
|
if node.timeValueNode !== timeValueNode {
|
||||||
|
node.timeValueNode = timeValueNode
|
||||||
|
node.addSubnode(timeValueNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleLayout.0.size.width) * 0.5), y: insets.top), size: titleLayout.0.size)
|
||||||
|
titleNode.frame = titleFrame
|
||||||
|
|
||||||
|
let priceLabelFrame = CGRect(origin: CGPoint(x: insets.left, y: titleFrame.maxY + titleSpacing), size: priceLabelLayout.0.size)
|
||||||
|
priceLabelNode.frame = priceLabelFrame
|
||||||
|
priceValueNode.frame = CGRect(origin: CGPoint(x: priceLabelFrame.maxX + labelSpacing, y: priceLabelFrame.minY), size: priceValueLayout.0.size)
|
||||||
|
|
||||||
|
let timeLabelFrame = CGRect(origin: CGPoint(x: insets.left, y: priceLabelFrame.maxY + valuesVerticalSpacing), size: timeLabelLayout.0.size)
|
||||||
|
timeLabelNode.frame = timeLabelFrame
|
||||||
|
timeValueNode.frame = CGRect(origin: CGPoint(x: timeLabelFrame.maxX + labelSpacing, y: timeLabelFrame.minY), size: timeValueLayout.0.size)
|
||||||
|
|
||||||
|
return node
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2331,13 +2331,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if message.effectivelyIncoming(strongSelf.context.account.peerId) {
|
if message.effectivelyIncoming(strongSelf.context.account.peerId) {
|
||||||
switch buttonType {
|
switch buttonType {
|
||||||
case 0:
|
case 0:
|
||||||
let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, approve: false, timestamp: nil).startStandalone()
|
let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, action: .reject(comment: nil)).startStandalone()
|
||||||
case 1:
|
case 1:
|
||||||
var timestamp: Int32?
|
var timestamp: Int32?
|
||||||
if attribute.timestamp == nil {
|
if attribute.timestamp == nil {
|
||||||
timestamp = Int32(Date().timeIntervalSince1970) + 1 * 60 * 60
|
timestamp = Int32(Date().timeIntervalSince1970) + 1 * 60 * 60
|
||||||
}
|
}
|
||||||
let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, approve: true, timestamp: timestamp).startStandalone()
|
let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, action: .approve(timestamp: timestamp)).startStandalone()
|
||||||
case 2:
|
case 2:
|
||||||
//suggest changes
|
//suggest changes
|
||||||
break
|
break
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user