Various improvements

This commit is contained in:
Isaac 2025-06-11 13:37:58 +08:00
parent 400de1ef11
commit c42b2bd9c0
36 changed files with 936 additions and 166 deletions

View File

@ -1031,8 +1031,9 @@ public enum StarsWithdrawalScreenSubject {
case postSuggestion case postSuggestion
} }
case withdraw case withdraw(completion: (Int64) -> Void)
case enterAmount(current: StarsAmount, minValue: StarsAmount, fractionAfterCommission: Int, kind: PaidMessageKind) 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)
} }
public protocol SharedAccountContext: AnyObject { public protocol SharedAccountContext: AnyObject {
@ -1221,7 +1222,7 @@ public protocol SharedAccountContext: AnyObject {
func makeStarsStatisticsScreen(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) -> ViewController func makeStarsStatisticsScreen(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) -> ViewController
func makeStarsAmountScreen(context: AccountContext, initialValue: Int64?, completion: @escaping (Int64) -> Void) -> ViewController func makeStarsAmountScreen(context: AccountContext, initialValue: Int64?, completion: @escaping (Int64) -> Void) -> ViewController
func makeStarsWithdrawalScreen(context: AccountContext, stats: StarsRevenueStats, completion: @escaping (Int64) -> Void) -> ViewController func makeStarsWithdrawalScreen(context: AccountContext, stats: StarsRevenueStats, completion: @escaping (Int64) -> Void) -> ViewController
func makeStarsWithdrawalScreen(context: AccountContext, subject: StarsWithdrawalScreenSubject, completion: @escaping (Int64) -> Void) -> ViewController func makeStarsWithdrawalScreen(context: AccountContext, subject: StarsWithdrawalScreenSubject) -> ViewController
func makeStarGiftResellScreen(context: AccountContext, gift: StarGift.UniqueGift, update: Bool, completion: @escaping (Int64) -> Void) -> ViewController func makeStarGiftResellScreen(context: AccountContext, gift: StarGift.UniqueGift, update: Bool, completion: @escaping (Int64) -> Void) -> ViewController
func makeStarsGiftScreen(context: AccountContext, message: EngineMessage) -> ViewController func makeStarsGiftScreen(context: AccountContext, message: EngineMessage) -> ViewController
func makeStarsGiveawayBoostScreen(context: AccountContext, peerId: EnginePeer.Id, boost: ChannelBoostersContext.State.Boost) -> ViewController func makeStarsGiveawayBoostScreen(context: AccountContext, peerId: EnginePeer.Id, boost: ChannelBoostersContext.State.Boost) -> ViewController

View File

@ -961,6 +961,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
}, presentForwardOptions: { _ in }, presentForwardOptions: { _ in
}, presentReplyOptions: { _ in }, presentReplyOptions: { _ in
}, presentLinkOptions: { _ in }, presentLinkOptions: { _ in
}, presentSuggestPostOptions: {
}, shareSelectedMessages: { }, shareSelectedMessages: {
}, updateTextInputStateAndMode: { [weak self] f in }, updateTextInputStateAndMode: { [weak self] f in
if let strongSelf = self { if let strongSelf = self {
@ -1246,7 +1247,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
}, joinGroupCall: { _ in }, joinGroupCall: { _ in
}, presentInviteMembers: { }, presentInviteMembers: {
}, presentGigagroupHelp: { }, presentGigagroupHelp: {
}, openSuggestPost: { }, openMonoforum: {
}, editMessageMedia: { _, _ in }, editMessageMedia: { _, _ in
}, updateShowCommands: { _ in }, updateShowCommands: { _ in
}, updateShowSendAsPeers: { _ in }, updateShowSendAsPeers: { _ in
@ -1264,6 +1265,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
}, addDoNotTranslateLanguage: { _ in }, addDoNotTranslateLanguage: { _ in
}, hideTranslationPanel: { }, hideTranslationPanel: {
}, openPremiumGift: { }, openPremiumGift: {
}, openSuggestPost: {
}, openPremiumRequiredForMessaging: { }, openPremiumRequiredForMessaging: {
}, openStarsPurchase: { _ in }, openStarsPurchase: { _ in
}, openMessagePayment: { }, openMessagePayment: {

View File

@ -495,6 +495,16 @@ public final class ChatInterfaceState: Codable, Equatable {
} }
} }
public struct PostSuggestionState: Codable, Equatable {
public var price: Int64
public var timestamp: Int32?
public init(price: Int64, timestamp: Int32?) {
self.price = price
self.timestamp = timestamp
}
}
public let timestamp: Int32 public let timestamp: Int32
public let composeInputState: ChatTextInputState public let composeInputState: ChatTextInputState
public let composeDisableUrlPreviews: [String] public let composeDisableUrlPreviews: [String]
@ -510,6 +520,7 @@ public final class ChatInterfaceState: Codable, Equatable {
public let silentPosting: Bool public let silentPosting: Bool
public let inputLanguage: String? public let inputLanguage: String?
public let sendMessageEffect: Int64? public let sendMessageEffect: Int64?
public let postSuggestionState: PostSuggestionState?
public var synchronizeableInputState: SynchronizeableChatInputState? { public var synchronizeableInputState: SynchronizeableChatInputState? {
if self.composeInputState.inputText.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && self.replyMessageSubject == nil { if self.composeInputState.inputText.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && self.replyMessageSubject == nil {
@ -561,9 +572,10 @@ public final class ChatInterfaceState: Codable, Equatable {
self.silentPosting = false self.silentPosting = false
self.inputLanguage = nil self.inputLanguage = nil
self.sendMessageEffect = nil self.sendMessageEffect = nil
self.postSuggestionState = nil
} }
public init(timestamp: Int32, composeInputState: ChatTextInputState, composeDisableUrlPreviews: [String], replyMessageSubject: ReplyMessageSubject?, forwardMessageIds: [EngineMessage.Id]?, forwardOptionsState: ChatInterfaceForwardOptionsState?, editMessage: ChatEditMessageState?, selectionState: ChatInterfaceSelectionState?, messageActionsState: ChatInterfaceMessageActionsState, historyScrollState: ChatInterfaceHistoryScrollState?, mediaRecordingMode: ChatTextInputMediaRecordingButtonMode, mediaDraftState: ChatInterfaceMediaDraftState?, silentPosting: Bool, inputLanguage: String?, sendMessageEffect: Int64?) { public init(timestamp: Int32, composeInputState: ChatTextInputState, composeDisableUrlPreviews: [String], replyMessageSubject: ReplyMessageSubject?, forwardMessageIds: [EngineMessage.Id]?, forwardOptionsState: ChatInterfaceForwardOptionsState?, editMessage: ChatEditMessageState?, selectionState: ChatInterfaceSelectionState?, messageActionsState: ChatInterfaceMessageActionsState, historyScrollState: ChatInterfaceHistoryScrollState?, mediaRecordingMode: ChatTextInputMediaRecordingButtonMode, mediaDraftState: ChatInterfaceMediaDraftState?, silentPosting: Bool, inputLanguage: String?, sendMessageEffect: Int64?, postSuggestionState: PostSuggestionState?) {
self.timestamp = timestamp self.timestamp = timestamp
self.composeInputState = composeInputState self.composeInputState = composeInputState
self.composeDisableUrlPreviews = composeDisableUrlPreviews self.composeDisableUrlPreviews = composeDisableUrlPreviews
@ -579,6 +591,7 @@ public final class ChatInterfaceState: Codable, Equatable {
self.silentPosting = silentPosting self.silentPosting = silentPosting
self.inputLanguage = inputLanguage self.inputLanguage = inputLanguage
self.sendMessageEffect = sendMessageEffect self.sendMessageEffect = sendMessageEffect
self.postSuggestionState = postSuggestionState
} }
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
@ -650,8 +663,8 @@ public final class ChatInterfaceState: Codable, Equatable {
self.silentPosting = ((try? container.decode(Int32.self, forKey: "sip")) ?? 0) != 0 self.silentPosting = ((try? container.decode(Int32.self, forKey: "sip")) ?? 0) != 0
self.inputLanguage = try? container.decodeIfPresent(String.self, forKey: "inputLanguage") self.inputLanguage = try? container.decodeIfPresent(String.self, forKey: "inputLanguage")
self.sendMessageEffect = try? container.decodeIfPresent(Int64.self, forKey: "sendMessageEffect") self.sendMessageEffect = try? container.decodeIfPresent(Int64.self, forKey: "sendMessageEffect")
self.postSuggestionState = try? container.decodeIfPresent(PostSuggestionState.self, forKey: "postSuggestionState")
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -710,6 +723,7 @@ public final class ChatInterfaceState: Codable, Equatable {
} }
try container.encodeIfPresent(self.sendMessageEffect, forKey: "sendMessageEffect") try container.encodeIfPresent(self.sendMessageEffect, forKey: "sendMessageEffect")
try container.encodeIfPresent(self.postSuggestionState, forKey: "postSuggestionState")
} }
public static func ==(lhs: ChatInterfaceState, rhs: ChatInterfaceState) -> Bool { public static func ==(lhs: ChatInterfaceState, rhs: ChatInterfaceState) -> Bool {
@ -747,17 +761,20 @@ public final class ChatInterfaceState: Codable, Equatable {
if lhs.sendMessageEffect != rhs.sendMessageEffect { if lhs.sendMessageEffect != rhs.sendMessageEffect {
return false return false
} }
if lhs.postSuggestionState != rhs.postSuggestionState {
return false
}
return lhs.composeInputState == rhs.composeInputState && lhs.replyMessageSubject == rhs.replyMessageSubject && lhs.selectionState == rhs.selectionState && lhs.editMessage == rhs.editMessage return lhs.composeInputState == rhs.composeInputState && lhs.replyMessageSubject == rhs.replyMessageSubject && lhs.selectionState == rhs.selectionState && lhs.editMessage == rhs.editMessage
} }
public func withUpdatedComposeInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState { public func withUpdatedComposeInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState {
let updatedComposeInputState = inputState let updatedComposeInputState = inputState
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedComposeDisableUrlPreviews(_ disableUrlPreviews: [String]) -> ChatInterfaceState { public func withUpdatedComposeDisableUrlPreviews(_ disableUrlPreviews: [String]) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: disableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: disableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedEffectiveInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState { public func withUpdatedEffectiveInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState {
@ -769,19 +786,19 @@ public final class ChatInterfaceState: Codable, Equatable {
updatedComposeInputState = inputState updatedComposeInputState = inputState
} }
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: updatedEditMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: updatedEditMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedReplyMessageSubject(_ replyMessageSubject: ReplyMessageSubject?) -> ChatInterfaceState { public func withUpdatedReplyMessageSubject(_ replyMessageSubject: ReplyMessageSubject?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedForwardMessageIds(_ forwardMessageIds: [EngineMessage.Id]?) -> ChatInterfaceState { public func withUpdatedForwardMessageIds(_ forwardMessageIds: [EngineMessage.Id]?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedForwardOptionsState(_ forwardOptionsState: ChatInterfaceForwardOptionsState?) -> ChatInterfaceState { public func withUpdatedForwardOptionsState(_ forwardOptionsState: ChatInterfaceForwardOptionsState?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedSelectedMessages(_ messageIds: [EngineMessage.Id]) -> ChatInterfaceState { public func withUpdatedSelectedMessages(_ messageIds: [EngineMessage.Id]) -> ChatInterfaceState {
@ -792,7 +809,7 @@ public final class ChatInterfaceState: Codable, Equatable {
for messageId in messageIds { for messageId in messageIds {
selectedIds.insert(messageId) selectedIds.insert(messageId)
} }
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withToggledSelectedMessages(_ messageIds: [EngineMessage.Id], value: Bool) -> ChatInterfaceState { public func withToggledSelectedMessages(_ messageIds: [EngineMessage.Id], value: Bool) -> ChatInterfaceState {
@ -807,47 +824,51 @@ public final class ChatInterfaceState: Codable, Equatable {
selectedIds.remove(messageId) selectedIds.remove(messageId)
} }
} }
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withoutSelectionState() -> ChatInterfaceState { public func withoutSelectionState() -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: nil, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: nil, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedTimestamp(_ timestamp: Int32) -> ChatInterfaceState { public func withUpdatedTimestamp(_ timestamp: Int32) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedEditMessage(_ editMessage: ChatEditMessageState?) -> ChatInterfaceState { public func withUpdatedEditMessage(_ editMessage: ChatEditMessageState?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedMessageActionsState(_ f: (ChatInterfaceMessageActionsState) -> ChatInterfaceMessageActionsState) -> ChatInterfaceState { public func withUpdatedMessageActionsState(_ f: (ChatInterfaceMessageActionsState) -> ChatInterfaceMessageActionsState) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: f(self.messageActionsState), historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: f(self.messageActionsState), historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedHistoryScrollState(_ historyScrollState: ChatInterfaceHistoryScrollState?) -> ChatInterfaceState { public func withUpdatedHistoryScrollState(_ historyScrollState: ChatInterfaceHistoryScrollState?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedMediaRecordingMode(_ mediaRecordingMode: ChatTextInputMediaRecordingButtonMode) -> ChatInterfaceState { public func withUpdatedMediaRecordingMode(_ mediaRecordingMode: ChatTextInputMediaRecordingButtonMode) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedMediaDraftState(_ mediaDraftState: ChatInterfaceMediaDraftState?) -> ChatInterfaceState { public func withUpdatedMediaDraftState(_ mediaDraftState: ChatInterfaceMediaDraftState?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedSilentPosting(_ silentPosting: Bool) -> ChatInterfaceState { public func withUpdatedSilentPosting(_ silentPosting: Bool) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedInputLanguage(_ inputLanguage: String?) -> ChatInterfaceState { public func withUpdatedInputLanguage(_ inputLanguage: String?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: inputLanguage, sendMessageEffect: self.sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: self.postSuggestionState)
} }
public func withUpdatedSendMessageEffect(_ sendMessageEffect: Int64?) -> ChatInterfaceState { public func withUpdatedSendMessageEffect(_ sendMessageEffect: Int64?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: sendMessageEffect) return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: sendMessageEffect, postSuggestionState: self.postSuggestionState)
}
public func withUpdatedPostSuggestionState(_ postSuggestionState: PostSuggestionState?) -> ChatInterfaceState {
return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreviews: self.composeDisableUrlPreviews, replyMessageSubject: self.replyMessageSubject, forwardMessageIds: self.forwardMessageIds, forwardOptionsState: self.forwardOptionsState, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, mediaDraftState: self.mediaDraftState, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage, sendMessageEffect: self.sendMessageEffect, postSuggestionState: postSuggestionState)
} }
public static func parse(_ state: OpaqueChatInterfaceState) -> ChatInterfaceState { public static func parse(_ state: OpaqueChatInterfaceState) -> ChatInterfaceState {

View File

@ -78,6 +78,7 @@ public final class ChatPanelInterfaceInteraction {
public let presentForwardOptions: (ASDisplayNode) -> Void public let presentForwardOptions: (ASDisplayNode) -> Void
public let presentReplyOptions: (ASDisplayNode) -> Void public let presentReplyOptions: (ASDisplayNode) -> Void
public let presentLinkOptions: (ASDisplayNode) -> Void public let presentLinkOptions: (ASDisplayNode) -> Void
public let presentSuggestPostOptions: () -> Void
public let shareSelectedMessages: () -> Void public let shareSelectedMessages: () -> Void
public let updateTextInputStateAndMode: (@escaping (ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void public let updateTextInputStateAndMode: (@escaping (ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void
public let updateInputModeAndDismissedButtonKeyboardMessageId: ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void public let updateInputModeAndDismissedButtonKeyboardMessageId: ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void
@ -150,7 +151,7 @@ public final class ChatPanelInterfaceInteraction {
public let joinGroupCall: (CachedChannelData.ActiveCall) -> Void public let joinGroupCall: (CachedChannelData.ActiveCall) -> Void
public let presentInviteMembers: () -> Void public let presentInviteMembers: () -> Void
public let presentGigagroupHelp: () -> Void public let presentGigagroupHelp: () -> Void
public let openSuggestPost: () -> Void public let openMonoforum: () -> Void
public let updateShowCommands: ((Bool) -> Bool) -> Void public let updateShowCommands: ((Bool) -> Bool) -> Void
public let updateShowSendAsPeers: ((Bool) -> Bool) -> Void public let updateShowSendAsPeers: ((Bool) -> Bool) -> Void
public let openInviteRequests: () -> Void public let openInviteRequests: () -> Void
@ -167,6 +168,7 @@ public final class ChatPanelInterfaceInteraction {
public let addDoNotTranslateLanguage: (String) -> Void public let addDoNotTranslateLanguage: (String) -> Void
public let hideTranslationPanel: () -> Void public let hideTranslationPanel: () -> Void
public let openPremiumGift: () -> Void public let openPremiumGift: () -> Void
public let openSuggestPost: () -> Void
public let openPremiumRequiredForMessaging: () -> Void public let openPremiumRequiredForMessaging: () -> Void
public let openStarsPurchase: (Int64?) -> Void public let openStarsPurchase: (Int64?) -> Void
public let openMessagePayment: () -> Void public let openMessagePayment: () -> Void
@ -199,6 +201,7 @@ public final class ChatPanelInterfaceInteraction {
presentForwardOptions: @escaping (ASDisplayNode) -> Void, presentForwardOptions: @escaping (ASDisplayNode) -> Void,
presentReplyOptions: @escaping (ASDisplayNode) -> Void, presentReplyOptions: @escaping (ASDisplayNode) -> Void,
presentLinkOptions: @escaping (ASDisplayNode) -> Void, presentLinkOptions: @escaping (ASDisplayNode) -> Void,
presentSuggestPostOptions: @escaping () -> Void,
shareSelectedMessages: @escaping () -> Void, shareSelectedMessages: @escaping () -> Void,
updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void,
updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void,
@ -270,7 +273,7 @@ public final class ChatPanelInterfaceInteraction {
joinGroupCall: @escaping (CachedChannelData.ActiveCall) -> Void, joinGroupCall: @escaping (CachedChannelData.ActiveCall) -> Void,
presentInviteMembers: @escaping () -> Void, presentInviteMembers: @escaping () -> Void,
presentGigagroupHelp: @escaping () -> Void, presentGigagroupHelp: @escaping () -> Void,
openSuggestPost: @escaping () -> Void, openMonoforum: @escaping () -> Void,
editMessageMedia: @escaping (MessageId, Bool) -> Void, editMessageMedia: @escaping (MessageId, Bool) -> Void,
updateShowCommands: @escaping ((Bool) -> Bool) -> Void, updateShowCommands: @escaping ((Bool) -> Bool) -> Void,
updateShowSendAsPeers: @escaping ((Bool) -> Bool) -> Void, updateShowSendAsPeers: @escaping ((Bool) -> Bool) -> Void,
@ -288,6 +291,7 @@ public final class ChatPanelInterfaceInteraction {
addDoNotTranslateLanguage: @escaping (String) -> Void, addDoNotTranslateLanguage: @escaping (String) -> Void,
hideTranslationPanel: @escaping () -> Void, hideTranslationPanel: @escaping () -> Void,
openPremiumGift: @escaping () -> Void, openPremiumGift: @escaping () -> Void,
openSuggestPost: @escaping () -> Void,
openPremiumRequiredForMessaging: @escaping () -> Void, openPremiumRequiredForMessaging: @escaping () -> Void,
openStarsPurchase: @escaping (Int64?) -> Void, openStarsPurchase: @escaping (Int64?) -> Void,
openMessagePayment: @escaping () -> Void, openMessagePayment: @escaping () -> Void,
@ -319,6 +323,7 @@ public final class ChatPanelInterfaceInteraction {
self.presentForwardOptions = presentForwardOptions self.presentForwardOptions = presentForwardOptions
self.presentReplyOptions = presentReplyOptions self.presentReplyOptions = presentReplyOptions
self.presentLinkOptions = presentLinkOptions self.presentLinkOptions = presentLinkOptions
self.presentSuggestPostOptions = presentSuggestPostOptions
self.shareSelectedMessages = shareSelectedMessages self.shareSelectedMessages = shareSelectedMessages
self.updateTextInputStateAndMode = updateTextInputStateAndMode self.updateTextInputStateAndMode = updateTextInputStateAndMode
self.updateInputModeAndDismissedButtonKeyboardMessageId = updateInputModeAndDismissedButtonKeyboardMessageId self.updateInputModeAndDismissedButtonKeyboardMessageId = updateInputModeAndDismissedButtonKeyboardMessageId
@ -391,7 +396,7 @@ public final class ChatPanelInterfaceInteraction {
self.joinGroupCall = joinGroupCall self.joinGroupCall = joinGroupCall
self.presentInviteMembers = presentInviteMembers self.presentInviteMembers = presentInviteMembers
self.presentGigagroupHelp = presentGigagroupHelp self.presentGigagroupHelp = presentGigagroupHelp
self.openSuggestPost = openSuggestPost self.openMonoforum = openMonoforum
self.updateShowCommands = updateShowCommands self.updateShowCommands = updateShowCommands
self.updateShowSendAsPeers = updateShowSendAsPeers self.updateShowSendAsPeers = updateShowSendAsPeers
self.openInviteRequests = openInviteRequests self.openInviteRequests = openInviteRequests
@ -408,6 +413,7 @@ public final class ChatPanelInterfaceInteraction {
self.addDoNotTranslateLanguage = addDoNotTranslateLanguage self.addDoNotTranslateLanguage = addDoNotTranslateLanguage
self.hideTranslationPanel = hideTranslationPanel self.hideTranslationPanel = hideTranslationPanel
self.openPremiumGift = openPremiumGift self.openPremiumGift = openPremiumGift
self.openSuggestPost = openSuggestPost
self.openPremiumRequiredForMessaging = openPremiumRequiredForMessaging self.openPremiumRequiredForMessaging = openPremiumRequiredForMessaging
self.openStarsPurchase = openStarsPurchase self.openStarsPurchase = openStarsPurchase
self.openMessagePayment = openMessagePayment self.openMessagePayment = openMessagePayment
@ -447,6 +453,7 @@ public final class ChatPanelInterfaceInteraction {
}, presentForwardOptions: { _ in }, presentForwardOptions: { _ in
}, presentReplyOptions: { _ in }, presentReplyOptions: { _ in
}, presentLinkOptions: { _ in }, presentLinkOptions: { _ in
}, presentSuggestPostOptions: {
}, shareSelectedMessages: { }, shareSelectedMessages: {
}, updateTextInputStateAndMode: updateTextInputStateAndMode, updateInputModeAndDismissedButtonKeyboardMessageId: updateInputModeAndDismissedButtonKeyboardMessageId, openStickers: { }, updateTextInputStateAndMode: updateTextInputStateAndMode, updateInputModeAndDismissedButtonKeyboardMessageId: updateInputModeAndDismissedButtonKeyboardMessageId, openStickers: {
}, editMessage: { }, editMessage: {
@ -519,7 +526,7 @@ public final class ChatPanelInterfaceInteraction {
}, joinGroupCall: { _ in }, joinGroupCall: { _ in
}, presentInviteMembers: { }, presentInviteMembers: {
}, presentGigagroupHelp: { }, presentGigagroupHelp: {
}, openSuggestPost: { }, openMonoforum: {
}, editMessageMedia: { _, _ in }, editMessageMedia: { _, _ in
}, updateShowCommands: { _ in }, updateShowCommands: { _ in
}, updateShowSendAsPeers: { _ in }, updateShowSendAsPeers: { _ in
@ -537,6 +544,7 @@ public final class ChatPanelInterfaceInteraction {
}, addDoNotTranslateLanguage: { _ in }, addDoNotTranslateLanguage: { _ in
}, hideTranslationPanel: { }, hideTranslationPanel: {
}, openPremiumGift: { }, openPremiumGift: {
}, openSuggestPost: {
}, openPremiumRequiredForMessaging: { }, openPremiumRequiredForMessaging: {
}, openStarsPurchase: { _ in }, openStarsPurchase: { _ in
}, openMessagePayment: { }, openMessagePayment: {

View File

@ -11,6 +11,7 @@ public enum ChatTextInputAccessoryItem: Equatable {
case messageAutoremoveTimeout case messageAutoremoveTimeout
case scheduledMessages case scheduledMessages
case gift case gift
case suggestPost
} }
public enum InputMode: Hashable { public enum InputMode: Hashable {
@ -27,6 +28,7 @@ public enum ChatTextInputAccessoryItem: Equatable {
case messageAutoremoveTimeout(Int32?) case messageAutoremoveTimeout(Int32?)
case scheduledMessages case scheduledMessages
case gift case gift
case suggestPost
public var key: Key { public var key: Key {
switch self { switch self {
@ -44,6 +46,8 @@ public enum ChatTextInputAccessoryItem: Equatable {
return .scheduledMessages return .scheduledMessages
case .gift: case .gift:
return .gift return .gift
case .suggestPost:
return .suggestPost
} }
} }
} }

View File

@ -376,13 +376,18 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP
if case let .paidMessages(value) = stateValue.with({ $0 }).updatedValue { if case let .paidMessages(value) = stateValue.with({ $0 }).updatedValue {
currentAmount = value currentAmount = value
} }
let starsScreen = context.sharedContext.makeStarsWithdrawalScreen(context: context, subject: .enterAmount(current: currentAmount, minValue: StarsAmount(value: 1, nanos: 0), fractionAfterCommission: 80, kind: .privacy), completion: { amount in let starsScreen = context.sharedContext.makeStarsWithdrawalScreen(context: context, subject: .enterAmount(
current: currentAmount,
minValue: StarsAmount(value: 1, nanos: 0),
fractionAfterCommission: 80, kind: .privacy,
completion: { amount in
updateState { state in updateState { state in
var state = state var state = state
state.updatedValue = .paidMessages(StarsAmount(value: amount, nanos: 0)) state.updatedValue = .paidMessages(StarsAmount(value: amount, nanos: 0))
return state return state
} }
}) }
))
pushControllerImpl?(starsScreen) pushControllerImpl?(starsScreen)
} }
) )

View File

@ -68,6 +68,7 @@ public struct MessageHistoryThreadData: Codable, Equatable {
case isHidden case isHidden
case notificationSettings case notificationSettings
case isMarkedUnread case isMarkedUnread
case isMessageFeeRemoved
} }
public var creationDate: Int32 public var creationDate: Int32
@ -82,6 +83,7 @@ public struct MessageHistoryThreadData: Codable, Equatable {
public var isClosed: Bool public var isClosed: Bool
public var isHidden: Bool public var isHidden: Bool
public var notificationSettings: TelegramPeerNotificationSettings public var notificationSettings: TelegramPeerNotificationSettings
public var isMessageFeeRemoved: Bool
public init( public init(
creationDate: Int32, creationDate: Int32,
@ -95,7 +97,8 @@ public struct MessageHistoryThreadData: Codable, Equatable {
maxOutgoingReadId: Int32, maxOutgoingReadId: Int32,
isClosed: Bool, isClosed: Bool,
isHidden: Bool, isHidden: Bool,
notificationSettings: TelegramPeerNotificationSettings notificationSettings: TelegramPeerNotificationSettings,
isMessageFeeRemoved: Bool
) { ) {
self.creationDate = creationDate self.creationDate = creationDate
self.isOwnedByMe = isOwnedByMe self.isOwnedByMe = isOwnedByMe
@ -109,6 +112,7 @@ public struct MessageHistoryThreadData: Codable, Equatable {
self.isClosed = isClosed self.isClosed = isClosed
self.isHidden = isHidden self.isHidden = isHidden
self.notificationSettings = notificationSettings self.notificationSettings = notificationSettings
self.isMessageFeeRemoved = isMessageFeeRemoved
} }
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
@ -126,6 +130,7 @@ public struct MessageHistoryThreadData: Codable, Equatable {
self.isClosed = try container.decodeIfPresent(Bool.self, forKey: .isClosed) ?? false self.isClosed = try container.decodeIfPresent(Bool.self, forKey: .isClosed) ?? false
self.isHidden = try container.decodeIfPresent(Bool.self, forKey: .isHidden) ?? false self.isHidden = try container.decodeIfPresent(Bool.self, forKey: .isHidden) ?? false
self.notificationSettings = try container.decode(TelegramPeerNotificationSettings.self, forKey: .notificationSettings) self.notificationSettings = try container.decode(TelegramPeerNotificationSettings.self, forKey: .notificationSettings)
self.isMessageFeeRemoved = try container.decodeIfPresent(Bool.self, forKey: .isMessageFeeRemoved) ?? false
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -143,6 +148,7 @@ public struct MessageHistoryThreadData: Codable, Equatable {
try container.encode(self.isClosed, forKey: .isClosed) try container.encode(self.isClosed, forKey: .isClosed)
try container.encode(self.isHidden, forKey: .isHidden) try container.encode(self.isHidden, forKey: .isHidden)
try container.encode(self.notificationSettings, forKey: .notificationSettings) try container.encode(self.notificationSettings, forKey: .notificationSettings)
try container.encode(self.isMessageFeeRemoved, forKey: .isMessageFeeRemoved)
} }
} }
@ -668,7 +674,8 @@ public func _internal_fillSavedMessageHistory(accountPeerId: PeerId, postbox: Po
maxOutgoingReadId: 0, maxOutgoingReadId: 0,
isClosed: false, isClosed: false,
isHidden: false, isHidden: false,
notificationSettings: TelegramPeerNotificationSettings.defaultSettings notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
isMessageFeeRemoved: false
), ),
topMessage: message.id.id, topMessage: message.id.id,
unreadMentionsCount: 0, unreadMentionsCount: 0,
@ -786,7 +793,8 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post
maxOutgoingReadId: 0, maxOutgoingReadId: 0,
isClosed: false, isClosed: false,
isHidden: false, isHidden: false,
notificationSettings: TelegramPeerNotificationSettings.defaultSettings notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
isMessageFeeRemoved: false
) )
var topTimestamp: Int32 = 1 var topTimestamp: Int32 = 1
@ -840,7 +848,8 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post
maxOutgoingReadId: readOutboxMaxId, maxOutgoingReadId: readOutboxMaxId,
isClosed: false, isClosed: false,
isHidden: false, isHidden: false,
notificationSettings: TelegramPeerNotificationSettings.defaultSettings notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
isMessageFeeRemoved: (flags & (1 << 4)) != 0
) )
var topTimestamp: Int32 = 1 var topTimestamp: Int32 = 1
@ -989,7 +998,8 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post
maxOutgoingReadId: readOutboxMaxId, maxOutgoingReadId: readOutboxMaxId,
isClosed: (flags & (1 << 2)) != 0, isClosed: (flags & (1 << 2)) != 0,
isHidden: (flags & (1 << 6)) != 0, isHidden: (flags & (1 << 6)) != 0,
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings) notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings),
isMessageFeeRemoved: false
) )
var topTimestamp = date var topTimestamp = date

View File

@ -2107,7 +2107,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
maxOutgoingReadId: readOutboxMaxId, maxOutgoingReadId: readOutboxMaxId,
isClosed: (flags & (1 << 2)) != 0, isClosed: (flags & (1 << 2)) != 0,
isHidden: (flags & (1 << 6)) != 0, isHidden: (flags & (1 << 6)) != 0,
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings) notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings),
isMessageFeeRemoved: false
), ),
topMessageId: topMessage, topMessageId: topMessage,
unreadMentionCount: unreadMentionsCount, unreadMentionCount: unreadMentionsCount,
@ -2140,7 +2141,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
maxOutgoingReadId: readOutboxMaxId, maxOutgoingReadId: readOutboxMaxId,
isClosed: false, isClosed: false,
isHidden: false, isHidden: false,
notificationSettings: TelegramPeerNotificationSettings.defaultSettings notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
isMessageFeeRemoved: (flags & (1 << 4)) != 0
), ),
topMessageId: topMessage, topMessageId: topMessage,
unreadMentionCount: 0, unreadMentionCount: 0,
@ -2266,7 +2268,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
maxOutgoingReadId: readOutboxMaxId, maxOutgoingReadId: readOutboxMaxId,
isClosed: (flags & (1 << 2)) != 0, isClosed: (flags & (1 << 2)) != 0,
isHidden: (flags & (1 << 6)) != 0, isHidden: (flags & (1 << 6)) != 0,
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings) notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings),
isMessageFeeRemoved: false
) )
if let entry = StoredMessageHistoryThreadInfo(data) { if let entry = StoredMessageHistoryThreadInfo(data) {
transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: Int64(id), info: entry) transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: Int64(id), info: entry)
@ -2296,7 +2299,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
maxOutgoingReadId: readOutboxMaxId, maxOutgoingReadId: readOutboxMaxId,
isClosed: false, isClosed: false,
isHidden: false, isHidden: false,
notificationSettings: TelegramPeerNotificationSettings.defaultSettings notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
isMessageFeeRemoved: (flags & (1 << 4)) != 0
) )
if let entry = StoredMessageHistoryThreadInfo(data) { if let entry = StoredMessageHistoryThreadInfo(data) {
transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: peer.peerId.toInt64(), info: entry) transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: peer.peerId.toInt64(), info: entry)
@ -2429,7 +2433,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
maxOutgoingReadId: readOutboxMaxId, maxOutgoingReadId: readOutboxMaxId,
isClosed: (flags & (1 << 2)) != 0, isClosed: (flags & (1 << 2)) != 0,
isHidden: (flags & (1 << 6)) != 0, isHidden: (flags & (1 << 6)) != 0,
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings) notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings),
isMessageFeeRemoved: false
), ),
topMessageId: topMessage, topMessageId: topMessage,
unreadMentionCount: unreadMentionsCount, unreadMentionCount: unreadMentionsCount,
@ -2459,7 +2464,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
maxOutgoingReadId: readOutboxMaxId, maxOutgoingReadId: readOutboxMaxId,
isClosed: false, isClosed: false,
isHidden: false, isHidden: false,
notificationSettings: TelegramPeerNotificationSettings.defaultSettings notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
isMessageFeeRemoved: (flags & (1 << 4)) != 0
), ),
topMessageId: topMessage, topMessageId: topMessage,
unreadMentionCount: 0, unreadMentionCount: 0,

View File

@ -3,15 +3,26 @@ import TelegramApi
import Postbox import Postbox
import SwiftSignalKit import SwiftSignalKit
func _internal_getPaidMessagesRevenue(account: Account, peerId: PeerId) -> Signal<StarsAmount?, NoError> { func _internal_getPaidMessagesRevenue(account: Account, scopePeerId: PeerId, peerId: PeerId) -> Signal<StarsAmount?, NoError> {
return account.postbox.transaction { transaction -> Api.InputUser? in return account.postbox.transaction { transaction -> (Api.InputPeer?, Api.InputUser?) in
return transaction.getPeer(peerId).flatMap(apiInputUser) return (transaction.getPeer(scopePeerId).flatMap(apiInputPeer), transaction.getPeer(peerId).flatMap(apiInputUser))
}
|> mapToSignal { scopeInputPeer, inputUser -> Signal<StarsAmount?, NoError> in
if scopePeerId != account.peerId {
if scopeInputPeer == nil {
return .never()
}
} }
|> mapToSignal { inputUser -> Signal<StarsAmount?, NoError> in
guard let inputUser else { guard let inputUser else {
return .single(nil) return .single(nil)
} }
return account.network.request(Api.functions.account.getPaidMessagesRevenue(flags: 0, parentPeer: nil, userId: inputUser))
var flags: Int32 = 0
if scopePeerId != account.peerId, scopeInputPeer != nil {
flags |= 1 << 0
}
return account.network.request(Api.functions.account.getPaidMessagesRevenue(flags: 0, parentPeer: scopeInputPeer, userId: inputUser))
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<Api.account.PaidMessagesRevenue?, NoError> in |> `catch` { _ -> Signal<Api.account.PaidMessagesRevenue?, NoError> in
return .single(nil) return .single(nil)
@ -28,11 +39,16 @@ func _internal_getPaidMessagesRevenue(account: Account, peerId: PeerId) -> Signa
} }
} }
func _internal_addNoPaidMessagesException(account: Account, peerId: PeerId, refundCharged: Bool) -> Signal<Never, NoError> { func _internal_addNoPaidMessagesException(account: Account, scopePeerId: PeerId, peerId: PeerId, refundCharged: Bool) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> Api.InputUser? in return account.postbox.transaction { transaction -> (Api.InputPeer?, Api.InputUser?) in
return transaction.getPeer(peerId).flatMap(apiInputUser) return (transaction.getPeer(scopePeerId).flatMap(apiInputPeer), transaction.getPeer(peerId).flatMap(apiInputUser))
}
|> mapToSignal { scopeInputPeer, inputUser -> Signal<Never, NoError> in
if scopePeerId != account.peerId {
if scopeInputPeer == nil {
return .never()
}
} }
|> mapToSignal { inputUser -> Signal<Never, NoError> in
guard let inputUser else { guard let inputUser else {
return .never() return .never()
} }
@ -40,11 +56,24 @@ func _internal_addNoPaidMessagesException(account: Account, peerId: PeerId, refu
if refundCharged { if refundCharged {
flags |= (1 << 0) flags |= (1 << 0)
} }
return account.network.request(Api.functions.account.toggleNoPaidMessagesException(flags: flags, parentPeer: nil, userId: inputUser)) if scopePeerId != account.peerId, scopeInputPeer != nil {
flags |= (1 << 1)
}
return account.network.request(Api.functions.account.toggleNoPaidMessagesException(flags: flags, parentPeer: scopeInputPeer, userId: inputUser))
|> `catch` { _ -> Signal<Api.Bool, NoError> in |> `catch` { _ -> Signal<Api.Bool, NoError> in
return .single(.boolFalse) return .single(.boolFalse)
} |> mapToSignal { _ in } |> mapToSignal { _ in
return account.postbox.transaction { transaction -> Void in return account.postbox.transaction { transaction -> Void in
if scopePeerId != account.peerId, scopeInputPeer != nil {
guard var data = transaction.getMessageHistoryThreadInfo(peerId: scopePeerId, threadId: peerId.toInt64())?.data.get(MessageHistoryThreadData.self) else {
return
}
data.isMessageFeeRemoved = true
if let entry = StoredMessageHistoryThreadInfo(data) {
transaction.setMessageHistoryThreadInfo(peerId: scopePeerId, threadId: peerId.toInt64(), info: entry)
}
} else {
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData in
if let cachedData = cachedData as? CachedUserData { if let cachedData = cachedData as? CachedUserData {
var settings = cachedData.peerStatusSettings ?? .init() var settings = cachedData.peerStatusSettings ?? .init()
@ -54,6 +83,7 @@ func _internal_addNoPaidMessagesException(account: Account, peerId: PeerId, refu
return cachedData return cachedData
}) })
} }
}
|> ignoreValues |> ignoreValues
} }
|> ignoreValues |> ignoreValues

View File

@ -1494,12 +1494,12 @@ public extension TelegramEngine {
return _internal_applyChannelBoost(account: self.account, peerId: peerId, slots: slots) return _internal_applyChannelBoost(account: self.account, peerId: peerId, slots: slots)
} }
public func getPaidMessagesRevenue(peerId: EnginePeer.Id) -> Signal<StarsAmount?, NoError> { public func getPaidMessagesRevenue(scopePeerId: EnginePeer.Id, peerId: EnginePeer.Id) -> Signal<StarsAmount?, NoError> {
return _internal_getPaidMessagesRevenue(account: self.account, peerId: peerId) return _internal_getPaidMessagesRevenue(account: self.account, scopePeerId: scopePeerId, peerId: peerId)
} }
public func addNoPaidMessagesException(peerId: EnginePeer.Id, refundCharged: Bool) -> Signal<Never, NoError> { public func addNoPaidMessagesException(scopePeerId: EnginePeer.Id, peerId: EnginePeer.Id, refundCharged: Bool) -> Signal<Never, NoError> {
return _internal_addNoPaidMessagesException(account: self.account, peerId: peerId, refundCharged: refundCharged) return _internal_addNoPaidMessagesException(account: self.account, scopePeerId: scopePeerId, peerId: peerId, refundCharged: refundCharged)
} }
public func updateChannelPaidMessagesStars(peerId: EnginePeer.Id, stars: StarsAmount?, broadcastMessagesAllowed: Bool) -> Signal<Never, NoError> { public func updateChannelPaidMessagesStars(peerId: EnginePeer.Id, stars: StarsAmount?, broadcastMessagesAllowed: Bool) -> Signal<Never, NoError> {

View File

@ -224,6 +224,7 @@ public enum PresentationResourceKey: Int32 {
case chatInputTextFieldTimerImage case chatInputTextFieldTimerImage
case chatInputTextFieldScheduleImage case chatInputTextFieldScheduleImage
case chatInputTextFieldGiftImage case chatInputTextFieldGiftImage
case chatInputTextFieldSuggestPostImage
case chatInputSearchPanelUpImage case chatInputSearchPanelUpImage
case chatInputSearchPanelUpDisabledImage case chatInputSearchPanelUpDisabledImage

View File

@ -562,6 +562,12 @@ public struct PresentationResourcesChat {
}) })
} }
public static func chatInputTextFieldSuggestPostImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputTextFieldSuggestPostImage.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/AccessoryIconSuggestPost"), color: theme.chat.inputPanel.inputControlColor)
})
}
public static func chatInputTextFieldKeyboardImage(_ theme: PresentationTheme) -> UIImage? { public static func chatInputTextFieldKeyboardImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputTextFieldKeyboardImage.rawValue, { theme in return theme.image(PresentationResourceKey.chatInputTextFieldKeyboardImage.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/AccessoryIconKeyboard"), color: theme.chat.inputPanel.inputControlColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/AccessoryIconKeyboard"), color: theme.chat.inputPanel.inputControlColor)

View File

@ -228,7 +228,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
} }
@objc private func suggestedPostPressed() { @objc private func suggestedPostPressed() {
self.interfaceInteraction?.openSuggestPost() self.interfaceInteraction?.openMonoforum()
} }
@objc private func buttonPressed() { @objc private func buttonPressed() {

View File

@ -109,7 +109,7 @@ public final class ChatLoadingPlaceholderMessageContainer {
} }
} }
public func update(size: CGSize, hasAvatar: Bool, rect: CGRect, transition: ContainedViewLayoutTransition) { public func update(size: CGSize, isSidebarOpen: Bool, hasAvatar: Bool, rect: CGRect, transition: ContainedViewLayoutTransition) {
var avatarOffset: CGFloat = 0.0 var avatarOffset: CGFloat = 0.0
if hasAvatar && self.avatarNode == nil { if hasAvatar && self.avatarNode == nil {
@ -127,13 +127,25 @@ public final class ChatLoadingPlaceholderMessageContainer {
} }
if let avatarNode = self.avatarNode, let avatarBorderNode = self.avatarBorderNode { if let avatarNode = self.avatarNode, let avatarBorderNode = self.avatarBorderNode {
let avatarFrame = CGRect(origin: CGPoint(x: rect.minX + 3.0, y: rect.maxY + 1.0 - avatarSize.height), size: avatarSize) var avatarFrame = CGRect(origin: CGPoint(x: rect.minX + 3.0, y: rect.maxY + 1.0 - avatarSize.height), size: avatarSize)
if isSidebarOpen {
avatarFrame.origin.x -= avatarFrame.width * 0.5
avatarFrame.origin.y += avatarFrame.height * 0.5
}
transition.updateFrame(node: avatarNode, frame: avatarFrame) transition.updatePosition(node: avatarNode, position: avatarFrame.center)
transition.updateFrame(node: avatarBorderNode, frame: avatarFrame) transition.updateBounds(node: avatarNode, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size))
transition.updateTransformScale(node: avatarNode, scale: isSidebarOpen ? 0.001 : 1.0)
transition.updateAlpha(node: avatarNode, alpha: isSidebarOpen ? 0.0 : 1.0)
transition.updatePosition(node: avatarBorderNode, position: avatarFrame.center)
transition.updateBounds(node: avatarBorderNode, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size))
transition.updateTransformScale(node: avatarBorderNode, scale: isSidebarOpen ? 0.001 : 1.0)
transition.updateAlpha(node: avatarBorderNode, alpha: isSidebarOpen ? 0.0 : 1.0)
if !isSidebarOpen {
avatarOffset += avatarSize.width - 1.0 avatarOffset += avatarSize.width - 1.0
} }
}
let bubbleFrame = CGRect(origin: CGPoint(x: rect.minX + 3.0 + avatarOffset, y: rect.origin.y), size: CGSize(width: rect.width, height: rect.height)) let bubbleFrame = CGRect(origin: CGPoint(x: rect.minX + 3.0 + avatarOffset, y: rect.origin.y), size: CGSize(width: rect.width, height: rect.height))
transition.updateFrame(node: self.bubbleNode, frame: bubbleFrame) transition.updateFrame(node: self.bubbleNode, frame: bubbleFrame)
@ -161,7 +173,7 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode {
private var absolutePosition: (CGRect, CGSize)? private var absolutePosition: (CGRect, CGSize)?
private var validLayout: (CGSize, UIEdgeInsets, LayoutMetrics)? private var validLayout: (CGSize, Bool, UIEdgeInsets, LayoutMetrics)?
public init(context: AccountContext, theme: PresentationTheme, chatWallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners, backgroundNode: WallpaperBackgroundNode) { public init(context: AccountContext, theme: PresentationTheme, chatWallpaper: TelegramWallpaper, bubbleCorners: PresentationChatBubbleCorners, backgroundNode: WallpaperBackgroundNode) {
self.context = context self.context = context
@ -295,7 +307,7 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode {
private var didAnimateOut = false private var didAnimateOut = false
public func animateOut(_ historyNode: ListView, completion: @escaping () -> Void = {}) { public func animateOut(_ historyNode: ListView, completion: @escaping () -> Void = {}) {
guard let (size, _, _) = self.validLayout else { guard let (size, isSidebarOpen, _, _) = self.validLayout else {
return return
} }
let listNode = historyNode let listNode = historyNode
@ -376,7 +388,7 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode {
let messageContainer = self.messageContainers[k] let messageContainer = self.messageContainers[k]
let messageSize = messageContainer.frame.size let messageSize = messageContainer.frame.size
messageContainer.update(size: size, hasAvatar: self.chatType != .channel && self.chatType != .user, rect: CGRect(origin: CGPoint(x: 0.0, y: offset - messageSize.height), size: messageSize), transition: transition) messageContainer.update(size: size, isSidebarOpen: isSidebarOpen, hasAvatar: self.chatType != .channel && self.chatType != .user, rect: CGRect(origin: CGPoint(x: 0.0, y: offset - messageSize.height), size: messageSize), transition: transition)
offset -= messageSize.height offset -= messageSize.height
} }
} }
@ -439,14 +451,14 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode {
if self.chatType != chatType { if self.chatType != chatType {
self.chatType = chatType self.chatType = chatType
if let (size, insets, metrics) = self.validLayout { if let (size, isSidebarOpen, insets, metrics) = self.validLayout {
self.updateLayout(size: size, insets: insets, metrics: metrics, transition: .immediate) self.updateLayout(size: size, isSidebarOpen: isSidebarOpen, insets: insets, metrics: metrics, transition: .immediate)
} }
} }
} }
public func updateLayout(size: CGSize, insets: UIEdgeInsets, metrics: LayoutMetrics, transition: ContainedViewLayoutTransition) { public func updateLayout(size: CGSize, isSidebarOpen: Bool, insets: UIEdgeInsets, metrics: LayoutMetrics, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, insets, metrics) self.validLayout = (size, isSidebarOpen, insets, metrics)
let bounds = CGRect(origin: .zero, size: size) let bounds = CGRect(origin: .zero, size: size)
@ -500,7 +512,7 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode {
for messageContainer in self.messageContainers { for messageContainer in self.messageContainers {
let messageSize = dimensions[index % 14] let messageSize = dimensions[index % 14]
messageContainer.update(size: bounds.size, hasAvatar: self.chatType != .channel && self.chatType != .user, rect: CGRect(origin: CGPoint(x: insets.left, y: bounds.size.height - insets.bottom - offset - messageSize.height), size: messageSize), transition: transition) messageContainer.update(size: bounds.size, isSidebarOpen: isSidebarOpen, hasAvatar: self.chatType != .channel && self.chatType != .user, rect: CGRect(origin: CGPoint(x: insets.left, y: bounds.size.height - insets.bottom - offset - messageSize.height), size: messageSize), transition: transition)
offset += messageSize.height offset += messageSize.height
index += 1 index += 1
} }

View File

@ -2436,8 +2436,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
if isSidePanelOpen && incoming { if isSidePanelOpen && incoming {
hasTitleAvatar = true hasTitleAvatar = true
if let channel = item.message.peers[item.message.id.peerId], channel.isMonoForum {
} else {
hasTitleTopicNavigation = item.chatLocation.threadId == nil hasTitleTopicNavigation = item.chatLocation.threadId == nil
} }
}
let inlineBotNameColor = messageTheme.accentTextColor let inlineBotNameColor = messageTheme.accentTextColor

View File

@ -72,6 +72,7 @@ public final class ChatRecentActionsController: TelegramBaseController {
}, presentForwardOptions: { _ in }, presentForwardOptions: { _ in
}, presentReplyOptions: { _ in }, presentReplyOptions: { _ in
}, presentLinkOptions: { _ in }, presentLinkOptions: { _ in
}, presentSuggestPostOptions: {
}, shareSelectedMessages: { }, shareSelectedMessages: {
}, updateTextInputStateAndMode: { _ in }, updateTextInputStateAndMode: { _ in
}, updateInputModeAndDismissedButtonKeyboardMessageId: { _ in }, updateInputModeAndDismissedButtonKeyboardMessageId: { _ in
@ -146,7 +147,7 @@ public final class ChatRecentActionsController: TelegramBaseController {
}, joinGroupCall: { _ in }, joinGroupCall: { _ in
}, presentInviteMembers: { }, presentInviteMembers: {
}, presentGigagroupHelp: { }, presentGigagroupHelp: {
}, openSuggestPost: { }, openMonoforum: {
}, editMessageMedia: { _, _ in }, editMessageMedia: { _, _ in
}, updateShowCommands: { _ in }, updateShowCommands: { _ in
}, updateShowSendAsPeers: { _ in }, updateShowSendAsPeers: { _ in
@ -164,6 +165,7 @@ public final class ChatRecentActionsController: TelegramBaseController {
}, addDoNotTranslateLanguage: { _ in }, addDoNotTranslateLanguage: { _ in
}, hideTranslationPanel: { }, hideTranslationPanel: {
}, openPremiumGift: { }, openPremiumGift: {
}, openSuggestPost: {
}, openPremiumRequiredForMessaging: { }, openPremiumRequiredForMessaging: {
}, openStarsPurchase: { _ in }, openStarsPurchase: { _ in
}, openMessagePayment: { }, openMessagePayment: {

View File

@ -0,0 +1,37 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "SuggestPostAccessoryPanelNode",
module_name = "SuggestPostAccessoryPanelNode",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/AsyncDisplayKit",
"//submodules/TelegramCore",
"//submodules/Postbox",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/Display",
"//submodules/TelegramPresentationData",
"//submodules/TelegramUIPreferences",
"//submodules/AccountContext",
"//submodules/LocalizedPeerData",
"//submodules/PhotoResources",
"//submodules/TelegramStringFormatting",
"//submodules/TextFormat",
"//submodules/ChatPresentationInterfaceState",
"//submodules/TelegramUI/Components/TextNodeWithEntities",
"//submodules/TelegramUI/Components/AnimationCache",
"//submodules/TelegramUI/Components/MultiAnimationRenderer",
"//submodules/TelegramUI/Components/Chat/AccessoryPanelNode",
"//submodules/TelegramUI/Components/CompositeTextNode",
"//submodules/TelegramNotices",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,315 @@
import Foundation
import UIKit
import AsyncDisplayKit
import TelegramCore
import Postbox
import SwiftSignalKit
import Display
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import LocalizedPeerData
import PhotoResources
import TelegramStringFormatting
import TextFormat
import ChatPresentationInterfaceState
import TextNodeWithEntities
import AnimationCache
import MultiAnimationRenderer
import AccessoryPanelNode
import TelegramNotices
import AppBundle
import CompositeTextNode
public final class SuggestPostAccessoryPanelNode: AccessoryPanelNode {
private var previousMediaReference: AnyMediaReference?
public let closeButton: HighlightableButtonNode
public let lineNode: ASImageNode
public let iconView: UIImageView
public let titleNode: CompositeTextNode
public let textNode: ImmediateTextNodeWithEntities
private let actionArea: AccessibilityAreaNode
private let context: AccountContext
public var theme: PresentationTheme
public var strings: PresentationStrings
private var textIsOptions: Bool = false
private var validLayout: (size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState)?
private var inlineTextStarImage: UIImage?
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, animationCache: AnimationCache?, animationRenderer: MultiAnimationRenderer?) {
self.context = context
self.theme = theme
self.strings = strings
self.closeButton = HighlightableButtonNode()
self.closeButton.accessibilityLabel = strings.VoiceOver_DiscardPreparedContent
self.closeButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0)
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(theme), for: [])
self.closeButton.displaysAsynchronously = false
self.lineNode = ASImageNode()
self.lineNode.displayWithoutProcessing = true
self.lineNode.displaysAsynchronously = false
self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme)
self.iconView = UIImageView()
self.iconView.image = UIImage(bundleImageName: "Chat/Input/Accessory Panels/SuggestPostIcon")?.withRenderingMode(.alwaysTemplate)
self.iconView.tintColor = theme.chat.inputPanel.panelControlAccentColor
self.titleNode = CompositeTextNode()
self.textNode = ImmediateTextNodeWithEntities()
self.textNode.maximumNumberOfLines = 1
self.textNode.displaysAsynchronously = false
self.textNode.insets = UIEdgeInsets(top: 3.0, left: 0.0, bottom: 3.0, right: 0.0)
self.textNode.visibility = true
self.textNode.spoilerColor = self.theme.chat.inputPanel.secondaryTextColor
if let animationCache = animationCache, let animationRenderer = animationRenderer {
self.textNode.arguments = TextNodeWithEntities.Arguments(
context: context,
cache: animationCache,
renderer: animationRenderer,
placeholderColor: theme.list.mediaPlaceholderColor,
attemptSynchronous: false
)
}
self.actionArea = AccessibilityAreaNode()
super.init()
self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside])
self.addSubnode(self.closeButton)
self.addSubnode(self.lineNode)
self.view.addSubview(self.iconView)
self.addSubnode(self.titleNode)
self.addSubnode(self.textNode)
self.addSubnode(self.actionArea)
//TODO:localize
var titleText: [CompositeTextNode.Component] = []
titleText.append(.text(NSAttributedString(string: "Suggest a post below", font: Font.medium(15.0), textColor: self.theme.chat.inputPanel.panelControlAccentColor)))
self.titleNode.components = titleText
}
deinit {
}
override public func didLoad() {
super.didLoad()
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
}
override public func animateIn() {
self.iconView.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2)
}
override public func animateOut() {
self.iconView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false)
}
override public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
self.updateThemeAndStrings(theme: theme, strings: strings, force: false)
}
private func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, force: Bool) {
if self.theme !== theme || force {
self.theme = theme
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(theme), for: [])
self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme)
self.iconView.tintColor = theme.chat.inputPanel.panelControlAccentColor
self.titleNode.components = self.titleNode.components.map { item in
switch item {
case let .text(text):
let updatedText = NSMutableAttributedString(attributedString: text)
updatedText.addAttribute(.foregroundColor, value: theme.chat.inputPanel.panelControlAccentColor, range: NSRange(location: 0, length: updatedText.length))
return .text(updatedText)
case let .icon(icon):
if let iconImage = generateTintedImage(image: icon, color: theme.chat.inputPanel.panelControlAccentColor) {
return .icon(iconImage)
} else {
return .icon(icon)
}
}
}
if let text = self.textNode.attributedText {
let updatedText = NSMutableAttributedString(attributedString: text)
updatedText.addAttribute(.foregroundColor, value: self.textIsOptions ? self.theme.chat.inputPanel.secondaryTextColor : self.theme.chat.inputPanel.primaryTextColor, range: NSRange(location: 0, length: updatedText.length))
self.textNode.attributedText = updatedText
}
self.textNode.spoilerColor = self.theme.chat.inputPanel.secondaryTextColor
if let (size, inset, interfaceState) = self.validLayout {
self.updateState(size: size, inset: inset, interfaceState: interfaceState)
}
}
}
override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
return CGSize(width: constrainedSize.width, height: 45.0)
}
override public func updateState(size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState) {
self.validLayout = (size, inset, interfaceState)
let bounds = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: 45.0))
let leftInset: CGFloat = 55.0 + inset
let textLineInset: CGFloat = 10.0
let rightInset: CGFloat = 55.0
let textRightInset: CGFloat = 20.0
let closeButtonSize = CGSize(width: 44.0, height: bounds.height)
let closeButtonFrame = CGRect(origin: CGPoint(x: bounds.width - closeButtonSize.width - inset, y: 2.0), size: closeButtonSize)
self.closeButton.frame = closeButtonFrame
self.actionArea.frame = CGRect(origin: CGPoint(x: leftInset, y: 2.0), size: CGSize(width: closeButtonFrame.minX - leftInset, height: bounds.height))
if self.lineNode.supernode == self {
self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0))
}
if let icon = self.iconView.image {
self.iconView.frame = CGRect(origin: CGPoint(x: 7.0 + inset, y: 10.0), size: icon.size)
}
let imageTextInset: CGFloat = 0.0
let titleSize = self.titleNode.update(constrainedSize: CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
if self.titleNode.supernode == self {
self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 7.0), size: titleSize)
}
let textFont = Font.regular(15.0)
var inlineTextStarImage: UIImage?
if let current = self.inlineTextStarImage {
inlineTextStarImage = current
} else {
if let image = UIImage(bundleImageName: "Premium/Stars/StarSmall") {
let starInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
inlineTextStarImage = generateImage(CGSize(width: starInsets.left + image.size.width + starInsets.right, height: image.size.height), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
UIGraphicsPushContext(context)
defer {
UIGraphicsPopContext()
}
image.draw(at: CGPoint(x: starInsets.left, y: starInsets.top))
})?.withRenderingMode(.alwaysOriginal)
self.inlineTextStarImage = inlineTextStarImage
}
}
let textString: NSAttributedString
if let postSuggestionState = interfaceState.interfaceState.postSuggestionState, postSuggestionState.price != 0 {
if let timestamp = postSuggestionState.timestamp {
let timeString = humanReadableStringForTimestamp(strings: interfaceState.strings, dateTimeFormat: interfaceState.dateTimeFormat, timestamp: timestamp, alwaysShowTime: true, allowYesterday: false, format: HumanReadableStringFormat(
dateFormatString: { value in
return PresentationStrings.FormattedString(string: interfaceState.strings.SuggestPost_SetTimeFormat_Date(value).string, ranges: [])
},
tomorrowFormatString: { value in
return PresentationStrings.FormattedString(string: interfaceState.strings.SuggestPost_SetTimeFormat_TomorrowAt(value).string, ranges: [])
},
todayFormatString: { value in
return PresentationStrings.FormattedString(string: interfaceState.strings.SuggestPost_SetTimeFormat_TodayAt(value).string, ranges: [])
},
yesterdayFormatString: { value in
return PresentationStrings.FormattedString(string: interfaceState.strings.SuggestPost_SetTimeFormat_TodayAt(value).string, ranges: [])
}
)).string
textString = NSAttributedString(string: "#\(postSuggestionState.price) 📅 \(timeString)", font: textFont, textColor: self.theme.chat.inputPanel.primaryTextColor)
} else {
textString = NSAttributedString(string: "#\(postSuggestionState.price) for publishing anytime", font: textFont, textColor: self.theme.chat.inputPanel.primaryTextColor)
}
} else {
textString = NSAttributedString(string: "Tap to offer a price for publishing", font: textFont, textColor: self.theme.chat.inputPanel.primaryTextColor)
}
let mutableTextString = NSMutableAttributedString(attributedString: textString)
if let range = mutableTextString.string.range(of: "#"), let starImage = inlineTextStarImage {
final class RunDelegateData {
let ascent: CGFloat
let descent: CGFloat
let width: CGFloat
init(ascent: CGFloat, descent: CGFloat, width: CGFloat) {
self.ascent = ascent
self.descent = descent
self.width = width
}
}
let runDelegateData = RunDelegateData(
ascent: Font.regular(15.0).ascender,
descent: Font.regular(15.0).descender,
width: starImage.size.width + 2.0
)
var callbacks = CTRunDelegateCallbacks(
version: kCTRunDelegateCurrentVersion,
dealloc: { dataRef in
Unmanaged<RunDelegateData>.fromOpaque(dataRef).release()
},
getAscent: { dataRef in
let data = Unmanaged<RunDelegateData>.fromOpaque(dataRef)
return data.takeUnretainedValue().ascent
},
getDescent: { dataRef in
let data = Unmanaged<RunDelegateData>.fromOpaque(dataRef)
return data.takeUnretainedValue().descent
},
getWidth: { dataRef in
let data = Unmanaged<RunDelegateData>.fromOpaque(dataRef)
return data.takeUnretainedValue().width
}
)
if let runDelegate = CTRunDelegateCreate(&callbacks, Unmanaged.passRetained(runDelegateData).toOpaque()) {
mutableTextString.addAttribute(NSAttributedString.Key(kCTRunDelegateAttributeName as String), value: runDelegate, range: NSRange(range, in: mutableTextString.string))
}
mutableTextString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: mutableTextString.string))
mutableTextString.addAttribute(.foregroundColor, value: UIColor(rgb: 0xffffff), range: NSRange(range, in: mutableTextString.string))
mutableTextString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: mutableTextString.string))
}
self.textNode.attributedText = mutableTextString
let textSize = self.textNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
let textFrame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset - self.textNode.insets.left, y: 25.0 - self.textNode.insets.top), size: textSize)
if self.textNode.supernode == self {
self.textNode.frame = textFrame
}
}
@objc private func closePressed() {
if let dismiss = self.dismiss {
dismiss()
}
}
private var previousTapTimestamp: Double?
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
let timestamp = CFAbsoluteTimeGetCurrent()
if let previousTapTimestamp = self.previousTapTimestamp, previousTapTimestamp + 1.0 > timestamp {
return
}
self.previousTapTimestamp = CFAbsoluteTimeGetCurrent()
self.interfaceInteraction?.presentSuggestPostOptions()
Queue.mainQueue().after(1.5) {
self.updateThemeAndStrings(theme: self.theme, strings: self.strings, force: true)
}
}
}
}

