mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-01 16:06:59 +00:00
Suggested posts
This commit is contained in:
parent
f1b98f6dd8
commit
0a30a0ee56
@ -1034,6 +1034,7 @@ public enum StarsWithdrawalScreenSubject {
|
||||
case withdraw(completion: (Int64) -> Void)
|
||||
case enterAmount(current: StarsAmount, minValue: StarsAmount, fractionAfterCommission: Int, kind: PaidMessageKind, completion: (Int64) -> Void)
|
||||
case postSuggestion(channel: EnginePeer, current: StarsAmount, timestamp: Int32?, completion: (Int64, Int32?) -> Void)
|
||||
case postSuggestionModification(current: StarsAmount, timestamp: Int32?, completion: (Int64, Int32?) -> Void)
|
||||
}
|
||||
|
||||
public protocol SharedAccountContext: AnyObject {
|
||||
|
@ -189,7 +189,7 @@ private final class PromptAlertContentNode: AlertContentNode {
|
||||
return self.isUserInteractionEnabled
|
||||
}
|
||||
|
||||
init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], text: String, titleFont: PromptControllerTitleFont, value: String?, characterLimit: Int) {
|
||||
init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], text: String, titleFont: PromptControllerTitleFont, value: String?, placeholder: String?, characterLimit: Int) {
|
||||
self.strings = strings
|
||||
self.text = text
|
||||
self.titleFont = titleFont
|
||||
@ -197,7 +197,7 @@ private final class PromptAlertContentNode: AlertContentNode {
|
||||
self.textNode = ASTextNode()
|
||||
self.textNode.maximumNumberOfLines = 2
|
||||
|
||||
self.inputFieldNode = PromptInputFieldNode(theme: ptheme, placeholder: "", characterLimit: characterLimit)
|
||||
self.inputFieldNode = PromptInputFieldNode(theme: ptheme, placeholder: placeholder ?? "", characterLimit: characterLimit)
|
||||
self.inputFieldNode.text = value ?? ""
|
||||
|
||||
self.actionNodesSeparator = ASDisplayNode()
|
||||
@ -407,7 +407,7 @@ public enum PromptControllerTitleFont {
|
||||
case bold
|
||||
}
|
||||
|
||||
public func promptController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, text: String, titleFont: PromptControllerTitleFont = .regular, value: String?, characterLimit: Int = 1000, apply: @escaping (String?) -> Void) -> AlertController {
|
||||
public func promptController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, text: String, titleFont: PromptControllerTitleFont = .regular, value: String?, placeholder: String? = nil, characterLimit: Int = 1000, apply: @escaping (String?) -> Void) -> AlertController {
|
||||
let presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
var dismissImpl: ((Bool) -> Void)?
|
||||
@ -421,8 +421,9 @@ public func promptController(sharedContext: SharedAccountContext, updatedPresent
|
||||
applyImpl?()
|
||||
})]
|
||||
|
||||
let contentNode = PromptAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, text: text, titleFont: titleFont, value: value, characterLimit: characterLimit)
|
||||
let contentNode = PromptAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, text: text, titleFont: titleFont, value: value, placeholder: placeholder, characterLimit: characterLimit)
|
||||
contentNode.complete = {
|
||||
dismissImpl?(true)
|
||||
applyImpl?()
|
||||
}
|
||||
applyImpl = { [weak contentNode] in
|
||||
|
@ -1591,6 +1591,7 @@ public extension TelegramEngine {
|
||||
public enum MonoforumSuggestedPostAction {
|
||||
case approve(timestamp: Int32?)
|
||||
case reject(comment: String?)
|
||||
case proposeChanges(amount: Int64, timestamp: Int32?)
|
||||
}
|
||||
|
||||
public func monoforumPerformSuggestedPostAction(id: EngineMessage.Id, action: MonoforumSuggestedPostAction) -> Signal<Never, NoError> {
|
||||
@ -1601,6 +1602,34 @@ public extension TelegramEngine {
|
||||
|
||||
func _internal_monoforumPerformSuggestedPostAction(account: Account, id: EngineMessage.Id, action: TelegramEngine.Messages.MonoforumSuggestedPostAction) -> Signal<Never, NoError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
if case let .proposeChanges(amount, timestamp) = action, let message = transaction.getMessage(id) {
|
||||
var attributes: [MessageAttribute] = []
|
||||
attributes.append(SuggestedPostMessageAttribute(
|
||||
amount: amount,
|
||||
timestamp: timestamp,
|
||||
state: nil
|
||||
))
|
||||
|
||||
var mediaReference: AnyMediaReference?
|
||||
if let media = message.media.first {
|
||||
mediaReference = .message(message: MessageReference(message), media: media)
|
||||
}
|
||||
|
||||
let enqueueMessage: EnqueueMessage = .message(
|
||||
text: message.text,
|
||||
attributes: attributes,
|
||||
inlineStickers: [:],
|
||||
mediaReference: mediaReference,
|
||||
threadId: message.threadId,
|
||||
replyToMessageId: EngineMessageReplySubject(messageId: message.id, quote: nil),
|
||||
replyToStoryId: nil,
|
||||
localGroupingKey: nil,
|
||||
correlationId: nil,
|
||||
bubbleUpEmojiOrStickersets: []
|
||||
)
|
||||
let _ = enqueueMessages(transaction: transaction, account: account, peerId: id.peerId, messages: [(true, enqueueMessage)])
|
||||
}
|
||||
|
||||
return transaction.getPeer(id.peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<Never, NoError> in
|
||||
@ -1626,6 +1655,8 @@ func _internal_monoforumPerformSuggestedPostAction(account: Account, id: EngineM
|
||||
if rejectComment != nil {
|
||||
flags |= 1 << 2
|
||||
}
|
||||
case .proposeChanges:
|
||||
flags |= 1 << 1
|
||||
}
|
||||
return account.network.request(Api.functions.messages.toggleSuggestedPostApproval(flags: flags, peer: inputPeer, msgId: id.id, scheduleDate: timestamp, rejectComment: rejectComment))
|
||||
|> map(Optional.init)
|
||||
|
@ -1390,18 +1390,35 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
}
|
||||
|
||||
let string: String
|
||||
switch status {
|
||||
case .approved:
|
||||
if messageText.isEmpty {
|
||||
string = "Your message was approved"
|
||||
} else {
|
||||
string = "Your message \"\(messageText)\" was approved"
|
||||
if !message.flags.contains(.Incoming) {
|
||||
switch status {
|
||||
case .approved:
|
||||
if messageText.isEmpty {
|
||||
string = "The message was approved"
|
||||
} else {
|
||||
string = "The message \"\(messageText)\" was approved"
|
||||
}
|
||||
case .rejected:
|
||||
if messageText.isEmpty {
|
||||
string = "The message was declined"
|
||||
} else {
|
||||
string = "The message \"\(messageText)\" was declined"
|
||||
}
|
||||
}
|
||||
case .rejected:
|
||||
if messageText.isEmpty {
|
||||
string = "Your message was rejected"
|
||||
} else {
|
||||
string = "Your message \"\(messageText)\" was rejected"
|
||||
} else {
|
||||
switch status {
|
||||
case .approved:
|
||||
if messageText.isEmpty {
|
||||
string = "Your message was approved"
|
||||
} else {
|
||||
string = "Your message \"\(messageText)\" was approved"
|
||||
}
|
||||
case .rejected:
|
||||
if messageText.isEmpty {
|
||||
string = "Your message was declined"
|
||||
} else {
|
||||
string = "Your message \"\(messageText)\" was declined"
|
||||
}
|
||||
}
|
||||
}
|
||||
attributedString = NSAttributedString(string: string, font: titleFont, textColor: primaryTextColor)
|
||||
|
@ -231,22 +231,27 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
//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: [])
|
||||
return PresentationStrings.FormattedString(string: item.presentationData.strings.SuggestPost_SetTimeFormat_Date(value).string.lowercased(), ranges: [])
|
||||
},
|
||||
tomorrowFormatString: { value in
|
||||
return PresentationStrings.FormattedString(string: item.presentationData.strings.SuggestPost_SetTimeFormat_TomorrowAt(value).string, ranges: [])
|
||||
return PresentationStrings.FormattedString(string: item.presentationData.strings.SuggestPost_SetTimeFormat_TomorrowAt(value).string.lowercased(), ranges: [])
|
||||
},
|
||||
todayFormatString: { value in
|
||||
return PresentationStrings.FormattedString(string: item.presentationData.strings.SuggestPost_SetTimeFormat_TodayAt(value).string, ranges: [])
|
||||
return PresentationStrings.FormattedString(string: item.presentationData.strings.SuggestPost_SetTimeFormat_TodayAt(value).string.lowercased(), ranges: [])
|
||||
},
|
||||
yesterdayFormatString: { value in
|
||||
return PresentationStrings.FormattedString(string: item.presentationData.strings.SuggestPost_SetTimeFormat_TodayAt(value).string, ranges: [])
|
||||
return PresentationStrings.FormattedString(string: item.presentationData.strings.SuggestPost_SetTimeFormat_TodayAt(value).string.lowercased(), 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."
|
||||
let rawString: String
|
||||
if !item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||
rawString = "📅 The post will be automatically published on **\(channelName)** **\(timeString)**.\n\n💰 The user have been charged \(amountString).\n\n⌛ **\(channelName)** will receive the Stars once the post has been live for 24 hours.\n\n🔄 If your remove the post before it has been live for 24 hours, the user's Stars will be refunded."
|
||||
} else {
|
||||
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),
|
||||
@ -257,15 +262,28 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
))
|
||||
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."
|
||||
if !item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||
switch reason {
|
||||
case .generic:
|
||||
if let comment {
|
||||
rawString = "You declined the post with the following comment:\n\n" + comment
|
||||
} else {
|
||||
rawString = "You declined the post."
|
||||
}
|
||||
case .lowBalance:
|
||||
rawString = "**\(channelName)** was unable to post the message, because the user did not have enough Stars."
|
||||
}
|
||||
} else {
|
||||
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."
|
||||
}
|
||||
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),
|
||||
@ -344,7 +362,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
backgroundSize.height += imageSize.height + 10.0
|
||||
}
|
||||
|
||||
let titleSpacing: CGFloat = 18.0
|
||||
let titleSpacing: CGFloat = 14.0
|
||||
|
||||
var contentInsets = UIEdgeInsets()
|
||||
var contentOuterInsets = UIEdgeInsets()
|
||||
@ -353,8 +371,8 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
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)
|
||||
contentInsets = UIEdgeInsets(top: 12.0, left: 16.0, bottom: 12.0, right: 16.0)
|
||||
contentOuterInsets = UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0)
|
||||
|
||||
backgroundSize.width += contentInsets.left + contentInsets.right
|
||||
backgroundSize.height += contentInsets.top + contentInsets.bottom
|
||||
|
@ -2797,7 +2797,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
actionButtonsFinalize = buttonsLayout
|
||||
|
||||
lastNodeTopPosition = .None(.Both)
|
||||
} else if incoming, let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.isMonoForum, item.chatLocation.threadId != nil, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = item.message.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.sendSomething), let attribute = item.message.attributes.first(where: { $0 is SuggestedPostMessageAttribute }) as? SuggestedPostMessageAttribute, attribute.state == nil {
|
||||
} else if incoming, /*let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = item.message.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.sendSomething),*/ let attribute = item.message.attributes.first(where: { $0 is SuggestedPostMessageAttribute }) as? SuggestedPostMessageAttribute, attribute.state == nil {
|
||||
//TODO:localize
|
||||
var buttonDecline: UInt8 = 0
|
||||
var buttonApprove: UInt8 = 1
|
||||
|
@ -22,6 +22,7 @@ swift_library(
|
||||
"//submodules/WallpaperBackgroundNode",
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageItem",
|
||||
"//submodules/TelegramStringFormatting",
|
||||
"//submodules/Markdown",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -12,6 +12,7 @@ import AccountContext
|
||||
import WallpaperBackgroundNode
|
||||
import ChatMessageItem
|
||||
import TelegramStringFormatting
|
||||
import Markdown
|
||||
|
||||
public final class ChatMessageSuggestedPostInfoNode: ASDisplayNode {
|
||||
private var titleNode: TextNode?
|
||||
@ -84,10 +85,23 @@ public final class ChatMessageSuggestedPostInfoNode: ASDisplayNode {
|
||||
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."
|
||||
if item.message.author is TelegramChannel {
|
||||
titleText = "**\(item.message.author.flatMap(EnginePeer.init)?.compactDisplayTitle ?? " ")** suggests a new price,\ntime, and text for your 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 titleAttributedText = parseMarkdownIntoAttributedString(titleText, attributes: MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: serviceColor.primaryText),
|
||||
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: serviceColor.primaryText),
|
||||
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: serviceColor.primaryText),
|
||||
linkAttribute: { url in
|
||||
return ("URL", url)
|
||||
}
|
||||
))
|
||||
|
||||
let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedText, 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()))
|
||||
|
@ -1400,13 +1400,10 @@ private final class ChatSendStarsScreenComponent: Component {
|
||||
guard let environment = self.environment else {
|
||||
return
|
||||
}
|
||||
guard case let .suggestPost(suggestPostData) = component.initialData.subjectInitialData else {
|
||||
return
|
||||
}
|
||||
|
||||
let mode: ChatScheduleTimeControllerMode = .suggestPost
|
||||
let mode: ChatScheduleTimeControllerMode = .suggestPost(needsTime: false)
|
||||
let theme = environment.theme
|
||||
let controller = ChatScheduleTimeController(context: component.context, updatedPresentationData: (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) }), peerId: suggestPostData.peer.id, mode: mode, style: .default, currentTime: self.currentSuggestPostTimestamp, minimalTime: nil, dismissByTapOutside: true, completion: { [weak self] time in
|
||||
let controller = ChatScheduleTimeController(context: component.context, updatedPresentationData: (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) }), mode: mode, style: .default, currentTime: self.currentSuggestPostTimestamp, minimalTime: nil, dismissByTapOutside: true, completion: { [weak self] time in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import TelegramPresentationData
|
||||
public enum ChatScheduleTimeControllerMode {
|
||||
case scheduledMessages(sendWhenOnlineAvailable: Bool)
|
||||
case reminders
|
||||
case suggestPost
|
||||
case suggestPost(needsTime: Bool)
|
||||
}
|
||||
|
||||
public enum ChatScheduleTimeControllerStyle {
|
||||
@ -27,7 +27,6 @@ public final class ChatScheduleTimeController: ViewController {
|
||||
private var animatedIn = false
|
||||
|
||||
private let context: AccountContext
|
||||
private let peerId: PeerId
|
||||
private let mode: ChatScheduleTimeControllerMode
|
||||
private let style: ChatScheduleTimeControllerStyle
|
||||
private let currentTime: Int32?
|
||||
@ -40,9 +39,8 @@ public final class ChatScheduleTimeController: ViewController {
|
||||
|
||||
public var dismissed: () -> Void = {}
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, mode: ChatScheduleTimeControllerMode, style: ChatScheduleTimeControllerStyle, currentTime: Int32? = nil, minimalTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: ChatScheduleTimeControllerMode, style: ChatScheduleTimeControllerStyle, currentTime: Int32? = nil, minimalTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.mode = mode
|
||||
self.style = style
|
||||
self.currentTime = currentTime != scheduleWhenOnlineTimestamp ? currentTime : nil
|
||||
|
@ -100,10 +100,16 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, ASScrollViewDel
|
||||
title = self.presentationData.strings.Conversation_ScheduleMessage_Title
|
||||
case .reminders:
|
||||
title = self.presentationData.strings.Conversation_SetReminder_Title
|
||||
case .suggestPost:
|
||||
//TODO:localize
|
||||
title = "Time"
|
||||
text = "Set the date and time you want\nyour message to be published."
|
||||
case let .suggestPost(needsTime):
|
||||
if needsTime {
|
||||
//TODO:localize
|
||||
title = "Time"
|
||||
text = "Set the date and time you want\nthis message to be published."
|
||||
} else {
|
||||
//TODO:localize
|
||||
title = "Time"
|
||||
text = "Set the date and time you want\nyour message to be published."
|
||||
}
|
||||
}
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
@ -169,7 +175,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, ASScrollViewDel
|
||||
self.contentContainerNode.addSubnode(self.doneButton)
|
||||
if case .scheduledMessages(true) = self.mode {
|
||||
self.contentContainerNode.addSubnode(self.onlineButton)
|
||||
} else if case .suggestPost = self.mode {
|
||||
} else if case let .suggestPost(needsTime) = self.mode, !needsTime {
|
||||
self.contentContainerNode.addSubnode(self.onlineButton)
|
||||
}
|
||||
|
||||
@ -425,7 +431,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, ASScrollViewDel
|
||||
var buttonOffset: CGFloat = 0.0
|
||||
if case .scheduledMessages(true) = self.mode {
|
||||
buttonOffset += 64.0
|
||||
} else if case .suggestPost = self.mode {
|
||||
} else if case let .suggestPost(needsTime) = self.mode, !needsTime {
|
||||
buttonOffset += 64.0
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ private final class SheetContent: CombinedComponent {
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
static var body: (CombinedComponentContext<SheetContent>) -> CGSize {
|
||||
let closeButton = Child(Button.self)
|
||||
let title = Child(Text.self)
|
||||
let amountSection = Child(ListSectionComponent.self)
|
||||
@ -61,7 +61,7 @@ private final class SheetContent: CombinedComponent {
|
||||
let balanceValue = Child(MultilineTextComponent.self)
|
||||
let balanceIcon = Child(BundleIconComponent.self)
|
||||
|
||||
return { context in
|
||||
return { (context: CombinedComponentContext<SheetContent>) -> CGSize in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
let component = context.component
|
||||
let state = context.state
|
||||
@ -160,9 +160,14 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
minAmount = StarsAmount(value: minAmountValue, nanos: 0)
|
||||
maxAmount = StarsAmount(value: resaleConfiguration.paidMessageMaxAmount, nanos: 0)
|
||||
case .suggestedPost:
|
||||
case let .suggestedPost(mode, _, _, _):
|
||||
//TODO:localize
|
||||
titleString = "Suggest Terms"
|
||||
switch mode {
|
||||
case .sender:
|
||||
titleString = "Suggest Terms"
|
||||
case .admin:
|
||||
titleString = "Suggest Changes"
|
||||
}
|
||||
amountTitle = "ENTER A PRICE IN STARS"
|
||||
amountPlaceholder = "Price"
|
||||
|
||||
@ -321,6 +326,13 @@ private final class SheetContent: CombinedComponent {
|
||||
text: .plain(amountInfoString),
|
||||
maximumNumberOfLines: 0
|
||||
))
|
||||
case .admin:
|
||||
//TODO:localize
|
||||
let amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString("Choose how many Stars you charge for the message.", attributes: amountMarkdownAttributes, textAlignment: .natural))
|
||||
amountFooter = AnyComponent(MultilineTextComponent(
|
||||
text: .plain(amountInfoString),
|
||||
maximumNumberOfLines: 0
|
||||
))
|
||||
}
|
||||
default:
|
||||
amountFooter = nil
|
||||
@ -380,11 +392,19 @@ private final class SheetContent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width - amountAdditionalLabel.size.width / 2.0 - sideInset - 16.0, y: contentSize.height - amountAdditionalLabel.size.height / 2.0)))
|
||||
}
|
||||
|
||||
if case .suggestedPost = component.mode {
|
||||
if case let .suggestedPost(mode, _, _, _) = component.mode {
|
||||
contentSize.height += 24.0
|
||||
|
||||
//TODO:localize
|
||||
let timestampFooterString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString("Select the date and time you want your message to be published.", attributes: amountMarkdownAttributes, textAlignment: .natural))
|
||||
let footerString: String
|
||||
switch mode {
|
||||
case .sender:
|
||||
footerString = "Select the date and time you want your message to be published."
|
||||
case .admin:
|
||||
footerString = "Select the date and time you want to publish the message."
|
||||
}
|
||||
|
||||
let timestampFooterString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString(footerString, attributes: amountMarkdownAttributes, textAlignment: .natural))
|
||||
let timestampFooter = AnyComponent(MultilineTextComponent(
|
||||
text: .plain(timestampFooterString),
|
||||
maximumNumberOfLines: 0
|
||||
@ -443,15 +463,9 @@ private final class SheetContent: CombinedComponent {
|
||||
return
|
||||
}
|
||||
let component = state.component
|
||||
guard case let .suggestedPost(mode, _, _, _) = component.mode else {
|
||||
return
|
||||
}
|
||||
guard case let .sender(channel) = mode else {
|
||||
return
|
||||
}
|
||||
|
||||
let theme = environment.theme
|
||||
let controller = ChatScheduleTimeController(context: state.context, updatedPresentationData: (state.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), state.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) }), peerId: channel.id, mode: .suggestPost, style: .default, currentTime: state.timestamp, minimalTime: nil, dismissByTapOutside: true, completion: { [weak state] time in
|
||||
let controller = ChatScheduleTimeController(context: state.context, updatedPresentationData: (state.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), state.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) }), mode: .suggestPost(needsTime: false), style: .default, currentTime: state.timestamp, minimalTime: nil, dismissByTapOutside: true, completion: { [weak state] time in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
@ -498,6 +512,8 @@ private final class SheetContent: CombinedComponent {
|
||||
} else {
|
||||
buttonString = "Offer"
|
||||
}
|
||||
case .admin:
|
||||
buttonString = "Update Terms"
|
||||
}
|
||||
} else if let amount = state.amount {
|
||||
buttonString = "\(environment.strings.Stars_Withdraw_Withdraw) # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))"
|
||||
@ -770,6 +786,7 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer {
|
||||
public enum Mode {
|
||||
public enum SuggestedPostMode {
|
||||
case sender(channel: EnginePeer)
|
||||
case admin
|
||||
}
|
||||
|
||||
case withdraw(StarsRevenueStats, completion: (Int64) -> Void)
|
||||
|
@ -466,6 +466,7 @@ final class StoryItemSetContainerSendMessage {
|
||||
guard let peerId = focusedItem.peerId else {
|
||||
return
|
||||
}
|
||||
let _ = peerId
|
||||
let controller = component.controller() as? StoryContainerScreen
|
||||
|
||||
var sendWhenOnlineAvailable = false
|
||||
@ -473,7 +474,7 @@ final class StoryItemSetContainerSendMessage {
|
||||
sendWhenOnlineAvailable = true
|
||||
}
|
||||
|
||||
let timeController = ChatScheduleTimeController(context: component.context, updatedPresentationData: nil, peerId: peerId, mode: .scheduledMessages(sendWhenOnlineAvailable: sendWhenOnlineAvailable), style: .media, currentTime: nil, minimalTime: nil, dismissByTapOutside: true, completion: { [weak self, weak view] time in
|
||||
let timeController = ChatScheduleTimeController(context: component.context, updatedPresentationData: nil, mode: .scheduledMessages(sendWhenOnlineAvailable: sendWhenOnlineAvailable), style: .media, currentTime: nil, minimalTime: nil, dismissByTapOutside: true, completion: { [weak self, weak view] time in
|
||||
guard let self, let view else {
|
||||
return
|
||||
}
|
||||
@ -2607,7 +2608,7 @@ final class StoryItemSetContainerSendMessage {
|
||||
mode = .scheduledMessages(sendWhenOnlineAvailable: sendWhenOnlineAvailable)
|
||||
}
|
||||
let theme = component.theme
|
||||
let controller = ChatScheduleTimeController(context: component.context, updatedPresentationData: (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) }), peerId: peer.id, mode: mode, style: style, currentTime: selectedTime, minimalTime: nil, dismissByTapOutside: dismissByTapOutside, completion: { time in
|
||||
let controller = ChatScheduleTimeController(context: component.context, updatedPresentationData: (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) }), mode: mode, style: style, currentTime: selectedTime, minimalTime: nil, dismissByTapOutside: dismissByTapOutside, completion: { time in
|
||||
completion(time)
|
||||
})
|
||||
view.endEditing(true)
|
||||
|
@ -137,6 +137,7 @@ import ChatMessagePaymentAlertController
|
||||
import TelegramCallsUI
|
||||
import QuickShareScreen
|
||||
import PostSuggestionsSettingsScreen
|
||||
import PromptUI
|
||||
|
||||
public final class ChatControllerOverlayPresentationData {
|
||||
public let expandData: (ASDisplayNode?, () -> Void)
|
||||
@ -2331,16 +2332,49 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if message.effectivelyIncoming(strongSelf.context.account.peerId) {
|
||||
switch buttonType {
|
||||
case 0:
|
||||
let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, action: .reject(comment: nil)).startStandalone()
|
||||
//TODO:localize
|
||||
let promptController = promptController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: strongSelf.updatedPresentationData, text: "Comment", titleFont: .bold, value: "", placeholder: "Optional", characterLimit: 4096, apply: { value in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let value {
|
||||
let _ = self.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, action: .reject(comment: value.isEmpty ? nil : value)).startStandalone()
|
||||
}
|
||||
})
|
||||
strongSelf.present(promptController, in: .window(.root))
|
||||
case 1:
|
||||
var timestamp: Int32?
|
||||
if attribute.timestamp == nil {
|
||||
let controller = ChatScheduleTimeController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, mode: .suggestPost(needsTime: true), style: .default, currentTime: nil, minimalTime: nil, dismissByTapOutside: true, completion: { [weak strongSelf] time in
|
||||
guard let strongSelf else {
|
||||
return
|
||||
}
|
||||
if time != 0 {
|
||||
let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, action: .approve(timestamp: time)).startStandalone()
|
||||
}
|
||||
})
|
||||
strongSelf.view.endEditing(true)
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
|
||||
timestamp = Int32(Date().timeIntervalSince1970) + 1 * 60 * 60
|
||||
} else {
|
||||
let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, action: .approve(timestamp: timestamp)).startStandalone()
|
||||
}
|
||||
let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, action: .approve(timestamp: timestamp)).startStandalone()
|
||||
case 2:
|
||||
//suggest changes
|
||||
break
|
||||
strongSelf.push(strongSelf.context.sharedContext.makeStarsWithdrawalScreen(
|
||||
context: strongSelf.context,
|
||||
subject: .postSuggestionModification(
|
||||
current: StarsAmount(value: attribute.amount, nanos: 0),
|
||||
timestamp: attribute.timestamp,
|
||||
completion: { [weak strongSelf] price, timestamp in
|
||||
guard let strongSelf else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, action: .proposeChanges(amount: price, timestamp: timestamp)).startStandalone()
|
||||
}
|
||||
)
|
||||
))
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -9525,7 +9559,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
} else {
|
||||
mode = .scheduledMessages(sendWhenOnlineAvailable: sendWhenOnlineAvailable)
|
||||
}
|
||||
let controller = ChatScheduleTimeController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peerId, mode: mode, style: style, currentTime: selectedTime, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, dismissByTapOutside: dismissByTapOutside, completion: { time in
|
||||
let controller = ChatScheduleTimeController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, mode: mode, style: style, currentTime: selectedTime, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, dismissByTapOutside: dismissByTapOutside, completion: { time in
|
||||
completion(time)
|
||||
})
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
|
@ -233,7 +233,7 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
|
||||
}
|
||||
}
|
||||
|
||||
if isTextEmpty, let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let mainChannel = chatPresentationInterfaceState.renderedPeer?.chatOrMonoforumMainPeer as? TelegramChannel, !mainChannel.hasPermission(.sendSomething) {
|
||||
if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let mainChannel = chatPresentationInterfaceState.renderedPeer?.chatOrMonoforumMainPeer as? TelegramChannel, !mainChannel.hasPermission(.sendSomething) {
|
||||
if chatPresentationInterfaceState.interfaceState.postSuggestionState == nil {
|
||||
accessoryItems.append(.suggestPost)
|
||||
}
|
||||
|
@ -3731,6 +3731,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
mode = .paidMessages(current: current.value, minValue: minValue.value, fractionAfterCommission: fractionAfterCommission, kind: kind, completion: completion)
|
||||
case let .postSuggestion(channel, current, timestamp, completion):
|
||||
mode = .suggestedPost(mode: .sender(channel: channel), price: current.value, timestamp: timestamp, completion: completion)
|
||||
case let .postSuggestionModification(current, timestamp, completion):
|
||||
mode = .suggestedPost(mode: .admin, price: current.value, timestamp: timestamp, completion: completion)
|
||||
}
|
||||
return StarsWithdrawScreen(context: context, mode: mode)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user