mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-06 17:00:13 +00:00
Various improvements
This commit is contained in:
parent
400de1ef11
commit
c42b2bd9c0
@ -1031,8 +1031,9 @@ public enum StarsWithdrawalScreenSubject {
|
||||
case postSuggestion
|
||||
}
|
||||
|
||||
case withdraw
|
||||
case enterAmount(current: StarsAmount, minValue: StarsAmount, fractionAfterCommission: Int, kind: PaidMessageKind)
|
||||
case withdraw(completion: (Int64) -> Void)
|
||||
case enterAmount(current: StarsAmount, minValue: StarsAmount, fractionAfterCommission: Int, kind: PaidMessageKind, completion: (Int64) -> Void)
|
||||
case postSuggestion(channel: EnginePeer, current: StarsAmount, timestamp: Int32?, completion: (Int64, Int32?) -> Void)
|
||||
}
|
||||
|
||||
public protocol SharedAccountContext: AnyObject {
|
||||
@ -1221,7 +1222,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makeStarsStatisticsScreen(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) -> 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, 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 makeStarsGiftScreen(context: AccountContext, message: EngineMessage) -> ViewController
|
||||
func makeStarsGiveawayBoostScreen(context: AccountContext, peerId: EnginePeer.Id, boost: ChannelBoostersContext.State.Boost) -> ViewController
|
||||
|
||||
@ -961,6 +961,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
}, presentForwardOptions: { _ in
|
||||
}, presentReplyOptions: { _ in
|
||||
}, presentLinkOptions: { _ in
|
||||
}, presentSuggestPostOptions: {
|
||||
}, shareSelectedMessages: {
|
||||
}, updateTextInputStateAndMode: { [weak self] f in
|
||||
if let strongSelf = self {
|
||||
@ -1246,7 +1247,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
}, joinGroupCall: { _ in
|
||||
}, presentInviteMembers: {
|
||||
}, presentGigagroupHelp: {
|
||||
}, openSuggestPost: {
|
||||
}, openMonoforum: {
|
||||
}, editMessageMedia: { _, _ in
|
||||
}, updateShowCommands: { _ in
|
||||
}, updateShowSendAsPeers: { _ in
|
||||
@ -1264,6 +1265,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
}, addDoNotTranslateLanguage: { _ in
|
||||
}, hideTranslationPanel: {
|
||||
}, openPremiumGift: {
|
||||
}, openSuggestPost: {
|
||||
}, openPremiumRequiredForMessaging: {
|
||||
}, openStarsPurchase: { _ in
|
||||
}, openMessagePayment: {
|
||||
|
||||
@ -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 composeInputState: ChatTextInputState
|
||||
public let composeDisableUrlPreviews: [String]
|
||||
@ -510,6 +520,7 @@ public final class ChatInterfaceState: Codable, Equatable {
|
||||
public let silentPosting: Bool
|
||||
public let inputLanguage: String?
|
||||
public let sendMessageEffect: Int64?
|
||||
public let postSuggestionState: PostSuggestionState?
|
||||
|
||||
public var synchronizeableInputState: SynchronizeableChatInputState? {
|
||||
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.inputLanguage = 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.composeInputState = composeInputState
|
||||
self.composeDisableUrlPreviews = composeDisableUrlPreviews
|
||||
@ -579,6 +591,7 @@ public final class ChatInterfaceState: Codable, Equatable {
|
||||
self.silentPosting = silentPosting
|
||||
self.inputLanguage = inputLanguage
|
||||
self.sendMessageEffect = sendMessageEffect
|
||||
self.postSuggestionState = postSuggestionState
|
||||
}
|
||||
|
||||
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.inputLanguage = try? container.decodeIfPresent(String.self, forKey: "inputLanguage")
|
||||
|
||||
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 {
|
||||
@ -710,6 +723,7 @@ public final class ChatInterfaceState: Codable, Equatable {
|
||||
}
|
||||
|
||||
try container.encodeIfPresent(self.sendMessageEffect, forKey: "sendMessageEffect")
|
||||
try container.encodeIfPresent(self.postSuggestionState, forKey: "postSuggestionState")
|
||||
}
|
||||
|
||||
public static func ==(lhs: ChatInterfaceState, rhs: ChatInterfaceState) -> Bool {
|
||||
@ -747,17 +761,20 @@ public final class ChatInterfaceState: Codable, Equatable {
|
||||
if lhs.sendMessageEffect != rhs.sendMessageEffect {
|
||||
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
|
||||
}
|
||||
|
||||
public func withUpdatedComposeInputState(_ inputState: ChatTextInputState) -> ChatInterfaceState {
|
||||
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 {
|
||||
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 {
|
||||
@ -769,19 +786,19 @@ public final class ChatInterfaceState: Codable, Equatable {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
@ -792,7 +809,7 @@ public final class ChatInterfaceState: Codable, Equatable {
|
||||
for messageId in messageIds {
|
||||
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 {
|
||||
@ -807,47 +824,51 @@ public final class ChatInterfaceState: Codable, Equatable {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
@ -78,6 +78,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
public let presentForwardOptions: (ASDisplayNode) -> Void
|
||||
public let presentReplyOptions: (ASDisplayNode) -> Void
|
||||
public let presentLinkOptions: (ASDisplayNode) -> Void
|
||||
public let presentSuggestPostOptions: () -> Void
|
||||
public let shareSelectedMessages: () -> Void
|
||||
public let updateTextInputStateAndMode: (@escaping (ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void
|
||||
public let updateInputModeAndDismissedButtonKeyboardMessageId: ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void
|
||||
@ -150,7 +151,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
public let joinGroupCall: (CachedChannelData.ActiveCall) -> Void
|
||||
public let presentInviteMembers: () -> Void
|
||||
public let presentGigagroupHelp: () -> Void
|
||||
public let openSuggestPost: () -> Void
|
||||
public let openMonoforum: () -> Void
|
||||
public let updateShowCommands: ((Bool) -> Bool) -> Void
|
||||
public let updateShowSendAsPeers: ((Bool) -> Bool) -> Void
|
||||
public let openInviteRequests: () -> Void
|
||||
@ -167,6 +168,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
public let addDoNotTranslateLanguage: (String) -> Void
|
||||
public let hideTranslationPanel: () -> Void
|
||||
public let openPremiumGift: () -> Void
|
||||
public let openSuggestPost: () -> Void
|
||||
public let openPremiumRequiredForMessaging: () -> Void
|
||||
public let openStarsPurchase: (Int64?) -> Void
|
||||
public let openMessagePayment: () -> Void
|
||||
@ -199,6 +201,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
presentForwardOptions: @escaping (ASDisplayNode) -> Void,
|
||||
presentReplyOptions: @escaping (ASDisplayNode) -> Void,
|
||||
presentLinkOptions: @escaping (ASDisplayNode) -> Void,
|
||||
presentSuggestPostOptions: @escaping () -> Void,
|
||||
shareSelectedMessages: @escaping () -> Void,
|
||||
updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void,
|
||||
updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void,
|
||||
@ -270,7 +273,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
joinGroupCall: @escaping (CachedChannelData.ActiveCall) -> Void,
|
||||
presentInviteMembers: @escaping () -> Void,
|
||||
presentGigagroupHelp: @escaping () -> Void,
|
||||
openSuggestPost: @escaping () -> Void,
|
||||
openMonoforum: @escaping () -> Void,
|
||||
editMessageMedia: @escaping (MessageId, Bool) -> Void,
|
||||
updateShowCommands: @escaping ((Bool) -> Bool) -> Void,
|
||||
updateShowSendAsPeers: @escaping ((Bool) -> Bool) -> Void,
|
||||
@ -288,6 +291,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
addDoNotTranslateLanguage: @escaping (String) -> Void,
|
||||
hideTranslationPanel: @escaping () -> Void,
|
||||
openPremiumGift: @escaping () -> Void,
|
||||
openSuggestPost: @escaping () -> Void,
|
||||
openPremiumRequiredForMessaging: @escaping () -> Void,
|
||||
openStarsPurchase: @escaping (Int64?) -> Void,
|
||||
openMessagePayment: @escaping () -> Void,
|
||||
@ -319,6 +323,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
self.presentForwardOptions = presentForwardOptions
|
||||
self.presentReplyOptions = presentReplyOptions
|
||||
self.presentLinkOptions = presentLinkOptions
|
||||
self.presentSuggestPostOptions = presentSuggestPostOptions
|
||||
self.shareSelectedMessages = shareSelectedMessages
|
||||
self.updateTextInputStateAndMode = updateTextInputStateAndMode
|
||||
self.updateInputModeAndDismissedButtonKeyboardMessageId = updateInputModeAndDismissedButtonKeyboardMessageId
|
||||
@ -391,7 +396,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
self.joinGroupCall = joinGroupCall
|
||||
self.presentInviteMembers = presentInviteMembers
|
||||
self.presentGigagroupHelp = presentGigagroupHelp
|
||||
self.openSuggestPost = openSuggestPost
|
||||
self.openMonoforum = openMonoforum
|
||||
self.updateShowCommands = updateShowCommands
|
||||
self.updateShowSendAsPeers = updateShowSendAsPeers
|
||||
self.openInviteRequests = openInviteRequests
|
||||
@ -408,6 +413,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
self.addDoNotTranslateLanguage = addDoNotTranslateLanguage
|
||||
self.hideTranslationPanel = hideTranslationPanel
|
||||
self.openPremiumGift = openPremiumGift
|
||||
self.openSuggestPost = openSuggestPost
|
||||
self.openPremiumRequiredForMessaging = openPremiumRequiredForMessaging
|
||||
self.openStarsPurchase = openStarsPurchase
|
||||
self.openMessagePayment = openMessagePayment
|
||||
@ -447,6 +453,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
}, presentForwardOptions: { _ in
|
||||
}, presentReplyOptions: { _ in
|
||||
}, presentLinkOptions: { _ in
|
||||
}, presentSuggestPostOptions: {
|
||||
}, shareSelectedMessages: {
|
||||
}, updateTextInputStateAndMode: updateTextInputStateAndMode, updateInputModeAndDismissedButtonKeyboardMessageId: updateInputModeAndDismissedButtonKeyboardMessageId, openStickers: {
|
||||
}, editMessage: {
|
||||
@ -519,7 +526,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
}, joinGroupCall: { _ in
|
||||
}, presentInviteMembers: {
|
||||
}, presentGigagroupHelp: {
|
||||
}, openSuggestPost: {
|
||||
}, openMonoforum: {
|
||||
}, editMessageMedia: { _, _ in
|
||||
}, updateShowCommands: { _ in
|
||||
}, updateShowSendAsPeers: { _ in
|
||||
@ -537,6 +544,7 @@ public final class ChatPanelInterfaceInteraction {
|
||||
}, addDoNotTranslateLanguage: { _ in
|
||||
}, hideTranslationPanel: {
|
||||
}, openPremiumGift: {
|
||||
}, openSuggestPost: {
|
||||
}, openPremiumRequiredForMessaging: {
|
||||
}, openStarsPurchase: { _ in
|
||||
}, openMessagePayment: {
|
||||
|
||||
@ -11,6 +11,7 @@ public enum ChatTextInputAccessoryItem: Equatable {
|
||||
case messageAutoremoveTimeout
|
||||
case scheduledMessages
|
||||
case gift
|
||||
case suggestPost
|
||||
}
|
||||
|
||||
public enum InputMode: Hashable {
|
||||
@ -27,6 +28,7 @@ public enum ChatTextInputAccessoryItem: Equatable {
|
||||
case messageAutoremoveTimeout(Int32?)
|
||||
case scheduledMessages
|
||||
case gift
|
||||
case suggestPost
|
||||
|
||||
public var key: Key {
|
||||
switch self {
|
||||
@ -44,6 +46,8 @@ public enum ChatTextInputAccessoryItem: Equatable {
|
||||
return .scheduledMessages
|
||||
case .gift:
|
||||
return .gift
|
||||
case .suggestPost:
|
||||
return .suggestPost
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,13 +376,18 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP
|
||||
if case let .paidMessages(value) = stateValue.with({ $0 }).updatedValue {
|
||||
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
|
||||
updateState { state in
|
||||
var state = state
|
||||
state.updatedValue = .paidMessages(StarsAmount(value: amount, nanos: 0))
|
||||
return state
|
||||
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
|
||||
var state = state
|
||||
state.updatedValue = .paidMessages(StarsAmount(value: amount, nanos: 0))
|
||||
return state
|
||||
}
|
||||
}
|
||||
})
|
||||
))
|
||||
pushControllerImpl?(starsScreen)
|
||||
}
|
||||
)
|
||||
|
||||
@ -68,6 +68,7 @@ public struct MessageHistoryThreadData: Codable, Equatable {
|
||||
case isHidden
|
||||
case notificationSettings
|
||||
case isMarkedUnread
|
||||
case isMessageFeeRemoved
|
||||
}
|
||||
|
||||
public var creationDate: Int32
|
||||
@ -82,6 +83,7 @@ public struct MessageHistoryThreadData: Codable, Equatable {
|
||||
public var isClosed: Bool
|
||||
public var isHidden: Bool
|
||||
public var notificationSettings: TelegramPeerNotificationSettings
|
||||
public var isMessageFeeRemoved: Bool
|
||||
|
||||
public init(
|
||||
creationDate: Int32,
|
||||
@ -95,7 +97,8 @@ public struct MessageHistoryThreadData: Codable, Equatable {
|
||||
maxOutgoingReadId: Int32,
|
||||
isClosed: Bool,
|
||||
isHidden: Bool,
|
||||
notificationSettings: TelegramPeerNotificationSettings
|
||||
notificationSettings: TelegramPeerNotificationSettings,
|
||||
isMessageFeeRemoved: Bool
|
||||
) {
|
||||
self.creationDate = creationDate
|
||||
self.isOwnedByMe = isOwnedByMe
|
||||
@ -109,6 +112,7 @@ public struct MessageHistoryThreadData: Codable, Equatable {
|
||||
self.isClosed = isClosed
|
||||
self.isHidden = isHidden
|
||||
self.notificationSettings = notificationSettings
|
||||
self.isMessageFeeRemoved = isMessageFeeRemoved
|
||||
}
|
||||
|
||||
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.isHidden = try container.decodeIfPresent(Bool.self, forKey: .isHidden) ?? false
|
||||
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 {
|
||||
@ -143,6 +148,7 @@ public struct MessageHistoryThreadData: Codable, Equatable {
|
||||
try container.encode(self.isClosed, forKey: .isClosed)
|
||||
try container.encode(self.isHidden, forKey: .isHidden)
|
||||
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,
|
||||
isClosed: false,
|
||||
isHidden: false,
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
|
||||
isMessageFeeRemoved: false
|
||||
),
|
||||
topMessage: message.id.id,
|
||||
unreadMentionsCount: 0,
|
||||
@ -786,7 +793,8 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post
|
||||
maxOutgoingReadId: 0,
|
||||
isClosed: false,
|
||||
isHidden: false,
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
|
||||
isMessageFeeRemoved: false
|
||||
)
|
||||
|
||||
var topTimestamp: Int32 = 1
|
||||
@ -840,7 +848,8 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post
|
||||
maxOutgoingReadId: readOutboxMaxId,
|
||||
isClosed: false,
|
||||
isHidden: false,
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
|
||||
isMessageFeeRemoved: (flags & (1 << 4)) != 0
|
||||
)
|
||||
|
||||
var topTimestamp: Int32 = 1
|
||||
@ -989,7 +998,8 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post
|
||||
maxOutgoingReadId: readOutboxMaxId,
|
||||
isClosed: (flags & (1 << 2)) != 0,
|
||||
isHidden: (flags & (1 << 6)) != 0,
|
||||
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings)
|
||||
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings),
|
||||
isMessageFeeRemoved: false
|
||||
)
|
||||
|
||||
var topTimestamp = date
|
||||
|
||||
@ -2107,7 +2107,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
|
||||
maxOutgoingReadId: readOutboxMaxId,
|
||||
isClosed: (flags & (1 << 2)) != 0,
|
||||
isHidden: (flags & (1 << 6)) != 0,
|
||||
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings)
|
||||
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings),
|
||||
isMessageFeeRemoved: false
|
||||
),
|
||||
topMessageId: topMessage,
|
||||
unreadMentionCount: unreadMentionsCount,
|
||||
@ -2140,7 +2141,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
|
||||
maxOutgoingReadId: readOutboxMaxId,
|
||||
isClosed: false,
|
||||
isHidden: false,
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
|
||||
isMessageFeeRemoved: (flags & (1 << 4)) != 0
|
||||
),
|
||||
topMessageId: topMessage,
|
||||
unreadMentionCount: 0,
|
||||
@ -2266,7 +2268,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
|
||||
maxOutgoingReadId: readOutboxMaxId,
|
||||
isClosed: (flags & (1 << 2)) != 0,
|
||||
isHidden: (flags & (1 << 6)) != 0,
|
||||
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings)
|
||||
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings),
|
||||
isMessageFeeRemoved: false
|
||||
)
|
||||
if let entry = StoredMessageHistoryThreadInfo(data) {
|
||||
transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: Int64(id), info: entry)
|
||||
@ -2296,7 +2299,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
|
||||
maxOutgoingReadId: readOutboxMaxId,
|
||||
isClosed: false,
|
||||
isHidden: false,
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
|
||||
isMessageFeeRemoved: (flags & (1 << 4)) != 0
|
||||
)
|
||||
if let entry = StoredMessageHistoryThreadInfo(data) {
|
||||
transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: peer.peerId.toInt64(), info: entry)
|
||||
@ -2429,7 +2433,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
|
||||
maxOutgoingReadId: readOutboxMaxId,
|
||||
isClosed: (flags & (1 << 2)) != 0,
|
||||
isHidden: (flags & (1 << 6)) != 0,
|
||||
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings)
|
||||
notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings),
|
||||
isMessageFeeRemoved: false
|
||||
),
|
||||
topMessageId: topMessage,
|
||||
unreadMentionCount: unreadMentionsCount,
|
||||
@ -2459,7 +2464,8 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
|
||||
maxOutgoingReadId: readOutboxMaxId,
|
||||
isClosed: false,
|
||||
isHidden: false,
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings
|
||||
notificationSettings: TelegramPeerNotificationSettings.defaultSettings,
|
||||
isMessageFeeRemoved: (flags & (1 << 4)) != 0
|
||||
),
|
||||
topMessageId: topMessage,
|
||||
unreadMentionCount: 0,
|
||||
|
||||
@ -3,15 +3,26 @@ import TelegramApi
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
|
||||
func _internal_getPaidMessagesRevenue(account: Account, peerId: PeerId) -> Signal<StarsAmount?, NoError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputUser? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputUser)
|
||||
func _internal_getPaidMessagesRevenue(account: Account, scopePeerId: PeerId, peerId: PeerId) -> Signal<StarsAmount?, NoError> {
|
||||
return account.postbox.transaction { transaction -> (Api.InputPeer?, Api.InputUser?) in
|
||||
return (transaction.getPeer(scopePeerId).flatMap(apiInputPeer), transaction.getPeer(peerId).flatMap(apiInputUser))
|
||||
}
|
||||
|> mapToSignal { inputUser -> Signal<StarsAmount?, NoError> in
|
||||
|> mapToSignal { scopeInputPeer, inputUser -> Signal<StarsAmount?, NoError> in
|
||||
if scopePeerId != account.peerId {
|
||||
if scopeInputPeer == nil {
|
||||
return .never()
|
||||
}
|
||||
}
|
||||
guard let inputUser else {
|
||||
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)
|
||||
|> `catch` { _ -> Signal<Api.account.PaidMessagesRevenue?, NoError> in
|
||||
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> {
|
||||
return account.postbox.transaction { transaction -> Api.InputUser? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputUser)
|
||||
func _internal_addNoPaidMessagesException(account: Account, scopePeerId: PeerId, peerId: PeerId, refundCharged: Bool) -> Signal<Never, NoError> {
|
||||
return account.postbox.transaction { transaction -> (Api.InputPeer?, Api.InputUser?) in
|
||||
return (transaction.getPeer(scopePeerId).flatMap(apiInputPeer), transaction.getPeer(peerId).flatMap(apiInputUser))
|
||||
}
|
||||
|> mapToSignal { inputUser -> Signal<Never, NoError> in
|
||||
|> mapToSignal { scopeInputPeer, inputUser -> Signal<Never, NoError> in
|
||||
if scopePeerId != account.peerId {
|
||||
if scopeInputPeer == nil {
|
||||
return .never()
|
||||
}
|
||||
}
|
||||
guard let inputUser else {
|
||||
return .never()
|
||||
}
|
||||
@ -40,19 +56,33 @@ func _internal_addNoPaidMessagesException(account: Account, peerId: PeerId, refu
|
||||
if refundCharged {
|
||||
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
|
||||
return .single(.boolFalse)
|
||||
} |> mapToSignal { _ in
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData in
|
||||
if let cachedData = cachedData as? CachedUserData {
|
||||
var settings = cachedData.peerStatusSettings ?? .init()
|
||||
settings.paidMessageStars = nil
|
||||
return cachedData.withUpdatedPeerStatusSettings(settings)
|
||||
if scopePeerId != account.peerId, scopeInputPeer != nil {
|
||||
guard var data = transaction.getMessageHistoryThreadInfo(peerId: scopePeerId, threadId: peerId.toInt64())?.data.get(MessageHistoryThreadData.self) else {
|
||||
return
|
||||
}
|
||||
return cachedData
|
||||
})
|
||||
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
|
||||
if let cachedData = cachedData as? CachedUserData {
|
||||
var settings = cachedData.peerStatusSettings ?? .init()
|
||||
settings.paidMessageStars = nil
|
||||
return cachedData.withUpdatedPeerStatusSettings(settings)
|
||||
}
|
||||
return cachedData
|
||||
})
|
||||
}
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
@ -1494,12 +1494,12 @@ public extension TelegramEngine {
|
||||
return _internal_applyChannelBoost(account: self.account, peerId: peerId, slots: slots)
|
||||
}
|
||||
|
||||
public func getPaidMessagesRevenue(peerId: EnginePeer.Id) -> Signal<StarsAmount?, NoError> {
|
||||
return _internal_getPaidMessagesRevenue(account: self.account, peerId: peerId)
|
||||
public func getPaidMessagesRevenue(scopePeerId: EnginePeer.Id, peerId: EnginePeer.Id) -> Signal<StarsAmount?, NoError> {
|
||||
return _internal_getPaidMessagesRevenue(account: self.account, scopePeerId: scopePeerId, peerId: peerId)
|
||||
}
|
||||
|
||||
public func addNoPaidMessagesException(peerId: EnginePeer.Id, refundCharged: Bool) -> Signal<Never, NoError> {
|
||||
return _internal_addNoPaidMessagesException(account: self.account, peerId: peerId, refundCharged: refundCharged)
|
||||
public func addNoPaidMessagesException(scopePeerId: EnginePeer.Id, peerId: EnginePeer.Id, refundCharged: Bool) -> Signal<Never, NoError> {
|
||||
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> {
|
||||
|
||||
@ -224,6 +224,7 @@ public enum PresentationResourceKey: Int32 {
|
||||
case chatInputTextFieldTimerImage
|
||||
case chatInputTextFieldScheduleImage
|
||||
case chatInputTextFieldGiftImage
|
||||
case chatInputTextFieldSuggestPostImage
|
||||
|
||||
case chatInputSearchPanelUpImage
|
||||
case chatInputSearchPanelUpDisabledImage
|
||||
|
||||
@ -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? {
|
||||
return theme.image(PresentationResourceKey.chatInputTextFieldKeyboardImage.rawValue, { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Text/AccessoryIconKeyboard"), color: theme.chat.inputPanel.inputControlColor)
|
||||
|
||||
@ -228,7 +228,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
|
||||
@objc private func suggestedPostPressed() {
|
||||
self.interfaceInteraction?.openSuggestPost()
|
||||
self.interfaceInteraction?.openMonoforum()
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
|
||||
@ -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
|
||||
|
||||
if hasAvatar && self.avatarNode == nil {
|
||||
@ -127,12 +127,24 @@ public final class ChatLoadingPlaceholderMessageContainer {
|
||||
}
|
||||
|
||||
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.updateFrame(node: avatarBorderNode, frame: avatarFrame)
|
||||
transition.updatePosition(node: avatarNode, position: avatarFrame.center)
|
||||
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)
|
||||
|
||||
avatarOffset += avatarSize.width - 1.0
|
||||
if !isSidebarOpen {
|
||||
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))
|
||||
@ -161,7 +173,7 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
|
||||
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) {
|
||||
self.context = context
|
||||
@ -295,7 +307,7 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
|
||||
private var didAnimateOut = false
|
||||
public func animateOut(_ historyNode: ListView, completion: @escaping () -> Void = {}) {
|
||||
guard let (size, _, _) = self.validLayout else {
|
||||
guard let (size, isSidebarOpen, _, _) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
let listNode = historyNode
|
||||
@ -376,7 +388,7 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
let messageContainer = self.messageContainers[k]
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -439,14 +451,14 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
|
||||
if self.chatType != chatType {
|
||||
self.chatType = chatType
|
||||
if let (size, insets, metrics) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, metrics: metrics, transition: .immediate)
|
||||
if let (size, isSidebarOpen, insets, metrics) = self.validLayout {
|
||||
self.updateLayout(size: size, isSidebarOpen: isSidebarOpen, insets: insets, metrics: metrics, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, insets: UIEdgeInsets, metrics: LayoutMetrics, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, insets, metrics)
|
||||
public func updateLayout(size: CGSize, isSidebarOpen: Bool, insets: UIEdgeInsets, metrics: LayoutMetrics, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, isSidebarOpen, insets, metrics)
|
||||
|
||||
let bounds = CGRect(origin: .zero, size: size)
|
||||
|
||||
@ -500,7 +512,7 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
||||
|
||||
for messageContainer in self.messageContainers {
|
||||
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
|
||||
index += 1
|
||||
}
|
||||
|
||||
@ -2436,7 +2436,11 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
|
||||
if isSidePanelOpen && incoming {
|
||||
hasTitleAvatar = true
|
||||
hasTitleTopicNavigation = item.chatLocation.threadId == nil
|
||||
|
||||
if let channel = item.message.peers[item.message.id.peerId], channel.isMonoForum {
|
||||
} else {
|
||||
hasTitleTopicNavigation = item.chatLocation.threadId == nil
|
||||
}
|
||||
}
|
||||
|
||||
let inlineBotNameColor = messageTheme.accentTextColor
|
||||
|
||||
@ -72,6 +72,7 @@ public final class ChatRecentActionsController: TelegramBaseController {
|
||||
}, presentForwardOptions: { _ in
|
||||
}, presentReplyOptions: { _ in
|
||||
}, presentLinkOptions: { _ in
|
||||
}, presentSuggestPostOptions: {
|
||||
}, shareSelectedMessages: {
|
||||
}, updateTextInputStateAndMode: { _ in
|
||||
}, updateInputModeAndDismissedButtonKeyboardMessageId: { _ in
|
||||
@ -146,7 +147,7 @@ public final class ChatRecentActionsController: TelegramBaseController {
|
||||
}, joinGroupCall: { _ in
|
||||
}, presentInviteMembers: {
|
||||
}, presentGigagroupHelp: {
|
||||
}, openSuggestPost: {
|
||||
}, openMonoforum: {
|
||||
}, editMessageMedia: { _, _ in
|
||||
}, updateShowCommands: { _ in
|
||||
}, updateShowSendAsPeers: { _ in
|
||||
@ -164,6 +165,7 @@ public final class ChatRecentActionsController: TelegramBaseController {
|
||||
}, addDoNotTranslateLanguage: { _ in
|
||||
}, hideTranslationPanel: {
|
||||
}, openPremiumGift: {
|
||||
}, openSuggestPost: {
|
||||
}, openPremiumRequiredForMessaging: {
|
||||
}, openStarsPurchase: { _ in
|
||||
}, openMessagePayment: {
|
||||
|
||||
@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -335,6 +335,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
||||
}, presentForwardOptions: { _ in
|
||||
}, presentReplyOptions: { _ in
|
||||
}, presentLinkOptions: { _ in
|
||||
}, presentSuggestPostOptions: {
|
||||
}, shareSelectedMessages: {
|
||||
shareMessages()
|
||||
}, updateTextInputStateAndMode: { _ in
|
||||
@ -410,7 +411,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
||||
}, joinGroupCall: { _ in
|
||||
}, presentInviteMembers: {
|
||||
}, presentGigagroupHelp: {
|
||||
}, openSuggestPost: {
|
||||
}, openMonoforum: {
|
||||
}, editMessageMedia: { _, _ in
|
||||
}, updateShowCommands: { _ in
|
||||
}, updateShowSendAsPeers: { _ in
|
||||
@ -429,6 +430,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
||||
}, addDoNotTranslateLanguage: { _ in
|
||||
}, hideTranslationPanel: {
|
||||
}, openPremiumGift: {
|
||||
}, openSuggestPost: {
|
||||
}, openPremiumRequiredForMessaging: {
|
||||
}, openStarsPurchase: { _ in
|
||||
}, openMessagePayment: {
|
||||
@ -1252,6 +1254,7 @@ private enum InfoSection: Int, CaseIterable {
|
||||
case peerInfoTrailing
|
||||
case peerSettings
|
||||
case peerMembers
|
||||
case channelMonoforum
|
||||
case botAffiliateProgram
|
||||
}
|
||||
|
||||
@ -1698,6 +1701,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
let ItemMemberRequests = 8
|
||||
let ItemBalance = 9
|
||||
let ItemEdit = 10
|
||||
let ItemPeerPersonalChannel = 11
|
||||
|
||||
if let _ = data.threadData {
|
||||
let mainUsername: String
|
||||
@ -1935,6 +1939,21 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
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 {
|
||||
|
||||
@ -373,7 +373,7 @@ final class PostSuggestionsSettingsScreenComponent: Component {
|
||||
}
|
||||
|
||||
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 {
|
||||
return
|
||||
}
|
||||
@ -382,7 +382,7 @@ final class PostSuggestionsSettingsScreenComponent: Component {
|
||||
if !self.isUpdating {
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
})
|
||||
}))
|
||||
environment.controller()?.push(starsScreen)
|
||||
},
|
||||
openPremiumInfo: nil
|
||||
|
||||
@ -594,6 +594,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
strongSelf.controller?.presentInGlobalOverlay(contextController)
|
||||
}, presentReplyOptions: { _ in
|
||||
}, presentLinkOptions: { _ in
|
||||
}, presentSuggestPostOptions: {
|
||||
}, shareSelectedMessages: {
|
||||
}, updateTextInputStateAndMode: { [weak self] f in
|
||||
if let strongSelf = self {
|
||||
@ -799,7 +800,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
}, joinGroupCall: { _ in
|
||||
}, presentInviteMembers: {
|
||||
}, presentGigagroupHelp: {
|
||||
}, openSuggestPost: {
|
||||
}, openMonoforum: {
|
||||
}, editMessageMedia: { _, _ in
|
||||
}, updateShowCommands: { _ in
|
||||
}, updateShowSendAsPeers: { _ in
|
||||
@ -817,6 +818,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
}, addDoNotTranslateLanguage: { _ in
|
||||
}, hideTranslationPanel: {
|
||||
}, openPremiumGift: {
|
||||
}, openSuggestPost: {
|
||||
}, openPremiumRequiredForMessaging: {
|
||||
}, openStarsPurchase: { _ in
|
||||
}, openMessagePayment: {
|
||||
|
||||
@ -37,6 +37,7 @@ swift_library(
|
||||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/PasswordSetupUI",
|
||||
"//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController",
|
||||
"//submodules/TelegramUI/Components/ChatScheduleTimeController",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@ -21,6 +21,8 @@ import PresentationDataUtils
|
||||
import ListSectionComponent
|
||||
import TelegramStringFormatting
|
||||
import UndoUI
|
||||
import ListActionItemComponent
|
||||
import ChatScheduleTimeController
|
||||
|
||||
private let amountTag = GenericComponentViewTag()
|
||||
|
||||
@ -29,25 +31,22 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
let context: AccountContext
|
||||
let mode: StarsWithdrawScreen.Mode
|
||||
let controller: () -> ViewController?
|
||||
let dismiss: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
mode: StarsWithdrawScreen.Mode,
|
||||
controller: @escaping () -> ViewController?,
|
||||
dismiss: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
self.controller = controller
|
||||
self.dismiss = dismiss
|
||||
}
|
||||
|
||||
static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.mode != rhs.mode {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -56,6 +55,7 @@ private final class SheetContent: CombinedComponent {
|
||||
let title = Child(Text.self)
|
||||
let amountSection = Child(ListSectionComponent.self)
|
||||
let amountAdditionalLabel = Child(MultilineTextComponent.self)
|
||||
let timestampSection = Child(ListSectionComponent.self)
|
||||
let button = Child(ButtonComponent.self)
|
||||
let balanceTitle = Child(MultilineTextComponent.self)
|
||||
let balanceValue = Child(MultilineTextComponent.self)
|
||||
@ -66,6 +66,8 @@ private final class SheetContent: CombinedComponent {
|
||||
let component = context.component
|
||||
let state = context.state
|
||||
|
||||
state.component = component
|
||||
|
||||
let controller = environment.controller
|
||||
|
||||
let theme = environment.theme.withModalBlocksBackground()
|
||||
@ -111,7 +113,7 @@ private final class SheetContent: CombinedComponent {
|
||||
let resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 })
|
||||
|
||||
switch component.mode {
|
||||
case let .withdraw(status):
|
||||
case let .withdraw(status, _):
|
||||
titleString = environment.strings.Stars_Withdraw_Title
|
||||
amountTitle = environment.strings.Stars_Withdraw_AmountTitle
|
||||
amountPlaceholder = environment.strings.Stars_Withdraw_AmountPlaceholder
|
||||
@ -144,20 +146,28 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
minAmount = StarsAmount(value: 1, 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
|
||||
amountTitle = environment.strings.Stars_SellGift_AmountTitle
|
||||
amountPlaceholder = environment.strings.Stars_SellGift_AmountPlaceholder
|
||||
|
||||
minAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMinAmount, nanos: 0)
|
||||
maxAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMaxAmount, nanos: 0)
|
||||
case let .paidMessages(_, minAmountValue, _, _):
|
||||
case let .paidMessages(_, minAmountValue, _, _, _):
|
||||
titleString = environment.strings.Stars_SendMessage_AdjustmentTitle
|
||||
amountTitle = environment.strings.Stars_SendMessage_AdjustmentSectionHeader
|
||||
amountPlaceholder = environment.strings.Stars_SendMessage_AdjustmentPlaceholder
|
||||
|
||||
minAmount = StarsAmount(value: minAmountValue, 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(
|
||||
@ -176,7 +186,7 @@ private final class SheetContent: CombinedComponent {
|
||||
balance = state.balance
|
||||
} else if case .reaction = component.mode {
|
||||
balance = state.balance
|
||||
} else if case let .withdraw(starsState) = component.mode {
|
||||
} else if case let .withdraw(starsState, _) = component.mode {
|
||||
balance = starsState.balances.availableBalance
|
||||
} else {
|
||||
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))
|
||||
amountFooter = AnyComponent(MultilineTextComponent(
|
||||
text: .plain(amountInfoString),
|
||||
@ -288,7 +298,7 @@ private final class SheetContent: CombinedComponent {
|
||||
text: .plain(amountInfoString),
|
||||
maximumNumberOfLines: 0
|
||||
))
|
||||
case let .paidMessages(_, _, fractionAfterCommission, _):
|
||||
case let .paidMessages(_, _, fractionAfterCommission, _, _):
|
||||
let amountInfoString: NSAttributedString
|
||||
if let value = state.amount?.value, value > 0 {
|
||||
let fullValue: Int64 = Int64(value) * 1_000_000_000 * Int64(fractionAfterCommission) / 100
|
||||
@ -301,6 +311,16 @@ private final class SheetContent: CombinedComponent {
|
||||
text: .plain(amountInfoString),
|
||||
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:
|
||||
amountFooter = nil
|
||||
}
|
||||
@ -358,6 +378,103 @@ private final class SheetContent: CombinedComponent {
|
||||
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)))
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
let buttonString: String
|
||||
@ -371,6 +488,16 @@ private final class SheetContent: CombinedComponent {
|
||||
}
|
||||
} else if case .paidMessages = component.mode {
|
||||
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 {
|
||||
buttonString = "\(environment.strings.Stars_Withdraw_Withdraw) # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))"
|
||||
} else {
|
||||
@ -393,7 +520,7 @@ private final class SheetContent: CombinedComponent {
|
||||
let amount = state.amount ?? StarsAmount.zero
|
||||
if amount > StarsAmount.zero {
|
||||
isButtonEnabled = true
|
||||
} else if case let .paidMessages(_, minValue, _, _) = context.component.mode {
|
||||
} else if case let .paidMessages(_, minValue, _, _, _) = context.component.mode {
|
||||
if minValue <= 0 {
|
||||
isButtonEnabled = true
|
||||
}
|
||||
@ -414,11 +541,27 @@ private final class SheetContent: CombinedComponent {
|
||||
isEnabled: isButtonEnabled,
|
||||
displaysProgress: false,
|
||||
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 {
|
||||
controller.presentMinAmountTooltip(minAmount.value)
|
||||
} 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()
|
||||
}
|
||||
}
|
||||
@ -442,10 +585,13 @@ private final class SheetContent: CombinedComponent {
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
private let context: AccountContext
|
||||
private let mode: StarsWithdrawScreen.Mode
|
||||
fileprivate let context: AccountContext
|
||||
fileprivate let mode: StarsWithdrawScreen.Mode
|
||||
|
||||
fileprivate var component: SheetContent
|
||||
|
||||
fileprivate var amount: StarsAmount?
|
||||
fileprivate var timestamp: Int32?
|
||||
|
||||
fileprivate var balance: StarsAmount?
|
||||
private var stateDisposable: Disposable?
|
||||
@ -454,27 +600,30 @@ private final class SheetContent: CombinedComponent {
|
||||
var cachedStarImage: (UIImage, PresentationTheme)?
|
||||
var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
mode: StarsWithdrawScreen.Mode
|
||||
) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
init(component: SheetContent) {
|
||||
self.context = component.context
|
||||
self.mode = component.mode
|
||||
self.component = component
|
||||
|
||||
var amount: StarsAmount?
|
||||
switch mode {
|
||||
case let .withdraw(stats):
|
||||
case let .withdraw(stats, _):
|
||||
amount = StarsAmount(value: stats.balances.availableBalance.value, nanos: 0)
|
||||
case .accountWithdraw:
|
||||
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) }
|
||||
case .reaction:
|
||||
amount = nil
|
||||
case .starGiftResell:
|
||||
amount = nil
|
||||
case let .paidMessages(initialValue, _, _, _):
|
||||
case let .paidMessages(initialValue, _, _, _, _):
|
||||
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
|
||||
@ -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 let resellStars = giftToMatch.resellStars {
|
||||
self.amount = StarsAmount(value: resellStars, nanos: 0)
|
||||
@ -528,7 +677,7 @@ private final class SheetContent: CombinedComponent {
|
||||
}
|
||||
|
||||
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 {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.mode != rhs.mode {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -570,6 +713,9 @@ private final class StarsWithdrawSheetComponent: CombinedComponent {
|
||||
content: AnyComponent<EnvironmentType>(SheetContent(
|
||||
context: context.component.context,
|
||||
mode: context.component.mode,
|
||||
controller: {
|
||||
return controller()
|
||||
},
|
||||
dismiss: {
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
@ -620,27 +766,29 @@ private final class StarsWithdrawSheetComponent: CombinedComponent {
|
||||
}
|
||||
|
||||
public final class StarsWithdrawScreen: ViewControllerComponentContainer {
|
||||
public enum Mode: Equatable {
|
||||
case withdraw(StarsRevenueStats)
|
||||
case accountWithdraw
|
||||
case paidMedia(Int64?)
|
||||
case reaction(Int64?)
|
||||
case starGiftResell(StarGift.UniqueGift, Bool)
|
||||
case paidMessages(current: Int64, minValue: Int64, fractionAfterCommission: Int, kind: StarsWithdrawalScreenSubject.PaidMessageKind)
|
||||
public enum Mode {
|
||||
public enum SuggestedPostMode {
|
||||
case sender(channel: EnginePeer)
|
||||
}
|
||||
|
||||
case withdraw(StarsRevenueStats, completion: (Int64) -> Void)
|
||||
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 mode: StarsWithdrawScreen.Mode
|
||||
fileprivate let completion: (Int64) -> Void
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
mode: StarsWithdrawScreen.Mode,
|
||||
completion: @escaping (Int64) -> Void
|
||||
mode: StarsWithdrawScreen.Mode
|
||||
) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
self.completion = completion
|
||||
|
||||
super.init(
|
||||
context: context,
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "suggest_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "suggest_24.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -306,6 +306,9 @@ extension ChatControllerImpl {
|
||||
if let requestsState = previousState.requestsState, requestsState.count > 0 && !previousInvitationRequestsPeersDismissed {
|
||||
didDisplayActionsPanel = true
|
||||
}
|
||||
if previousState.removePaidMessageFeeData != nil {
|
||||
didDisplayActionsPanel = true
|
||||
}
|
||||
|
||||
var displayActionsPanel = false
|
||||
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 {
|
||||
displayActionsPanel = true
|
||||
}
|
||||
if contentData.state.removePaidMessageFeeData != nil {
|
||||
displayActionsPanel = true
|
||||
}
|
||||
|
||||
if displayActionsPanel != didDisplayActionsPanel {
|
||||
animated = true
|
||||
@ -1894,6 +1900,11 @@ extension ChatControllerImpl {
|
||||
return
|
||||
}
|
||||
presentChatLinkOptions(selfController: self, sourceNode: sourceNode)
|
||||
}, presentSuggestPostOptions: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.presentSuggestPostOptions()
|
||||
}, shareSelectedMessages: { [weak self] in
|
||||
if let strongSelf = self, let selectedIds = strongSelf.presentationInterfaceState.interfaceState.selectionState?.selectedIds, !selectedIds.isEmpty {
|
||||
strongSelf.commitPurposefulAction()
|
||||
@ -3734,7 +3745,7 @@ extension ChatControllerImpl {
|
||||
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)
|
||||
}
|
||||
}, openSuggestPost: { [weak self] in
|
||||
}, openMonoforum: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -4084,6 +4095,22 @@ extension ChatControllerImpl {
|
||||
})
|
||||
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
|
||||
guard let self else {
|
||||
return
|
||||
|
||||
@ -977,3 +977,39 @@ private func chatLinkOptions(selfController: ChatControllerImpl, sourceNode: ASD
|
||||
func presentChatLinkOptions(selfController: ChatControllerImpl, sourceNode: ASDisplayNode) {
|
||||
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
|
||||
})
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -4595,24 +4595,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let chatPeer = self.presentationInterfaceState.renderedPeer?.chatOrMonoforumMainPeer else {
|
||||
return
|
||||
}
|
||||
let controller = chatMessageRemovePaymentAlertController(
|
||||
context: self.context,
|
||||
presentationData: self.presentationData,
|
||||
updatedPresentationData: self.updatedPresentationData,
|
||||
peer: removePaidMessageFeeData.peer,
|
||||
chatPeer: EnginePeer(chatPeer),
|
||||
amount: StarsAmount(value: 123, nanos: 0), //TODO:release
|
||||
navigationController: self.navigationController as? NavigationController,
|
||||
completion: { [weak self] refund in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self
|
||||
|
||||
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
|
||||
}
|
||||
)
|
||||
self.present(controller, in: .window(.root))
|
||||
|
||||
let controller = chatMessageRemovePaymentAlertController(
|
||||
context: self.context,
|
||||
presentationData: self.presentationData,
|
||||
updatedPresentationData: self.updatedPresentationData,
|
||||
peer: removePaidMessageFeeData.peer,
|
||||
chatPeer: EnginePeer(chatPeer),
|
||||
amount: revenue == StarsAmount(value: 0, nanos: 0) ? nil : revenue,
|
||||
navigationController: self.navigationController as? NavigationController,
|
||||
completion: { [weak self] refund in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self.context.engine.peers.addNoPaidMessagesException(scopePeerId: peer.id, peerId: removePaidMessageFeeData.peer.id, refundCharged: refund).start()
|
||||
}
|
||||
)
|
||||
self.present(controller, in: .window(.root))
|
||||
})
|
||||
} 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
|
||||
guard let self else {
|
||||
return
|
||||
@ -4629,7 +4637,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let self else {
|
||||
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))
|
||||
|
||||
@ -1189,13 +1189,15 @@ extension ChatControllerImpl {
|
||||
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 {
|
||||
let threadPeerId = savedMessagesPeerId
|
||||
let basicPeerKey: PostboxViewKey = .peer(peerId: threadPeerId, components: [])
|
||||
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])
|
||||
|> map { views -> (peer: EnginePeer?, messageCount: Int, presence: EnginePeer.Presence?)? in
|
||||
let threadInfoKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: savedMessagesPeerId.toInt64())
|
||||
|
||||
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 presence: EnginePeer.Presence?
|
||||
if let peerView = views.views[basicPeerKey] as? PeerView {
|
||||
@ -1208,7 +1210,12 @@ extension ChatControllerImpl {
|
||||
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
|
||||
if lhs?.peer != rhs?.peer {
|
||||
@ -1220,6 +1227,9 @@ extension ChatControllerImpl {
|
||||
if lhs?.presence != rhs?.presence {
|
||||
return false
|
||||
}
|
||||
if lhs?.isMonoforumFeeRemoved != rhs?.isMonoforumFeeRemoved {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
@ -1473,7 +1483,7 @@ extension ChatControllerImpl {
|
||||
}
|
||||
|
||||
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) {
|
||||
removePaidMessageFeeData = ChatPresentationInterfaceState.RemovePaidMessageFeeData(
|
||||
peer: peer,
|
||||
|
||||
@ -32,6 +32,7 @@ import ChatInputPanelNode
|
||||
import ChatInputContextPanelNode
|
||||
import TextSelectionNode
|
||||
import ReplyAccessoryPanelNode
|
||||
import SuggestPostAccessoryPanelNode
|
||||
import ChatMessageItemView
|
||||
import ChatMessageSelectionNode
|
||||
import ManagedDiceAnimationNode
|
||||
@ -371,7 +372,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
let contentBounds = self.loadingNode.frame
|
||||
loadingPlaceholderNode.frame = contentBounds
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -1712,6 +1713,12 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
strongSelf.interfaceInteraction?.setupEditMessage(nil, { _ in })
|
||||
} else if let _ = accessoryPanelNode as? WebpagePreviewAccessoryPanelNode {
|
||||
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)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
switch customChatContents.kind {
|
||||
case .hashTagSearch:
|
||||
|
||||
@ -8,6 +8,7 @@ import ChatControllerInteraction
|
||||
import AccessoryPanelNode
|
||||
import ForwardAccessoryPanelNode
|
||||
import ReplyAccessoryPanelNode
|
||||
import SuggestPostAccessoryPanelNode
|
||||
|
||||
func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: AccessoryPanelNode?, chatControllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> AccessoryPanelNode? {
|
||||
if case .standard(.previewing) = chatPresentationInterfaceState.mode {
|
||||
@ -92,6 +93,16 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -157,6 +157,9 @@ private final class AccessoryItemIconButtonNode: HighlightTrackingButtonNode {
|
||||
} else {
|
||||
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):
|
||||
if let timeout = timeout {
|
||||
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 {
|
||||
switch item {
|
||||
case .input, .botInput, .silentPost, .commands, .scheduledMessages, .gift:
|
||||
case .input, .botInput, .silentPost, .commands, .scheduledMessages, .gift, .suggestPost:
|
||||
return 32.0
|
||||
case let .messageAutoremoveTimeout(timeout):
|
||||
var imageWidth = (image?.size.width ?? 0.0) + CGFloat(8.0)
|
||||
@ -4707,6 +4710,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
||||
self.interfaceInteraction?.openScheduledMessages()
|
||||
case .gift:
|
||||
self.interfaceInteraction?.openPremiumGift()
|
||||
case .suggestPost:
|
||||
self.interfaceInteraction?.openSuggestPost()
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
@ -3715,26 +3715,28 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
switch subject {
|
||||
case .withdraw:
|
||||
mode = .accountWithdraw
|
||||
case let .enterAmount(current, minValue, fractionAfterCommission, kind):
|
||||
mode = .paidMessages(current: current.value, minValue: minValue.value, fractionAfterCommission: fractionAfterCommission, kind: kind)
|
||||
case let .withdraw(completion):
|
||||
mode = .accountWithdraw(completion: completion)
|
||||
case let .enterAmount(current, minValue, fractionAfterCommission, kind, completion):
|
||||
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 {
|
||||
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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user