View File

@ -335,6 +335,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
}, presentForwardOptions: { _ in }, presentForwardOptions: { _ in
}, presentReplyOptions: { _ in }, presentReplyOptions: { _ in
}, presentLinkOptions: { _ in }, presentLinkOptions: { _ in
}, presentSuggestPostOptions: {
}, shareSelectedMessages: { }, shareSelectedMessages: {
shareMessages() shareMessages()
}, updateTextInputStateAndMode: { _ in }, updateTextInputStateAndMode: { _ in
@ -410,7 +411,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
}, joinGroupCall: { _ in }, joinGroupCall: { _ in
}, presentInviteMembers: { }, presentInviteMembers: {
}, presentGigagroupHelp: { }, presentGigagroupHelp: {
}, openSuggestPost: { }, openMonoforum: {
}, editMessageMedia: { _, _ in }, editMessageMedia: { _, _ in
}, updateShowCommands: { _ in }, updateShowCommands: { _ in
}, updateShowSendAsPeers: { _ in }, updateShowSendAsPeers: { _ in
@ -429,6 +430,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
}, addDoNotTranslateLanguage: { _ in }, addDoNotTranslateLanguage: { _ in
}, hideTranslationPanel: { }, hideTranslationPanel: {
}, openPremiumGift: { }, openPremiumGift: {
}, openSuggestPost: {
}, openPremiumRequiredForMessaging: { }, openPremiumRequiredForMessaging: {
}, openStarsPurchase: { _ in }, openStarsPurchase: { _ in
}, openMessagePayment: { }, openMessagePayment: {
@ -1252,6 +1254,7 @@ private enum InfoSection: Int, CaseIterable {
case peerInfoTrailing case peerInfoTrailing
case peerSettings case peerSettings
case peerMembers case peerMembers
case channelMonoforum
case botAffiliateProgram case botAffiliateProgram
} }
@ -1698,6 +1701,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
let ItemMemberRequests = 8 let ItemMemberRequests = 8
let ItemBalance = 9 let ItemBalance = 9
let ItemEdit = 10 let ItemEdit = 10
let ItemPeerPersonalChannel = 11
if let _ = data.threadData { if let _ = data.threadData {
let mainUsername: String let mainUsername: String
@ -1935,6 +1939,21 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
interaction.openEditing() interaction.openEditing()
})) }))
} }
if let personalChannel = data.personalChannel {
let peerId = personalChannel.peer.peerId
items[.channelMonoforum]?.append(PeerInfoScreenPersonalChannelItem(id: ItemPeerPersonalChannel, context: context, data: personalChannel, controller: { [weak interaction] in
guard let interaction else {
return nil
}
return interaction.getController()
}, action: { [weak interaction] in
guard let interaction else {
return
}
interaction.openChat(peerId)
}))
}
} }
} }
} else if let group = data.peer as? TelegramGroup { } else if let group = data.peer as? TelegramGroup {

View File

@ -373,7 +373,7 @@ final class PostSuggestionsSettingsScreenComponent: Component {
} }
let currentAmount: StarsAmount = StarsAmount(value: Int64(self.starCount), nanos: 0) let currentAmount: StarsAmount = StarsAmount(value: Int64(self.starCount), nanos: 0)
let starsScreen = component.context.sharedContext.makeStarsWithdrawalScreen(context: component.context, subject: .enterAmount(current: currentAmount, minValue: StarsAmount(value: 0, nanos: 0), fractionAfterCommission: component.paidMessageCommissionPermille / 10, kind: .postSuggestion), completion: { [weak self] amount in let starsScreen = component.context.sharedContext.makeStarsWithdrawalScreen(context: component.context, subject: .enterAmount(current: currentAmount, minValue: StarsAmount(value: 0, nanos: 0), fractionAfterCommission: component.paidMessageCommissionPermille / 10, kind: .postSuggestion, completion: { [weak self] amount in
guard let self else { guard let self else {
return return
} }
@ -382,7 +382,7 @@ final class PostSuggestionsSettingsScreenComponent: Component {
if !self.isUpdating { if !self.isUpdating {
self.state?.updated(transition: .immediate) self.state?.updated(transition: .immediate)
} }
}) }))
environment.controller()?.push(starsScreen) environment.controller()?.push(starsScreen)
}, },
openPremiumInfo: nil openPremiumInfo: nil

