mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
1a89986990
commit
b2351194d4
@ -13819,12 +13819,12 @@ Sorry for the inconvenience.";
|
|||||||
"GroupInfo.Permissions.ChargeForMessages" = "Charge for Messages";
|
"GroupInfo.Permissions.ChargeForMessages" = "Charge for Messages";
|
||||||
"GroupInfo.Permissions.ChargeForMessagesInfo" = "If you turn this on, regular members of the group will have to pay Stars to send messages.";
|
"GroupInfo.Permissions.ChargeForMessagesInfo" = "If you turn this on, regular members of the group will have to pay Stars to send messages.";
|
||||||
"GroupInfo.Permissions.MessagePrice" = "SET YOUR PRICE PER MESSAGE";
|
"GroupInfo.Permissions.MessagePrice" = "SET YOUR PRICE PER MESSAGE";
|
||||||
"GroupInfo.Permissions.MessagePriceInfo" = "Your group will receive 85% of the selected fee (%1$@) for each incoming message.";
|
"GroupInfo.Permissions.MessagePriceInfo" = "Your group will receive %1$@% of the selected fee (%2$@) for each incoming message.";
|
||||||
|
|
||||||
"Privacy.Messages.ChargeForMessages" = "Charge for Messages";
|
"Privacy.Messages.ChargeForMessages" = "Charge for Messages";
|
||||||
"Privacy.Messages.ChargeForMessagesInfo" = "Charge a fee for messages from people outide your contacts or those you haven't messaged first.";
|
"Privacy.Messages.ChargeForMessagesInfo" = "Charge a fee for messages from people outide your contacts or those you haven't messaged first.";
|
||||||
"Privacy.Messages.MessagePrice" = "SET YOUR PRICE PER MESSAGE";
|
"Privacy.Messages.MessagePrice" = "SET YOUR PRICE PER MESSAGE";
|
||||||
"Privacy.Messages.MessagePriceInfo" = "Your will receive 85% of the selected fee (%1$@) for each incoming message.";
|
"Privacy.Messages.MessagePriceInfo" = "Your will receive %1$@% of the selected fee (%2$@) for each incoming message.";
|
||||||
|
|
||||||
"Privacy.Messages.RemoveFeeHeader" = "EXCEPTIONS";
|
"Privacy.Messages.RemoveFeeHeader" = "EXCEPTIONS";
|
||||||
"Privacy.Messages.RemoveFee" = "Remove Fee";
|
"Privacy.Messages.RemoveFee" = "Remove Fee";
|
||||||
@ -13874,3 +13874,5 @@ Sorry for the inconvenience.";
|
|||||||
"Gift.Send.PayWithStars.Info" = "Your balance is **%@**. [Get More Stars >]()";
|
"Gift.Send.PayWithStars.Info" = "Your balance is **%@**. [Get More Stars >]()";
|
||||||
|
|
||||||
"Chat.PanelCustomStatusShortInfo" = "%@ is a mark for [Premium subscribers >]()";
|
"Chat.PanelCustomStatusShortInfo" = "%@ is a mark for [Premium subscribers >]()";
|
||||||
|
|
||||||
|
"Chat.InputTextPaidMessagePlaceholder" = "Message for %@";
|
||||||
|
@ -1353,20 +1353,43 @@ public struct StickersSearchConfiguration {
|
|||||||
|
|
||||||
public struct StarsSubscriptionConfiguration {
|
public struct StarsSubscriptionConfiguration {
|
||||||
static var defaultValue: StarsSubscriptionConfiguration {
|
static var defaultValue: StarsSubscriptionConfiguration {
|
||||||
return StarsSubscriptionConfiguration(maxFee: 2500, usdWithdrawRate: 1200)
|
return StarsSubscriptionConfiguration(
|
||||||
|
maxFee: 2500,
|
||||||
|
usdWithdrawRate: 1200,
|
||||||
|
paidMessageMaxAmount: 10000,
|
||||||
|
paidMessageCommissionPermille: 850
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public let maxFee: Int64?
|
public let maxFee: Int64
|
||||||
public let usdWithdrawRate: Int64?
|
public let usdWithdrawRate: Int64
|
||||||
|
public let paidMessageMaxAmount: Int64
|
||||||
|
public let paidMessageCommissionPermille: Int32
|
||||||
|
|
||||||
fileprivate init(maxFee: Int64?, usdWithdrawRate: Int64?) {
|
fileprivate init(
|
||||||
|
maxFee: Int64,
|
||||||
|
usdWithdrawRate: Int64,
|
||||||
|
paidMessageMaxAmount: Int64,
|
||||||
|
paidMessageCommissionPermille: Int32
|
||||||
|
) {
|
||||||
self.maxFee = maxFee
|
self.maxFee = maxFee
|
||||||
self.usdWithdrawRate = usdWithdrawRate
|
self.usdWithdrawRate = usdWithdrawRate
|
||||||
|
self.paidMessageMaxAmount = paidMessageMaxAmount
|
||||||
|
self.paidMessageCommissionPermille = paidMessageCommissionPermille
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func with(appConfiguration: AppConfiguration) -> StarsSubscriptionConfiguration {
|
public static func with(appConfiguration: AppConfiguration) -> StarsSubscriptionConfiguration {
|
||||||
if let data = appConfiguration.data, let value = data["stars_subscription_amount_max"] as? Double, let usdRate = data["stars_usd_withdraw_rate_x1000"] as? Double {
|
if let data = appConfiguration.data {
|
||||||
return StarsSubscriptionConfiguration(maxFee: Int64(value), usdWithdrawRate: Int64(usdRate))
|
let maxFee = (data["stars_subscription_amount_max"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.maxFee
|
||||||
|
let usdWithdrawRate = (data["stars_usd_withdraw_rate_x1000"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.usdWithdrawRate
|
||||||
|
let paidMessageMaxAmount = (data["stars_paid_message_amount_max"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.paidMessageMaxAmount
|
||||||
|
let paidMessageCommissionPermille = (data["stars_paid_message_commission_permille"] as? Double).flatMap(Int32.init) ?? StarsSubscriptionConfiguration.defaultValue.paidMessageCommissionPermille
|
||||||
|
return StarsSubscriptionConfiguration(
|
||||||
|
maxFee: maxFee,
|
||||||
|
usdWithdrawRate: usdWithdrawRate,
|
||||||
|
paidMessageMaxAmount: paidMessageMaxAmount,
|
||||||
|
paidMessageCommissionPermille: paidMessageCommissionPermille
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return .defaultValue
|
return .defaultValue
|
||||||
}
|
}
|
||||||
|
@ -76,5 +76,5 @@ public enum ShareControllerSubject {
|
|||||||
case image([ImageRepresentationWithReference])
|
case image([ImageRepresentationWithReference])
|
||||||
case media(AnyMediaReference, MediaParameters?)
|
case media(AnyMediaReference, MediaParameters?)
|
||||||
case mapMedia(TelegramMediaMap)
|
case mapMedia(TelegramMediaMap)
|
||||||
case fromExternal(([PeerId], [PeerId: Int64], String, ShareControllerAccountContext, Bool) -> Signal<ShareControllerExternalStatus, ShareControllerError>)
|
case fromExternal(Int, ([PeerId], [PeerId: Int64], [PeerId: StarsAmount], String, ShareControllerAccountContext, Bool) -> Signal<ShareControllerExternalStatus, ShareControllerError>)
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ final class GameControllerNode: ViewControllerTracingNode {
|
|||||||
if eventName == "share_game" || eventName == "share_score" {
|
if eventName == "share_game" || eventName == "share_score" {
|
||||||
if let (botPeer, gameName) = self.shareData(), let addressName = botPeer.addressName, !addressName.isEmpty, !gameName.isEmpty {
|
if let (botPeer, gameName) = self.shareData(), let addressName = botPeer.addressName, !addressName.isEmpty, !gameName.isEmpty {
|
||||||
if eventName == "share_score" {
|
if eventName == "share_score" {
|
||||||
self.present(ShareController(context: self.context, subject: .fromExternal({ [weak self] peerIds, threadIds, text, account, _ in
|
self.present(ShareController(context: self.context, subject: .fromExternal(1, { [weak self] peerIds, threadIds, requireStars, text, account, _ in
|
||||||
if let strongSelf = self, let message = strongSelf.message, let account = account as? ShareControllerAppAccountContext {
|
if let strongSelf = self, let message = strongSelf.message, let account = account as? ShareControllerAppAccountContext {
|
||||||
let signals = peerIds.map { TelegramEngine(account: account.context.account).messages.forwardGameWithScore(messageId: message.id, to: $0, threadId: threadIds[$0], as: nil) }
|
let signals = peerIds.map { TelegramEngine(account: account.context.account).messages.forwardGameWithScore(messageId: message.id, to: $0, threadId: threadIds[$0], as: nil) }
|
||||||
return .single(.preparing(false))
|
return .single(.preparing(false))
|
||||||
|
@ -493,13 +493,10 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
|
|||||||
if state.subscriptionEnabled {
|
if state.subscriptionEnabled {
|
||||||
var label: String = ""
|
var label: String = ""
|
||||||
if let subscriptionFee = state.subscriptionFee, subscriptionFee > StarsAmount.zero {
|
if let subscriptionFee = state.subscriptionFee, subscriptionFee > StarsAmount.zero {
|
||||||
var usdRate = 0.012
|
let usdRate = Double(configuration.usdWithdrawRate) / 1000.0 / 100.0
|
||||||
if let usdWithdrawRate = configuration.usdWithdrawRate {
|
|
||||||
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
|
||||||
}
|
|
||||||
label = presentationData.strings.InviteLink_Create_FeePerMonth("≈\(formatTonUsdValue(subscriptionFee.value, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))").string
|
label = presentationData.strings.InviteLink_Create_FeePerMonth("≈\(formatTonUsdValue(subscriptionFee.value, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))").string
|
||||||
}
|
}
|
||||||
entries.append(.subscriptionFee(presentationData.theme, presentationData.strings.InviteLink_Create_FeePlaceholder, isEditingEnabled, state.subscriptionFee, label, configuration.maxFee.flatMap({ StarsAmount(value: $0, nanos: 0) })))
|
entries.append(.subscriptionFee(presentationData.theme, presentationData.strings.InviteLink_Create_FeePlaceholder, isEditingEnabled, state.subscriptionFee, label, StarsAmount(value: configuration.maxFee, nanos: 0)))
|
||||||
}
|
}
|
||||||
let infoText: String
|
let infoText: String
|
||||||
if let _ = invite, state.subscriptionEnabled {
|
if let _ = invite, state.subscriptionEnabled {
|
||||||
|
@ -594,10 +594,7 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
guard let peer else {
|
guard let peer else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var usdRate = 0.012
|
let usdRate = Double(configuration.usdWithdrawRate) / 1000.0 / 100.0
|
||||||
if let usdWithdrawRate = configuration.usdWithdrawRate {
|
|
||||||
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
|
||||||
}
|
|
||||||
let subscriptionController = context.sharedContext.makeStarsSubscriptionScreen(context: context, peer: peer, pricing: pricing, importer: importer, usdRate: usdRate)
|
let subscriptionController = context.sharedContext.makeStarsSubscriptionScreen(context: context, peer: peer, pricing: pricing, importer: importer, usdRate: usdRate)
|
||||||
self?.controller?.push(subscriptionController)
|
self?.controller?.push(subscriptionController)
|
||||||
})
|
})
|
||||||
@ -834,10 +831,7 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
context.account.postbox.loadedPeerWithId(adminId)
|
context.account.postbox.loadedPeerWithId(adminId)
|
||||||
) |> deliverOnMainQueue).start(next: { [weak self] presentationData, state, requestsState, creatorPeer in
|
) |> deliverOnMainQueue).start(next: { [weak self] presentationData, state, requestsState, creatorPeer in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
var usdRate = 0.012
|
let usdRate = Double(configuration.usdWithdrawRate) / 1000.0 / 100.0
|
||||||
if let usdWithdrawRate = configuration.usdWithdrawRate {
|
|
||||||
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
|
||||||
}
|
|
||||||
|
|
||||||
var entries: [InviteLinkViewEntry] = []
|
var entries: [InviteLinkViewEntry] = []
|
||||||
|
|
||||||
|
@ -720,7 +720,7 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen
|
|||||||
entries.append(.conversionInfo(presentationData.theme, presentationData.strings.GroupInfo_Permissions_BroadcastConvertInfo(presentationStringsFormattedNumber(participantsLimit, presentationData.dateTimeFormat.groupingSeparator)).string))
|
entries.append(.conversionInfo(presentationData.theme, presentationData.strings.GroupInfo_Permissions_BroadcastConvertInfo(presentationStringsFormattedNumber(participantsLimit, presentationData.dateTimeFormat.groupingSeparator)).string))
|
||||||
}
|
}
|
||||||
|
|
||||||
if channel.hasPermission(.banMembers) {
|
if cachedData.flags.contains(.paidMessagesAvailable) && channel.hasPermission(.banMembers) {
|
||||||
let sendPaidMessageStars = state.modifiedStarsAmount?.value ?? (cachedData.sendPaidMessageStars?.value ?? 0)
|
let sendPaidMessageStars = state.modifiedStarsAmount?.value ?? (cachedData.sendPaidMessageStars?.value ?? 0)
|
||||||
let chargeEnabled = sendPaidMessageStars > 0
|
let chargeEnabled = sendPaidMessageStars > 0
|
||||||
entries.append(.chargeForMessages(presentationData.theme, presentationData.strings.GroupInfo_Permissions_ChargeForMessages, chargeEnabled))
|
entries.append(.chargeForMessages(presentationData.theme, presentationData.strings.GroupInfo_Permissions_ChargeForMessages, chargeEnabled))
|
||||||
@ -728,15 +728,13 @@ private func channelPermissionsControllerEntries(context: AccountContext, presen
|
|||||||
|
|
||||||
if chargeEnabled {
|
if chargeEnabled {
|
||||||
var price: String = ""
|
var price: String = ""
|
||||||
var usdRate = 0.012
|
let usdRate = Double(configuration.usdWithdrawRate) / 1000.0 / 100.0
|
||||||
if let usdWithdrawRate = configuration.usdWithdrawRate {
|
|
||||||
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
|
||||||
}
|
|
||||||
price = "≈\(formatTonUsdValue(sendPaidMessageStars, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
price = "≈\(formatTonUsdValue(sendPaidMessageStars, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||||
|
|
||||||
entries.append(.messagePriceHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_MessagePrice))
|
entries.append(.messagePriceHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_MessagePrice))
|
||||||
entries.append(.messagePrice(presentationData.theme, sendPaidMessageStars, price))
|
entries.append(.messagePrice(presentationData.theme, sendPaidMessageStars, price))
|
||||||
entries.append(.messagePriceInfo(presentationData.theme, presentationData.strings.GroupInfo_Permissions_MessagePriceInfo(price).string))
|
entries.append(.messagePriceInfo(presentationData.theme, presentationData.strings.GroupInfo_Permissions_MessagePriceInfo("\(configuration.paidMessageCommissionPermille / 10)", price).string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ private enum GlobalAutoremoveEntry: ItemListNodeEntry {
|
|||||||
case footer(value: GlobalPrivacySettings.NonContactChatsPrivacy)
|
case footer(value: GlobalPrivacySettings.NonContactChatsPrivacy)
|
||||||
case priceHeader
|
case priceHeader
|
||||||
case price(value: Int64, price: String)
|
case price(value: Int64, price: String)
|
||||||
case priceInfo(value: String)
|
case priceInfo(commission: Int32, value: String)
|
||||||
case exceptionsHeader
|
case exceptionsHeader
|
||||||
case exceptions(count: Int)
|
case exceptions(count: Int)
|
||||||
case exceptionsInfo
|
case exceptionsInfo
|
||||||
@ -153,8 +153,8 @@ private enum GlobalAutoremoveEntry: ItemListNodeEntry {
|
|||||||
return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, minValue: 1, maxValue: 10000, value: value, price: price, sectionId: self.section, updated: { value in
|
return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, minValue: 1, maxValue: 10000, value: value, price: price, sectionId: self.section, updated: { value in
|
||||||
arguments.updateValue(.paidMessages(StarsAmount(value: value, nanos: 0)))
|
arguments.updateValue(.paidMessages(StarsAmount(value: value, nanos: 0)))
|
||||||
})
|
})
|
||||||
case let .priceInfo(value):
|
case let .priceInfo(commission, value):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .markdown(presentationData.strings.Privacy_Messages_MessagePriceInfo(value).string), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .markdown(presentationData.strings.Privacy_Messages_MessagePriceInfo("\(commission)", value).string), sectionId: self.section)
|
||||||
case .exceptionsHeader:
|
case .exceptionsHeader:
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: presentationData.strings.Privacy_Messages_RemoveFeeHeader, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: presentationData.strings.Privacy_Messages_RemoveFeeHeader, sectionId: self.section)
|
||||||
case let .exceptions(count):
|
case let .exceptions(count):
|
||||||
@ -184,14 +184,12 @@ private func incomingMessagePrivacyScreenEntries(presentationData: PresentationD
|
|||||||
entries.append(.footer(value: state.updatedValue))
|
entries.append(.footer(value: state.updatedValue))
|
||||||
entries.append(.priceHeader)
|
entries.append(.priceHeader)
|
||||||
|
|
||||||
var usdRate = 0.012
|
let usdRate = Double(configuration.usdWithdrawRate) / 1000.0 / 100.0
|
||||||
if let usdWithdrawRate = configuration.usdWithdrawRate {
|
|
||||||
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
|
||||||
}
|
|
||||||
let price = "≈\(formatTonUsdValue(amount.value, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
let price = "≈\(formatTonUsdValue(amount.value, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||||
|
|
||||||
entries.append(.price(value: amount.value, price: price))
|
entries.append(.price(value: amount.value, price: price))
|
||||||
entries.append(.priceInfo(value: price))
|
entries.append(.priceInfo(commission: configuration.paidMessageCommissionPermille / 10, value: price))
|
||||||
entries.append(.exceptionsHeader)
|
entries.append(.exceptionsHeader)
|
||||||
entries.append(.exceptions(count: state.disableFor.count))
|
entries.append(.exceptions(count: state.disableFor.count))
|
||||||
entries.append(.exceptionsInfo)
|
entries.append(.exceptionsInfo)
|
||||||
|
@ -676,6 +676,8 @@ public final class ShareController: ViewController {
|
|||||||
messageCount = messages.count
|
messageCount = messages.count
|
||||||
} else if case let .image(images) = self.subject {
|
} else if case let .image(images) = self.subject {
|
||||||
messageCount = images.count
|
messageCount = images.count
|
||||||
|
} else if case let .fromExternal(count, _) = self.subject {
|
||||||
|
messageCount = count
|
||||||
}
|
}
|
||||||
|
|
||||||
var mediaParameters: ShareControllerSubject.MediaParameters?
|
var mediaParameters: ShareControllerSubject.MediaParameters?
|
||||||
@ -1253,18 +1255,18 @@ public final class ShareController: ViewController {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> map { views -> ([EnginePeer.Id: EnginePeer?], [EnginePeer.Id: Int64]) in
|
|> map { views -> ([EnginePeer.Id: EnginePeer?], [EnginePeer.Id: StarsAmount]) in
|
||||||
var result: [EnginePeer.Id: EnginePeer?] = [:]
|
var result: [EnginePeer.Id: EnginePeer?] = [:]
|
||||||
var requiresStars: [EnginePeer.Id: Int64] = [:]
|
var requiresStars: [EnginePeer.Id: StarsAmount] = [:]
|
||||||
for peerId in peerIds {
|
for peerId in peerIds {
|
||||||
if let view = views.views[PostboxViewKey.basicPeer(peerId)] as? BasicPeerView, let peer = view.peer {
|
if let view = views.views[PostboxViewKey.basicPeer(peerId)] as? BasicPeerView, let peer = view.peer {
|
||||||
result[peerId] = EnginePeer(peer)
|
result[peerId] = EnginePeer(peer)
|
||||||
if peer is TelegramUser, let cachedPeerDataView = views.views[PostboxViewKey.cachedPeerData(peerId: peerId)] as? CachedPeerDataView {
|
if peer is TelegramUser, let cachedPeerDataView = views.views[PostboxViewKey.cachedPeerData(peerId: peerId)] as? CachedPeerDataView {
|
||||||
if let cachedData = cachedPeerDataView.cachedPeerData as? CachedUserData {
|
if let cachedData = cachedPeerDataView.cachedPeerData as? CachedUserData {
|
||||||
requiresStars[peerId] = cachedData.sendPaidMessageStars?.value
|
requiresStars[peerId] = cachedData.sendPaidMessageStars
|
||||||
}
|
}
|
||||||
} else if let channel = peer as? TelegramChannel {
|
} else if let channel = peer as? TelegramChannel {
|
||||||
requiresStars[peerId] = channel.sendPaidMessageStars?.value
|
requiresStars[peerId] = channel.sendPaidMessageStars
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1284,7 +1286,7 @@ public final class ShareController: ViewController {
|
|||||||
subject = selectedValue.subject
|
subject = selectedValue.subject
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformMessages(_ messages: [StandaloneSendEnqueueMessage], showNames: Bool, silently: Bool, sendPaidMessageStars: Int64?) -> [StandaloneSendEnqueueMessage] {
|
func transformMessages(_ messages: [StandaloneSendEnqueueMessage], showNames: Bool, silently: Bool, sendPaidMessageStars: StarsAmount?) -> [StandaloneSendEnqueueMessage] {
|
||||||
return messages.map { message in
|
return messages.map { message in
|
||||||
var message = message
|
var message = message
|
||||||
if !showNames {
|
if !showNames {
|
||||||
@ -1296,7 +1298,7 @@ public final class ShareController: ViewController {
|
|||||||
if silently {
|
if silently {
|
||||||
message.isSilent = true
|
message.isSilent = true
|
||||||
}
|
}
|
||||||
message.sendPaidMessageStars = sendPaidMessageStars.flatMap { StarsAmount(value: $0, nanos: 0) }
|
message.sendPaidMessageStars = sendPaidMessageStars
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1839,8 +1841,8 @@ public final class ShareController: ViewController {
|
|||||||
messages: messagesToEnqueue
|
messages: messagesToEnqueue
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
case let .fromExternal(f):
|
case let .fromExternal(_, f):
|
||||||
return f(peerIds, topicIds, text, strongSelf.currentContext, silently)
|
return f(peerIds, topicIds, requiresStars, text, strongSelf.currentContext, silently)
|
||||||
|> map { state -> ShareState in
|
|> map { state -> ShareState in
|
||||||
switch state {
|
switch state {
|
||||||
case let .preparing(long):
|
case let .preparing(long):
|
||||||
@ -1907,18 +1909,18 @@ public final class ShareController: ViewController {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> map { views -> ([EnginePeer.Id: EnginePeer?], [EnginePeer.Id: Int64]) in
|
|> map { views -> ([EnginePeer.Id: EnginePeer?], [EnginePeer.Id: StarsAmount]) in
|
||||||
var result: [EnginePeer.Id: EnginePeer?] = [:]
|
var result: [EnginePeer.Id: EnginePeer?] = [:]
|
||||||
var requiresStars: [EnginePeer.Id: Int64] = [:]
|
var requiresStars: [EnginePeer.Id: StarsAmount] = [:]
|
||||||
for peerId in peerIds {
|
for peerId in peerIds {
|
||||||
if let view = views.views[PostboxViewKey.basicPeer(peerId)] as? BasicPeerView, let peer = view.peer {
|
if let view = views.views[PostboxViewKey.basicPeer(peerId)] as? BasicPeerView, let peer = view.peer {
|
||||||
result[peerId] = EnginePeer(peer)
|
result[peerId] = EnginePeer(peer)
|
||||||
if peer is TelegramUser, let cachedPeerDataView = views.views[PostboxViewKey.cachedPeerData(peerId: peerId)] as? CachedPeerDataView {
|
if peer is TelegramUser, let cachedPeerDataView = views.views[PostboxViewKey.cachedPeerData(peerId: peerId)] as? CachedPeerDataView {
|
||||||
if let cachedData = cachedPeerDataView.cachedPeerData as? CachedUserData {
|
if let cachedData = cachedPeerDataView.cachedPeerData as? CachedUserData {
|
||||||
requiresStars[peerId] = cachedData.sendPaidMessageStars?.value
|
requiresStars[peerId] = cachedData.sendPaidMessageStars
|
||||||
}
|
}
|
||||||
} else if let channel = peer as? TelegramChannel {
|
} else if let channel = peer as? TelegramChannel {
|
||||||
requiresStars[peerId] = channel.sendPaidMessageStars?.value
|
requiresStars[peerId] = channel.sendPaidMessageStars
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1938,7 +1940,7 @@ public final class ShareController: ViewController {
|
|||||||
subject = selectedValue.subject
|
subject = selectedValue.subject
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformMessages(_ messages: [EnqueueMessage], showNames: Bool, silently: Bool, sendPaidMessageStars: Int64?) -> [EnqueueMessage] {
|
func transformMessages(_ messages: [EnqueueMessage], showNames: Bool, silently: Bool, sendPaidMessageStars: StarsAmount?) -> [EnqueueMessage] {
|
||||||
return messages.map { message in
|
return messages.map { message in
|
||||||
return message.withUpdatedAttributes({ attributes in
|
return message.withUpdatedAttributes({ attributes in
|
||||||
var attributes = attributes
|
var attributes = attributes
|
||||||
@ -1949,7 +1951,7 @@ public final class ShareController: ViewController {
|
|||||||
attributes.append(NotificationInfoMessageAttribute(flags: .muted))
|
attributes.append(NotificationInfoMessageAttribute(flags: .muted))
|
||||||
}
|
}
|
||||||
if let sendPaidMessageStars {
|
if let sendPaidMessageStars {
|
||||||
attributes.append(PaidStarsMessageAttribute(stars: StarsAmount(value: sendPaidMessageStars, nanos: 0), postponeSending: false))
|
attributes.append(PaidStarsMessageAttribute(stars: sendPaidMessageStars, postponeSending: false))
|
||||||
}
|
}
|
||||||
return attributes
|
return attributes
|
||||||
})
|
})
|
||||||
@ -2321,8 +2323,8 @@ public final class ShareController: ViewController {
|
|||||||
messagesToEnqueue = transformMessages(messagesToEnqueue, showNames: showNames, silently: silently, sendPaidMessageStars: requiresStars[peerId])
|
messagesToEnqueue = transformMessages(messagesToEnqueue, showNames: showNames, silently: silently, sendPaidMessageStars: requiresStars[peerId])
|
||||||
shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messagesToEnqueue))
|
shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messagesToEnqueue))
|
||||||
}
|
}
|
||||||
case let .fromExternal(f):
|
case let .fromExternal(_, f):
|
||||||
return f(peerIds, topicIds, text, currentContext, silently)
|
return f(peerIds, topicIds, requiresStars, text, currentContext, silently)
|
||||||
|> map { state -> ShareState in
|
|> map { state -> ShareState in
|
||||||
switch state {
|
switch state {
|
||||||
case let .preparing(long):
|
case let .preparing(long):
|
||||||
|
@ -409,7 +409,7 @@ public func preparedShareItems(postbox: Postbox, network: Network, to peerId: Pe
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sentShareItems(accountPeerId: PeerId, postbox: Postbox, network: Network, stateManager: AccountStateManager, auxiliaryMethods: AccountAuxiliaryMethods, to peerIds: [PeerId], threadIds: [PeerId: Int64], items: [PreparedShareItemContent], silently: Bool, additionalText: String) -> Signal<Float, Void> {
|
public func sentShareItems(accountPeerId: PeerId, postbox: Postbox, network: Network, stateManager: AccountStateManager, auxiliaryMethods: AccountAuxiliaryMethods, to peerIds: [PeerId], threadIds: [PeerId: Int64], requireStars: [PeerId: StarsAmount], items: [PreparedShareItemContent], silently: Bool, additionalText: String) -> Signal<Float, Void> {
|
||||||
var messages: [StandaloneSendEnqueueMessage] = []
|
var messages: [StandaloneSendEnqueueMessage] = []
|
||||||
var groupingKey: Int64?
|
var groupingKey: Int64?
|
||||||
var mediaTypes: (photo: Int, video: Int, music: Int, other: Int) = (0, 0, 0, 0)
|
var mediaTypes: (photo: Int, video: Int, music: Int, other: Int) = (0, 0, 0, 0)
|
||||||
@ -498,6 +498,16 @@ public func sentShareItems(accountPeerId: PeerId, postbox: Postbox, network: Net
|
|||||||
|
|
||||||
var peerSignals: Signal<Float, StandaloneSendMessagesError> = .single(0.0)
|
var peerSignals: Signal<Float, StandaloneSendMessagesError> = .single(0.0)
|
||||||
for peerId in peerIds {
|
for peerId in peerIds {
|
||||||
|
var peerMessages = messages
|
||||||
|
if let amount = requireStars[peerId] {
|
||||||
|
var updatedMessages: [StandaloneSendEnqueueMessage] = []
|
||||||
|
for message in peerMessages {
|
||||||
|
var message = message
|
||||||
|
message.sendPaidMessageStars = amount
|
||||||
|
updatedMessages.append(message)
|
||||||
|
}
|
||||||
|
peerMessages = updatedMessages
|
||||||
|
}
|
||||||
peerSignals = peerSignals |> then(standaloneSendEnqueueMessages(
|
peerSignals = peerSignals |> then(standaloneSendEnqueueMessages(
|
||||||
accountPeerId: accountPeerId,
|
accountPeerId: accountPeerId,
|
||||||
postbox: postbox,
|
postbox: postbox,
|
||||||
@ -506,7 +516,7 @@ public func sentShareItems(accountPeerId: PeerId, postbox: Postbox, network: Net
|
|||||||
auxiliaryMethods: auxiliaryMethods,
|
auxiliaryMethods: auxiliaryMethods,
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
threadId: threadIds[peerId],
|
threadId: threadIds[peerId],
|
||||||
messages: messages
|
messages: peerMessages
|
||||||
)
|
)
|
||||||
|> mapToSignal { status -> Signal<Float, StandaloneSendMessagesError> in
|
|> mapToSignal { status -> Signal<Float, StandaloneSendMessagesError> in
|
||||||
switch status {
|
switch status {
|
||||||
|
@ -1390,7 +1390,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
|
dict[1314881805] = { return Api.payments.PaymentResult.parse_paymentResult($0) }
|
||||||
dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) }
|
dict[-666824391] = { return Api.payments.PaymentResult.parse_paymentVerificationNeeded($0) }
|
||||||
dict[-74456004] = { return Api.payments.SavedInfo.parse_savedInfo($0) }
|
dict[-74456004] = { return Api.payments.SavedInfo.parse_savedInfo($0) }
|
||||||
dict[-1779201615] = { return Api.payments.SavedStarGifts.parse_savedStarGifts($0) }
|
dict[-418915641] = { return Api.payments.SavedStarGifts.parse_savedStarGifts($0) }
|
||||||
dict[377215243] = { return Api.payments.StarGiftUpgradePreview.parse_starGiftUpgradePreview($0) }
|
dict[377215243] = { return Api.payments.StarGiftUpgradePreview.parse_starGiftUpgradePreview($0) }
|
||||||
dict[-2069218660] = { return Api.payments.StarGiftWithdrawalUrl.parse_starGiftWithdrawalUrl($0) }
|
dict[-2069218660] = { return Api.payments.StarGiftWithdrawalUrl.parse_starGiftWithdrawalUrl($0) }
|
||||||
dict[-1877571094] = { return Api.payments.StarGifts.parse_starGifts($0) }
|
dict[-1877571094] = { return Api.payments.StarGifts.parse_starGifts($0) }
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
public extension Api.payments {
|
public extension Api.payments {
|
||||||
enum SavedStarGifts: TypeConstructorDescription {
|
enum SavedStarGifts: TypeConstructorDescription {
|
||||||
case savedStarGifts(flags: Int32, count: Int32, chatNotificationsEnabled: Api.Bool?, gifts: [Api.SavedStarGift], nextOffset: String?, chats: [Api.Chat], users: [Api.User])
|
case savedStarGifts(flags: Int32, count: Int32, chatNotificationsEnabled: Api.Bool?, pinnedToTop: [Int64]?, gifts: [Api.SavedStarGift], nextOffset: String?, chats: [Api.Chat], users: [Api.User])
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .savedStarGifts(let flags, let count, let chatNotificationsEnabled, let gifts, let nextOffset, let chats, let users):
|
case .savedStarGifts(let flags, let count, let chatNotificationsEnabled, let pinnedToTop, let gifts, let nextOffset, let chats, let users):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-1779201615)
|
buffer.appendInt32(-418915641)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt32(count, buffer: buffer, boxed: false)
|
serializeInt32(count, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 1) != 0 {chatNotificationsEnabled!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 1) != 0 {chatNotificationsEnabled!.serialize(buffer, true)}
|
||||||
|
if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(pinnedToTop!.count))
|
||||||
|
for item in pinnedToTop! {
|
||||||
|
serializeInt64(item, buffer: buffer, boxed: false)
|
||||||
|
}}
|
||||||
buffer.appendInt32(481674261)
|
buffer.appendInt32(481674261)
|
||||||
buffer.appendInt32(Int32(gifts.count))
|
buffer.appendInt32(Int32(gifts.count))
|
||||||
for item in gifts {
|
for item in gifts {
|
||||||
@ -33,8 +38,8 @@ public extension Api.payments {
|
|||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
switch self {
|
||||||
case .savedStarGifts(let flags, let count, let chatNotificationsEnabled, let gifts, let nextOffset, let chats, let users):
|
case .savedStarGifts(let flags, let count, let chatNotificationsEnabled, let pinnedToTop, let gifts, let nextOffset, let chats, let users):
|
||||||
return ("savedStarGifts", [("flags", flags as Any), ("count", count as Any), ("chatNotificationsEnabled", chatNotificationsEnabled as Any), ("gifts", gifts as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)])
|
return ("savedStarGifts", [("flags", flags as Any), ("count", count as Any), ("chatNotificationsEnabled", chatNotificationsEnabled as Any), ("pinnedToTop", pinnedToTop as Any), ("gifts", gifts as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,29 +52,34 @@ public extension Api.payments {
|
|||||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||||
_3 = Api.parse(reader, signature: signature) as? Api.Bool
|
_3 = Api.parse(reader, signature: signature) as? Api.Bool
|
||||||
} }
|
} }
|
||||||
var _4: [Api.SavedStarGift]?
|
var _4: [Int64]?
|
||||||
|
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
|
||||||
|
_4 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||||
|
} }
|
||||||
|
var _5: [Api.SavedStarGift]?
|
||||||
if let _ = reader.readInt32() {
|
if let _ = reader.readInt32() {
|
||||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedStarGift.self)
|
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedStarGift.self)
|
||||||
}
|
}
|
||||||
var _5: String?
|
var _6: String?
|
||||||
if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(reader) }
|
if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) }
|
||||||
var _6: [Api.Chat]?
|
var _7: [Api.Chat]?
|
||||||
if let _ = reader.readInt32() {
|
if let _ = reader.readInt32() {
|
||||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||||
}
|
}
|
||||||
var _7: [Api.User]?
|
var _8: [Api.User]?
|
||||||
if let _ = reader.readInt32() {
|
if let _ = reader.readInt32() {
|
||||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||||
}
|
}
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||||
let _c4 = _4 != nil
|
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
||||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
let _c5 = _5 != nil
|
||||||
let _c6 = _6 != nil
|
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||||
let _c7 = _7 != nil
|
let _c7 = _7 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
let _c8 = _8 != nil
|
||||||
return Api.payments.SavedStarGifts.savedStarGifts(flags: _1!, count: _2!, chatNotificationsEnabled: _3, gifts: _4!, nextOffset: _5, chats: _6!, users: _7!)
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
||||||
|
return Api.payments.SavedStarGifts.savedStarGifts(flags: _1!, count: _2!, chatNotificationsEnabled: _3, pinnedToTop: _4, gifts: _5!, nextOffset: _6, chats: _7!, users: _8!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -9717,6 +9717,26 @@ public extension Api.functions.payments {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public extension Api.functions.payments {
|
||||||
|
static func toggleStarGiftsPinnedToTop(peer: Api.InputPeer, stargift: [Api.InputSavedStarGift]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||||
|
let buffer = Buffer()
|
||||||
|
buffer.appendInt32(353626032)
|
||||||
|
peer.serialize(buffer, true)
|
||||||
|
buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(stargift.count))
|
||||||
|
for item in stargift {
|
||||||
|
item.serialize(buffer, true)
|
||||||
|
}
|
||||||
|
return (FunctionDescription(name: "payments.toggleStarGiftsPinnedToTop", parameters: [("peer", String(describing: peer)), ("stargift", String(describing: stargift))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||||
|
let reader = BufferReader(buffer)
|
||||||
|
var result: Api.Bool?
|
||||||
|
if let signature = reader.readInt32() {
|
||||||
|
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
public extension Api.functions.payments {
|
public extension Api.functions.payments {
|
||||||
static func transferStarGift(stargift: Api.InputSavedStarGift, toId: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
static func transferStarGift(stargift: Api.InputSavedStarGift, toId: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
|
@ -129,6 +129,7 @@ public func standaloneSendEnqueueMessages(
|
|||||||
struct MessageResult {
|
struct MessageResult {
|
||||||
var result: PendingMessageUploadedContentResult
|
var result: PendingMessageUploadedContentResult
|
||||||
var media: [Media]
|
var media: [Media]
|
||||||
|
var attributes: [MessageAttribute]
|
||||||
}
|
}
|
||||||
|
|
||||||
let signals: [Signal<MessageResult, PendingMessageUploadError>] = messages.map { message in
|
let signals: [Signal<MessageResult, PendingMessageUploadError>] = messages.map { message in
|
||||||
@ -178,6 +179,9 @@ public func standaloneSendEnqueueMessages(
|
|||||||
if message.isSilent {
|
if message.isSilent {
|
||||||
attributes.append(NotificationInfoMessageAttribute(flags: .muted))
|
attributes.append(NotificationInfoMessageAttribute(flags: .muted))
|
||||||
}
|
}
|
||||||
|
if let sendPaidMessageStars = message.sendPaidMessageStars {
|
||||||
|
attributes.append(PaidStarsMessageAttribute(stars: sendPaidMessageStars, postponeSending: false))
|
||||||
|
}
|
||||||
|
|
||||||
let content = messageContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: { _, _, _, _ in
|
let content = messageContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: { _, _, _, _ in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -191,7 +195,7 @@ public func standaloneSendEnqueueMessages(
|
|||||||
}
|
}
|
||||||
return contentResult
|
return contentResult
|
||||||
|> map { contentResult in
|
|> map { contentResult in
|
||||||
return MessageResult(result: contentResult, media: media)
|
return MessageResult(result: contentResult, media: media, attributes: attributes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +205,7 @@ public func standaloneSendEnqueueMessages(
|
|||||||
}
|
}
|
||||||
|> mapToSignal { contentResults -> Signal<StandaloneSendMessageStatus, StandaloneSendMessagesError> in
|
|> mapToSignal { contentResults -> Signal<StandaloneSendMessageStatus, StandaloneSendMessagesError> in
|
||||||
var progressSum: Float = 0.0
|
var progressSum: Float = 0.0
|
||||||
var allResults: [(result: PendingMessageUploadedContentAndReuploadInfo, media: [Media])] = []
|
var allResults: [(result: PendingMessageUploadedContentAndReuploadInfo, media: [Media], attributes: [MessageAttribute])] = []
|
||||||
var allDone = true
|
var allDone = true
|
||||||
for result in contentResults {
|
for result in contentResults {
|
||||||
switch result.result {
|
switch result.result {
|
||||||
@ -209,13 +213,13 @@ public func standaloneSendEnqueueMessages(
|
|||||||
allDone = false
|
allDone = false
|
||||||
progressSum += value.progress
|
progressSum += value.progress
|
||||||
case let .content(content):
|
case let .content(content):
|
||||||
allResults.append((content, result.media))
|
allResults.append((content, result.media, result.attributes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if allDone {
|
if allDone {
|
||||||
var sendSignals: [Signal<Never, StandaloneSendMessagesError>] = []
|
var sendSignals: [Signal<Never, StandaloneSendMessagesError>] = []
|
||||||
|
|
||||||
for (content, media) in allResults {
|
for (content, media, attributes) in allResults {
|
||||||
var text: String = ""
|
var text: String = ""
|
||||||
switch content.content {
|
switch content.content {
|
||||||
case let .text(textValue):
|
case let .text(textValue):
|
||||||
@ -235,7 +239,7 @@ public func standaloneSendEnqueueMessages(
|
|||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
content: content,
|
content: content,
|
||||||
text: text,
|
text: text,
|
||||||
attributes: [],
|
attributes: attributes,
|
||||||
media: media,
|
media: media,
|
||||||
threadId: threadId
|
threadId: threadId
|
||||||
))
|
))
|
||||||
@ -328,6 +332,7 @@ private func sendUploadedMessageContent(
|
|||||||
var videoTimestamp: Int32?
|
var videoTimestamp: Int32?
|
||||||
var sendAsPeerId: PeerId?
|
var sendAsPeerId: PeerId?
|
||||||
var bubbleUpEmojiOrStickersets = false
|
var bubbleUpEmojiOrStickersets = false
|
||||||
|
var allowPaidStars: Int64?
|
||||||
|
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
|
|
||||||
@ -365,6 +370,8 @@ private func sendUploadedMessageContent(
|
|||||||
} else if let attribute = attribute as? ForwardVideoTimestampAttribute {
|
} else if let attribute = attribute as? ForwardVideoTimestampAttribute {
|
||||||
flags |= Int32(1 << 20)
|
flags |= Int32(1 << 20)
|
||||||
videoTimestamp = attribute.timestamp
|
videoTimestamp = attribute.timestamp
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute {
|
||||||
|
allowPaidStars = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,6 +397,11 @@ private func sendUploadedMessageContent(
|
|||||||
flags |= (1 << 13)
|
flags |= (1 << 13)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let _ = allowPaidStars {
|
||||||
|
flags |= 1 << 21
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let dependencyTag: PendingMessageRequestDependencyTag? = nil//(messageId: messageId)
|
let dependencyTag: PendingMessageRequestDependencyTag? = nil//(messageId: messageId)
|
||||||
|
|
||||||
let sendMessageRequest: Signal<NetworkRequestResult<Api.Updates>, MTRpcError>
|
let sendMessageRequest: Signal<NetworkRequestResult<Api.Updates>, MTRpcError>
|
||||||
@ -415,7 +427,7 @@ private func sendUploadedMessageContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: nil), info: .acknowledgement, tag: dependencyTag)
|
sendMessageRequest = network.requestWithAdditionalInfo(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars), info: .acknowledgement, tag: dependencyTag)
|
||||||
case let .media(inputMedia, text):
|
case let .media(inputMedia, text):
|
||||||
if bubbleUpEmojiOrStickersets {
|
if bubbleUpEmojiOrStickersets {
|
||||||
flags |= Int32(1 << 15)
|
flags |= Int32(1 << 15)
|
||||||
@ -437,7 +449,7 @@ private func sendUploadedMessageContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: nil), tag: dependencyTag)
|
sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars), tag: dependencyTag)
|
||||||
|> map(NetworkRequestResult.result)
|
|> map(NetworkRequestResult.result)
|
||||||
case let .forward(sourceInfo):
|
case let .forward(sourceInfo):
|
||||||
var topMsgId: Int32?
|
var topMsgId: Int32?
|
||||||
@ -447,7 +459,7 @@ private func sendUploadedMessageContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let forwardSourceInfoAttribute = forwardSourceInfoAttribute, let sourcePeer = transaction.getPeer(forwardSourceInfoAttribute.messageId.peerId), let sourceInputPeer = apiInputPeer(sourcePeer) {
|
if let forwardSourceInfoAttribute = forwardSourceInfoAttribute, let sourcePeer = transaction.getPeer(forwardSourceInfoAttribute.messageId.peerId), let sourceInputPeer = apiInputPeer(sourcePeer) {
|
||||||
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer, topMsgId: topMsgId, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, videoTimestamp: videoTimestamp, allowPaidStars: nil), tag: dependencyTag)
|
sendMessageRequest = network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: sourceInputPeer, id: [sourceInfo.messageId.id], randomId: [uniqueId], toPeer: inputPeer, topMsgId: topMsgId, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, videoTimestamp: videoTimestamp, allowPaidStars: allowPaidStars), tag: dependencyTag)
|
||||||
|> map(NetworkRequestResult.result)
|
|> map(NetworkRequestResult.result)
|
||||||
} else {
|
} else {
|
||||||
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
sendMessageRequest = .fail(MTRpcError(errorCode: 400, errorDescription: "internal"))
|
||||||
@ -473,7 +485,7 @@ private func sendUploadedMessageContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessageRequest = network.request(Api.functions.messages.sendInlineBotResult(flags: flags, peer: inputPeer, replyTo: replyTo, randomId: uniqueId, queryId: chatContextResult.queryId, id: chatContextResult.id, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, allowPaidStars: nil))
|
sendMessageRequest = network.request(Api.functions.messages.sendInlineBotResult(flags: flags, peer: inputPeer, replyTo: replyTo, randomId: uniqueId, queryId: chatContextResult.queryId, id: chatContextResult.id, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, allowPaidStars: allowPaidStars))
|
||||||
|> map(NetworkRequestResult.result)
|
|> map(NetworkRequestResult.result)
|
||||||
case .messageScreenshot:
|
case .messageScreenshot:
|
||||||
let replyTo: Api.InputReplyTo
|
let replyTo: Api.InputReplyTo
|
||||||
@ -585,6 +597,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|
|||||||
var replyToStoryId: StoryId?
|
var replyToStoryId: StoryId?
|
||||||
var scheduleTime: Int32?
|
var scheduleTime: Int32?
|
||||||
var sendAsPeerId: PeerId?
|
var sendAsPeerId: PeerId?
|
||||||
|
var allowPaidStars: Int64?
|
||||||
|
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
flags |= (1 << 7)
|
flags |= (1 << 7)
|
||||||
@ -609,6 +622,8 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|
|||||||
scheduleTime = attribute.scheduleTime
|
scheduleTime = attribute.scheduleTime
|
||||||
} else if let attribute = attribute as? SendAsMessageAttribute {
|
} else if let attribute = attribute as? SendAsMessageAttribute {
|
||||||
sendAsPeerId = attribute.peerId
|
sendAsPeerId = attribute.peerId
|
||||||
|
} else if let attribute = attribute as? PaidStarsMessageAttribute {
|
||||||
|
allowPaidStars = attribute.stars.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -622,6 +637,11 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|
|||||||
flags |= (1 << 13)
|
flags |= (1 << 13)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let _ = allowPaidStars {
|
||||||
|
flags |= 1 << 21
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let sendMessageRequest: Signal<Api.Updates, NoError>
|
let sendMessageRequest: Signal<Api.Updates, NoError>
|
||||||
switch content {
|
switch content {
|
||||||
case let .text(text):
|
case let .text(text):
|
||||||
@ -641,7 +661,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|
|||||||
replyTo = .inputReplyToMessage(flags: flags, replyToMsgId: threadId, topMsgId: threadId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
replyTo = .inputReplyToMessage(flags: flags, replyToMsgId: threadId, topMsgId: threadId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessageRequest = account.network.request(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: nil))
|
sendMessageRequest = account.network.request(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars))
|
||||||
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
@ -662,7 +682,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|
|||||||
replyTo = .inputReplyToMessage(flags: flags, replyToMsgId: threadId, topMsgId: threadId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
replyTo = .inputReplyToMessage(flags: flags, replyToMsgId: threadId, topMsgId: threadId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: nil))
|
sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyTo: replyTo, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars))
|
||||||
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
|> `catch` { _ -> Signal<Api.Updates, NoError> in
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ public struct CachedChannelFlags: OptionSet {
|
|||||||
public static let paidMediaAllowed = CachedChannelFlags(rawValue: 1 << 11)
|
public static let paidMediaAllowed = CachedChannelFlags(rawValue: 1 << 11)
|
||||||
public static let canViewStarsRevenue = CachedChannelFlags(rawValue: 1 << 12)
|
public static let canViewStarsRevenue = CachedChannelFlags(rawValue: 1 << 12)
|
||||||
public static let starGiftsAvailable = CachedChannelFlags(rawValue: 1 << 13)
|
public static let starGiftsAvailable = CachedChannelFlags(rawValue: 1 << 13)
|
||||||
|
public static let paidMessagesAvailable = CachedChannelFlags(rawValue: 1 << 14)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct CachedChannelParticipantsSummary: PostboxCoding, Equatable {
|
public struct CachedChannelParticipantsSummary: PostboxCoding, Equatable {
|
||||||
|
@ -375,22 +375,18 @@ public extension TelegramEngine.EngineData.Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var key: PostboxViewKey {
|
var key: PostboxViewKey {
|
||||||
return .cachedPeerData(peerId: self.id)
|
return .peer(peerId: self.id, components: [.cachedData])
|
||||||
}
|
}
|
||||||
|
|
||||||
func extract(view: PostboxView) -> Result {
|
func extract(view: PostboxView) -> Result {
|
||||||
guard let view = view as? CachedPeerDataView else {
|
guard let view = view as? PeerView else {
|
||||||
preconditionFailure()
|
preconditionFailure()
|
||||||
}
|
}
|
||||||
guard let cachedPeerData = view.cachedPeerData else {
|
if let cachedPeerData = view.cachedData as? CachedUserData {
|
||||||
return nil
|
return cachedPeerData.sendPaidMessageStars
|
||||||
}
|
} else if let channel = peerViewMainPeer(view) as? TelegramChannel {
|
||||||
switch cachedPeerData {
|
|
||||||
case let user as CachedUserData:
|
|
||||||
return user.sendPaidMessageStars
|
|
||||||
case let channel as CachedChannelData:
|
|
||||||
return channel.sendPaidMessageStars
|
return channel.sendPaidMessageStars
|
||||||
default:
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,15 @@ import Foundation
|
|||||||
import Postbox
|
import Postbox
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
|
|
||||||
func _internal_enqueueOutgoingMessageWithChatContextResult(account: Account, to peerId: PeerId, threadId: Int64?, botId: PeerId, result: ChatContextResult, replyToMessageId: EngineMessageReplySubject?, replyToStoryId: StoryId?, hideVia: Bool, silentPosting: Bool, scheduleTime: Int32?, correlationId: Int64?) -> Bool {
|
func _internal_enqueueOutgoingMessageWithChatContextResult(account: Account, to peerId: PeerId, threadId: Int64?, botId: PeerId, result: ChatContextResult, replyToMessageId: EngineMessageReplySubject?, replyToStoryId: StoryId?, hideVia: Bool, silentPosting: Bool, scheduleTime: Int32?, sendPaidMessageStars: StarsAmount?, postpone: Bool, correlationId: Int64?) -> Bool {
|
||||||
guard let message = _internal_outgoingMessageWithChatContextResult(to: peerId, threadId: threadId, botId: botId, result: result, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, hideVia: hideVia, silentPosting: silentPosting, scheduleTime: scheduleTime, correlationId: correlationId) else {
|
guard let message = _internal_outgoingMessageWithChatContextResult(to: peerId, threadId: threadId, botId: botId, result: result, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, hideVia: hideVia, silentPosting: silentPosting, scheduleTime: scheduleTime, sendPaidMessageStars: sendPaidMessageStars, postpone: postpone, correlationId: correlationId) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let _ = enqueueMessages(account: account, peerId: peerId, messages: [message]).start()
|
let _ = enqueueMessages(account: account, peerId: peerId, messages: [message]).start()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_outgoingMessageWithChatContextResult(to peerId: PeerId, threadId: Int64?, botId: PeerId, result: ChatContextResult, replyToMessageId: EngineMessageReplySubject?, replyToStoryId: StoryId?, hideVia: Bool, silentPosting: Bool, scheduleTime: Int32?, correlationId: Int64?) -> EnqueueMessage? {
|
func _internal_outgoingMessageWithChatContextResult(to peerId: PeerId, threadId: Int64?, botId: PeerId, result: ChatContextResult, replyToMessageId: EngineMessageReplySubject?, replyToStoryId: StoryId?, hideVia: Bool, silentPosting: Bool, scheduleTime: Int32?, sendPaidMessageStars: StarsAmount?, postpone: Bool, correlationId: Int64?) -> EnqueueMessage? {
|
||||||
var replyToMessageId = replyToMessageId
|
var replyToMessageId = replyToMessageId
|
||||||
if replyToMessageId == nil, let threadId = threadId {
|
if replyToMessageId == nil, let threadId = threadId {
|
||||||
replyToMessageId = EngineMessageReplySubject(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: MessageId.Id(clamping: threadId)), quote: nil)
|
replyToMessageId = EngineMessageReplySubject(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: MessageId.Id(clamping: threadId)), quote: nil)
|
||||||
@ -32,6 +32,9 @@ func _internal_outgoingMessageWithChatContextResult(to peerId: PeerId, threadId:
|
|||||||
if silentPosting {
|
if silentPosting {
|
||||||
attributes.append(NotificationInfoMessageAttribute(flags: .muted))
|
attributes.append(NotificationInfoMessageAttribute(flags: .muted))
|
||||||
}
|
}
|
||||||
|
if let sendPaidMessageStars {
|
||||||
|
attributes.append(PaidStarsMessageAttribute(stars: sendPaidMessageStars, postponeSending: postpone))
|
||||||
|
}
|
||||||
switch result.message {
|
switch result.message {
|
||||||
case let .auto(caption, entities, replyMarkup):
|
case let .auto(caption, entities, replyMarkup):
|
||||||
if let entities = entities {
|
if let entities = entities {
|
||||||
|
@ -247,13 +247,14 @@ public extension TelegramEngine {
|
|||||||
storyId: StoryId? = nil,
|
storyId: StoryId? = nil,
|
||||||
content: EngineOutgoingMessageContent,
|
content: EngineOutgoingMessageContent,
|
||||||
silentPosting: Bool = false,
|
silentPosting: Bool = false,
|
||||||
scheduleTime: Int32? = nil
|
scheduleTime: Int32? = nil,
|
||||||
|
sendPaidMessageStars: StarsAmount? = nil
|
||||||
) -> Signal<[MessageId?], NoError> {
|
) -> Signal<[MessageId?], NoError> {
|
||||||
var message: EnqueueMessage?
|
var message: EnqueueMessage?
|
||||||
if case let .preparedInlineMessage(preparedInlineMessage) = content {
|
if case let .preparedInlineMessage(preparedInlineMessage) = content {
|
||||||
message = self.outgoingMessageWithChatContextResult(to: peerId, threadId: nil, botId: preparedInlineMessage.botId, result: preparedInlineMessage.result, replyToMessageId: replyToMessageId, replyToStoryId: storyId, hideVia: true, silentPosting: silentPosting, scheduleTime: scheduleTime, correlationId: nil)
|
message = self.outgoingMessageWithChatContextResult(to: peerId, threadId: nil, botId: preparedInlineMessage.botId, result: preparedInlineMessage.result, replyToMessageId: replyToMessageId, replyToStoryId: storyId, hideVia: true, silentPosting: silentPosting, scheduleTime: scheduleTime, sendPaidMessageStars: sendPaidMessageStars, postpone: false, correlationId: nil)
|
||||||
} else if case let .contextResult(results, result) = content {
|
} else if case let .contextResult(results, result) = content {
|
||||||
message = self.outgoingMessageWithChatContextResult(to: peerId, threadId: nil, botId: results.botId, result: result, replyToMessageId: replyToMessageId, replyToStoryId: storyId, hideVia: true, silentPosting: silentPosting, scheduleTime: scheduleTime, correlationId: nil)
|
message = self.outgoingMessageWithChatContextResult(to: peerId, threadId: nil, botId: results.botId, result: result, replyToMessageId: replyToMessageId, replyToStoryId: storyId, hideVia: true, silentPosting: silentPosting, scheduleTime: scheduleTime, sendPaidMessageStars: sendPaidMessageStars, postpone: false, correlationId: nil)
|
||||||
} else {
|
} else {
|
||||||
var attributes: [MessageAttribute] = []
|
var attributes: [MessageAttribute] = []
|
||||||
if silentPosting {
|
if silentPosting {
|
||||||
@ -262,6 +263,9 @@ public extension TelegramEngine {
|
|||||||
if let scheduleTime = scheduleTime {
|
if let scheduleTime = scheduleTime {
|
||||||
attributes.append(OutgoingScheduleInfoMessageAttribute(scheduleTime: scheduleTime))
|
attributes.append(OutgoingScheduleInfoMessageAttribute(scheduleTime: scheduleTime))
|
||||||
}
|
}
|
||||||
|
if let sendPaidMessageStars {
|
||||||
|
attributes.append(PaidStarsMessageAttribute(stars: sendPaidMessageStars, postponeSending: false))
|
||||||
|
}
|
||||||
|
|
||||||
var text: String = ""
|
var text: String = ""
|
||||||
var mediaReference: AnyMediaReference?
|
var mediaReference: AnyMediaReference?
|
||||||
@ -301,12 +305,12 @@ public extension TelegramEngine {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func enqueueOutgoingMessageWithChatContextResult(to peerId: PeerId, threadId: Int64?, botId: PeerId, result: ChatContextResult, replyToMessageId: EngineMessageReplySubject? = nil, replyToStoryId: StoryId? = nil, hideVia: Bool = false, silentPosting: Bool = false, scheduleTime: Int32? = nil, correlationId: Int64? = nil) -> Bool {
|
public func enqueueOutgoingMessageWithChatContextResult(to peerId: PeerId, threadId: Int64?, botId: PeerId, result: ChatContextResult, replyToMessageId: EngineMessageReplySubject? = nil, replyToStoryId: StoryId? = nil, hideVia: Bool = false, silentPosting: Bool = false, scheduleTime: Int32? = nil, sendPaidMessageStars: StarsAmount?, postpone: Bool = false, correlationId: Int64? = nil) -> Bool {
|
||||||
return _internal_enqueueOutgoingMessageWithChatContextResult(account: self.account, to: peerId, threadId: threadId, botId: botId, result: result, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, hideVia: hideVia, silentPosting: silentPosting, scheduleTime: scheduleTime, correlationId: correlationId)
|
return _internal_enqueueOutgoingMessageWithChatContextResult(account: self.account, to: peerId, threadId: threadId, botId: botId, result: result, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, hideVia: hideVia, silentPosting: silentPosting, scheduleTime: scheduleTime, sendPaidMessageStars: sendPaidMessageStars, postpone: postpone, correlationId: correlationId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func outgoingMessageWithChatContextResult(to peerId: PeerId, threadId: Int64?, botId: PeerId, result: ChatContextResult, replyToMessageId: EngineMessageReplySubject?, replyToStoryId: StoryId?, hideVia: Bool, silentPosting: Bool, scheduleTime: Int32?, correlationId: Int64?) -> EnqueueMessage? {
|
public func outgoingMessageWithChatContextResult(to peerId: PeerId, threadId: Int64?, botId: PeerId, result: ChatContextResult, replyToMessageId: EngineMessageReplySubject?, replyToStoryId: StoryId?, hideVia: Bool, silentPosting: Bool, scheduleTime: Int32?, sendPaidMessageStars: StarsAmount?, postpone: Bool, correlationId: Int64?) -> EnqueueMessage? {
|
||||||
return _internal_outgoingMessageWithChatContextResult(to: peerId, threadId: threadId, botId: botId, result: result, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, hideVia: hideVia, silentPosting: silentPosting, scheduleTime: scheduleTime, correlationId: correlationId)
|
return _internal_outgoingMessageWithChatContextResult(to: peerId, threadId: threadId, botId: botId, result: result, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, hideVia: hideVia, silentPosting: silentPosting, scheduleTime: scheduleTime, sendPaidMessageStars: sendPaidMessageStars, postpone: postpone, correlationId: correlationId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setMessageReactions(
|
public func setMessageReactions(
|
||||||
|
@ -1064,7 +1064,8 @@ private final class ProfileGiftsContextImpl {
|
|||||||
}
|
}
|
||||||
return postbox.transaction { transaction -> ([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?) in
|
return postbox.transaction { transaction -> ([ProfileGiftsContext.State.StarGift], Int32, String?, Bool?) in
|
||||||
switch result {
|
switch result {
|
||||||
case let .savedStarGifts(_, count, apiNotificationsEnabled, apiGifts, nextOffset, chats, users):
|
case let .savedStarGifts(_, count, apiNotificationsEnabled, pinnedToTop, apiGifts, nextOffset, chats, users):
|
||||||
|
let _ = pinnedToTop
|
||||||
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
|
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
|
||||||
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
|
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ func _internal_addGroupMember(account: Account, peerId: PeerId, memberId: PeerId
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return TelegramInvitePeersResult(forbiddenPeers: missingInviteesValue.compactMap { invitee -> TelegramForbiddenInvitePeer? in
|
let result = TelegramInvitePeersResult(forbiddenPeers: missingInviteesValue.compactMap { invitee -> TelegramForbiddenInvitePeer? in
|
||||||
switch invitee {
|
switch invitee {
|
||||||
case let .missingInvitee(flags, userId):
|
case let .missingInvitee(flags, userId):
|
||||||
guard let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))) else {
|
guard let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))) else {
|
||||||
@ -113,6 +113,10 @@ func _internal_addGroupMember(account: Account, peerId: PeerId, memberId: PeerId
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let _ = _internal_updateIsPremiumRequiredToContact(account: account, peerIds: result.forbiddenPeers.map { $0.peer.id }).startStandalone()
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|> mapError { _ -> AddGroupMemberError in }
|
|> mapError { _ -> AddGroupMemberError in }
|
||||||
|> mapToSignal { result -> Signal<Void, AddGroupMemberError> in
|
|> mapToSignal { result -> Signal<Void, AddGroupMemberError> in
|
||||||
@ -186,6 +190,8 @@ func _internal_addChannelMember(account: Account, peerId: PeerId, memberId: Peer
|
|||||||
switch result {
|
switch result {
|
||||||
case let .invitedUsers(updates, missingInvitees):
|
case let .invitedUsers(updates, missingInvitees):
|
||||||
if case let .missingInvitee(flags, _) = missingInvitees.first {
|
if case let .missingInvitee(flags, _) = missingInvitees.first {
|
||||||
|
let _ = _internal_updateIsPremiumRequiredToContact(account: account, peerIds: [memberPeer.id]).startStandalone()
|
||||||
|
|
||||||
return .fail(.restricted(TelegramForbiddenInvitePeer(
|
return .fail(.restricted(TelegramForbiddenInvitePeer(
|
||||||
peer: EnginePeer(memberPeer),
|
peer: EnginePeer(memberPeer),
|
||||||
canInviteWithPremium: (flags & (1 << 0)) != 0,
|
canInviteWithPremium: (flags & (1 << 0)) != 0,
|
||||||
@ -302,7 +308,7 @@ func _internal_addChannelMembers(account: Account, peerId: PeerId, memberIds: [P
|
|||||||
account.viewTracker.forceUpdateCachedPeerData(peerId: peerId)
|
account.viewTracker.forceUpdateCachedPeerData(peerId: peerId)
|
||||||
|
|
||||||
return account.postbox.transaction { transaction -> TelegramInvitePeersResult in
|
return account.postbox.transaction { transaction -> TelegramInvitePeersResult in
|
||||||
return TelegramInvitePeersResult(forbiddenPeers: missingInviteesValue.compactMap { invitee -> TelegramForbiddenInvitePeer? in
|
let result = TelegramInvitePeersResult(forbiddenPeers: missingInviteesValue.compactMap { invitee -> TelegramForbiddenInvitePeer? in
|
||||||
switch invitee {
|
switch invitee {
|
||||||
case let .missingInvitee(flags, userId):
|
case let .missingInvitee(flags, userId):
|
||||||
guard let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))) else {
|
guard let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))) else {
|
||||||
@ -315,6 +321,8 @@ func _internal_addChannelMembers(account: Account, peerId: PeerId, memberIds: [P
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
let _ = _internal_updateIsPremiumRequiredToContact(account: account, peerIds: result.forbiddenPeers.map { $0.peer.id }).startStandalone()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|> castError(AddChannelMemberError.self)
|
|> castError(AddChannelMemberError.self)
|
||||||
}
|
}
|
||||||
|
@ -620,6 +620,10 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
|
|||||||
if (flags2 & Int32(1 << 19)) != 0 {
|
if (flags2 & Int32(1 << 19)) != 0 {
|
||||||
channelFlags.insert(.starGiftsAvailable)
|
channelFlags.insert(.starGiftsAvailable)
|
||||||
}
|
}
|
||||||
|
if (flags2 & Int32(1 << 20)) != 0 {
|
||||||
|
channelFlags.insert(.paidMessagesAvailable)
|
||||||
|
}
|
||||||
|
|
||||||
let sendAsPeerId = defaultSendAs?.peerId
|
let sendAsPeerId = defaultSendAs?.peerId
|
||||||
|
|
||||||
let linkedDiscussionPeerId: PeerId?
|
let linkedDiscussionPeerId: PeerId?
|
||||||
|
@ -2877,7 +2877,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
bottomBubbleAttributes = contentPropertiesAndLayouts[i + 1].3
|
bottomBubbleAttributes = contentPropertiesAndLayouts[i + 1].3
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 || (i == 1 && contentPropertiesAndLayouts[0].1.isDetached) {
|
||||||
topPosition = firstNodeTopPosition
|
topPosition = firstNodeTopPosition
|
||||||
} else {
|
} else {
|
||||||
topPosition = .Neighbour(topBubbleAttributes.isAttachment, topBubbleAttributes.neighborType, topBubbleAttributes.neighborSpacing)
|
topPosition = .Neighbour(topBubbleAttributes.isAttachment, topBubbleAttributes.neighborType, topBubbleAttributes.neighborSpacing)
|
||||||
@ -3002,11 +3002,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (size, apply) = finalize(maxContentWidth)
|
let (size, apply) = finalize(maxContentWidth)
|
||||||
var containerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentNodeOriginY), size: size)
|
let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentNodeOriginY), size: size)
|
||||||
if size.height == 33.0 && detachedContentNodesHeight > 0.0 {
|
|
||||||
//TODO:unmock
|
|
||||||
containerFrame = containerFrame.offsetBy(dx: 0.0, dy: 2.0)
|
|
||||||
}
|
|
||||||
contentNodeFramesPropertiesAndApply.append((containerFrame, properties, contentGroupId == nil, apply))
|
contentNodeFramesPropertiesAndApply.append((containerFrame, properties, contentGroupId == nil, apply))
|
||||||
|
|
||||||
if contentProperties.neighborType == .media && unlockButtonPosition == nil {
|
if contentProperties.neighborType == .media && unlockButtonPosition == nil {
|
||||||
|
@ -330,21 +330,32 @@ private class ChatMessagePaymentAlertController: AlertController {
|
|||||||
self.parentNavigationController = navigationController
|
self.parentNavigationController = navigationController
|
||||||
|
|
||||||
super.init(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
|
super.init(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
|
||||||
|
|
||||||
|
self.willDismiss = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.animateOut()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(coder aDecoder: NSCoder) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
preconditionFailure()
|
preconditionFailure()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func dismissAnimated() {
|
private func animateOut() {
|
||||||
super.dismissAnimated()
|
|
||||||
|
|
||||||
if let view = self.balance.view {
|
if let view = self.balance.view {
|
||||||
view.layer.animateScale(from: 1.0, to: 0.8, duration: 0.4, removeOnCompletion: false)
|
view.layer.animateScale(from: 1.0, to: 0.8, duration: 0.4, removeOnCompletion: false)
|
||||||
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func dismissAnimated() {
|
||||||
|
super.dismissAnimated()
|
||||||
|
|
||||||
|
self.animateOut()
|
||||||
|
}
|
||||||
|
|
||||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
super.containerLayoutUpdated(layout, transition: transition)
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
|
||||||
|
@ -251,6 +251,9 @@ public final class ChatUserInfoItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
let infoConstrainedSize = CGSize(width: constrainedWidth * 0.7, height: CGFloat.greatestFiniteMagnitude)
|
let infoConstrainedSize = CGSize(width: constrainedWidth * 0.7, height: CGFloat.greatestFiniteMagnitude)
|
||||||
|
|
||||||
|
var maxTitleWidth: CGFloat = 0.0
|
||||||
|
var maxValueWidth: CGFloat = 0.0
|
||||||
|
|
||||||
var registrationDateText: String?
|
var registrationDateText: String?
|
||||||
let registrationDateTitleLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
let registrationDateTitleLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
||||||
let registrationDateValueLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
let registrationDateValueLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
||||||
@ -272,7 +275,8 @@ public final class ChatUserInfoItemNode: ListViewItemNode {
|
|||||||
backgroundSize.height += verticalSpacing
|
backgroundSize.height += verticalSpacing
|
||||||
backgroundSize.height += registrationDateValueLayoutAndApply?.0.size.height ?? 0
|
backgroundSize.height += registrationDateValueLayoutAndApply?.0.size.height ?? 0
|
||||||
|
|
||||||
backgroundSize.width = max(backgroundSize.width, horizontalContentInset * 2.0 + (registrationDateTitleLayoutAndApply?.0.size.width ?? 0) + attributeSpacing + (registrationDateValueLayoutAndApply?.0.size.width ?? 0))
|
maxTitleWidth = max(maxTitleWidth, (registrationDateTitleLayoutAndApply?.0.size.width ?? 0))
|
||||||
|
maxValueWidth = max(maxValueWidth, (registrationDateValueLayoutAndApply?.0.size.width ?? 0))
|
||||||
} else {
|
} else {
|
||||||
registrationDateTitleLayoutAndApply = nil
|
registrationDateTitleLayoutAndApply = nil
|
||||||
registrationDateValueLayoutAndApply = nil
|
registrationDateValueLayoutAndApply = nil
|
||||||
@ -297,7 +301,8 @@ public final class ChatUserInfoItemNode: ListViewItemNode {
|
|||||||
backgroundSize.height += verticalSpacing
|
backgroundSize.height += verticalSpacing
|
||||||
backgroundSize.height += phoneCountryValueLayoutAndApply?.0.size.height ?? 0
|
backgroundSize.height += phoneCountryValueLayoutAndApply?.0.size.height ?? 0
|
||||||
|
|
||||||
backgroundSize.width = max(backgroundSize.width, horizontalContentInset * 2.0 + (phoneCountryTitleLayoutAndApply?.0.size.width ?? 0) + attributeSpacing + (phoneCountryValueLayoutAndApply?.0.size.width ?? 0))
|
maxTitleWidth = max(maxTitleWidth, (phoneCountryTitleLayoutAndApply?.0.size.width ?? 0))
|
||||||
|
maxValueWidth = max(maxValueWidth, (phoneCountryValueLayoutAndApply?.0.size.width ?? 0))
|
||||||
} else {
|
} else {
|
||||||
phoneCountryTitleLayoutAndApply = nil
|
phoneCountryTitleLayoutAndApply = nil
|
||||||
phoneCountryValueLayoutAndApply = nil
|
phoneCountryValueLayoutAndApply = nil
|
||||||
@ -322,12 +327,15 @@ public final class ChatUserInfoItemNode: ListViewItemNode {
|
|||||||
backgroundSize.height += verticalSpacing
|
backgroundSize.height += verticalSpacing
|
||||||
backgroundSize.height += locationCountryValueLayoutAndApply?.0.size.height ?? 0
|
backgroundSize.height += locationCountryValueLayoutAndApply?.0.size.height ?? 0
|
||||||
|
|
||||||
backgroundSize.width = max(backgroundSize.width, horizontalContentInset * 2.0 + (locationCountryTitleLayoutAndApply?.0.size.width ?? 0) + attributeSpacing + (locationCountryValueLayoutAndApply?.0.size.width ?? 0))
|
maxTitleWidth = max(maxTitleWidth, (locationCountryTitleLayoutAndApply?.0.size.width ?? 0))
|
||||||
|
maxValueWidth = max(maxValueWidth, (locationCountryValueLayoutAndApply?.0.size.width ?? 0))
|
||||||
} else {
|
} else {
|
||||||
locationCountryTitleLayoutAndApply = nil
|
locationCountryTitleLayoutAndApply = nil
|
||||||
locationCountryValueLayoutAndApply = nil
|
locationCountryValueLayoutAndApply = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backgroundSize.width = horizontalContentInset * 3.0 + maxTitleWidth + attributeSpacing + maxValueWidth
|
||||||
|
|
||||||
let (groupsLayout, groupsApply) = makeGroupsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "No groups in common", font: Font.regular(13.0), textColor: subtitleColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (groupsLayout, groupsApply) = makeGroupsLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "No groups in common", font: Font.regular(13.0), textColor: subtitleColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
backgroundSize.height += verticalSpacing * 2.0 + paragraphSpacing
|
backgroundSize.height += verticalSpacing * 2.0 + paragraphSpacing
|
||||||
backgroundSize.height += groupsLayout.size.height
|
backgroundSize.height += groupsLayout.size.height
|
||||||
|
@ -502,9 +502,7 @@ final class GiftOptionsScreenComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:unmock
|
|
||||||
let context = component.context
|
let context = component.context
|
||||||
|
|
||||||
let alertController = giftTransferAlertController(
|
let alertController = giftTransferAlertController(
|
||||||
context: context,
|
context: context,
|
||||||
gift: transferGift,
|
gift: transferGift,
|
||||||
@ -517,9 +515,18 @@ final class GiftOptionsScreenComponent: Component {
|
|||||||
guard let controller, let navigationController = controller.navigationController as? NavigationController else {
|
guard let controller, let navigationController = controller.navigationController as? NavigationController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var controllers = navigationController.viewControllers
|
|
||||||
controllers = controllers.filter { !($0 is ContactSelectionController) && !($0 is GiftOptionsScreen) }
|
|
||||||
if peer.id.namespace == Namespaces.Peer.CloudChannel {
|
if peer.id.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
var controllers = navigationController.viewControllers
|
||||||
|
controllers = controllers.filter { !($0 is GiftSetupScreen) && !($0 is GiftOptionsScreenProtocol) }
|
||||||
|
var foundController = false
|
||||||
|
for controller in controllers.reversed() {
|
||||||
|
if let controller = controller as? PeerInfoScreen, controller.peerId == component.peerId {
|
||||||
|
foundController = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundController {
|
||||||
if let controller = context.sharedContext.makePeerInfoController(
|
if let controller = context.sharedContext.makePeerInfoController(
|
||||||
context: context,
|
context: context,
|
||||||
updatedPresentationData: nil,
|
updatedPresentationData: nil,
|
||||||
@ -531,23 +538,31 @@ final class GiftOptionsScreenComponent: Component {
|
|||||||
) {
|
) {
|
||||||
controllers.append(controller)
|
controllers.append(controller)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
navigationController.setViewControllers(controllers, animated: true)
|
||||||
} else {
|
} else {
|
||||||
|
var controllers = navigationController.viewControllers
|
||||||
|
controllers = controllers.filter { !($0 is GiftSetupScreen) && !($0 is GiftOptionsScreenProtocol) && !($0 is PeerInfoScreen) && !($0 is ContactSelectionController) }
|
||||||
var foundController = false
|
var foundController = false
|
||||||
for controller in controllers.reversed() {
|
for controller in controllers.reversed() {
|
||||||
if let chatController = controller as? ChatController, case .peer(id: peer.id) = chatController.chatLocation {
|
if let chatController = controller as? ChatController, case .peer(id: component.peerId) = chatController.chatLocation {
|
||||||
chatController.hintPlayNextOutgoingGift()
|
chatController.hintPlayNextOutgoingGift()
|
||||||
foundController = true
|
foundController = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !foundController {
|
if !foundController {
|
||||||
let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(.default), params: nil)
|
let chatController = component.context.sharedContext.makeChatController(context: component.context, chatLocation: .peer(id: component.peerId), subject: nil, botStart: nil, mode: .standard(.default), params: nil)
|
||||||
chatController.hintPlayNextOutgoingGift()
|
chatController.hintPlayNextOutgoingGift()
|
||||||
controllers.append(chatController)
|
controllers.append(chatController)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
navigationController.setViewControllers(controllers, animated: true)
|
navigationController.setViewControllers(controllers, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let completion = component.completion {
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
controller.present(alertController, in: .window(.root))
|
controller.present(alertController, in: .window(.root))
|
||||||
}
|
}
|
||||||
|
@ -253,7 +253,7 @@ public func presentedLegacyShortcutCamera(context: AccountContext, saveCapturedM
|
|||||||
nativeGenerator(_1, _2, _3, nil)
|
nativeGenerator(_1, _2, _3, nil)
|
||||||
})
|
})
|
||||||
if let parentController = parentController {
|
if let parentController = parentController {
|
||||||
parentController.present(ShareController(context: context, subject: .fromExternal({ peerIds, _, text, account, silently in
|
parentController.present(ShareController(context: context, subject: .fromExternal(1, { peerIds, _, _, text, account, silently in
|
||||||
guard let account = account as? ShareControllerAppAccountContext else {
|
guard let account = account as? ShareControllerAppAccountContext else {
|
||||||
return .single(.done)
|
return .single(.done)
|
||||||
}
|
}
|
||||||
|
@ -214,6 +214,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView {
|
|||||||
strings: presentationData.strings,
|
strings: presentationData.strings,
|
||||||
style: .media,
|
style: .media,
|
||||||
placeholder: .plain(presentationData.strings.MediaPicker_AddCaption),
|
placeholder: .plain(presentationData.strings.MediaPicker_AddCaption),
|
||||||
|
sendPaidMessageStars: nil,
|
||||||
maxLength: Int(self.context.userLimits.maxCaptionLength),
|
maxLength: Int(self.context.userLimits.maxCaptionLength),
|
||||||
queryTypes: [.mention, .hashtag],
|
queryTypes: [.mention, .hashtag],
|
||||||
alwaysDarkWhenHasText: false,
|
alwaysDarkWhenHasText: false,
|
||||||
|
@ -1365,6 +1365,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
strings: environment.strings,
|
strings: environment.strings,
|
||||||
style: .editor,
|
style: .editor,
|
||||||
placeholder: .plain(environment.strings.Story_Editor_InputPlaceholderAddCaption),
|
placeholder: .plain(environment.strings.Story_Editor_InputPlaceholderAddCaption),
|
||||||
|
sendPaidMessageStars: nil,
|
||||||
maxLength: Int(component.context.userLimits.maxStoryCaptionLength),
|
maxLength: Int(component.context.userLimits.maxStoryCaptionLength),
|
||||||
queryTypes: [.mention, .hashtag],
|
queryTypes: [.mention, .hashtag],
|
||||||
alwaysDarkWhenHasText: false,
|
alwaysDarkWhenHasText: false,
|
||||||
|
@ -251,6 +251,7 @@ final class StoryPreviewComponent: Component {
|
|||||||
strings: presentationData.strings,
|
strings: presentationData.strings,
|
||||||
style: .story,
|
style: .story,
|
||||||
placeholder: .plain(presentationData.strings.Story_InputPlaceholderReplyPrivately),
|
placeholder: .plain(presentationData.strings.Story_InputPlaceholderReplyPrivately),
|
||||||
|
sendPaidMessageStars: nil,
|
||||||
maxLength: nil,
|
maxLength: nil,
|
||||||
queryTypes: [],
|
queryTypes: [],
|
||||||
alwaysDarkWhenHasText: false,
|
alwaysDarkWhenHasText: false,
|
||||||
|
@ -160,6 +160,7 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
public let strings: PresentationStrings
|
public let strings: PresentationStrings
|
||||||
public let style: Style
|
public let style: Style
|
||||||
public let placeholder: Placeholder
|
public let placeholder: Placeholder
|
||||||
|
public let sendPaidMessageStars: StarsAmount?
|
||||||
public let maxLength: Int?
|
public let maxLength: Int?
|
||||||
public let queryTypes: ContextQueryTypes
|
public let queryTypes: ContextQueryTypes
|
||||||
public let alwaysDarkWhenHasText: Bool
|
public let alwaysDarkWhenHasText: Bool
|
||||||
@ -218,6 +219,7 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
strings: PresentationStrings,
|
strings: PresentationStrings,
|
||||||
style: Style,
|
style: Style,
|
||||||
placeholder: Placeholder,
|
placeholder: Placeholder,
|
||||||
|
sendPaidMessageStars: StarsAmount?,
|
||||||
maxLength: Int?,
|
maxLength: Int?,
|
||||||
queryTypes: ContextQueryTypes,
|
queryTypes: ContextQueryTypes,
|
||||||
alwaysDarkWhenHasText: Bool,
|
alwaysDarkWhenHasText: Bool,
|
||||||
@ -276,6 +278,7 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
self.style = style
|
self.style = style
|
||||||
self.nextInputMode = nextInputMode
|
self.nextInputMode = nextInputMode
|
||||||
self.placeholder = placeholder
|
self.placeholder = placeholder
|
||||||
|
self.sendPaidMessageStars = sendPaidMessageStars
|
||||||
self.maxLength = maxLength
|
self.maxLength = maxLength
|
||||||
self.queryTypes = queryTypes
|
self.queryTypes = queryTypes
|
||||||
self.alwaysDarkWhenHasText = alwaysDarkWhenHasText
|
self.alwaysDarkWhenHasText = alwaysDarkWhenHasText
|
||||||
@ -346,6 +349,9 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
if lhs.placeholder != rhs.placeholder {
|
if lhs.placeholder != rhs.placeholder {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.sendPaidMessageStars != rhs.sendPaidMessageStars {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.maxLength != rhs.maxLength {
|
if lhs.maxLength != rhs.maxLength {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -849,6 +855,37 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
)
|
)
|
||||||
let isEditing = self.textFieldExternalState.isEditing || component.forceIsEditing
|
let isEditing = self.textFieldExternalState.isEditing || component.forceIsEditing
|
||||||
|
|
||||||
|
let placeholderTransition: ComponentTransition = (previousPlaceholder != nil && previousPlaceholder != component.placeholder) ? ComponentTransition(animation: .curve(duration: 0.3, curve: .spring)) : .immediate
|
||||||
|
let placeholderSize: CGSize
|
||||||
|
if case let .plain(string) = component.placeholder, string.contains("#") {
|
||||||
|
let attributedPlaceholder = NSMutableAttributedString(string: string, font:Font.regular(17.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.3))
|
||||||
|
if let range = attributedPlaceholder.string.range(of: "#") {
|
||||||
|
attributedPlaceholder.addAttribute(.attachment, value: PresentationResourcesChat.chatPlaceholderStarIcon(component.theme)!, range: NSRange(range, in: attributedPlaceholder.string))
|
||||||
|
attributedPlaceholder.addAttribute(.foregroundColor, value: UIColor(rgb: 0xffffff, alpha: 0.3), range: NSRange(range, in: attributedPlaceholder.string))
|
||||||
|
attributedPlaceholder.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedPlaceholder.string))
|
||||||
|
}
|
||||||
|
|
||||||
|
placeholderSize = self.placeholder.update(
|
||||||
|
transition: placeholderTransition,
|
||||||
|
component: AnyComponent(MultilineTextComponent(text: .plain(attributedPlaceholder))),
|
||||||
|
environment: {},
|
||||||
|
containerSize: availableTextFieldSize
|
||||||
|
)
|
||||||
|
|
||||||
|
let vibrancyAttributedPlaceholder = NSMutableAttributedString(string: string, font:Font.regular(17.0), textColor: UIColor.black)
|
||||||
|
if let range = vibrancyAttributedPlaceholder.string.range(of: "#") {
|
||||||
|
vibrancyAttributedPlaceholder.addAttribute(.attachment, value: PresentationResourcesChat.chatPlaceholderStarIcon(component.theme)!, range: NSRange(range, in: vibrancyAttributedPlaceholder.string))
|
||||||
|
vibrancyAttributedPlaceholder.addAttribute(.foregroundColor, value: UIColor.black, range: NSRange(range, in: vibrancyAttributedPlaceholder.string))
|
||||||
|
vibrancyAttributedPlaceholder.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: vibrancyAttributedPlaceholder.string))
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = self.vibrancyPlaceholder.update(
|
||||||
|
transition: placeholderTransition,
|
||||||
|
component: AnyComponent(MultilineTextComponent(text: .plain(attributedPlaceholder))),
|
||||||
|
environment: {},
|
||||||
|
containerSize: availableTextFieldSize
|
||||||
|
)
|
||||||
|
} else {
|
||||||
var placeholderItems: [AnimatedTextComponent.Item] = []
|
var placeholderItems: [AnimatedTextComponent.Item] = []
|
||||||
switch component.placeholder {
|
switch component.placeholder {
|
||||||
case let .plain(string):
|
case let .plain(string):
|
||||||
@ -864,8 +901,7 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let placeholderTransition: ComponentTransition = (previousPlaceholder != nil && previousPlaceholder != component.placeholder) ? ComponentTransition(animation: .curve(duration: 0.3, curve: .spring)) : .immediate
|
placeholderSize = self.placeholder.update(
|
||||||
let placeholderSize = self.placeholder.update(
|
|
||||||
transition: placeholderTransition,
|
transition: placeholderTransition,
|
||||||
component: AnyComponent(AnimatedTextComponent(
|
component: AnyComponent(AnimatedTextComponent(
|
||||||
font: Font.regular(17.0),
|
font: Font.regular(17.0),
|
||||||
@ -886,6 +922,8 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
environment: {},
|
environment: {},
|
||||||
containerSize: availableTextFieldSize
|
containerSize: availableTextFieldSize
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if !isEditing && component.setMediaRecordingActive == nil {
|
if !isEditing && component.setMediaRecordingActive == nil {
|
||||||
insets.right = defaultInsets.left
|
insets.right = defaultInsets.left
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ swift_library(
|
|||||||
"//submodules/PeerPresenceStatusManager",
|
"//submodules/PeerPresenceStatusManager",
|
||||||
"//submodules/UndoUI",
|
"//submodules/UndoUI",
|
||||||
"//submodules/AnimatedAvatarSetNode",
|
"//submodules/AnimatedAvatarSetNode",
|
||||||
|
"//submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -17,6 +17,7 @@ import UndoUI
|
|||||||
import AnimatedAvatarSetNode
|
import AnimatedAvatarSetNode
|
||||||
import AvatarNode
|
import AvatarNode
|
||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
|
import ChatMessagePaymentAlertController
|
||||||
|
|
||||||
private final class SendInviteLinkScreenComponent: Component {
|
private final class SendInviteLinkScreenComponent: Component {
|
||||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||||
@ -26,19 +27,22 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
let link: String?
|
let link: String?
|
||||||
let peers: [TelegramForbiddenInvitePeer]
|
let peers: [TelegramForbiddenInvitePeer]
|
||||||
let peerPresences: [EnginePeer.Id: EnginePeer.Presence]
|
let peerPresences: [EnginePeer.Id: EnginePeer.Presence]
|
||||||
|
let sendPaidMessageStars: [EnginePeer.Id: StarsAmount]
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
peer: EnginePeer,
|
peer: EnginePeer,
|
||||||
link: String?,
|
link: String?,
|
||||||
peers: [TelegramForbiddenInvitePeer],
|
peers: [TelegramForbiddenInvitePeer],
|
||||||
peerPresences: [EnginePeer.Id: EnginePeer.Presence]
|
peerPresences: [EnginePeer.Id: EnginePeer.Presence],
|
||||||
|
sendPaidMessageStars: [EnginePeer.Id: StarsAmount]
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.link = link
|
self.link = link
|
||||||
self.peers = peers
|
self.peers = peers
|
||||||
self.peerPresences = peerPresences
|
self.peerPresences = peerPresences
|
||||||
|
self.sendPaidMessageStars = sendPaidMessageStars
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: SendInviteLinkScreenComponent, rhs: SendInviteLinkScreenComponent) -> Bool {
|
static func ==(lhs: SendInviteLinkScreenComponent, rhs: SendInviteLinkScreenComponent) -> Bool {
|
||||||
@ -54,6 +58,9 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
if lhs.peerPresences != rhs.peerPresences {
|
if lhs.peerPresences != rhs.peerPresences {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.sendPaidMessageStars != rhs.sendPaidMessageStars {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,6 +273,38 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func presentPaidMessageAlertIfNeeded(peers: [EnginePeer], requiresStars: [EnginePeer.Id: StarsAmount], completion: @escaping () -> Void) {
|
||||||
|
guard let component = self.component else {
|
||||||
|
completion()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var totalAmount: StarsAmount = .zero
|
||||||
|
for peer in peers {
|
||||||
|
if let amount = requiresStars[peer.id] {
|
||||||
|
totalAmount = totalAmount + amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if totalAmount.value > 0 {
|
||||||
|
let controller = chatMessagePaymentAlertController(
|
||||||
|
context: component.context,
|
||||||
|
presentationData: component.context.sharedContext.currentPresentationData.with { $0 },
|
||||||
|
updatedPresentationData: nil,
|
||||||
|
peers: peers,
|
||||||
|
count: 1,
|
||||||
|
amount: totalAmount,
|
||||||
|
totalAmount: totalAmount,
|
||||||
|
hasCheck: false,
|
||||||
|
navigationController: self.environment?.controller()?.navigationController as? NavigationController,
|
||||||
|
completion: { _ in
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.environment?.controller()?.present(controller, in: .window(.root))
|
||||||
|
} else {
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func update(component: SendInviteLinkScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
func update(component: SendInviteLinkScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
||||||
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||||
let themeUpdated = self.environment?.theme !== environment.theme
|
let themeUpdated = self.environment?.theme !== environment.theme
|
||||||
@ -851,7 +890,22 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
} else if let link = component.link {
|
} else if let link = component.link {
|
||||||
let selectedPeers = component.peers.filter { self.selectedItems.contains($0.peer.id) }
|
let selectedPeers = component.peers.filter { self.selectedItems.contains($0.peer.id) }
|
||||||
|
|
||||||
let _ = enqueueMessagesToMultiplePeers(account: component.context.account, peerIds: Array(self.selectedItems), threadIds: [:], messages: [.message(text: link, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]).start()
|
self.presentPaidMessageAlertIfNeeded(
|
||||||
|
peers: selectedPeers.map { $0.peer },
|
||||||
|
requiresStars: component.sendPaidMessageStars,
|
||||||
|
completion: { [weak self] in
|
||||||
|
guard let self, let component = self.component, let controller = self.environment?.controller() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for peerId in Array(self.selectedItems) {
|
||||||
|
var messageAttributes: [EngineMessage.Attribute] = []
|
||||||
|
if let sendPaidMessageStars = component.sendPaidMessageStars[peerId] {
|
||||||
|
messageAttributes.append(PaidStarsMessageAttribute(stars: sendPaidMessageStars, postponeSending: false))
|
||||||
|
}
|
||||||
|
let _ = enqueueMessages(account: component.context.account, peerId: peerId, messages: [.message(text: link, attributes: messageAttributes, inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]).startStandalone()
|
||||||
|
}
|
||||||
|
|
||||||
let text: String
|
let text: String
|
||||||
if selectedPeers.count == 1 {
|
if selectedPeers.count == 1 {
|
||||||
text = environment.strings.Conversation_ShareLinkTooltip_Chat_One(selectedPeers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast).replacingOccurrences(of: "*", with: "")).string
|
text = environment.strings.Conversation_ShareLinkTooltip_Chat_One(selectedPeers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast).replacingOccurrences(of: "*", with: "")).string
|
||||||
@ -865,6 +919,8 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
controller.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: false, text: text), elevatedLayout: false, action: { _ in return false }), in: .window(.root))
|
controller.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: false, text: text), elevatedLayout: false, action: { _ in return false }), in: .window(.root))
|
||||||
|
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
|
}
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
}
|
}
|
||||||
@ -1083,16 +1139,21 @@ public class SendInviteLinkScreen: ViewControllerComponentContainer {
|
|||||||
self.link = link
|
self.link = link
|
||||||
self.peers = peers
|
self.peers = peers
|
||||||
|
|
||||||
super.init(context: context, component: SendInviteLinkScreenComponent(context: context, peer: peer, link: link, peers: peers, peerPresences: [:]), navigationBarAppearance: .none)
|
super.init(context: context, component: SendInviteLinkScreenComponent(context: context, peer: peer, link: link, peers: peers, peerPresences: [:], sendPaidMessageStars: [:]), navigationBarAppearance: .none)
|
||||||
|
|
||||||
self.statusBar.statusBarStyle = .Ignore
|
self.statusBar.statusBarStyle = .Ignore
|
||||||
self.navigationPresentation = .flatModal
|
self.navigationPresentation = .flatModal
|
||||||
self.blocksBackgroundWhenInOverlay = true
|
self.blocksBackgroundWhenInOverlay = true
|
||||||
|
|
||||||
self.presenceDisposable = (context.engine.data.subscribe(EngineDataMap(
|
self.presenceDisposable = (context.engine.data.subscribe(
|
||||||
|
EngineDataMap(
|
||||||
peers.map(\.peer.id).map(TelegramEngine.EngineData.Item.Peer.Presence.init(id:))
|
peers.map(\.peer.id).map(TelegramEngine.EngineData.Item.Peer.Presence.init(id:))
|
||||||
))
|
),
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] presences in
|
EngineDataMap(
|
||||||
|
peers.map(\.peer.id).map(TelegramEngine.EngineData.Item.Peer.SendPaidMessageStars.init(id:))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] presences, sendPaidMessageStars in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1102,7 +1163,13 @@ public class SendInviteLinkScreen: ViewControllerComponentContainer {
|
|||||||
parsedPresences[id] = presence
|
parsedPresences[id] = presence
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.updateComponent(component: AnyComponent(SendInviteLinkScreenComponent(context: context, peer: peer, link: link, peers: peers, peerPresences: parsedPresences)), transition: .immediate)
|
var parsedSendPaidMessageStars: [EnginePeer.Id: StarsAmount] = [:]
|
||||||
|
for (id, sendPaidMessageStars) in sendPaidMessageStars {
|
||||||
|
if let sendPaidMessageStars {
|
||||||
|
parsedSendPaidMessageStars[id] = sendPaidMessageStars
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.updateComponent(component: AnyComponent(SendInviteLinkScreenComponent(context: context, peer: peer, link: link, peers: peers, peerPresences: parsedPresences, sendPaidMessageStars: parsedSendPaidMessageStars)), transition: .immediate)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,8 +525,8 @@ public class ShareRootControllerImpl {
|
|||||||
} |> runOn(Queue.mainQueue())
|
} |> runOn(Queue.mainQueue())
|
||||||
}
|
}
|
||||||
|
|
||||||
let sentItems: ([PeerId], [PeerId: Int64], [PreparedShareItemContent], ShareControllerAccountContext, Bool, String) -> Signal<ShareControllerExternalStatus, NoError> = { peerIds, threadIds, contents, account, silently, additionalText in
|
let sentItems: ([PeerId], [PeerId: Int64], [PeerId: StarsAmount], [PreparedShareItemContent], ShareControllerAccountContext, Bool, String) -> Signal<ShareControllerExternalStatus, NoError> = { peerIds, threadIds, requireStars, contents, account, silently, additionalText in
|
||||||
let sentItems = sentShareItems(accountPeerId: account.accountPeerId, postbox: account.stateManager.postbox, network: account.stateManager.network, stateManager: account.stateManager, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: nil), to: peerIds, threadIds: threadIds, items: contents, silently: silently, additionalText: additionalText)
|
let sentItems = sentShareItems(accountPeerId: account.accountPeerId, postbox: account.stateManager.postbox, network: account.stateManager.network, stateManager: account.stateManager, auxiliaryMethods: makeTelegramAccountAuxiliaryMethods(uploadInBackground: nil), to: peerIds, threadIds: threadIds, requireStars: requireStars, items: contents, silently: silently, additionalText: additionalText)
|
||||||
|> `catch` { _ -> Signal<
|
|> `catch` { _ -> Signal<
|
||||||
Float, NoError> in
|
Float, NoError> in
|
||||||
return .complete()
|
return .complete()
|
||||||
@ -538,7 +538,19 @@ public class ShareRootControllerImpl {
|
|||||||
|> then(.single(.done))
|
|> then(.single(.done))
|
||||||
}
|
}
|
||||||
|
|
||||||
let shareController = ShareController(environment: environment, currentContext: context, subject: .fromExternal({ peerIds, threadIds, additionalText, account, silently in
|
var itemCount = 1
|
||||||
|
|
||||||
|
if let extensionItems = self?.getExtensionContext()?.inputItems as? [NSExtensionItem] {
|
||||||
|
for item in extensionItems {
|
||||||
|
if let attachments = item.attachments {
|
||||||
|
itemCount = 0
|
||||||
|
for _ in attachments {
|
||||||
|
itemCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let shareController = ShareController(environment: environment, currentContext: context, subject: .fromExternal(itemCount, { peerIds, threadIds, requireStars, additionalText, account, silently in
|
||||||
if let strongSelf = self, let inputItems = strongSelf.getExtensionContext()?.inputItems, !inputItems.isEmpty, !peerIds.isEmpty {
|
if let strongSelf = self, let inputItems = strongSelf.getExtensionContext()?.inputItems, !inputItems.isEmpty, !peerIds.isEmpty {
|
||||||
let rawSignals = TGItemProviderSignals.itemSignals(forInputItems: inputItems)!
|
let rawSignals = TGItemProviderSignals.itemSignals(forInputItems: inputItems)!
|
||||||
return preparedShareItems(postbox: account.stateManager.postbox, network: account.stateManager.network, to: peerIds[0], dataItems: rawSignals)
|
return preparedShareItems(postbox: account.stateManager.postbox, network: account.stateManager.network, to: peerIds[0], dataItems: rawSignals)
|
||||||
@ -564,11 +576,11 @@ public class ShareRootControllerImpl {
|
|||||||
return requestUserInteraction(value)
|
return requestUserInteraction(value)
|
||||||
|> castError(ShareControllerError.self)
|
|> castError(ShareControllerError.self)
|
||||||
|> mapToSignal { contents -> Signal<ShareControllerExternalStatus, ShareControllerError> in
|
|> mapToSignal { contents -> Signal<ShareControllerExternalStatus, ShareControllerError> in
|
||||||
return sentItems(peerIds, threadIds, contents, account, silently, additionalText)
|
return sentItems(peerIds, threadIds, requireStars, contents, account, silently, additionalText)
|
||||||
|> castError(ShareControllerError.self)
|
|> castError(ShareControllerError.self)
|
||||||
}
|
}
|
||||||
case let .done(contents):
|
case let .done(contents):
|
||||||
return sentItems(peerIds, threadIds, contents, account, silently, additionalText)
|
return sentItems(peerIds, threadIds, requireStars, contents, account, silently, additionalText)
|
||||||
|> castError(ShareControllerError.self)
|
|> castError(ShareControllerError.self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,9 +124,9 @@ private final class SheetContent: CombinedComponent {
|
|||||||
minAmount = StarsAmount(value: 1, nanos: 0)
|
minAmount = StarsAmount(value: 1, nanos: 0)
|
||||||
maxAmount = configuration.maxPaidMediaAmount.flatMap { StarsAmount(value: $0, nanos: 0) }
|
maxAmount = configuration.maxPaidMediaAmount.flatMap { StarsAmount(value: $0, nanos: 0) }
|
||||||
|
|
||||||
var usdRate = 0.012
|
|
||||||
if let usdWithdrawRate = configuration.usdWithdrawRate, let amount = state.amount, amount > StarsAmount.zero {
|
if let usdWithdrawRate = configuration.usdWithdrawRate, let amount = state.amount, amount > StarsAmount.zero {
|
||||||
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
let usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
||||||
amountLabel = "≈\(formatTonUsdValue(amount.value, divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat))"
|
amountLabel = "≈\(formatTonUsdValue(amount.value, divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat))"
|
||||||
} else {
|
} else {
|
||||||
amountLabel = nil
|
amountLabel = nil
|
||||||
|
@ -98,6 +98,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/SliderContextItem",
|
"//submodules/TelegramUI/Components/SliderContextItem",
|
||||||
"//submodules/TelegramUI/Components/InteractiveTextComponent",
|
"//submodules/TelegramUI/Components/InteractiveTextComponent",
|
||||||
"//submodules/TelegramUI/Components/SaveProgressScreen",
|
"//submodules/TelegramUI/Components/SaveProgressScreen",
|
||||||
|
"//submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -219,7 +219,8 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
||||||
preferHighQualityStories: preferHighQualityStories,
|
preferHighQualityStories: preferHighQualityStories,
|
||||||
boostsToUnrestrict: nil,
|
boostsToUnrestrict: nil,
|
||||||
appliedBoosts: nil
|
appliedBoosts: nil,
|
||||||
|
sendPaidMessageStars: cachedUserData.sendPaidMessageStars
|
||||||
)
|
)
|
||||||
} else if let cachedChannelData = cachedPeerDataView.cachedPeerData as? CachedChannelData {
|
} else if let cachedChannelData = cachedPeerDataView.cachedPeerData as? CachedChannelData {
|
||||||
additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
||||||
@ -230,7 +231,8 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
||||||
preferHighQualityStories: preferHighQualityStories,
|
preferHighQualityStories: preferHighQualityStories,
|
||||||
boostsToUnrestrict: cachedChannelData.boostsToUnrestrict,
|
boostsToUnrestrict: cachedChannelData.boostsToUnrestrict,
|
||||||
appliedBoosts: cachedChannelData.appliedBoosts
|
appliedBoosts: cachedChannelData.appliedBoosts,
|
||||||
|
sendPaidMessageStars: cachedChannelData.sendPaidMessageStars
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
||||||
@ -241,7 +243,8 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
||||||
preferHighQualityStories: preferHighQualityStories,
|
preferHighQualityStories: preferHighQualityStories,
|
||||||
boostsToUnrestrict: nil,
|
boostsToUnrestrict: nil,
|
||||||
appliedBoosts: nil
|
appliedBoosts: nil,
|
||||||
|
sendPaidMessageStars: nil
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -253,7 +256,8 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
||||||
preferHighQualityStories: preferHighQualityStories,
|
preferHighQualityStories: preferHighQualityStories,
|
||||||
boostsToUnrestrict: nil,
|
boostsToUnrestrict: nil,
|
||||||
appliedBoosts: nil
|
appliedBoosts: nil,
|
||||||
|
sendPaidMessageStars: nil
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
let state = stateView.value?.get(Stories.PeerState.self)
|
let state = stateView.value?.get(Stories.PeerState.self)
|
||||||
@ -1182,7 +1186,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||||||
TelegramEngine.EngineData.Item.NotificationSettings.Global(),
|
TelegramEngine.EngineData.Item.NotificationSettings.Global(),
|
||||||
TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging(id: storyId.peerId),
|
TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging(id: storyId.peerId),
|
||||||
TelegramEngine.EngineData.Item.Peer.BoostsToUnrestrict(id: storyId.peerId),
|
TelegramEngine.EngineData.Item.Peer.BoostsToUnrestrict(id: storyId.peerId),
|
||||||
TelegramEngine.EngineData.Item.Peer.AppliedBoosts(id: storyId.peerId)
|
TelegramEngine.EngineData.Item.Peer.AppliedBoosts(id: storyId.peerId),
|
||||||
|
TelegramEngine.EngineData.Item.Peer.SendPaidMessageStars(id: storyId.peerId)
|
||||||
),
|
),
|
||||||
item |> mapToSignal { item -> Signal<(Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile], [StoryId: EngineStoryItem?]), NoError> in
|
item |> mapToSignal { item -> Signal<(Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile], [StoryId: EngineStoryItem?]), NoError> in
|
||||||
return context.account.postbox.transaction { transaction -> (Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile], [StoryId: EngineStoryItem?]) in
|
return context.account.postbox.transaction { transaction -> (Stories.StoredItem?, [PeerId: Peer], [MediaId: TelegramMediaFile], [StoryId: EngineStoryItem?]) in
|
||||||
@ -1253,7 +1258,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings, isPremiumRequiredForMessaging, boostsToUnrestrict, appliedBoosts) = data
|
let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings, isPremiumRequiredForMessaging, boostsToUnrestrict, appliedBoosts, sendPaidMessageStars) = data
|
||||||
let (item, peers, allEntityFiles, forwardInfoStories) = itemAndPeers
|
let (item, peers, allEntityFiles, forwardInfoStories) = itemAndPeers
|
||||||
|
|
||||||
guard let peer else {
|
guard let peer else {
|
||||||
@ -1270,7 +1275,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||||||
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
||||||
preferHighQualityStories: preferHighQualityStories,
|
preferHighQualityStories: preferHighQualityStories,
|
||||||
boostsToUnrestrict: boostsToUnrestrict,
|
boostsToUnrestrict: boostsToUnrestrict,
|
||||||
appliedBoosts: appliedBoosts
|
appliedBoosts: appliedBoosts,
|
||||||
|
sendPaidMessageStars: sendPaidMessageStars
|
||||||
)
|
)
|
||||||
|
|
||||||
for (storyId, story) in forwardInfoStories {
|
for (storyId, story) in forwardInfoStories {
|
||||||
@ -1436,9 +1442,11 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
|
|||||||
TelegramEngine.EngineData.Item.NotificationSettings.Global.Result,
|
TelegramEngine.EngineData.Item.NotificationSettings.Global.Result,
|
||||||
TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging.Result,
|
TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging.Result,
|
||||||
TelegramEngine.EngineData.Item.Peer.BoostsToUnrestrict.Result,
|
TelegramEngine.EngineData.Item.Peer.BoostsToUnrestrict.Result,
|
||||||
TelegramEngine.EngineData.Item.Peer.AppliedBoosts.Result)
|
TelegramEngine.EngineData.Item.Peer.AppliedBoosts.Result,
|
||||||
|
TelegramEngine.EngineData.Item.Peer.SendPaidMessageStars.Result
|
||||||
|
)
|
||||||
|
|
||||||
init(data: (TelegramEngine.EngineData.Item.Peer.Peer.Result, TelegramEngine.EngineData.Item.Peer.Presence.Result, TelegramEngine.EngineData.Item.Peer.AreVoiceMessagesAvailable.Result, TelegramEngine.EngineData.Item.Peer.CanViewStats.Result, TelegramEngine.EngineData.Item.Peer.NotificationSettings.Result, TelegramEngine.EngineData.Item.NotificationSettings.Global.Result, TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging.Result, TelegramEngine.EngineData.Item.Peer.BoostsToUnrestrict.Result, TelegramEngine.EngineData.Item.Peer.AppliedBoosts.Result)) {
|
init(data: (TelegramEngine.EngineData.Item.Peer.Peer.Result, TelegramEngine.EngineData.Item.Peer.Presence.Result, TelegramEngine.EngineData.Item.Peer.AreVoiceMessagesAvailable.Result, TelegramEngine.EngineData.Item.Peer.CanViewStats.Result, TelegramEngine.EngineData.Item.Peer.NotificationSettings.Result, TelegramEngine.EngineData.Item.NotificationSettings.Global.Result, TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging.Result, TelegramEngine.EngineData.Item.Peer.BoostsToUnrestrict.Result, TelegramEngine.EngineData.Item.Peer.AppliedBoosts.Result, TelegramEngine.EngineData.Item.Peer.SendPaidMessageStars.Result)) {
|
||||||
self.data = data
|
self.data = data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1544,7 +1552,8 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
|
|||||||
TelegramEngine.EngineData.Item.NotificationSettings.Global(),
|
TelegramEngine.EngineData.Item.NotificationSettings.Global(),
|
||||||
TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging(id: peerId),
|
TelegramEngine.EngineData.Item.Peer.IsPremiumRequiredForMessaging(id: peerId),
|
||||||
TelegramEngine.EngineData.Item.Peer.BoostsToUnrestrict(id: peerId),
|
TelegramEngine.EngineData.Item.Peer.BoostsToUnrestrict(id: peerId),
|
||||||
TelegramEngine.EngineData.Item.Peer.AppliedBoosts(id: peerId)
|
TelegramEngine.EngineData.Item.Peer.AppliedBoosts(id: peerId),
|
||||||
|
TelegramEngine.EngineData.Item.Peer.SendPaidMessageStars(id: peerId)
|
||||||
) |> map { PeerData(data: $0) })
|
) |> map { PeerData(data: $0) })
|
||||||
self.currentPeerData = currentPeerData
|
self.currentPeerData = currentPeerData
|
||||||
|
|
||||||
@ -1563,7 +1572,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
|
|||||||
self.listState = state
|
self.listState = state
|
||||||
|
|
||||||
let stateValue: StoryContentContextState
|
let stateValue: StoryContentContextState
|
||||||
if let focusedIndex, let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings, isPremiumRequiredForMessaging, boostsToUnrestrict, appliedBoosts) = data?.data, let peer {
|
if let focusedIndex, let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings, isPremiumRequiredForMessaging, boostsToUnrestrict, appliedBoosts, sendPaidMessageStars) = data?.data, let peer {
|
||||||
let isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings(), topSearchPeers: [])
|
let isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings(), topSearchPeers: [])
|
||||||
let additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
let additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
||||||
isMuted: isMuted,
|
isMuted: isMuted,
|
||||||
@ -1573,7 +1582,8 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
|
|||||||
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
||||||
preferHighQualityStories: preferHighQualityStories,
|
preferHighQualityStories: preferHighQualityStories,
|
||||||
boostsToUnrestrict: boostsToUnrestrict,
|
boostsToUnrestrict: boostsToUnrestrict,
|
||||||
appliedBoosts: appliedBoosts
|
appliedBoosts: appliedBoosts,
|
||||||
|
sendPaidMessageStars: sendPaidMessageStars
|
||||||
)
|
)
|
||||||
|
|
||||||
let item = state.items[focusedIndex]
|
let item = state.items[focusedIndex]
|
||||||
@ -2462,7 +2472,8 @@ public final class RepostStoriesContentContextImpl: StoryContentContext {
|
|||||||
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
||||||
preferHighQualityStories: preferHighQualityStories,
|
preferHighQualityStories: preferHighQualityStories,
|
||||||
boostsToUnrestrict: nil,
|
boostsToUnrestrict: nil,
|
||||||
appliedBoosts: nil
|
appliedBoosts: nil,
|
||||||
|
sendPaidMessageStars: cachedUserData.sendPaidMessageStars
|
||||||
)
|
)
|
||||||
} else if let cachedChannelData = cachedPeerDataView.cachedPeerData as? CachedChannelData {
|
} else if let cachedChannelData = cachedPeerDataView.cachedPeerData as? CachedChannelData {
|
||||||
additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
||||||
@ -2473,7 +2484,8 @@ public final class RepostStoriesContentContextImpl: StoryContentContext {
|
|||||||
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
||||||
preferHighQualityStories: preferHighQualityStories,
|
preferHighQualityStories: preferHighQualityStories,
|
||||||
boostsToUnrestrict: cachedChannelData.boostsToUnrestrict,
|
boostsToUnrestrict: cachedChannelData.boostsToUnrestrict,
|
||||||
appliedBoosts: cachedChannelData.appliedBoosts
|
appliedBoosts: cachedChannelData.appliedBoosts,
|
||||||
|
sendPaidMessageStars: cachedChannelData.sendPaidMessageStars
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
additionalPeerData = StoryContentContextState.AdditionalPeerData(
|
||||||
@ -2484,7 +2496,8 @@ public final class RepostStoriesContentContextImpl: StoryContentContext {
|
|||||||
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
||||||
preferHighQualityStories: preferHighQualityStories,
|
preferHighQualityStories: preferHighQualityStories,
|
||||||
boostsToUnrestrict: nil,
|
boostsToUnrestrict: nil,
|
||||||
appliedBoosts: nil
|
appliedBoosts: nil,
|
||||||
|
sendPaidMessageStars: nil
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2497,7 +2510,8 @@ public final class RepostStoriesContentContextImpl: StoryContentContext {
|
|||||||
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
isPremiumRequiredForMessaging: isPremiumRequiredForMessaging,
|
||||||
preferHighQualityStories: preferHighQualityStories,
|
preferHighQualityStories: preferHighQualityStories,
|
||||||
boostsToUnrestrict: nil,
|
boostsToUnrestrict: nil,
|
||||||
appliedBoosts: nil
|
appliedBoosts: nil,
|
||||||
|
sendPaidMessageStars: nil
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +161,7 @@ public final class StoryContentContextState {
|
|||||||
public let preferHighQualityStories: Bool
|
public let preferHighQualityStories: Bool
|
||||||
public let boostsToUnrestrict: Int32?
|
public let boostsToUnrestrict: Int32?
|
||||||
public let appliedBoosts: Int32?
|
public let appliedBoosts: Int32?
|
||||||
|
public let sendPaidMessageStars: StarsAmount?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
isMuted: Bool,
|
isMuted: Bool,
|
||||||
@ -170,7 +171,8 @@ public final class StoryContentContextState {
|
|||||||
isPremiumRequiredForMessaging: Bool,
|
isPremiumRequiredForMessaging: Bool,
|
||||||
preferHighQualityStories: Bool,
|
preferHighQualityStories: Bool,
|
||||||
boostsToUnrestrict: Int32?,
|
boostsToUnrestrict: Int32?,
|
||||||
appliedBoosts: Int32?
|
appliedBoosts: Int32?,
|
||||||
|
sendPaidMessageStars: StarsAmount?
|
||||||
) {
|
) {
|
||||||
self.isMuted = isMuted
|
self.isMuted = isMuted
|
||||||
self.areVoiceMessagesAvailable = areVoiceMessagesAvailable
|
self.areVoiceMessagesAvailable = areVoiceMessagesAvailable
|
||||||
@ -180,6 +182,7 @@ public final class StoryContentContextState {
|
|||||||
self.preferHighQualityStories = preferHighQualityStories
|
self.preferHighQualityStories = preferHighQualityStories
|
||||||
self.boostsToUnrestrict = boostsToUnrestrict
|
self.boostsToUnrestrict = boostsToUnrestrict
|
||||||
self.appliedBoosts = appliedBoosts
|
self.appliedBoosts = appliedBoosts
|
||||||
|
self.sendPaidMessageStars = sendPaidMessageStars
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func == (lhs: StoryContentContextState.AdditionalPeerData, rhs: StoryContentContextState.AdditionalPeerData) -> Bool {
|
public static func == (lhs: StoryContentContextState.AdditionalPeerData, rhs: StoryContentContextState.AdditionalPeerData) -> Bool {
|
||||||
@ -207,6 +210,9 @@ public final class StoryContentContextState {
|
|||||||
if lhs.appliedBoosts != rhs.appliedBoosts {
|
if lhs.appliedBoosts != rhs.appliedBoosts {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.sendPaidMessageStars != rhs.sendPaidMessageStars {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2822,9 +2822,14 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inputPlaceholder = .counter(items)
|
inputPlaceholder = .counter(items)
|
||||||
|
} else {
|
||||||
|
if let sendPaidMessageStars = component.slice.additionalPeerData.sendPaidMessageStars {
|
||||||
|
let dateTimeFormat = component.context.sharedContext.currentPresentationData.with { $0 }.dateTimeFormat
|
||||||
|
inputPlaceholder = .plain(component.strings.Chat_InputTextPaidMessagePlaceholder(" # \(presentationStringsFormattedNumber(Int32(sendPaidMessageStars.value), dateTimeFormat.groupingSeparator))").string)
|
||||||
} else {
|
} else {
|
||||||
inputPlaceholder = .plain(isGroup ? component.strings.Story_InputPlaceholderReplyInGroup : component.strings.Story_InputPlaceholderReplyPrivately)
|
inputPlaceholder = .plain(isGroup ? component.strings.Story_InputPlaceholderReplyInGroup : component.strings.Story_InputPlaceholderReplyPrivately)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let startTime22 = CFAbsoluteTimeGetCurrent()
|
let startTime22 = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
@ -2867,6 +2872,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
strings: component.strings,
|
strings: component.strings,
|
||||||
style: .story,
|
style: .story,
|
||||||
placeholder: inputPlaceholder,
|
placeholder: inputPlaceholder,
|
||||||
|
sendPaidMessageStars: component.slice.additionalPeerData.sendPaidMessageStars,
|
||||||
maxLength: 4096,
|
maxLength: 4096,
|
||||||
queryTypes: [.mention, .hashtag, .emoji],
|
queryTypes: [.mention, .hashtag, .emoji],
|
||||||
alwaysDarkWhenHasText: component.metrics.widthClass == .regular,
|
alwaysDarkWhenHasText: component.metrics.widthClass == .regular,
|
||||||
@ -4648,6 +4654,10 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let sendPaidMessageStars = component.slice.additionalPeerData.sendPaidMessageStars {
|
||||||
|
messageAttributes.append(PaidStarsMessageAttribute(stars: sendPaidMessageStars, postponeSending: false))
|
||||||
|
}
|
||||||
|
|
||||||
let message: EnqueueMessage = .message(
|
let message: EnqueueMessage = .message(
|
||||||
text: text,
|
text: text,
|
||||||
attributes: messageAttributes,
|
attributes: messageAttributes,
|
||||||
@ -4693,7 +4703,6 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.displayLikeReactions {
|
if self.displayLikeReactions {
|
||||||
if component.slice.item.storyItem.myReaction == updateReaction.reaction {
|
if component.slice.item.storyItem.myReaction == updateReaction.reaction {
|
||||||
action()
|
action()
|
||||||
@ -4704,8 +4713,10 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.sendMessageContext.performWithPossibleStealthModeConfirmation(view: self, action: {
|
self.sendMessageContext.performWithPossibleStealthModeConfirmation(view: self, action: {
|
||||||
|
self.sendMessageContext.presentPaidMessageAlertIfNeeded(view: self, completion: {
|
||||||
action()
|
action()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ import LocationUI
|
|||||||
import ReactionSelectionNode
|
import ReactionSelectionNode
|
||||||
import StoryQualityUpgradeSheetScreen
|
import StoryQualityUpgradeSheetScreen
|
||||||
import AudioWaveform
|
import AudioWaveform
|
||||||
|
import ChatMessagePaymentAlertController
|
||||||
|
|
||||||
private var ObjCKey_DeinitWatcher: Int?
|
private var ObjCKey_DeinitWatcher: Int?
|
||||||
|
|
||||||
@ -491,6 +492,30 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
view.updateIsProgressPaused()
|
view.updateIsProgressPaused()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func presentPaidMessageAlertIfNeeded(view: StoryItemSetContainerComponent.View, completion: @escaping () -> Void) {
|
||||||
|
guard let component = view.component, let sendPaidMessageStars = component.slice.additionalPeerData.sendPaidMessageStars else {
|
||||||
|
completion()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||||
|
|
||||||
|
let controller = chatMessagePaymentAlertController(
|
||||||
|
context: component.context,
|
||||||
|
presentationData: presentationData,
|
||||||
|
updatedPresentationData: nil,
|
||||||
|
peers: [component.slice.effectivePeer],
|
||||||
|
count: 1,
|
||||||
|
amount: sendPaidMessageStars,
|
||||||
|
totalAmount: nil,
|
||||||
|
hasCheck: false,
|
||||||
|
navigationController: component.controller()?.navigationController as? NavigationController,
|
||||||
|
completion: { _ in
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
component.controller()?.present(controller, in: .window(.root))
|
||||||
|
}
|
||||||
|
|
||||||
func performWithPossibleStealthModeConfirmation(view: StoryItemSetContainerComponent.View, action: @escaping () -> Void) {
|
func performWithPossibleStealthModeConfirmation(view: StoryItemSetContainerComponent.View, action: @escaping () -> Void) {
|
||||||
guard let component = view.component, component.stealthModeTimeout != nil else {
|
guard let component = view.component, component.stealthModeTimeout != nil else {
|
||||||
action()
|
action()
|
||||||
@ -512,7 +537,6 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
|
|
||||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||||
if noticeCount < 1, let activeUntilTimestamp = config.stealthModeState.actualizedNow().activeUntilTimestamp, activeUntilTimestamp > timestamp {
|
if noticeCount < 1, let activeUntilTimestamp = config.stealthModeState.actualizedNow().activeUntilTimestamp, activeUntilTimestamp > timestamp {
|
||||||
|
|
||||||
let theme = component.theme
|
let theme = component.theme
|
||||||
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>) = (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) })
|
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>) = (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) })
|
||||||
|
|
||||||
@ -575,12 +599,21 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
|
|
||||||
let controller = component.controller() as? StoryContainerScreen
|
let controller = component.controller() as? StoryContainerScreen
|
||||||
|
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self, weak view] in
|
||||||
|
guard let self, let view else {
|
||||||
|
return
|
||||||
|
}
|
||||||
if let recordedAudioPreview = self.recordedAudioPreview, case let .audio(audio) = recordedAudioPreview {
|
if let recordedAudioPreview = self.recordedAudioPreview, case let .audio(audio) = recordedAudioPreview {
|
||||||
self.recordedAudioPreview = nil
|
self.recordedAudioPreview = nil
|
||||||
|
|
||||||
let waveformBuffer = audio.waveform.makeBitstream()
|
let waveformBuffer = audio.waveform.makeBitstream()
|
||||||
|
|
||||||
let messages: [EnqueueMessage] = [.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: audio.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(audio.fileSize), attributes: [.Audio(isVoice: true, duration: Int(audio.duration), title: nil, performer: nil, waveform: waveformBuffer)], alternativeRepresentations: [])), threadId: nil, replyToMessageId: nil, replyToStoryId: focusedStoryId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]
|
var messageAttributes: [MessageAttribute] = []
|
||||||
|
if let sendPaidMessageStars = component.slice.additionalPeerData.sendPaidMessageStars {
|
||||||
|
messageAttributes.append(PaidStarsMessageAttribute(stars: sendPaidMessageStars, postponeSending: false))
|
||||||
|
}
|
||||||
|
|
||||||
|
let messages: [EnqueueMessage] = [.message(text: "", attributes: messageAttributes, inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: audio.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(audio.fileSize), attributes: [.Audio(isVoice: true, duration: Int(audio.duration), title: nil, performer: nil, waveform: waveformBuffer)], alternativeRepresentations: [])), threadId: nil, replyToMessageId: nil, replyToStoryId: focusedStoryId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]
|
||||||
|
|
||||||
let _ = enqueueMessages(account: component.context.account, peerId: peerId, messages: messages).start()
|
let _ = enqueueMessages(account: component.context.account, peerId: peerId, messages: messages).start()
|
||||||
|
|
||||||
@ -601,7 +634,8 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
storyId: focusedStoryId,
|
storyId: focusedStoryId,
|
||||||
content: .text(text.string, entities),
|
content: .text(text.string, entities),
|
||||||
silentPosting: silentPosting,
|
silentPosting: silentPosting,
|
||||||
scheduleTime: scheduleTime
|
scheduleTime: scheduleTime,
|
||||||
|
sendPaidMessageStars: component.slice.additionalPeerData.sendPaidMessageStars
|
||||||
) |> deliverOnMainQueue).start(next: { [weak self, weak view] messageIds in
|
) |> deliverOnMainQueue).start(next: { [weak self, weak view] messageIds in
|
||||||
Queue.mainQueue().after(0.3) {
|
Queue.mainQueue().after(0.3) {
|
||||||
if let self, let view {
|
if let self, let view {
|
||||||
@ -623,6 +657,7 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func performSendStickerAction(view: StoryItemSetContainerComponent.View, fileReference: FileMediaReference) {
|
func performSendStickerAction(view: StoryItemSetContainerComponent.View, fileReference: FileMediaReference) {
|
||||||
@ -660,11 +695,16 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self, weak view] in
|
||||||
|
guard let self, let view else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let _ = (component.context.engine.messages.enqueueOutgoingMessage(
|
let _ = (component.context.engine.messages.enqueueOutgoingMessage(
|
||||||
to: peerId,
|
to: peerId,
|
||||||
replyTo: nil,
|
replyTo: nil,
|
||||||
storyId: focusedStoryId,
|
storyId: focusedStoryId,
|
||||||
content: .file(fileReference)
|
content: .file(fileReference),
|
||||||
|
sendPaidMessageStars: component.slice.additionalPeerData.sendPaidMessageStars
|
||||||
) |> deliverOnMainQueue).start(next: { [weak self, weak view] messageIds in
|
) |> deliverOnMainQueue).start(next: { [weak self, weak view] messageIds in
|
||||||
Queue.mainQueue().after(0.3) {
|
Queue.mainQueue().after(0.3) {
|
||||||
if let self, let view {
|
if let self, let view {
|
||||||
@ -681,6 +721,7 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
}
|
}
|
||||||
controller?.requestLayout(forceUpdate: true, transition: .animated(duration: 0.3, curve: .spring))
|
controller?.requestLayout(forceUpdate: true, transition: .animated(duration: 0.3, curve: .spring))
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func performSendContextResultAction(view: StoryItemSetContainerComponent.View, results: ChatContextResultCollection, result: ChatContextResult) {
|
func performSendContextResultAction(view: StoryItemSetContainerComponent.View, results: ChatContextResultCollection, result: ChatContextResult) {
|
||||||
@ -714,11 +755,16 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self, weak view] in
|
||||||
|
guard let self, let view else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let _ = (component.context.engine.messages.enqueueOutgoingMessage(
|
let _ = (component.context.engine.messages.enqueueOutgoingMessage(
|
||||||
to: peerId,
|
to: peerId,
|
||||||
replyTo: nil,
|
replyTo: nil,
|
||||||
storyId: focusedStoryId,
|
storyId: focusedStoryId,
|
||||||
content: .contextResult(results, result)
|
content: .contextResult(results, result),
|
||||||
|
sendPaidMessageStars: component.slice.additionalPeerData.sendPaidMessageStars
|
||||||
) |> deliverOnMainQueue).start(next: { [weak self, weak view] messageIds in
|
) |> deliverOnMainQueue).start(next: { [weak self, weak view] messageIds in
|
||||||
Queue.mainQueue().after(0.3) {
|
Queue.mainQueue().after(0.3) {
|
||||||
if let self, let view {
|
if let self, let view {
|
||||||
@ -734,18 +780,24 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
view.state?.updated(transition: .spring(duration: 0.3))
|
view.state?.updated(transition: .spring(duration: 0.3))
|
||||||
}
|
}
|
||||||
controller?.requestLayout(forceUpdate: true, transition: .animated(duration: 0.3, curve: .spring))
|
controller?.requestLayout(forceUpdate: true, transition: .animated(duration: 0.3, curve: .spring))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func enqueueGifData(view: StoryItemSetContainerComponent.View, data: Data) {
|
func enqueueGifData(view: StoryItemSetContainerComponent.View, data: Data) {
|
||||||
guard let component = view.component else {
|
guard let component = view.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let peer = component.slice.effectivePeer
|
let peer = component.slice.effectivePeer
|
||||||
let _ = (legacyEnqueueGifMessage(account: component.context.account, data: data) |> deliverOnMainQueue).start(next: { [weak self, weak view] message in
|
let _ = (legacyEnqueueGifMessage(account: component.context.account, data: data) |> deliverOnMainQueue).start(next: { [weak self, weak view] message in
|
||||||
if let self, let view {
|
if let self, let view {
|
||||||
self.sendMessages(view: view, peer: peer, messages: [message])
|
self.sendMessages(view: view, peer: peer, messages: [message])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func enqueueStickerImage(view: StoryItemSetContainerComponent.View, image: UIImage, isMemoji: Bool) {
|
func enqueueStickerImage(view: StoryItemSetContainerComponent.View, image: UIImage, isMemoji: Bool) {
|
||||||
@ -794,7 +846,12 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: Int64(data.count), attributes: fileAttributes, alternativeRepresentations: [])
|
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: Int64(data.count), attributes: fileAttributes, alternativeRepresentations: [])
|
||||||
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
||||||
|
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.sendMessages(view: view, peer: peer, messages: [message], silentPosting: false)
|
self.sendMessages(view: view, peer: peer, messages: [message], silentPosting: false)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -843,11 +900,16 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
self.videoRecorder.set(.single(nil))
|
self.videoRecorder.set(.single(nil))
|
||||||
|
|
||||||
self.performWithPossibleStealthModeConfirmation(view: view, action: { [weak self, weak view] in
|
self.performWithPossibleStealthModeConfirmation(view: view, action: { [weak self, weak view] in
|
||||||
|
guard let self, let view else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self, weak view] in
|
||||||
guard let self, let view else {
|
guard let self, let view else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.sendMessages(view: view, peer: peer, messages: [updatedMessage])
|
self.sendMessages(view: view, peer: peer, messages: [updatedMessage])
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}, displaySlowmodeTooltip: { [weak self] view, rect in
|
}, displaySlowmodeTooltip: { [weak self] view, rect in
|
||||||
//self?.interfaceInteraction?.displaySlowmodeTooltip(view, rect)
|
//self?.interfaceInteraction?.displaySlowmodeTooltip(view, rect)
|
||||||
let _ = self
|
let _ = self
|
||||||
@ -893,6 +955,10 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
let waveformBuffer: Data? = data.waveform
|
let waveformBuffer: Data? = data.waveform
|
||||||
|
|
||||||
self.performWithPossibleStealthModeConfirmation(view: view, action: { [weak self, weak view] in
|
self.performWithPossibleStealthModeConfirmation(view: view, action: { [weak self, weak view] in
|
||||||
|
guard let self, let view else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self, weak view] in
|
||||||
guard let self, let view else {
|
guard let self, let view else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -900,6 +966,7 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
|
|
||||||
HapticFeedback().tap()
|
HapticFeedback().tap()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else if let videoRecorderValue = self.videoRecorderValue {
|
} else if let videoRecorderValue = self.videoRecorderValue {
|
||||||
@ -1593,7 +1660,11 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
guard let view, let component = view.component else {
|
guard let view, let component = view.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: mediaReference, threadId: nil, replyToMessageId: nil, replyToStoryId: focusedStoryId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
var messageAttributes: [MessageAttribute] = []
|
||||||
|
if let sendPaidMessageStars = component.slice.additionalPeerData.sendPaidMessageStars {
|
||||||
|
messageAttributes.append(PaidStarsMessageAttribute(stars: sendPaidMessageStars, postponeSending: false))
|
||||||
|
}
|
||||||
|
let message: EnqueueMessage = .message(text: "", attributes: messageAttributes, inlineStickers: [:], mediaReference: mediaReference, threadId: nil, replyToMessageId: nil, replyToStoryId: focusedStoryId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
||||||
let _ = (enqueueMessages(account: component.context.account, peerId: peer.id, messages: [message.withUpdatedReplyToMessageId(nil)])
|
let _ = (enqueueMessages(account: component.context.account, peerId: peer.id, messages: [message.withUpdatedReplyToMessageId(nil)])
|
||||||
|> deliverOnMainQueue).start(next: { [weak self, weak view] messageIds in
|
|> deliverOnMainQueue).start(next: { [weak self, weak view] messageIds in
|
||||||
if let self, let view {
|
if let self, let view {
|
||||||
@ -1636,8 +1707,13 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: location), threadId: nil, replyToMessageId: nil, replyToStoryId: focusedStoryId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: location), threadId: nil, replyToMessageId: nil, replyToStoryId: focusedStoryId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.sendMessages(view: view, peer: peer, messages: [message])
|
self.sendMessages(view: view, peer: peer, messages: [message])
|
||||||
})
|
})
|
||||||
|
})
|
||||||
completion(controller, controller.mediaPickerContext)
|
completion(controller, controller.mediaPickerContext)
|
||||||
|
|
||||||
let _ = currentLocationController.swap(controller)
|
let _ = currentLocationController.swap(controller)
|
||||||
@ -1705,7 +1781,12 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.sendMessages(view: view, peer: peer, messages: enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime)
|
self.sendMessages(view: view, peer: peer, messages: enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime)
|
||||||
|
})
|
||||||
} else if let peer = peers.first {
|
} else if let peer = peers.first {
|
||||||
let dataSignal: Signal<(EnginePeer?, DeviceContactExtendedData?), NoError>
|
let dataSignal: Signal<(EnginePeer?, DeviceContactExtendedData?), NoError>
|
||||||
switch peer {
|
switch peer {
|
||||||
@ -1760,7 +1841,12 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
}
|
}
|
||||||
enqueueMessages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: focusedStoryId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
enqueueMessages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: focusedStoryId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
||||||
|
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.sendMessages(view: view, peer: targetPeer, messages: enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime)
|
self.sendMessages(view: view, peer: targetPeer, messages: enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
let contactController = component.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: component.context), environment: ShareControllerAppEnvironment(sharedContext: component.context.sharedContext), subject: .filter(peer: peerAndContactData.0?._asPeer(), contactId: nil, contactData: contactData, completion: { [weak self, weak view] peer, contactData in
|
let contactController = component.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: component.context), environment: ShareControllerAppEnvironment(sharedContext: component.context.sharedContext), subject: .filter(peer: peerAndContactData.0?._asPeer(), contactId: nil, contactData: contactData, completion: { [weak self, weak view] peer, contactData in
|
||||||
guard let self, let view else {
|
guard let self, let view else {
|
||||||
@ -1779,7 +1865,12 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
}
|
}
|
||||||
enqueueMessages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: focusedStoryId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
enqueueMessages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: focusedStoryId, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
||||||
|
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.sendMessages(view: view, peer: targetPeer, messages: enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime)
|
self.sendMessages(view: view, peer: targetPeer, messages: enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}), completed: nil, cancelled: nil)
|
}), completed: nil, cancelled: nil)
|
||||||
component.controller()?.push(contactController)
|
component.controller()?.push(contactController)
|
||||||
@ -2187,7 +2278,12 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !messages.isEmpty {
|
if !messages.isEmpty {
|
||||||
|
strongSelf.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
strongSelf.sendMessages(view: view, peer: peer, messages: messages)
|
strongSelf.sendMessages(view: view, peer: peer, messages: messages)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -2248,7 +2344,12 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
if !inputText.string.isEmpty {
|
if !inputText.string.isEmpty {
|
||||||
self.clearInputText(view: view)
|
self.clearInputText(view: view)
|
||||||
}
|
}
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.enqueueMediaMessages(view: view, peer: peer, replyToMessageId: nil, replyToStoryId: focusedStoryId, signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
self.enqueueMediaMessages(view: view, peer: peer, replyToMessageId: nil, replyToStoryId: focusedStoryId, signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -2268,7 +2369,19 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
guard let self, let view, let component = view.component else {
|
guard let self, let view, let component = view.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if component.context.engine.messages.enqueueOutgoingMessageWithChatContextResult(to: peer.id, threadId: nil, botId: results.botId, result: result, replyToMessageId: replyMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: storyId, hideVia: hideVia, silentPosting: silentPosting, scheduleTime: scheduleTime) {
|
if component.context.engine.messages.enqueueOutgoingMessageWithChatContextResult(
|
||||||
|
to: peer.id,
|
||||||
|
threadId: nil,
|
||||||
|
botId: results.botId,
|
||||||
|
result: result,
|
||||||
|
replyToMessageId: replyMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) },
|
||||||
|
replyToStoryId: storyId,
|
||||||
|
hideVia: hideVia,
|
||||||
|
silentPosting: silentPosting,
|
||||||
|
scheduleTime: scheduleTime,
|
||||||
|
sendPaidMessageStars: component.slice.additionalPeerData.sendPaidMessageStars,
|
||||||
|
postpone: false
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let attachmentController = self.attachmentController {
|
if let attachmentController = self.attachmentController {
|
||||||
@ -2413,6 +2526,10 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
let storeCapturedMedia = peer.id.namespace != Namespaces.Peer.SecretChat
|
let storeCapturedMedia = peer.id.namespace != Namespaces.Peer.SecretChat
|
||||||
|
|
||||||
presentedLegacyCamera(context: component.context, peer: peer._asPeer(), chatLocation: .peer(id: peer.id), cameraView: cameraView, menuController: nil, parentController: parentController, attachmentController: self.attachmentController, editingMedia: false, saveCapturedPhotos: storeCapturedMedia, mediaGrouping: true, initialCaption: inputText, hasSchedule: peer.id.namespace != Namespaces.Peer.SecretChat, enablePhoto: enablePhoto, enableVideo: enableVideo, sendMessagesWithSignals: { [weak self, weak view] signals, silentPosting, scheduleTime, parameters in
|
presentedLegacyCamera(context: component.context, peer: peer._asPeer(), chatLocation: .peer(id: peer.id), cameraView: cameraView, menuController: nil, parentController: parentController, attachmentController: self.attachmentController, editingMedia: false, saveCapturedPhotos: storeCapturedMedia, mediaGrouping: true, initialCaption: inputText, hasSchedule: peer.id.namespace != Namespaces.Peer.SecretChat, enablePhoto: enablePhoto, enableVideo: enableVideo, sendMessagesWithSignals: { [weak self, weak view] signals, silentPosting, scheduleTime, parameters in
|
||||||
|
guard let self, let view else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.presentPaidMessageAlertIfNeeded(view: view, completion: { [weak self, weak view] in
|
||||||
guard let self, let view else {
|
guard let self, let view else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2420,6 +2537,7 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
if !inputText.string.isEmpty {
|
if !inputText.string.isEmpty {
|
||||||
self.clearInputText(view: view)
|
self.clearInputText(view: view)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}, recognizedQRCode: { _ in
|
}, recognizedQRCode: { _ in
|
||||||
}, presentSchedulePicker: { [weak self, weak view] _, done in
|
}, presentSchedulePicker: { [weak self, weak view] _, done in
|
||||||
guard let self, let view else {
|
guard let self, let view else {
|
||||||
@ -2545,6 +2663,10 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
attributes.append(OutgoingScheduleInfoMessageAttribute(scheduleTime: scheduleTime))
|
attributes.append(OutgoingScheduleInfoMessageAttribute(scheduleTime: scheduleTime))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var messageAttributes: [MessageAttribute] = []
|
||||||
|
if let component = view.component, let sendPaidMessageStars = component.slice.additionalPeerData.sendPaidMessageStars {
|
||||||
|
messageAttributes.append(PaidStarsMessageAttribute(stars: sendPaidMessageStars, postponeSending: false))
|
||||||
|
}
|
||||||
return attributes
|
return attributes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2604,8 +2604,12 @@ extension ChatControllerImpl {
|
|||||||
strongSelf.interfaceInteraction?.displaySlowmodeTooltip(node.view, rect)
|
strongSelf.interfaceInteraction?.displaySlowmodeTooltip(node.view, rect)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
strongSelf.presentPaidMessageAlertIfNeeded(completion: { [weak self] postpone in
|
||||||
strongSelf.enqueueChatContextResult(results, result)
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.enqueueChatContextResult(results, result, postpone: postpone)
|
||||||
|
})
|
||||||
return true
|
return true
|
||||||
}, sendBotCommand: { [weak self] botPeer, command in
|
}, sendBotCommand: { [weak self] botPeer, command in
|
||||||
if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState) {
|
if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState) {
|
||||||
|
@ -250,10 +250,8 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var sendImmediately = false
|
var sendImmediately = false
|
||||||
if let _ = self.presentationInterfaceState.sendPaidMessageStars {
|
if let _ = self.presentationInterfaceState.sendPaidMessageStars, case .send = action {
|
||||||
if case .send = action {
|
|
||||||
updatedAction = .preview
|
updatedAction = .preview
|
||||||
}
|
|
||||||
sendImmediately = true
|
sendImmediately = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,10 +23,10 @@ extension ChatControllerImpl {
|
|||||||
if let sendPaidMessageStars = self.presentationInterfaceState.sendPaidMessageStars {
|
if let sendPaidMessageStars = self.presentationInterfaceState.sendPaidMessageStars {
|
||||||
let _ = (ApplicationSpecificNotice.dismissedPaidMessageWarningNamespace(accountManager: self.context.sharedContext.accountManager, peerId: peer.id)
|
let _ = (ApplicationSpecificNotice.dismissedPaidMessageWarningNamespace(accountManager: self.context.sharedContext.accountManager, peerId: peer.id)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] dismissedAmount in
|
|> deliverOnMainQueue).start(next: { [weak self] dismissedAmount in
|
||||||
guard let self else {
|
guard let self, let starsContext = self.context.starsContext else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let dismissedAmount, dismissedAmount == sendPaidMessageStars.value {
|
if let dismissedAmount, dismissedAmount == sendPaidMessageStars.value, let currentState = starsContext.currentState, currentState.balance > sendPaidMessageStars {
|
||||||
completion(true)
|
completion(true)
|
||||||
self.displayPaidMessageUndo(count: count, amount: sendPaidMessageStars)
|
self.displayPaidMessageUndo(count: count, amount: sendPaidMessageStars)
|
||||||
} else {
|
} else {
|
||||||
@ -37,14 +37,14 @@ extension ChatControllerImpl {
|
|||||||
let controller = chatMessagePaymentAlertController(
|
let controller = chatMessagePaymentAlertController(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
updatedPresentationData: nil,//self.updatedPresentationData,
|
updatedPresentationData: nil,
|
||||||
peers: [peer],
|
peers: [peer],
|
||||||
count: count,
|
count: count,
|
||||||
amount: sendPaidMessageStars,
|
amount: sendPaidMessageStars,
|
||||||
totalAmount: nil,
|
totalAmount: nil,
|
||||||
navigationController: self.navigationController as? NavigationController,
|
navigationController: self.navigationController as? NavigationController,
|
||||||
completion: { [weak self] dontAskAgain in
|
completion: { [weak self] dontAskAgain in
|
||||||
guard let self, let starsContext = self.context.starsContext else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9515,7 +9515,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func enqueueChatContextResult(_ results: ChatContextResultCollection, _ result: ChatContextResult, hideVia: Bool = false, closeMediaInput: Bool = false, silentPosting: Bool = false, resetTextInputState: Bool = true) {
|
func enqueueChatContextResult(_ results: ChatContextResultCollection, _ result: ChatContextResult, hideVia: Bool = false, closeMediaInput: Bool = false, silentPosting: Bool = false, resetTextInputState: Bool = true, postpone: Bool = false) {
|
||||||
if !canSendMessagesToChat(self.presentationInterfaceState) {
|
if !canSendMessagesToChat(self.presentationInterfaceState) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -9534,7 +9534,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let replyMessageSubject = self.presentationInterfaceState.interfaceState.replyMessageSubject
|
let replyMessageSubject = self.presentationInterfaceState.interfaceState.replyMessageSubject
|
||||||
if self.context.engine.messages.enqueueOutgoingMessageWithChatContextResult(to: peerId, threadId: self.chatLocation.threadId, botId: results.botId, result: result, replyToMessageId: replyMessageSubject?.subjectModel, hideVia: hideVia, silentPosting: silentPosting, scheduleTime: scheduleTime) {
|
|
||||||
|
let sendPaidMessageStars = self.presentationInterfaceState.sendPaidMessageStars
|
||||||
|
if self.context.engine.messages.enqueueOutgoingMessageWithChatContextResult(to: peerId, threadId: self.chatLocation.threadId, botId: results.botId, result: result, replyToMessageId: replyMessageSubject?.subjectModel, hideVia: hideVia, silentPosting: silentPosting, scheduleTime: scheduleTime, sendPaidMessageStars: sendPaidMessageStars, postpone: postpone) {
|
||||||
self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in
|
self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.chatDisplayNode.collapseInput()
|
strongSelf.chatDisplayNode.collapseInput()
|
||||||
|
@ -13,6 +13,7 @@ import ChatInterfaceState
|
|||||||
import PremiumUI
|
import PremiumUI
|
||||||
import ReactionSelectionNode
|
import ReactionSelectionNode
|
||||||
import TopMessageReactions
|
import TopMessageReactions
|
||||||
|
import ChatMessagePaymentAlertController
|
||||||
|
|
||||||
extension ChatControllerImpl {
|
extension ChatControllerImpl {
|
||||||
func forwardMessages(messageIds: [MessageId], options: ChatInterfaceForwardOptionsState? = nil, resetCurrent: Bool = false) {
|
func forwardMessages(messageIds: [MessageId], options: ChatInterfaceForwardOptionsState? = nil, resetCurrent: Bool = false) {
|
||||||
@ -94,9 +95,33 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
controller.multiplePeersSelected = { [weak self, weak controller] peers, peerMap, messageText, mode, forwardOptions, _ in
|
controller.multiplePeersSelected = { [weak self, weak controller] peers, peerMap, messageText, mode, forwardOptions, _ in
|
||||||
|
let peerIds = peers.map { $0.id }
|
||||||
|
|
||||||
|
let _ = (context.engine.data.get(
|
||||||
|
EngineDataMap(
|
||||||
|
peerIds.map(TelegramEngine.EngineData.Item.Peer.SendPaidMessageStars.init(id:))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self, weak controller] sendPaidMessageStars in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var count: Int32 = Int32(messages.count)
|
||||||
|
if messageText.string.count > 0 {
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
var totalAmount: StarsAmount = .zero
|
||||||
|
for peer in peers {
|
||||||
|
if let maybeAmount = sendPaidMessageStars[peer.id], let amount = maybeAmount {
|
||||||
|
totalAmount = totalAmount + amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let proceed = { [weak self, weak controller] in
|
||||||
guard let strongSelf = self, let strongController = controller else {
|
guard let strongSelf = self, let strongController = controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
strongController.dismiss()
|
strongController.dismiss()
|
||||||
|
|
||||||
var result: [EnqueueMessage] = []
|
var result: [EnqueueMessage] = []
|
||||||
@ -166,6 +191,16 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let maybeAmount = sendPaidMessageStars[peer.id], let amount = maybeAmount {
|
||||||
|
peerMessages = peerMessages.map { message -> EnqueueMessage in
|
||||||
|
return message.withUpdatedAttributes { attributes in
|
||||||
|
var attributes = attributes
|
||||||
|
attributes.append(PaidStarsMessageAttribute(stars: amount, postponeSending: false))
|
||||||
|
return attributes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: peerMessages)
|
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: peerMessages)
|
||||||
|> deliverOnMainQueue).startStandalone(next: { messageIds in
|
|> deliverOnMainQueue).startStandalone(next: { messageIds in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -279,6 +314,28 @@ extension ChatControllerImpl {
|
|||||||
commit(transformedMessages)
|
commit(transformedMessages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if totalAmount.value > 0 {
|
||||||
|
let controller = chatMessagePaymentAlertController(
|
||||||
|
context: nil,
|
||||||
|
presentationData: strongSelf.presentationData,
|
||||||
|
updatedPresentationData: nil,
|
||||||
|
peers: peers,
|
||||||
|
count: count,
|
||||||
|
amount: totalAmount,
|
||||||
|
totalAmount: totalAmount,
|
||||||
|
hasCheck: false,
|
||||||
|
navigationController: strongSelf.navigationController as? NavigationController,
|
||||||
|
completion: { _ in
|
||||||
|
proceed()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
strongSelf.present(controller, in: .window(.root))
|
||||||
|
} else {
|
||||||
|
proceed()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
controller.peerSelected = { [weak self, weak controller] peer, threadId in
|
controller.peerSelected = { [weak self, weak controller] peer, threadId in
|
||||||
guard let strongSelf = self, let strongController = controller else {
|
guard let strongSelf = self, let strongController = controller else {
|
||||||
return
|
return
|
||||||
|
@ -1927,8 +1927,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let sendPaidMessageStars = interfaceState.sendPaidMessageStars {
|
if let sendPaidMessageStars = interfaceState.sendPaidMessageStars {
|
||||||
//TODO:localize
|
placeholder = interfaceState.strings.Chat_InputTextPaidMessagePlaceholder(" # \(presentationStringsFormattedNumber(Int32(sendPaidMessageStars.value), interfaceState.dateTimeFormat.groupingSeparator))").string
|
||||||
placeholder = "Message for # \(presentationStringsFormattedNumber(Int32(sendPaidMessageStars.value), interfaceState.dateTimeFormat.groupingSeparator))"
|
|
||||||
placeholderHasStar = true
|
placeholderHasStar = true
|
||||||
} else {
|
} else {
|
||||||
placeholder = interfaceState.strings.Conversation_InputTextPlaceholder
|
placeholder = interfaceState.strings.Conversation_InputTextPlaceholder
|
||||||
|
Loading…
x
Reference in New Issue
Block a user