View File

@ -594,6 +594,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
strongSelf.controller?.presentInGlobalOverlay(contextController) strongSelf.controller?.presentInGlobalOverlay(contextController)
}, presentReplyOptions: { _ in }, presentReplyOptions: { _ in
}, presentLinkOptions: { _ in }, presentLinkOptions: { _ in
}, presentSuggestPostOptions: {
}, shareSelectedMessages: { }, shareSelectedMessages: {
}, updateTextInputStateAndMode: { [weak self] f in }, updateTextInputStateAndMode: { [weak self] f in
if let strongSelf = self { if let strongSelf = self {
@ -799,7 +800,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
}, joinGroupCall: { _ in }, joinGroupCall: { _ in
}, presentInviteMembers: { }, presentInviteMembers: {
}, presentGigagroupHelp: { }, presentGigagroupHelp: {
}, openSuggestPost: { }, openMonoforum: {
}, editMessageMedia: { _, _ in }, editMessageMedia: { _, _ in
}, updateShowCommands: { _ in }, updateShowCommands: { _ in
}, updateShowSendAsPeers: { _ in }, updateShowSendAsPeers: { _ in
@ -817,6 +818,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
}, addDoNotTranslateLanguage: { _ in }, addDoNotTranslateLanguage: { _ in
}, hideTranslationPanel: { }, hideTranslationPanel: {
}, openPremiumGift: { }, openPremiumGift: {
}, openSuggestPost: {
}, openPremiumRequiredForMessaging: { }, openPremiumRequiredForMessaging: {
}, openStarsPurchase: { _ in }, openStarsPurchase: { _ in
}, openMessagePayment: { }, openMessagePayment: {

View File

@ -37,6 +37,7 @@ swift_library(
"//submodules/Components/BundleIconComponent", "//submodules/Components/BundleIconComponent",
"//submodules/PasswordSetupUI", "//submodules/PasswordSetupUI",
"//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController", "//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController",
"//submodules/TelegramUI/Components/ChatScheduleTimeController",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -21,6 +21,8 @@ import PresentationDataUtils
import ListSectionComponent import ListSectionComponent
import TelegramStringFormatting import TelegramStringFormatting
import UndoUI import UndoUI
import ListActionItemComponent
import ChatScheduleTimeController
private let amountTag = GenericComponentViewTag() private let amountTag = GenericComponentViewTag()
@ -29,25 +31,22 @@ private final class SheetContent: CombinedComponent {
let context: AccountContext let context: AccountContext
let mode: StarsWithdrawScreen.Mode let mode: StarsWithdrawScreen.Mode
let controller: () -> ViewController?
let dismiss: () -> Void let dismiss: () -> Void
init( init(
context: AccountContext, context: AccountContext,
mode: StarsWithdrawScreen.Mode, mode: StarsWithdrawScreen.Mode,
controller: @escaping () -> ViewController?,
dismiss: @escaping () -> Void dismiss: @escaping () -> Void
) { ) {
self.context = context self.context = context
self.mode = mode self.mode = mode
self.controller = controller
self.dismiss = dismiss self.dismiss = dismiss
} }
static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool { static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.mode != rhs.mode {
return false
}
return true return true
} }
@ -56,6 +55,7 @@ private final class SheetContent: CombinedComponent {
let title = Child(Text.self) let title = Child(Text.self)
let amountSection = Child(ListSectionComponent.self) let amountSection = Child(ListSectionComponent.self)
let amountAdditionalLabel = Child(MultilineTextComponent.self) let amountAdditionalLabel = Child(MultilineTextComponent.self)
let timestampSection = Child(ListSectionComponent.self)
let button = Child(ButtonComponent.self) let button = Child(ButtonComponent.self)
let balanceTitle = Child(MultilineTextComponent.self) let balanceTitle = Child(MultilineTextComponent.self)
let balanceValue = Child(MultilineTextComponent.self) let balanceValue = Child(MultilineTextComponent.self)
@ -66,6 +66,8 @@ private final class SheetContent: CombinedComponent {
let component = context.component let component = context.component
let state = context.state let state = context.state
state.component = component
let controller = environment.controller let controller = environment.controller
let theme = environment.theme.withModalBlocksBackground() let theme = environment.theme.withModalBlocksBackground()
@ -111,7 +113,7 @@ private final class SheetContent: CombinedComponent {
let resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) let resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 })
switch component.mode { switch component.mode {
case let .withdraw(status): case let .withdraw(status, _):
titleString = environment.strings.Stars_Withdraw_Title titleString = environment.strings.Stars_Withdraw_Title
amountTitle = environment.strings.Stars_Withdraw_AmountTitle amountTitle = environment.strings.Stars_Withdraw_AmountTitle
amountPlaceholder = environment.strings.Stars_Withdraw_AmountPlaceholder amountPlaceholder = environment.strings.Stars_Withdraw_AmountPlaceholder
@ -144,20 +146,28 @@ private final class SheetContent: CombinedComponent {
minAmount = StarsAmount(value: 1, nanos: 0) minAmount = StarsAmount(value: 1, nanos: 0)
maxAmount = withdrawConfiguration.maxPaidMediaAmount.flatMap { StarsAmount(value: $0, nanos: 0) } maxAmount = withdrawConfiguration.maxPaidMediaAmount.flatMap { StarsAmount(value: $0, nanos: 0) }
case let .starGiftResell(_, update): case let .starGiftResell(_, update, _):
titleString = update ? environment.strings.Stars_SellGift_EditTitle : environment.strings.Stars_SellGift_Title titleString = update ? environment.strings.Stars_SellGift_EditTitle : environment.strings.Stars_SellGift_Title
amountTitle = environment.strings.Stars_SellGift_AmountTitle amountTitle = environment.strings.Stars_SellGift_AmountTitle
amountPlaceholder = environment.strings.Stars_SellGift_AmountPlaceholder amountPlaceholder = environment.strings.Stars_SellGift_AmountPlaceholder
minAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMinAmount, nanos: 0) minAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMinAmount, nanos: 0)
maxAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMaxAmount, nanos: 0) maxAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMaxAmount, nanos: 0)
case let .paidMessages(_, minAmountValue, _, _): case let .paidMessages(_, minAmountValue, _, _, _):
titleString = environment.strings.Stars_SendMessage_AdjustmentTitle titleString = environment.strings.Stars_SendMessage_AdjustmentTitle
amountTitle = environment.strings.Stars_SendMessage_AdjustmentSectionHeader amountTitle = environment.strings.Stars_SendMessage_AdjustmentSectionHeader
amountPlaceholder = environment.strings.Stars_SendMessage_AdjustmentPlaceholder amountPlaceholder = environment.strings.Stars_SendMessage_AdjustmentPlaceholder
minAmount = StarsAmount(value: minAmountValue, nanos: 0) minAmount = StarsAmount(value: minAmountValue, nanos: 0)
maxAmount = StarsAmount(value: resaleConfiguration.paidMessageMaxAmount, nanos: 0) maxAmount = StarsAmount(value: resaleConfiguration.paidMessageMaxAmount, nanos: 0)
case .suggestedPost:
//TODO:localize
titleString = "Suggest Terms"
amountTitle = "ENTER A PRICE IN STARS"
amountPlaceholder = environment.strings.Stars_SendMessage_AdjustmentPlaceholder
minAmount = StarsAmount(value: 1, nanos: 0)
maxAmount = StarsAmount(value: resaleConfiguration.paidMessageMaxAmount, nanos: 0)
} }
let title = title.update( let title = title.update(
@ -176,7 +186,7 @@ private final class SheetContent: CombinedComponent {
balance = state.balance balance = state.balance
} else if case .reaction = component.mode { } else if case .reaction = component.mode {
balance = state.balance balance = state.balance
} else if case let .withdraw(starsState) = component.mode { } else if case let .withdraw(starsState, _) = component.mode {
balance = starsState.balances.availableBalance balance = starsState.balances.availableBalance
} else { } else {
balance = nil balance = nil
@ -264,7 +274,7 @@ private final class SheetContent: CombinedComponent {
} }
} }
)) ))
case let .reaction(starsToTop): case let .reaction(starsToTop, _):
let amountInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_SendStars_AmountInfo("\(starsToTop ?? 0)").string, attributes: amountMarkdownAttributes, textAlignment: .natural)) let amountInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_SendStars_AmountInfo("\(starsToTop ?? 0)").string, attributes: amountMarkdownAttributes, textAlignment: .natural))
amountFooter = AnyComponent(MultilineTextComponent( amountFooter = AnyComponent(MultilineTextComponent(
text: .plain(amountInfoString), text: .plain(amountInfoString),
@ -288,7 +298,7 @@ private final class SheetContent: CombinedComponent {
text: .plain(amountInfoString), text: .plain(amountInfoString),
maximumNumberOfLines: 0 maximumNumberOfLines: 0
)) ))
case let .paidMessages(_, _, fractionAfterCommission, _): case let .paidMessages(_, _, fractionAfterCommission, _, _):
let amountInfoString: NSAttributedString let amountInfoString: NSAttributedString
if let value = state.amount?.value, value > 0 { if let value = state.amount?.value, value > 0 {
let fullValue: Int64 = Int64(value) * 1_000_000_000 * Int64(fractionAfterCommission) / 100 let fullValue: Int64 = Int64(value) * 1_000_000_000 * Int64(fractionAfterCommission) / 100
@ -301,6 +311,16 @@ private final class SheetContent: CombinedComponent {
text: .plain(amountInfoString), text: .plain(amountInfoString),
maximumNumberOfLines: 0 maximumNumberOfLines: 0
)) ))
case let .suggestedPost(mode, _, _, _):
switch mode {
case let .sender(channel):
//TODO:localize
let amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString("Choose how many Stars you want to offer \(channel.compactDisplayTitle) to publish this message.", attributes: amountMarkdownAttributes, textAlignment: .natural))
amountFooter = AnyComponent(MultilineTextComponent(
text: .plain(amountInfoString),
maximumNumberOfLines: 0
))
}
default: default:
amountFooter = nil amountFooter = nil
} }
@ -358,6 +378,103 @@ private final class SheetContent: CombinedComponent {
context.add(amountAdditionalLabel context.add(amountAdditionalLabel
.position(CGPoint(x: context.availableSize.width - amountAdditionalLabel.size.width / 2.0 - sideInset - 16.0, y: contentSize.height - amountAdditionalLabel.size.height / 2.0))) .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 {
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 timestampFooter = AnyComponent(MultilineTextComponent(
text: .plain(timestampFooterString),
maximumNumberOfLines: 0
))
let timeString: String
if let timestamp = state.timestamp {
timeString = humanReadableStringForTimestamp(strings: strings, dateTimeFormat: presentationData.dateTimeFormat, timestamp: timestamp, alwaysShowTime: true, allowYesterday: true, format: HumanReadableStringFormat(
dateFormatString: { value in
return PresentationStrings.FormattedString(string: strings.SuggestPost_SetTimeFormat_Date(value).string, ranges: [])
},
tomorrowFormatString: { value in
return PresentationStrings.FormattedString(string: strings.SuggestPost_SetTimeFormat_TomorrowAt(value).string, ranges: [])
},
todayFormatString: { value in
return PresentationStrings.FormattedString(string: strings.SuggestPost_SetTimeFormat_TodayAt(value).string, ranges: [])
},
yesterdayFormatString: { value in
return PresentationStrings.FormattedString(string: strings.SuggestPost_SetTimeFormat_TodayAt(value).string, ranges: [])
}
)).string
} else {
timeString = "Anytime"
}
let timestampSection = timestampSection.update(
component: ListSectionComponent(
theme: theme,
header: nil,
footer: timestampFooter,
items: [AnyComponentWithIdentity(
id: "timestamp",
component: AnyComponent(ListActionItemComponent(
theme: theme,
title: AnyComponent(VStack([
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: "Time",
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
textColor: environment.theme.list.itemPrimaryTextColor
)),
maximumNumberOfLines: 1
))),
], alignment: .left, spacing: 2.0)),
icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: timeString,
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
textColor: environment.theme.list.itemSecondaryTextColor
)),
maximumNumberOfLines: 1
)))),
accessory: .arrow,
action: { [weak state] _ in
guard let state else {
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
guard let state else {
return
}
state.timestamp = time == 0 ? nil : time
state.updated(transition: .immediate)
})
component.controller()?.view.endEditing(true)
component.controller()?.present(controller, in: .window(.root))
}
))
)]
),
environment: {},
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude),
transition: context.transition
)
context.add(timestampSection
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + timestampSection.size.height / 2.0))
.clipsToBounds(true)
.cornerRadius(10.0)
)
contentSize.height += timestampSection.size.height
}
contentSize.height += 32.0 contentSize.height += 32.0
let buttonString: String let buttonString: String
@ -371,6 +488,16 @@ private final class SheetContent: CombinedComponent {
} }
} else if case .paidMessages = component.mode { } else if case .paidMessages = component.mode {
buttonString = environment.strings.Stars_SendMessage_AdjustmentAction buttonString = environment.strings.Stars_SendMessage_AdjustmentAction
} else if case let .suggestedPost(mode, _, _, _) = component.mode {
//TODO:localize
switch mode {
case .sender:
if let amount = state.amount {
buttonString = "Offer # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))"
} else {
buttonString = "Offer"
}
}
} else if let amount = state.amount { } else if let amount = state.amount {
buttonString = "\(environment.strings.Stars_Withdraw_Withdraw) # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))" buttonString = "\(environment.strings.Stars_Withdraw_Withdraw) # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))"
} else { } else {
@ -393,7 +520,7 @@ private final class SheetContent: CombinedComponent {
let amount = state.amount ?? StarsAmount.zero let amount = state.amount ?? StarsAmount.zero
if amount > StarsAmount.zero { if amount > StarsAmount.zero {
isButtonEnabled = true isButtonEnabled = true
} else if case let .paidMessages(_, minValue, _, _) = context.component.mode { } else if case let .paidMessages(_, minValue, _, _, _) = context.component.mode {
if minValue <= 0 { if minValue <= 0 {
isButtonEnabled = true isButtonEnabled = true
} }
@ -414,11 +541,27 @@ private final class SheetContent: CombinedComponent {
isEnabled: isButtonEnabled, isEnabled: isButtonEnabled,
displaysProgress: false, displaysProgress: false,
action: { [weak state] in action: { [weak state] in
if let controller = controller() as? StarsWithdrawScreen, let amount = state?.amount { if let controller = controller() as? StarsWithdrawScreen, let state, let amount = state.amount {
if let minAmount, amount < minAmount { if let minAmount, amount < minAmount {
controller.presentMinAmountTooltip(minAmount.value) controller.presentMinAmountTooltip(minAmount.value)
} else { } else {
controller.completion(amount.value) switch state.mode {
case let .withdraw(_, completion):
completion(amount.value)
case let .accountWithdraw(completion):
completion(amount.value)
case let .paidMedia(_, completion):
completion(amount.value)
case let .reaction(_, completion):
completion(amount.value)
case let .starGiftResell(_, _, completion):
completion(amount.value)
case let .paidMessages(_, _, _, _, completion):
completion(amount.value)
case let .suggestedPost(_, _, _, completion):
completion(amount.value, state.timestamp)
}
controller.dismissAnimated() controller.dismissAnimated()
} }
} }
@ -442,10 +585,13 @@ private final class SheetContent: CombinedComponent {
} }
final class State: ComponentState { final class State: ComponentState {
private let context: AccountContext fileprivate let context: AccountContext
private let mode: StarsWithdrawScreen.Mode fileprivate let mode: StarsWithdrawScreen.Mode
fileprivate var component: SheetContent
fileprivate var amount: StarsAmount? fileprivate var amount: StarsAmount?
fileprivate var timestamp: Int32?
fileprivate var balance: StarsAmount? fileprivate var balance: StarsAmount?
private var stateDisposable: Disposable? private var stateDisposable: Disposable?
@ -454,27 +600,30 @@ private final class SheetContent: CombinedComponent {
var cachedStarImage: (UIImage, PresentationTheme)? var cachedStarImage: (UIImage, PresentationTheme)?
var cachedChevronImage: (UIImage, PresentationTheme)? var cachedChevronImage: (UIImage, PresentationTheme)?
init( init(component: SheetContent) {
context: AccountContext, self.context = component.context
mode: StarsWithdrawScreen.Mode self.mode = component.mode
) { self.component = component
self.context = context
self.mode = mode
var amount: StarsAmount? var amount: StarsAmount?
switch mode { switch mode {
case let .withdraw(stats): case let .withdraw(stats, _):
amount = StarsAmount(value: stats.balances.availableBalance.value, nanos: 0) amount = StarsAmount(value: stats.balances.availableBalance.value, nanos: 0)
case .accountWithdraw: case .accountWithdraw:
amount = context.starsContext?.currentState.flatMap { StarsAmount(value: $0.balance.value, nanos: 0) } amount = context.starsContext?.currentState.flatMap { StarsAmount(value: $0.balance.value, nanos: 0) }
case let .paidMedia(initialValue): case let .paidMedia(initialValue, _):
amount = initialValue.flatMap { StarsAmount(value: $0, nanos: 0) } amount = initialValue.flatMap { StarsAmount(value: $0, nanos: 0) }
case .reaction: case .reaction:
amount = nil amount = nil
case .starGiftResell: case .starGiftResell:
amount = nil amount = nil
case let .paidMessages(initialValue, _, _, _): case let .paidMessages(initialValue, _, _, _, _):
amount = StarsAmount(value: initialValue, nanos: 0) amount = StarsAmount(value: initialValue, nanos: 0)
case let .suggestedPost(_, initialValue, initialTimestamp, _):
if initialValue != 0 {
amount = StarsAmount(value: initialValue, nanos: 0)
}
self.timestamp = initialTimestamp
} }
self.amount = amount self.amount = amount
@ -491,7 +640,7 @@ private final class SheetContent: CombinedComponent {
}) })
} }
if case let .starGiftResell(giftToMatch, update) = self.mode { if case let .starGiftResell(giftToMatch, update, _) = self.mode {
if update { if update {
if let resellStars = giftToMatch.resellStars { if let resellStars = giftToMatch.resellStars {
self.amount = StarsAmount(value: resellStars, nanos: 0) self.amount = StarsAmount(value: resellStars, nanos: 0)
@ -528,7 +677,7 @@ private final class SheetContent: CombinedComponent {
} }
func makeState() -> State { func makeState() -> State {
return State(context: self.context, mode: self.mode) return State(component: self)
} }
} }
@ -547,12 +696,6 @@ private final class StarsWithdrawSheetComponent: CombinedComponent {
} }
static func ==(lhs: StarsWithdrawSheetComponent, rhs: StarsWithdrawSheetComponent) -> Bool { static func ==(lhs: StarsWithdrawSheetComponent, rhs: StarsWithdrawSheetComponent) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.mode != rhs.mode {
return false
}
return true return true
} }
@ -570,6 +713,9 @@ private final class StarsWithdrawSheetComponent: CombinedComponent {
content: AnyComponent<EnvironmentType>(SheetContent( content: AnyComponent<EnvironmentType>(SheetContent(
context: context.component.context, context: context.component.context,
mode: context.component.mode, mode: context.component.mode,
controller: {
return controller()
},
dismiss: { dismiss: {
animateOut.invoke(Action { _ in animateOut.invoke(Action { _ in
if let controller = controller() { if let controller = controller() {
@ -620,27 +766,29 @@ private final class StarsWithdrawSheetComponent: CombinedComponent {
} }
public final class StarsWithdrawScreen: ViewControllerComponentContainer { public final class StarsWithdrawScreen: ViewControllerComponentContainer {
public enum Mode: Equatable { public enum Mode {
case withdraw(StarsRevenueStats) public enum SuggestedPostMode {
case accountWithdraw case sender(channel: EnginePeer)
case paidMedia(Int64?) }
case reaction(Int64?)
case starGiftResell(StarGift.UniqueGift, Bool) case withdraw(StarsRevenueStats, completion: (Int64) -> Void)
case paidMessages(current: Int64, minValue: Int64, fractionAfterCommission: Int, kind: StarsWithdrawalScreenSubject.PaidMessageKind) case accountWithdraw(completion: (Int64) -> Void)
case paidMedia(Int64?, completion: (Int64) -> Void)
case reaction(Int64?, completion: (Int64) -> Void)
case starGiftResell(StarGift.UniqueGift, Bool, completion: (Int64) -> Void)
case paidMessages(current: Int64, minValue: Int64, fractionAfterCommission: Int, kind: StarsWithdrawalScreenSubject.PaidMessageKind, completion: (Int64) -> Void)
case suggestedPost(mode: SuggestedPostMode, price: Int64, timestamp: Int32?, completion: (Int64, Int32?) -> Void)
} }
private let context: AccountContext private let context: AccountContext
private let mode: StarsWithdrawScreen.Mode private let mode: StarsWithdrawScreen.Mode
fileprivate let completion: (Int64) -> Void
public init( public init(
context: AccountContext, context: AccountContext,
mode: StarsWithdrawScreen.Mode, mode: StarsWithdrawScreen.Mode
completion: @escaping (Int64) -> Void
) { ) {
self.context = context self.context = context
self.mode = mode self.mode = mode
self.completion = completion
super.init( super.init(
context: context, context: context,

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "suggest_30.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "suggest_24.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -306,6 +306,9 @@ extension ChatControllerImpl {
if let requestsState = previousState.requestsState, requestsState.count > 0 && !previousInvitationRequestsPeersDismissed { if let requestsState = previousState.requestsState, requestsState.count > 0 && !previousInvitationRequestsPeersDismissed {
didDisplayActionsPanel = true didDisplayActionsPanel = true
} }
if previousState.removePaidMessageFeeData != nil {
didDisplayActionsPanel = true
}
var displayActionsPanel = false var displayActionsPanel = false
if let contactStatus = contentData.state.contactStatus, !contactStatus.isEmpty, let peerStatusSettings = contactStatus.peerStatusSettings { if let contactStatus = contentData.state.contactStatus, !contactStatus.isEmpty, let peerStatusSettings = contactStatus.peerStatusSettings {
@ -339,6 +342,9 @@ extension ChatControllerImpl {
if let requestsState = contentData.state.requestsState, requestsState.count > 0 && !invitationRequestsPeersDismissed { if let requestsState = contentData.state.requestsState, requestsState.count > 0 && !invitationRequestsPeersDismissed {
displayActionsPanel = true displayActionsPanel = true
} }
if contentData.state.removePaidMessageFeeData != nil {
displayActionsPanel = true
}
if displayActionsPanel != didDisplayActionsPanel { if displayActionsPanel != didDisplayActionsPanel {
animated = true animated = true
@ -1894,6 +1900,11 @@ extension ChatControllerImpl {
return return
} }
presentChatLinkOptions(selfController: self, sourceNode: sourceNode) presentChatLinkOptions(selfController: self, sourceNode: sourceNode)
}, presentSuggestPostOptions: { [weak self] in
guard let self else {
return
}
self.presentSuggestPostOptions()
}, shareSelectedMessages: { [weak self] in }, shareSelectedMessages: { [weak self] in
if let strongSelf = self, let selectedIds = strongSelf.presentationInterfaceState.interfaceState.selectionState?.selectedIds, !selectedIds.isEmpty { if let strongSelf = self, let selectedIds = strongSelf.presentationInterfaceState.interfaceState.selectionState?.selectedIds, !selectedIds.isEmpty {
strongSelf.commitPurposefulAction() strongSelf.commitPurposefulAction()
@ -3734,7 +3745,7 @@ extension ChatControllerImpl {
if let strongSelf = self { if let strongSelf = self {
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .info(title: nil, text: strongSelf.presentationData.strings.Conversation_GigagroupDescription, timeout: nil, customUndoText: nil), elevatedLayout: false, action: { _ in return true }), in: .current) strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .info(title: nil, text: strongSelf.presentationData.strings.Conversation_GigagroupDescription, timeout: nil, customUndoText: nil), elevatedLayout: false, action: { _ in return true }), in: .current)
} }
}, openSuggestPost: { [weak self] in }, openMonoforum: { [weak self] in
guard let self else { guard let self else {
return return
} }
@ -4084,6 +4095,22 @@ extension ChatControllerImpl {
}) })
self.push(controller) self.push(controller)
} }
}, openSuggestPost: { [weak self] in
guard let self else {
return
}
self.updateChatPresentationInterfaceState(interactive: true, { state in
var state = state
state = state.updatedInterfaceState { interfaceState in
var interfaceState = interfaceState
interfaceState = interfaceState.withUpdatedPostSuggestionState(ChatInterfaceState.PostSuggestionState(
price: 0,
timestamp: nil
))
return interfaceState
}
return state
})
}, openPremiumRequiredForMessaging: { [weak self] in }, openPremiumRequiredForMessaging: { [weak self] in
guard let self else { guard let self else {
return return

View File

@ -977,3 +977,39 @@ private func chatLinkOptions(selfController: ChatControllerImpl, sourceNode: ASD
func presentChatLinkOptions(selfController: ChatControllerImpl, sourceNode: ASDisplayNode) { func presentChatLinkOptions(selfController: ChatControllerImpl, sourceNode: ASDisplayNode) {
presentChatInputOptions(selfController: selfController, sourceNode: sourceNode, initialId: .link) presentChatInputOptions(selfController: selfController, sourceNode: sourceNode, initialId: .link)
} }
extension ChatControllerImpl {
func presentSuggestPostOptions() {
guard let channel = self.presentationInterfaceState.renderedPeer?.chatOrMonoforumMainPeer as? TelegramChannel else {
return
}
guard let postSuggestionState = self.presentationInterfaceState.interfaceState.postSuggestionState else {
return
}
self.push(self.context.sharedContext.makeStarsWithdrawalScreen(
context: self.context,
subject: .postSuggestion(
channel: .channel(channel),
current: StarsAmount(value: postSuggestionState.price, nanos: 0),
timestamp: postSuggestionState.timestamp,
completion: { [weak self] price, timestamp in
guard let self else {
return
}
self.updateChatPresentationInterfaceState(interactive: true, { state in
var state = state
state = state.updatedInterfaceState { interfaceState in
var interfaceState = interfaceState
interfaceState = interfaceState.withUpdatedPostSuggestionState(ChatInterfaceState.PostSuggestionState(
price: price,
timestamp: timestamp
))
return interfaceState
}
return state
})
}
)
))
}
}

View File

@ -4595,24 +4595,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let chatPeer = self.presentationInterfaceState.renderedPeer?.chatOrMonoforumMainPeer else { guard let chatPeer = self.presentationInterfaceState.renderedPeer?.chatOrMonoforumMainPeer else {
return return
} }
let _ = (self.context.engine.peers.getPaidMessagesRevenue(scopePeerId: peer.id, peerId: removePaidMessageFeeData.peer.id)
|> deliverOnMainQueue).start(next: { [weak self] revenue in
guard let self else {
return
}
let controller = chatMessageRemovePaymentAlertController( let controller = chatMessageRemovePaymentAlertController(
context: self.context, context: self.context,
presentationData: self.presentationData, presentationData: self.presentationData,
updatedPresentationData: self.updatedPresentationData, updatedPresentationData: self.updatedPresentationData,
peer: removePaidMessageFeeData.peer, peer: removePaidMessageFeeData.peer,
chatPeer: EnginePeer(chatPeer), chatPeer: EnginePeer(chatPeer),
amount: StarsAmount(value: 123, nanos: 0), //TODO:release amount: revenue == StarsAmount(value: 0, nanos: 0) ? nil : revenue,
navigationController: self.navigationController as? NavigationController, navigationController: self.navigationController as? NavigationController,
completion: { [weak self] refund in completion: { [weak self] refund in
guard let self else { guard let self else {
return return
} }
let _ = self let _ = self.context.engine.peers.addNoPaidMessagesException(scopePeerId: peer.id, peerId: removePaidMessageFeeData.peer.id, refundCharged: refund).start()
} }
) )
self.present(controller, in: .window(.root)) self.present(controller, in: .window(.root))
})
} else { } else {
let _ = (self.context.engine.peers.getPaidMessagesRevenue(peerId: peer.id) let _ = (self.context.engine.peers.getPaidMessagesRevenue(scopePeerId: self.context.account.peerId, peerId: peer.id)
|> deliverOnMainQueue).start(next: { [weak self] revenue in |> deliverOnMainQueue).start(next: { [weak self] revenue in
guard let self else { guard let self else {
return return
@ -4629,7 +4637,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let self else { guard let self else {
return return
} }
let _ = self.context.engine.peers.addNoPaidMessagesException(peerId: peer.id, refundCharged: refund).start() let _ = self.context.engine.peers.addNoPaidMessagesException(scopePeerId: self.context.account.peerId, peerId: peer.id, refundCharged: refund).start()
} }
) )
self.present(controller, in: .window(.root)) self.present(controller, in: .window(.root))

View File

@ -1189,13 +1189,15 @@ extension ChatControllerImpl {
savedMessagesPeerId = nil savedMessagesPeerId = nil
} }
let savedMessagesPeer: Signal<(peer: EnginePeer?, messageCount: Int, presence: EnginePeer.Presence?)?, NoError> let savedMessagesPeer: Signal<(peer: EnginePeer?, messageCount: Int, presence: EnginePeer.Presence?, isMonoforumFeeRemoved: Bool)?, NoError>
if let savedMessagesPeerId { if let savedMessagesPeerId {
let threadPeerId = savedMessagesPeerId let threadPeerId = savedMessagesPeerId
let basicPeerKey: PostboxViewKey = .peer(peerId: threadPeerId, components: []) let basicPeerKey: PostboxViewKey = .peer(peerId: threadPeerId, components: [])
let countViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: savedMessagesPeerId.toInt64(), namespace: Namespaces.Message.Cloud, customTag: nil) let countViewKey: PostboxViewKey = .historyTagSummaryView(tag: MessageTags(), peerId: peerId, threadId: savedMessagesPeerId.toInt64(), namespace: Namespaces.Message.Cloud, customTag: nil)
savedMessagesPeer = context.account.postbox.combinedView(keys: [basicPeerKey, countViewKey]) let threadInfoKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: savedMessagesPeerId.toInt64())
|> map { views -> (peer: EnginePeer?, messageCount: Int, presence: EnginePeer.Presence?)? in
savedMessagesPeer = context.account.postbox.combinedView(keys: [basicPeerKey, countViewKey, threadInfoKey])
|> map { views -> (peer: EnginePeer?, messageCount: Int, presence: EnginePeer.Presence?, isMonoforumFeeRemoved: Bool)? in
var peer: EnginePeer? var peer: EnginePeer?
var presence: EnginePeer.Presence? var presence: EnginePeer.Presence?
if let peerView = views.views[basicPeerKey] as? PeerView { if let peerView = views.views[basicPeerKey] as? PeerView {
@ -1208,7 +1210,12 @@ extension ChatControllerImpl {
messageCount += Int(count) messageCount += Int(count)
} }
return (peer, messageCount, presence) var isMonoforumFeeRemoved = false
if let threadInfoView = views.views[threadInfoKey] as? MessageHistoryThreadInfoView, let threadInfo = threadInfoView.info?.data.get(MessageHistoryThreadData.self) {
isMonoforumFeeRemoved = threadInfo.isMessageFeeRemoved
}
return (peer, messageCount, presence, isMonoforumFeeRemoved)
} }
|> distinctUntilChanged(isEqual: { lhs, rhs in |> distinctUntilChanged(isEqual: { lhs, rhs in
if lhs?.peer != rhs?.peer { if lhs?.peer != rhs?.peer {
@ -1220,6 +1227,9 @@ extension ChatControllerImpl {
if lhs?.presence != rhs?.presence { if lhs?.presence != rhs?.presence {
return false return false
} }
if lhs?.isMonoforumFeeRemoved != rhs?.isMonoforumFeeRemoved {
return false
}
return true return true
}) })
} else { } else {
@ -1473,7 +1483,7 @@ extension ChatControllerImpl {
} }
var removePaidMessageFeeData: ChatPresentationInterfaceState.RemovePaidMessageFeeData? var removePaidMessageFeeData: ChatPresentationInterfaceState.RemovePaidMessageFeeData?
if !"".isEmpty, let peer = savedMessagesPeer?.peer, let channel = peerView.peers[peerView.peerId] as? TelegramChannel, let sendPaidMessageStars = channel.sendPaidMessageStars, channel.isMonoForum { if let savedMessagesPeer, !savedMessagesPeer.isMonoforumFeeRemoved, let peer = savedMessagesPeer.peer, let channel = peerView.peers[peerView.peerId] as? TelegramChannel, let sendPaidMessageStars = channel.sendPaidMessageStars, channel.isMonoForum {
if let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = peerView.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.sendSomething) { if let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = peerView.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.sendSomething) {
removePaidMessageFeeData = ChatPresentationInterfaceState.RemovePaidMessageFeeData( removePaidMessageFeeData = ChatPresentationInterfaceState.RemovePaidMessageFeeData(
peer: peer, peer: peer,

View File

@ -32,6 +32,7 @@ import ChatInputPanelNode
import ChatInputContextPanelNode import ChatInputContextPanelNode
import TextSelectionNode import TextSelectionNode
import ReplyAccessoryPanelNode import ReplyAccessoryPanelNode
import SuggestPostAccessoryPanelNode
import ChatMessageItemView import ChatMessageItemView
import ChatMessageSelectionNode import ChatMessageSelectionNode
import ManagedDiceAnimationNode import ManagedDiceAnimationNode
@ -371,7 +372,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
let contentBounds = self.loadingNode.frame let contentBounds = self.loadingNode.frame
loadingPlaceholderNode.frame = contentBounds loadingPlaceholderNode.frame = contentBounds
if let loadingPlaceholderNode = self.loadingPlaceholderNode, let validLayout = self.validLayout { if let loadingPlaceholderNode = self.loadingPlaceholderNode, let validLayout = self.validLayout {
loadingPlaceholderNode.updateLayout(size: contentBounds.size, insets: self.loadingNodeInsets, metrics: validLayout.0.metrics, transition: .immediate) loadingPlaceholderNode.updateLayout(size: contentBounds.size, isSidebarOpen: self.leftPanel != nil, insets: self.loadingNodeInsets, metrics: validLayout.0.metrics, transition: .immediate)
loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: .immediate) loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: .immediate)
} }
} }
@ -1712,6 +1713,12 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
strongSelf.interfaceInteraction?.setupEditMessage(nil, { _ in }) strongSelf.interfaceInteraction?.setupEditMessage(nil, { _ in })
} else if let _ = accessoryPanelNode as? WebpagePreviewAccessoryPanelNode { } else if let _ = accessoryPanelNode as? WebpagePreviewAccessoryPanelNode {
strongSelf.dismissUrlPreview() strongSelf.dismissUrlPreview()
} else if let _ = accessoryPanelNode as? SuggestPostAccessoryPanelNode {
strongSelf.requestUpdateChatInterfaceState(.animated(duration: 0.4, curve: .spring), false, { state in
var state = state
state = state.withUpdatedPostSuggestionState(nil)
return state
})
} }
} }
} }
@ -2250,7 +2257,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
self.loadingNode.updateLayout(size: contentBounds.size, insets: loadingNodeInsets, transition: transition) self.loadingNode.updateLayout(size: contentBounds.size, insets: loadingNodeInsets, transition: transition)
if let loadingPlaceholderNode = self.loadingPlaceholderNode { if let loadingPlaceholderNode = self.loadingPlaceholderNode {
loadingPlaceholderNode.updateLayout(size: contentBounds.size, insets: loadingNodeInsets, metrics: layout.metrics, transition: transition) loadingPlaceholderNode.updateLayout(size: contentBounds.size, isSidebarOpen: leftPanelSize != nil, insets: loadingNodeInsets, metrics: layout.metrics, transition: transition)
loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: transition) loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
} }

View File

@ -233,6 +233,12 @@ 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 chatPresentationInterfaceState.interfaceState.postSuggestionState == nil {
accessoryItems.append(.suggestPost)
}
}
if case let .customChatContents(customChatContents) = chatPresentationInterfaceState.subject { if case let .customChatContents(customChatContents) = chatPresentationInterfaceState.subject {
switch customChatContents.kind { switch customChatContents.kind {
case .hashTagSearch: case .hashTagSearch:

View File

@ -8,6 +8,7 @@ import ChatControllerInteraction
import AccessoryPanelNode import AccessoryPanelNode
import ForwardAccessoryPanelNode import ForwardAccessoryPanelNode
import ReplyAccessoryPanelNode import ReplyAccessoryPanelNode
import SuggestPostAccessoryPanelNode
func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: AccessoryPanelNode?, chatControllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> AccessoryPanelNode? { func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: AccessoryPanelNode?, chatControllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> AccessoryPanelNode? {
if case .standard(.previewing) = chatPresentationInterfaceState.mode { if case .standard(.previewing) = chatPresentationInterfaceState.mode {
@ -92,6 +93,16 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS
return nil return nil
} }
} }
} else if chatPresentationInterfaceState.interfaceState.postSuggestionState != nil {
if let replyPanelNode = currentPanel as? SuggestPostAccessoryPanelNode {
replyPanelNode.interfaceInteraction = interfaceInteraction
replyPanelNode.updateThemeAndStrings(theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings)
return replyPanelNode
} else {
let panelNode = SuggestPostAccessoryPanelNode(context: context, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder, dateTimeFormat: chatPresentationInterfaceState.dateTimeFormat, animationCache: chatControllerInteraction?.presentationContext.animationCache, animationRenderer: chatControllerInteraction?.presentationContext.animationRenderer)
panelNode.interfaceInteraction = interfaceInteraction
return panelNode
}
} else { } else {
return nil return nil
} }

View File

@ -157,6 +157,9 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
} else { } else {
return (PresentationResourcesChat.chatInputTextFieldSilentPostOffImage(theme), nil, strings.VoiceOver_SilentPostOff, 1.0, UIEdgeInsets()) return (PresentationResourcesChat.chatInputTextFieldSilentPostOffImage(theme), nil, strings.VoiceOver_SilentPostOff, 1.0, UIEdgeInsets())
} }
case .suggestPost:
//TODO:localize
return (PresentationResourcesChat.chatInputTextFieldSuggestPostImage(theme), nil, "Suggest post", 1.0, UIEdgeInsets())
case let .messageAutoremoveTimeout(timeout): case let .messageAutoremoveTimeout(timeout):
if let timeout = timeout { if let timeout = timeout {
return (nil, shortTimeIntervalString(strings: strings, value: timeout), strings.VoiceOver_SelfDestructTimerOn(timeIntervalString(strings: strings, value: timeout)).string, 1.0, UIEdgeInsets()) return (nil, shortTimeIntervalString(strings: strings, value: timeout), strings.VoiceOver_SelfDestructTimerOn(timeIntervalString(strings: strings, value: timeout)).string, 1.0, UIEdgeInsets())
@ -172,7 +175,7 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
private static func calculateWidth(item: ChatTextInputAccessoryItem, image: UIImage?, text: String?, strings: PresentationStrings) -> CGFloat { private static func calculateWidth(item: ChatTextInputAccessoryItem, image: UIImage?, text: String?, strings: PresentationStrings) -> CGFloat {
switch item { switch item {
case .input, .botInput, .silentPost, .commands, .scheduledMessages, .gift: case .input, .botInput, .silentPost, .commands, .scheduledMessages, .gift, .suggestPost:
return 32.0 return 32.0
case let .messageAutoremoveTimeout(timeout): case let .messageAutoremoveTimeout(timeout):
var imageWidth = (image?.size.width ?? 0.0) + CGFloat(8.0) var imageWidth = (image?.size.width ?? 0.0) + CGFloat(8.0)
@ -4707,6 +4710,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
self.interfaceInteraction?.openScheduledMessages() self.interfaceInteraction?.openScheduledMessages()
case .gift: case .gift:
self.interfaceInteraction?.openPremiumGift() self.interfaceInteraction?.openPremiumGift()
case .suggestPost:
self.interfaceInteraction?.openSuggestPost()
} }
break break
} }

View File

@ -3715,26 +3715,28 @@ public final class SharedAccountContextImpl: SharedAccountContext {
} }
public func makeStarsAmountScreen(context: AccountContext, initialValue: Int64?, completion: @escaping (Int64) -> Void) -> ViewController { public func makeStarsAmountScreen(context: AccountContext, initialValue: Int64?, completion: @escaping (Int64) -> Void) -> ViewController {
return StarsWithdrawScreen(context: context, mode: .paidMedia(initialValue), completion: completion) return StarsWithdrawScreen(context: context, mode: .paidMedia(initialValue, completion: completion))
} }
public func makeStarsWithdrawalScreen(context: AccountContext, stats: StarsRevenueStats, completion: @escaping (Int64) -> Void) -> ViewController { public func makeStarsWithdrawalScreen(context: AccountContext, stats: StarsRevenueStats, completion: @escaping (Int64) -> Void) -> ViewController {
return StarsWithdrawScreen(context: context, mode: .withdraw(stats), completion: completion) return StarsWithdrawScreen(context: context, mode: .withdraw(stats, completion: completion))
} }
public func makeStarsWithdrawalScreen(context: AccountContext, subject: StarsWithdrawalScreenSubject, completion: @escaping (Int64) -> Void) -> ViewController { public func makeStarsWithdrawalScreen(context: AccountContext, subject: StarsWithdrawalScreenSubject) -> ViewController {
let mode: StarsWithdrawScreen.Mode let mode: StarsWithdrawScreen.Mode
switch subject { switch subject {
case .withdraw: case let .withdraw(completion):
mode = .accountWithdraw mode = .accountWithdraw(completion: completion)
case let .enterAmount(current, minValue, fractionAfterCommission, kind): case let .enterAmount(current, minValue, fractionAfterCommission, kind, completion):
mode = .paidMessages(current: current.value, minValue: minValue.value, fractionAfterCommission: fractionAfterCommission, kind: kind) 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)
} }
return StarsWithdrawScreen(context: context, mode: mode, completion: completion) return StarsWithdrawScreen(context: context, mode: mode)
} }
public func makeStarGiftResellScreen(context: AccountContext, gift: StarGift.UniqueGift, update: Bool, completion: @escaping (Int64) -> Void) -> ViewController { public func makeStarGiftResellScreen(context: AccountContext, gift: StarGift.UniqueGift, update: Bool, completion: @escaping (Int64) -> Void) -> ViewController {
return StarsWithdrawScreen(context: context, mode: .starGiftResell(gift, update), completion: completion) return StarsWithdrawScreen(context: context, mode: .starGiftResell(gift, update, completion: completion))
} }
public func makeStarsGiftScreen(context: AccountContext, message: EngineMessage) -> ViewController { public func makeStarsGiftScreen(context: AccountContext, message: EngineMessage) -> ViewController {