mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
b1b5b348b3
@ -1506,7 +1506,10 @@ public struct StarsSubscriptionConfiguration {
|
|||||||
paidMessagesAvailable: false,
|
paidMessagesAvailable: false,
|
||||||
starGiftResaleMinAmount: 125,
|
starGiftResaleMinAmount: 125,
|
||||||
starGiftResaleMaxAmount: 3500,
|
starGiftResaleMaxAmount: 3500,
|
||||||
starGiftCommissionPermille: 80
|
starGiftCommissionPermille: 80,
|
||||||
|
channelMessageSuggestionCommissionPermille: 850,
|
||||||
|
channelMessageSuggestionMaxStarsAmount: 10000,
|
||||||
|
channelMessageSuggestionMaxTonAmount: 10000000000000
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1518,6 +1521,9 @@ public struct StarsSubscriptionConfiguration {
|
|||||||
public let starGiftResaleMinAmount: Int64
|
public let starGiftResaleMinAmount: Int64
|
||||||
public let starGiftResaleMaxAmount: Int64
|
public let starGiftResaleMaxAmount: Int64
|
||||||
public let starGiftCommissionPermille: Int32
|
public let starGiftCommissionPermille: Int32
|
||||||
|
public let channelMessageSuggestionCommissionPermille: Int32
|
||||||
|
public let channelMessageSuggestionMaxStarsAmount: Int64
|
||||||
|
public let channelMessageSuggestionMaxTonAmount: Int64
|
||||||
|
|
||||||
fileprivate init(
|
fileprivate init(
|
||||||
maxFee: Int64,
|
maxFee: Int64,
|
||||||
@ -1527,7 +1533,10 @@ public struct StarsSubscriptionConfiguration {
|
|||||||
paidMessagesAvailable: Bool,
|
paidMessagesAvailable: Bool,
|
||||||
starGiftResaleMinAmount: Int64,
|
starGiftResaleMinAmount: Int64,
|
||||||
starGiftResaleMaxAmount: Int64,
|
starGiftResaleMaxAmount: Int64,
|
||||||
starGiftCommissionPermille: Int32
|
starGiftCommissionPermille: Int32,
|
||||||
|
channelMessageSuggestionCommissionPermille: Int32,
|
||||||
|
channelMessageSuggestionMaxStarsAmount: Int64,
|
||||||
|
channelMessageSuggestionMaxTonAmount: Int64
|
||||||
) {
|
) {
|
||||||
self.maxFee = maxFee
|
self.maxFee = maxFee
|
||||||
self.usdWithdrawRate = usdWithdrawRate
|
self.usdWithdrawRate = usdWithdrawRate
|
||||||
@ -1537,6 +1546,9 @@ public struct StarsSubscriptionConfiguration {
|
|||||||
self.starGiftResaleMinAmount = starGiftResaleMinAmount
|
self.starGiftResaleMinAmount = starGiftResaleMinAmount
|
||||||
self.starGiftResaleMaxAmount = starGiftResaleMaxAmount
|
self.starGiftResaleMaxAmount = starGiftResaleMaxAmount
|
||||||
self.starGiftCommissionPermille = starGiftCommissionPermille
|
self.starGiftCommissionPermille = starGiftCommissionPermille
|
||||||
|
self.channelMessageSuggestionCommissionPermille = channelMessageSuggestionCommissionPermille
|
||||||
|
self.channelMessageSuggestionMaxStarsAmount = channelMessageSuggestionMaxStarsAmount
|
||||||
|
self.channelMessageSuggestionMaxTonAmount = channelMessageSuggestionMaxTonAmount
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func with(appConfiguration: AppConfiguration) -> StarsSubscriptionConfiguration {
|
public static func with(appConfiguration: AppConfiguration) -> StarsSubscriptionConfiguration {
|
||||||
@ -1550,6 +1562,10 @@ public struct StarsSubscriptionConfiguration {
|
|||||||
let starGiftResaleMaxAmount = (data["stars_stargift_resale_amount_max"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.starGiftResaleMaxAmount
|
let starGiftResaleMaxAmount = (data["stars_stargift_resale_amount_max"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.starGiftResaleMaxAmount
|
||||||
let starGiftCommissionPermille = (data["stars_stargift_resale_commission_permille"] as? Double).flatMap(Int32.init) ?? StarsSubscriptionConfiguration.defaultValue.starGiftCommissionPermille
|
let starGiftCommissionPermille = (data["stars_stargift_resale_commission_permille"] as? Double).flatMap(Int32.init) ?? StarsSubscriptionConfiguration.defaultValue.starGiftCommissionPermille
|
||||||
|
|
||||||
|
let channelMessageSuggestionCommissionPermille = (data["stars_suggested_post_commission_permille"] as? Double).flatMap(Int32.init) ?? StarsSubscriptionConfiguration.defaultValue.channelMessageSuggestionCommissionPermille
|
||||||
|
let channelMessageSuggestionMaxStarsAmount = (data["stars_suggested_post_amount_max"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.channelMessageSuggestionMaxStarsAmount
|
||||||
|
let channelMessageSuggestionMaxTonAmount = (data["ton_suggested_post_amount_max"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.channelMessageSuggestionMaxTonAmount
|
||||||
|
|
||||||
return StarsSubscriptionConfiguration(
|
return StarsSubscriptionConfiguration(
|
||||||
maxFee: maxFee,
|
maxFee: maxFee,
|
||||||
usdWithdrawRate: usdWithdrawRate,
|
usdWithdrawRate: usdWithdrawRate,
|
||||||
@ -1558,7 +1574,10 @@ public struct StarsSubscriptionConfiguration {
|
|||||||
paidMessagesAvailable: paidMessagesAvailable,
|
paidMessagesAvailable: paidMessagesAvailable,
|
||||||
starGiftResaleMinAmount: starGiftResaleMinAmount,
|
starGiftResaleMinAmount: starGiftResaleMinAmount,
|
||||||
starGiftResaleMaxAmount: starGiftResaleMaxAmount,
|
starGiftResaleMaxAmount: starGiftResaleMaxAmount,
|
||||||
starGiftCommissionPermille: starGiftCommissionPermille
|
starGiftCommissionPermille: starGiftCommissionPermille,
|
||||||
|
channelMessageSuggestionCommissionPermille: channelMessageSuggestionCommissionPermille,
|
||||||
|
channelMessageSuggestionMaxStarsAmount: channelMessageSuggestionMaxStarsAmount,
|
||||||
|
channelMessageSuggestionMaxTonAmount: channelMessageSuggestionMaxTonAmount
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return .defaultValue
|
return .defaultValue
|
||||||
|
|||||||
@ -182,6 +182,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
private let chatLocation: ChatLocation?
|
private let chatLocation: ChatLocation?
|
||||||
private let bannedSendPhotos: (Int32, Bool)?
|
private let bannedSendPhotos: (Int32, Bool)?
|
||||||
private let bannedSendVideos: (Int32, Bool)?
|
private let bannedSendVideos: (Int32, Bool)?
|
||||||
|
private let enableMultiselection: Bool
|
||||||
private let canBoostToUnrestrict: Bool
|
private let canBoostToUnrestrict: Bool
|
||||||
fileprivate let paidMediaAllowed: Bool
|
fileprivate let paidMediaAllowed: Bool
|
||||||
fileprivate let subject: Subject
|
fileprivate let subject: Subject
|
||||||
@ -1845,6 +1846,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
isScheduledMessages: Bool = false,
|
isScheduledMessages: Bool = false,
|
||||||
bannedSendPhotos: (Int32, Bool)? = nil,
|
bannedSendPhotos: (Int32, Bool)? = nil,
|
||||||
bannedSendVideos: (Int32, Bool)? = nil,
|
bannedSendVideos: (Int32, Bool)? = nil,
|
||||||
|
enableMultiselection: Bool = true,
|
||||||
canBoostToUnrestrict: Bool = false,
|
canBoostToUnrestrict: Bool = false,
|
||||||
paidMediaAllowed: Bool = false,
|
paidMediaAllowed: Bool = false,
|
||||||
subject: Subject,
|
subject: Subject,
|
||||||
@ -1868,6 +1870,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
self.isScheduledMessages = isScheduledMessages
|
self.isScheduledMessages = isScheduledMessages
|
||||||
self.bannedSendPhotos = bannedSendPhotos
|
self.bannedSendPhotos = bannedSendPhotos
|
||||||
self.bannedSendVideos = bannedSendVideos
|
self.bannedSendVideos = bannedSendVideos
|
||||||
|
self.enableMultiselection = enableMultiselection
|
||||||
self.canBoostToUnrestrict = canBoostToUnrestrict
|
self.canBoostToUnrestrict = canBoostToUnrestrict
|
||||||
self.paidMediaAllowed = paidMediaAllowed
|
self.paidMediaAllowed = paidMediaAllowed
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
@ -1877,7 +1880,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
self.mainButtonAction = mainButtonAction
|
self.mainButtonAction = mainButtonAction
|
||||||
self.secondaryButtonAction = secondaryButtonAction
|
self.secondaryButtonAction = secondaryButtonAction
|
||||||
|
|
||||||
let selectionContext = selectionContext ?? TGMediaSelectionContext()
|
let selectionContext = selectionContext ?? TGMediaSelectionContext(groupingAllowed: false, selectionLimit: enableMultiselection ? 100 : 1)!
|
||||||
|
|
||||||
self.titleView = MediaPickerTitleView(theme: self.presentationData.theme, segments: [self.presentationData.strings.Attachment_AllMedia, self.presentationData.strings.Attachment_SelectedMedia(1)], selectedIndex: 0)
|
self.titleView = MediaPickerTitleView(theme: self.presentationData.theme, segments: [self.presentationData.strings.Attachment_AllMedia, self.presentationData.strings.Attachment_SelectedMedia(1)], selectedIndex: 0)
|
||||||
|
|
||||||
@ -1924,11 +1927,12 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData))
|
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData))
|
||||||
|
|
||||||
self.statusBar.statusBarStyle = .Ignore
|
self.statusBar.statusBarStyle = .Ignore
|
||||||
|
|
||||||
selectionContext.attemptSelectingItem = { [weak self] item in
|
selectionContext.attemptSelectingItem = { [weak self] item in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if let _ = item as? TGMediaPickerGalleryPhotoItem {
|
if let _ = item as? TGMediaPickerGalleryPhotoItem {
|
||||||
if self.bannedSendPhotos != nil {
|
if self.bannedSendPhotos != nil {
|
||||||
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
@ -2807,7 +2811,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let selectionContext = self.interaction?.selectionState, let editingContext = self.interaction?.editingState {
|
if let selectionContext = self.interaction?.selectionState, let editingContext = self.interaction?.editingState {
|
||||||
selectionContext.selectionLimit = 10
|
selectionContext.selectionLimit = self.enableMultiselection ? 10 : 1
|
||||||
for case let item as TGMediaEditableItem in selectionContext.selectedItems() {
|
for case let item as TGMediaEditableItem in selectionContext.selectedItems() {
|
||||||
editingContext.setPrice(NSNumber(value: amount), for: item)
|
editingContext.setPrice(NSNumber(value: amount), for: item)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1666,7 +1666,7 @@ public struct StarsTransactionReference: PostboxCoding, Hashable, Equatable {
|
|||||||
public let id: String
|
public let id: String
|
||||||
public let isRefund: Bool
|
public let isRefund: Bool
|
||||||
|
|
||||||
public init(peerId: EnginePeer.Id, id: String, isRefund: Bool) {
|
public init(peerId: EnginePeer.Id, id: String, isRefund: Bool) {
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.id = id
|
self.id = id
|
||||||
self.isRefund = isRefund
|
self.isRefund = isRefund
|
||||||
|
|||||||
@ -160,6 +160,10 @@ public extension TelegramEngine {
|
|||||||
public func updateStarGiftResalePrice(reference: StarGiftReference, price: Int64?) -> Signal<Never, UpdateStarGiftPriceError> {
|
public func updateStarGiftResalePrice(reference: StarGiftReference, price: Int64?) -> Signal<Never, UpdateStarGiftPriceError> {
|
||||||
return _internal_updateStarGiftResalePrice(account: self.account, reference: reference, price: price)
|
return _internal_updateStarGiftResalePrice(account: self.account, reference: reference, price: price)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func getStarsTransaction(reference: StarsTransactionReference) -> Signal<StarsContext.State.Transaction?, NoError> {
|
||||||
|
return _internal_getStarsTransaction(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, transactionReference: reference)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1259,8 +1259,24 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
messagePeer = EnginePeer(messagePeerValue)
|
messagePeer = EnginePeer(messagePeerValue)
|
||||||
} else if message.id.peerId.namespace == Namespaces.Peer.CloudChannel, let peer = message.peers[message.id.peerId] as? TelegramChannel, peer.isMonoForum {
|
} else if message.id.peerId.namespace == Namespaces.Peer.CloudChannel, let peer = message.peers[message.id.peerId] as? TelegramChannel, peer.isMonoForum {
|
||||||
if let author = message.author, let threadId = message.threadId, let threadPeer = message.peers[PeerId(threadId)], author.id != threadPeer.id {
|
if let author = message.author, let threadId = message.threadId, let threadPeer = message.peers[PeerId(threadId)], author.id != threadPeer.id {
|
||||||
isOutgoing = true
|
if case .channel = author {
|
||||||
messagePeer = EnginePeer(threadPeer)
|
var isUser = true
|
||||||
|
if let peer = message.peers[message.id.peerId] as? TelegramChannel {
|
||||||
|
if peer.isMonoForum, let linkedMonoforumId = peer.linkedMonoforumId, let mainChannel = message.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.manageDirect) {
|
||||||
|
isUser = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isUser {
|
||||||
|
messagePeer = author
|
||||||
|
} else {
|
||||||
|
messagePeer = EnginePeer(threadPeer)
|
||||||
|
isOutgoing = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isOutgoing = true
|
||||||
|
messagePeer = EnginePeer(threadPeer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1450,12 +1466,51 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
attributedString = NSAttributedString(string: string, font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: string, font: titleFont, textColor: primaryTextColor)
|
||||||
case .suggestedPostSuccess:
|
case let .suggestedPostSuccess(amount):
|
||||||
|
var isUser = true
|
||||||
|
var channelName: String = ""
|
||||||
|
if let peer = message.peers[message.id.peerId] as? TelegramChannel {
|
||||||
|
channelName = peer.title
|
||||||
|
if peer.isMonoForum, let linkedMonoforumId = peer.linkedMonoforumId, let mainChannel = message.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.manageDirect) {
|
||||||
|
isUser = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = isUser
|
||||||
|
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
attributedString = NSAttributedString(string: "Suggested post was posted", font: titleFont, textColor: primaryTextColor)
|
let amountString: String
|
||||||
case .suggestedPostRefund:
|
switch amount.currency {
|
||||||
|
case .stars:
|
||||||
|
if amount.amount.value == 1 {
|
||||||
|
amountString = "1 Star"
|
||||||
|
} else {
|
||||||
|
amountString = "\(amount.amount.value) Stars"
|
||||||
|
}
|
||||||
|
case .ton:
|
||||||
|
amountString = "\(formatTonAmountText(amount.amount.value, dateTimeFormat: dateTimeFormat)) TON"
|
||||||
|
}
|
||||||
|
attributedString = parseMarkdownIntoAttributedString("**\(channelName)** received **\(amountString)** for publishing this post", attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil }))
|
||||||
|
case let .suggestedPostRefund(info):
|
||||||
|
var isUser = true
|
||||||
|
var channelName: String = ""
|
||||||
|
if let peer = message.peers[message.id.peerId] as? TelegramChannel {
|
||||||
|
channelName = peer.title
|
||||||
|
if peer.isMonoForum, let linkedMonoforumId = peer.linkedMonoforumId, let mainChannel = message.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.manageDirect) {
|
||||||
|
isUser = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = channelName
|
||||||
|
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
attributedString = NSAttributedString(string: "Suggested post was refunded", font: titleFont, textColor: primaryTextColor)
|
if info.isUserInitiated {
|
||||||
|
if isUser {
|
||||||
|
attributedString = NSAttributedString(string: "Suggested post was refunded because you didn't have enough funds", font: titleFont, textColor: primaryTextColor)
|
||||||
|
} else {
|
||||||
|
attributedString = NSAttributedString(string: "Suggested post was refunded because the user didn't have enough funds", font: titleFont, textColor: primaryTextColor)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attributedString = NSAttributedString(string: "Suggested post was refunded because the message was deleted", font: titleFont, textColor: primaryTextColor)
|
||||||
|
}
|
||||||
case let .giftTon(currency, amount, _, _, _):
|
case let .giftTon(currency, amount, _, _, _):
|
||||||
attributedString = nil
|
attributedString = nil
|
||||||
if !forAdditionalServiceMessage {
|
if !forAdditionalServiceMessage {
|
||||||
|
|||||||
@ -1408,7 +1408,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
layoutSize.height += additionalTopHeight
|
layoutSize.height += additionalTopHeight
|
||||||
imageFrame.origin.y += additionalTopHeight
|
imageFrame.origin.y += additionalTopHeight
|
||||||
|
|
||||||
var headersOffset: CGFloat = 0.0
|
var headersOffset: CGFloat = additionalTopHeight
|
||||||
if let (threadInfoSize, _) = threadInfoApply {
|
if let (threadInfoSize, _) = threadInfoApply {
|
||||||
headersOffset += threadInfoSize.height + 10.0
|
headersOffset += threadInfoSize.height + 10.0
|
||||||
}
|
}
|
||||||
@ -1625,7 +1625,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var headersOffset: CGFloat = 0.0
|
var headersOffset: CGFloat = additionalTopHeight
|
||||||
if let (threadInfoSize, threadInfoApply) = threadInfoApply {
|
if let (threadInfoSize, threadInfoApply) = threadInfoApply {
|
||||||
let threadInfoNode = threadInfoApply(synchronousLoads)
|
let threadInfoNode = threadInfoApply(synchronousLoads)
|
||||||
if strongSelf.threadInfoNode == nil {
|
if strongSelf.threadInfoNode == nil {
|
||||||
|
|||||||
@ -991,7 +991,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
baseShareButtonFrame.origin.x = dateAndStatusFrame.maxX + 8.0
|
baseShareButtonFrame.origin.x = dateAndStatusFrame.maxX + 8.0
|
||||||
}
|
}
|
||||||
|
|
||||||
var headersOffset: CGFloat = 0.0
|
var headersOffset: CGFloat = additionalTopHeight
|
||||||
if let (threadInfoSize, _) = threadInfoApply {
|
if let (threadInfoSize, _) = threadInfoApply {
|
||||||
headersOffset += threadInfoSize.height + 10.0
|
headersOffset += threadInfoSize.height + 10.0
|
||||||
}
|
}
|
||||||
@ -1149,7 +1149,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var headersOffset: CGFloat = 0.0
|
var headersOffset: CGFloat = additionalTopHeight
|
||||||
if let (threadInfoSize, threadInfoApply) = threadInfoApply {
|
if let (threadInfoSize, threadInfoApply) = threadInfoApply {
|
||||||
let threadInfoNode = threadInfoApply(synchronousLoads)
|
let threadInfoNode = threadInfoApply(synchronousLoads)
|
||||||
if strongSelf.threadInfoNode == nil {
|
if strongSelf.threadInfoNode == nil {
|
||||||
|
|||||||
@ -199,10 +199,14 @@ public final class ChatMessageSuggestedPostInfoNode: ASDisplayNode {
|
|||||||
contentHeight += titleLayout.0.size.height
|
contentHeight += titleLayout.0.size.height
|
||||||
contentHeight += titleSpacing
|
contentHeight += titleSpacing
|
||||||
|
|
||||||
maxContentWidth = max(maxContentWidth, priceLabelLayout.0.size.width + labelSpacing + priceValueLayout.0.size.width)
|
var tableContentWidth: CGFloat = 0.0
|
||||||
contentHeight += priceLabelLayout.0.size.height + valuesVerticalSpacing
|
tableContentWidth = max(tableContentWidth, priceLabelLayout.0.size.width + labelSpacing + priceValueLayout.0.size.width)
|
||||||
|
tableContentWidth = max(tableContentWidth, timeLabelLayout.0.size.width + labelSpacing + timeValueLayout.0.size.width)
|
||||||
|
|
||||||
maxContentWidth = max(maxContentWidth, timeLabelLayout.0.size.width + labelSpacing + timeValueLayout.0.size.width)
|
let labelValueOffset = labelSpacing + max(priceLabelLayout.0.size.width, timeLabelLayout.0.size.width)
|
||||||
|
|
||||||
|
maxContentWidth = max(maxContentWidth, tableContentWidth)
|
||||||
|
contentHeight += priceLabelLayout.0.size.height + valuesVerticalSpacing
|
||||||
contentHeight += timeLabelLayout.0.size.height
|
contentHeight += timeLabelLayout.0.size.height
|
||||||
|
|
||||||
let size = CGSize(width: insets.left + insets.right + maxContentWidth, height: insets.top + insets.bottom + contentHeight)
|
let size = CGSize(width: insets.left + insets.right + maxContentWidth, height: insets.top + insets.bottom + contentHeight)
|
||||||
@ -252,13 +256,15 @@ public final class ChatMessageSuggestedPostInfoNode: ASDisplayNode {
|
|||||||
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleLayout.0.size.width) * 0.5), y: insets.top), size: titleLayout.0.size)
|
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleLayout.0.size.width) * 0.5), y: insets.top), size: titleLayout.0.size)
|
||||||
titleNode.frame = titleFrame
|
titleNode.frame = titleFrame
|
||||||
|
|
||||||
let priceLabelFrame = CGRect(origin: CGPoint(x: insets.left, y: titleFrame.maxY + titleSpacing), size: priceLabelLayout.0.size)
|
let tableX: CGFloat = floor((size.width - tableContentWidth) * 0.5)
|
||||||
|
|
||||||
|
let priceLabelFrame = CGRect(origin: CGPoint(x: tableX, y: titleFrame.maxY + titleSpacing), size: priceLabelLayout.0.size)
|
||||||
priceLabelNode.frame = priceLabelFrame
|
priceLabelNode.frame = priceLabelFrame
|
||||||
priceValueNode.frame = CGRect(origin: CGPoint(x: priceLabelFrame.maxX + labelSpacing, y: priceLabelFrame.minY), size: priceValueLayout.0.size)
|
priceValueNode.frame = CGRect(origin: CGPoint(x: tableX + labelValueOffset, y: priceLabelFrame.minY), size: priceValueLayout.0.size)
|
||||||
|
|
||||||
let timeLabelFrame = CGRect(origin: CGPoint(x: insets.left, y: priceLabelFrame.maxY + valuesVerticalSpacing), size: timeLabelLayout.0.size)
|
let timeLabelFrame = CGRect(origin: CGPoint(x: tableX, y: priceLabelFrame.maxY + valuesVerticalSpacing), size: timeLabelLayout.0.size)
|
||||||
timeLabelNode.frame = timeLabelFrame
|
timeLabelNode.frame = timeLabelFrame
|
||||||
timeValueNode.frame = CGRect(origin: CGPoint(x: timeLabelFrame.maxX + labelSpacing, y: timeLabelFrame.minY), size: timeValueLayout.0.size)
|
timeValueNode.frame = CGRect(origin: CGPoint(x: tableX + labelValueOffset, y: timeLabelFrame.minY), size: timeValueLayout.0.size)
|
||||||
|
|
||||||
return node
|
return node
|
||||||
})
|
})
|
||||||
|
|||||||
@ -172,24 +172,27 @@ final class ForumSettingsScreenComponent: Component {
|
|||||||
if let controller = self.environment?.controller(), let navigationController = controller.navigationController as? NavigationController {
|
if let controller = self.environment?.controller(), let navigationController = controller.navigationController as? NavigationController {
|
||||||
var viewControllers = navigationController.viewControllers
|
var viewControllers = navigationController.viewControllers
|
||||||
|
|
||||||
if self.isOn && self.mode == .list {
|
if case .legacyGroup = peer {
|
||||||
for i in 0 ..< viewControllers.count {
|
|
||||||
if let chatController = viewControllers[i] as? ChatController, chatController.chatLocation.peerId == component.peerId {
|
|
||||||
let chatListController = component.context.sharedContext.makeChatListController(context: component.context, location: .forum(peerId: component.peerId), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false)
|
|
||||||
viewControllers[i] = chatListController
|
|
||||||
}
|
|
||||||
}
|
|
||||||
navigationController.setViewControllers(viewControllers, animated: false)
|
|
||||||
} else {
|
} else {
|
||||||
for i in (0 ..< viewControllers.count).reversed() {
|
if self.isOn && self.mode == .list {
|
||||||
if let chatListController = viewControllers[i] as? ChatListController, chatListController.location == .forum(peerId: component.peerId) {
|
for i in 0 ..< viewControllers.count {
|
||||||
viewControllers.remove(at: i)
|
if let chatController = viewControllers[i] as? ChatController, chatController.chatLocation.peerId == component.peerId {
|
||||||
|
let chatListController = component.context.sharedContext.makeChatListController(context: component.context, location: .forum(peerId: component.peerId), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false)
|
||||||
|
viewControllers[i] = chatListController
|
||||||
|
}
|
||||||
|
}
|
||||||
|
navigationController.setViewControllers(viewControllers, animated: false)
|
||||||
|
} else {
|
||||||
|
for i in (0 ..< viewControllers.count).reversed() {
|
||||||
|
if let chatListController = viewControllers[i] as? ChatListController, chatListController.location == .forum(peerId: component.peerId) {
|
||||||
|
viewControllers.remove(at: i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
navigationController.setViewControllers(viewControllers, animated: false)
|
||||||
|
|
||||||
|
if let baseController = navigationController as? TelegramRootControllerInterface, let chatListController = baseController.getChatsController() as? ChatListController {
|
||||||
|
chatListController.resetForumStackIfOpen()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
navigationController.setViewControllers(viewControllers, animated: false)
|
|
||||||
|
|
||||||
if let baseController = navigationController as? TelegramRootControllerInterface, let chatListController = baseController.getChatsController() as? ChatListController {
|
|
||||||
chatListController.resetForumStackIfOpen()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,6 +235,34 @@ final class ForumSettingsScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
if let resultPeerId {
|
if let resultPeerId {
|
||||||
self.peerIdPromise.set(resultPeerId)
|
self.peerIdPromise.set(resultPeerId)
|
||||||
|
|
||||||
|
let _ = component.context.engine.peers.setChannelForumMode(id: resultPeerId, isForum: true, displayForumAsTabs: self.mode == .tabs).startStandalone()
|
||||||
|
|
||||||
|
if let controller = self.environment?.controller(), let navigationController = controller.navigationController as? NavigationController {
|
||||||
|
var viewControllers = navigationController.viewControllers
|
||||||
|
if self.mode == .list {
|
||||||
|
for i in 0 ..< viewControllers.count {
|
||||||
|
if let chatController = viewControllers[i] as? ChatController, chatController.chatLocation.peerId == component.peerId {
|
||||||
|
let chatListController = component.context.sharedContext.makeChatListController(context: component.context, location: .forum(peerId: resultPeerId), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false)
|
||||||
|
viewControllers[i] = chatListController
|
||||||
|
}
|
||||||
|
}
|
||||||
|
navigationController.setViewControllers(viewControllers, animated: false)
|
||||||
|
} else {
|
||||||
|
for i in (0 ..< viewControllers.count).reversed() {
|
||||||
|
if let chatListController = viewControllers[i] as? ChatListController, chatListController.location == .forum(peerId: component.peerId) {
|
||||||
|
viewControllers.remove(at: i)
|
||||||
|
} else if let peerInfoScreen = viewControllers[i] as? PeerInfoScreen, peerInfoScreen.peerId == component.peerId {
|
||||||
|
viewControllers.remove(at: i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
navigationController.setViewControllers(viewControllers, animated: false)
|
||||||
|
|
||||||
|
if let baseController = navigationController as? TelegramRootControllerInterface, let chatListController = baseController.getChatsController() as? ChatListController {
|
||||||
|
chatListController.resetForumStackIfOpen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.isOn = false
|
self.isOn = false
|
||||||
self.state?.updated(transition: .easeInOut(duration: 0.2))
|
self.state?.updated(transition: .easeInOut(duration: 0.2))
|
||||||
|
|||||||
@ -2290,8 +2290,12 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
|
|||||||
if hasVoiceChat || canStartVoiceChat {
|
if hasVoiceChat || canStartVoiceChat {
|
||||||
result.append(.voiceChat)
|
result.append(.voiceChat)
|
||||||
}
|
}
|
||||||
|
if case let .broadcast(info) = channel.info, info.flags.contains(.hasMonoforum), !channel.hasPermission(.manageDirect) {
|
||||||
|
result.append(.message)
|
||||||
|
}
|
||||||
result.append(.mute)
|
result.append(.mute)
|
||||||
if hasDiscussion {
|
if case let .broadcast(info) = channel.info, info.flags.contains(.hasMonoforum), !channel.hasPermission(.manageDirect) {
|
||||||
|
} else if hasDiscussion {
|
||||||
result.append(.discussion)
|
result.append(.discussion)
|
||||||
}
|
}
|
||||||
result.append(.search)
|
result.append(.search)
|
||||||
|
|||||||
@ -1959,7 +1959,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let personalChannel = data.personalChannel {
|
if channel.hasPermission(.manageDirect), let personalChannel = data.personalChannel {
|
||||||
let peerId = personalChannel.peer.peerId
|
let peerId = personalChannel.peer.peerId
|
||||||
items[.channelMonoforum]?.append(PeerInfoScreenPersonalChannelItem(id: ItemPeerPersonalChannel, context: context, data: personalChannel, controller: { [weak interaction] in
|
items[.channelMonoforum]?.append(PeerInfoScreenPersonalChannelItem(id: ItemPeerPersonalChannel, context: context, data: personalChannel, controller: { [weak interaction] in
|
||||||
guard let interaction else {
|
guard let interaction else {
|
||||||
@ -5998,18 +5998,32 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
switch key {
|
switch key {
|
||||||
case .message:
|
case .message:
|
||||||
if let navigationController = controller.navigationController as? NavigationController, let peer = self.data?.peer {
|
if let navigationController = controller.navigationController as? NavigationController, let peer = self.data?.peer {
|
||||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in
|
if let channel = peer as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasMonoforum), let linkedMonoforumId = channel.linkedMonoforumId {
|
||||||
if let strongSelf = self, strongSelf.nearbyPeerDistance != nil {
|
Task { @MainActor [weak self] in
|
||||||
var viewControllers = navigationController.viewControllers
|
guard let self else {
|
||||||
viewControllers = viewControllers.filter { controller in
|
return
|
||||||
if controller is PeerInfoScreen {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
navigationController.setViewControllers(viewControllers, animated: false)
|
|
||||||
|
guard let peer = await self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: linkedMonoforumId)).get() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), keepStack: .default))
|
||||||
}
|
}
|
||||||
}))
|
} else {
|
||||||
|
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in
|
||||||
|
if let strongSelf = self, strongSelf.nearbyPeerDistance != nil {
|
||||||
|
var viewControllers = navigationController.viewControllers
|
||||||
|
viewControllers = viewControllers.filter { controller in
|
||||||
|
if controller is PeerInfoScreen {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
navigationController.setViewControllers(viewControllers, animated: false)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case .discussion:
|
case .discussion:
|
||||||
if let cachedData = self.data?.cachedData as? CachedChannelData, case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId {
|
if let cachedData = self.data?.cachedData as? CachedChannelData, case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId {
|
||||||
|
|||||||
@ -29,7 +29,7 @@ final class PostSuggestionsSettingsScreenComponent: Component {
|
|||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let usdWithdrawRate: Int64
|
let usdWithdrawRate: Int64
|
||||||
let paidMessageCommissionPermille: Int
|
let channelMessageSuggestionCommissionPermille: Int
|
||||||
let peer: EnginePeer?
|
let peer: EnginePeer?
|
||||||
let initialPrice: StarsAmount?
|
let initialPrice: StarsAmount?
|
||||||
let completion: () -> Void
|
let completion: () -> Void
|
||||||
@ -37,14 +37,14 @@ final class PostSuggestionsSettingsScreenComponent: Component {
|
|||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
usdWithdrawRate: Int64,
|
usdWithdrawRate: Int64,
|
||||||
paidMessageCommissionPermille: Int,
|
channelMessageSuggestionCommissionPermille: Int,
|
||||||
peer: EnginePeer?,
|
peer: EnginePeer?,
|
||||||
initialPrice: StarsAmount?,
|
initialPrice: StarsAmount?,
|
||||||
completion: @escaping () -> Void
|
completion: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.usdWithdrawRate = usdWithdrawRate
|
self.usdWithdrawRate = usdWithdrawRate
|
||||||
self.paidMessageCommissionPermille = paidMessageCommissionPermille
|
self.channelMessageSuggestionCommissionPermille = channelMessageSuggestionCommissionPermille
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.initialPrice = initialPrice
|
self.initialPrice = initialPrice
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
@ -373,7 +373,7 @@ final class PostSuggestionsSettingsScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let currentAmount: StarsAmount = StarsAmount(value: Int64(self.starCount), nanos: 0)
|
let currentAmount: StarsAmount = StarsAmount(value: Int64(self.starCount), nanos: 0)
|
||||||
let starsScreen = component.context.sharedContext.makeStarsWithdrawalScreen(context: component.context, subject: .enterAmount(current: currentAmount, minValue: StarsAmount(value: 0, nanos: 0), fractionAfterCommission: component.paidMessageCommissionPermille / 10, kind: .postSuggestion, completion: { [weak self] amount in
|
let starsScreen = component.context.sharedContext.makeStarsWithdrawalScreen(context: component.context, subject: .enterAmount(current: currentAmount, minValue: StarsAmount(value: 0, nanos: 0), fractionAfterCommission: component.channelMessageSuggestionCommissionPermille / 10, kind: .postSuggestion, completion: { [weak self] amount in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -404,7 +404,7 @@ final class PostSuggestionsSettingsScreenComponent: Component {
|
|||||||
)),
|
)),
|
||||||
footer: AnyComponent(MultilineTextComponent(
|
footer: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: environment.strings.ChannelMessages_PriceSectionFooterValue("\(component.paidMessageCommissionPermille / 10)").string,
|
string: environment.strings.ChannelMessages_PriceSectionFooterValue("\(component.channelMessageSuggestionCommissionPermille / 10)").string,
|
||||||
font: Font.regular(13.0),
|
font: Font.regular(13.0),
|
||||||
textColor: self.starCount == 0 ? .clear : environment.theme.list.freeTextColor
|
textColor: self.starCount == 0 ? .clear : environment.theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -503,7 +503,7 @@ public final class PostSuggestionsSettingsScreen: ViewControllerComponentContain
|
|||||||
super.init(context: context, component: PostSuggestionsSettingsScreenComponent(
|
super.init(context: context, component: PostSuggestionsSettingsScreenComponent(
|
||||||
context: context,
|
context: context,
|
||||||
usdWithdrawRate: configuration.usdWithdrawRate,
|
usdWithdrawRate: configuration.usdWithdrawRate,
|
||||||
paidMessageCommissionPermille: Int(configuration.paidMessageCommissionPermille),
|
channelMessageSuggestionCommissionPermille: Int(configuration.channelMessageSuggestionCommissionPermille),
|
||||||
peer: peer,
|
peer: peer,
|
||||||
initialPrice: initialPrice,
|
initialPrice: initialPrice,
|
||||||
completion: completion
|
completion: completion
|
||||||
|
|||||||
@ -220,14 +220,14 @@ private final class SheetContent: CombinedComponent {
|
|||||||
switch state.currency {
|
switch state.currency {
|
||||||
case .stars:
|
case .stars:
|
||||||
amountTitle = "ENTER A PRICE IN STARS"
|
amountTitle = "ENTER A PRICE IN STARS"
|
||||||
|
maxAmount = StarsAmount(value: resaleConfiguration.channelMessageSuggestionMaxStarsAmount, nanos: 0)
|
||||||
case .ton:
|
case .ton:
|
||||||
amountTitle = "ENTER A PRICE IN TON"
|
amountTitle = "ENTER A PRICE IN TON"
|
||||||
|
maxAmount = StarsAmount(value: resaleConfiguration.channelMessageSuggestionMaxTonAmount, nanos: 0)
|
||||||
}
|
}
|
||||||
amountPlaceholder = "Price"
|
amountPlaceholder = "Price"
|
||||||
|
|
||||||
minAmount = StarsAmount(value: 0, nanos: 0)
|
minAmount = StarsAmount(value: 0, nanos: 0)
|
||||||
//TODO:release
|
|
||||||
maxAmount = StarsAmount(value: resaleConfiguration.paidMessageMaxAmount, nanos: 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = title.update(
|
let title = title.update(
|
||||||
@ -299,69 +299,86 @@ private final class SheetContent: CombinedComponent {
|
|||||||
if let tonBalance = state.tonBalance {
|
if let tonBalance = state.tonBalance {
|
||||||
tonBalanceValue = tonBalance
|
tonBalanceValue = tonBalance
|
||||||
}
|
}
|
||||||
if case let .suggestedPost(mode, _, _, _) = component.mode, (state.currency == .ton || tonBalanceValue > StarsAmount.zero) {
|
|
||||||
//TODO:localize
|
if case let .suggestedPost(mode, _, _, _) = component.mode {
|
||||||
let selectedId: AnyHashable = state.currency == .stars ? AnyHashable(0 as Int) : AnyHashable(1 as Int)
|
var displayCurrencySelector = false
|
||||||
let starsTitle: String
|
|
||||||
let tonTitle: String
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .sender:
|
case let .sender(_, isFromAdmin):
|
||||||
starsTitle = "Offer Stars"
|
if isFromAdmin {
|
||||||
tonTitle = "Offer TON"
|
displayCurrencySelector = true
|
||||||
|
} else {
|
||||||
|
if state.currency == .ton || tonBalanceValue > StarsAmount.zero {
|
||||||
|
displayCurrencySelector = true
|
||||||
|
}
|
||||||
|
}
|
||||||
case .admin:
|
case .admin:
|
||||||
starsTitle = "Request Stars"
|
displayCurrencySelector = true
|
||||||
tonTitle = "Request TON"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let currencyToggle = currencyToggle.update(
|
if displayCurrencySelector {
|
||||||
component: TabSelectorComponent(
|
//TODO:localize
|
||||||
colors: TabSelectorComponent.Colors(
|
let selectedId: AnyHashable = state.currency == .stars ? AnyHashable(0 as Int) : AnyHashable(1 as Int)
|
||||||
foreground: theme.list.itemSecondaryTextColor,
|
let starsTitle: String
|
||||||
selection: theme.list.itemSecondaryTextColor.withMultipliedAlpha(0.15),
|
let tonTitle: String
|
||||||
simple: true
|
switch mode {
|
||||||
),
|
case .sender:
|
||||||
customLayout: TabSelectorComponent.CustomLayout(
|
starsTitle = "Offer Stars"
|
||||||
font: Font.medium(14.0),
|
tonTitle = "Offer TON"
|
||||||
spacing: 10.0
|
case .admin:
|
||||||
),
|
starsTitle = "Request Stars"
|
||||||
items: [
|
tonTitle = "Request TON"
|
||||||
TabSelectorComponent.Item(
|
}
|
||||||
id: AnyHashable(0),
|
|
||||||
content: .component(AnyComponent(CurrencyTabItemComponent(icon: .stars, title: starsTitle, theme: theme)))
|
let currencyToggle = currencyToggle.update(
|
||||||
|
component: TabSelectorComponent(
|
||||||
|
colors: TabSelectorComponent.Colors(
|
||||||
|
foreground: theme.list.itemSecondaryTextColor,
|
||||||
|
selection: theme.list.itemSecondaryTextColor.withMultipliedAlpha(0.15),
|
||||||
|
simple: true
|
||||||
),
|
),
|
||||||
TabSelectorComponent.Item(
|
customLayout: TabSelectorComponent.CustomLayout(
|
||||||
id: AnyHashable(1),
|
font: Font.medium(14.0),
|
||||||
content: .component(AnyComponent(CurrencyTabItemComponent(icon: .ton, title: tonTitle, theme: theme)))
|
spacing: 10.0
|
||||||
)
|
),
|
||||||
],
|
items: [
|
||||||
selectedId: selectedId,
|
TabSelectorComponent.Item(
|
||||||
setSelectedId: { [weak state] id in
|
id: AnyHashable(0),
|
||||||
guard let state else {
|
content: .component(AnyComponent(CurrencyTabItemComponent(icon: .stars, title: starsTitle, theme: theme)))
|
||||||
return
|
),
|
||||||
|
TabSelectorComponent.Item(
|
||||||
|
id: AnyHashable(1),
|
||||||
|
content: .component(AnyComponent(CurrencyTabItemComponent(icon: .ton, title: tonTitle, theme: theme)))
|
||||||
|
)
|
||||||
|
],
|
||||||
|
selectedId: selectedId,
|
||||||
|
setSelectedId: { [weak state] id in
|
||||||
|
guard let state else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let currency: CurrencyAmount.Currency
|
||||||
|
if id == AnyHashable(0) {
|
||||||
|
currency = .stars
|
||||||
|
} else {
|
||||||
|
currency = .ton
|
||||||
|
}
|
||||||
|
if state.currency != currency {
|
||||||
|
state.currency = currency
|
||||||
|
state.amount = nil
|
||||||
|
}
|
||||||
|
state.updated(transition: .spring(duration: 0.4))
|
||||||
}
|
}
|
||||||
|
),
|
||||||
let currency: CurrencyAmount.Currency
|
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 100.0),
|
||||||
if id == AnyHashable(0) {
|
transition: context.transition
|
||||||
currency = .stars
|
)
|
||||||
} else {
|
contentSize.height -= 17.0
|
||||||
currency = .ton
|
let currencyToggleFrame = CGRect(origin: CGPoint(x: floor((context.availableSize.width - currencyToggle.size.width) * 0.5), y: contentSize.height), size: currencyToggle.size)
|
||||||
}
|
context.add(currencyToggle
|
||||||
if state.currency != currency {
|
.position(currencyToggle.size.centered(in: currencyToggleFrame).center))
|
||||||
state.currency = currency
|
|
||||||
state.amount = nil
|
contentSize.height += currencyToggle.size.height + 29.0
|
||||||
}
|
}
|
||||||
state.updated(transition: .spring(duration: 0.4))
|
|
||||||
}
|
|
||||||
),
|
|
||||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 100.0),
|
|
||||||
transition: context.transition
|
|
||||||
)
|
|
||||||
contentSize.height -= 17.0
|
|
||||||
let currencyToggleFrame = CGRect(origin: CGPoint(x: floor((context.availableSize.width - currencyToggle.size.width) * 0.5), y: contentSize.height), size: currencyToggle.size)
|
|
||||||
context.add(currencyToggle
|
|
||||||
.position(currencyToggle.size.centered(in: currencyToggleFrame).center))
|
|
||||||
|
|
||||||
contentSize.height += currencyToggle.size.height + 29.0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let amountFont = Font.regular(13.0)
|
let amountFont = Font.regular(13.0)
|
||||||
@ -506,7 +523,7 @@ private final class SheetContent: CombinedComponent {
|
|||||||
accentColor: theme.list.itemAccentColor,
|
accentColor: theme.list.itemAccentColor,
|
||||||
value: state.amount?.value,
|
value: state.amount?.value,
|
||||||
minValue: minAmount?.value,
|
minValue: minAmount?.value,
|
||||||
maxValue: state.currency == .ton ? nil : maxAmount?.value,
|
maxValue: maxAmount?.value,
|
||||||
placeholderText: amountPlaceholder,
|
placeholderText: amountPlaceholder,
|
||||||
labelText: amountLabel,
|
labelText: amountLabel,
|
||||||
currency: state.currency,
|
currency: state.currency,
|
||||||
@ -658,7 +675,7 @@ private final class SheetContent: CombinedComponent {
|
|||||||
//TODO:localize
|
//TODO:localize
|
||||||
switch mode {
|
switch mode {
|
||||||
case .sender:
|
case .sender:
|
||||||
if let amount = state.amount {
|
if let amount = state.amount, amount != .zero {
|
||||||
let currencySymbol: String
|
let currencySymbol: String
|
||||||
let currencyAmount: String
|
let currencyAmount: String
|
||||||
switch state.currency {
|
switch state.currency {
|
||||||
@ -1164,9 +1181,14 @@ private final class AmountFieldStarsFormatter: NSObject, UITextFieldDelegate {
|
|||||||
// Convert and combine
|
// Convert and combine
|
||||||
if let whole = Int64(wholeSlice),
|
if let whole = Int64(wholeSlice),
|
||||||
let frac = Int64(fractionStr) {
|
let frac = Int64(fractionStr) {
|
||||||
|
|
||||||
|
let whole = min(whole, Int64.max / scale)
|
||||||
|
|
||||||
amount = whole * scale + frac
|
amount = whole * scale + frac
|
||||||
}
|
}
|
||||||
} else if let whole = Int64(text) { // string had no dot at all
|
} else if let whole = Int64(text) { // string had no dot at all
|
||||||
|
let whole = min(whole, Int64.max / scale)
|
||||||
|
|
||||||
amount = whole * scale
|
amount = whole * scale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1241,7 +1263,7 @@ private final class AmountFieldStarsFormatter: NSObject, UITextFieldDelegate {
|
|||||||
case .stars:
|
case .stars:
|
||||||
textField.text = "\(self.maxValue)"
|
textField.text = "\(self.maxValue)"
|
||||||
case .ton:
|
case .ton:
|
||||||
textField.text = "\(formatTonAmountText(self.maxValue, dateTimeFormat: self.dateTimeFormat))"
|
textField.text = "\(formatTonAmountText(self.maxValue, dateTimeFormat: PresentationDateTimeFormat(timeFormat: self.dateTimeFormat.timeFormat, dateFormat: self.dateTimeFormat.dateFormat, dateSeparator: "", dateSuffix: "", requiresFullYear: false, decimalSeparator: ".", groupingSeparator: "")))"
|
||||||
}
|
}
|
||||||
self.onTextChanged(text: self.textField.text ?? "")
|
self.onTextChanged(text: self.textField.text ?? "")
|
||||||
self.animateError()
|
self.animateError()
|
||||||
@ -1396,13 +1418,13 @@ private final class AmountFieldComponent: Component {
|
|||||||
|
|
||||||
self.textField.textColor = component.textColor
|
self.textField.textColor = component.textColor
|
||||||
if self.component?.currency != component.currency {
|
if self.component?.currency != component.currency {
|
||||||
if let value = component.value {
|
if let value = component.value, value != .zero {
|
||||||
var text = ""
|
var text = ""
|
||||||
switch component.currency {
|
switch component.currency {
|
||||||
case .stars:
|
case .stars:
|
||||||
text = "\(value)"
|
text = "\(value)"
|
||||||
case .ton:
|
case .ton:
|
||||||
text = "\(formatTonAmountText(value, dateTimeFormat: component.dateTimeFormat))"
|
text = "\(formatTonAmountText(value, dateTimeFormat: PresentationDateTimeFormat(timeFormat: component.dateTimeFormat.timeFormat, dateFormat: component.dateTimeFormat.dateFormat, dateSeparator: "", dateSuffix: "", requiresFullYear: false, decimalSeparator: ".", groupingSeparator: "")))"
|
||||||
}
|
}
|
||||||
self.textField.text = text
|
self.textField.text = text
|
||||||
} else {
|
} else {
|
||||||
@ -1461,7 +1483,7 @@ private final class AmountFieldComponent: Component {
|
|||||||
currency: component.currency,
|
currency: component.currency,
|
||||||
dateTimeFormat: component.dateTimeFormat,
|
dateTimeFormat: component.dateTimeFormat,
|
||||||
minValue: component.minValue ?? 0,
|
minValue: component.minValue ?? 0,
|
||||||
maxValue: component.maxValue ?? Int64.max,
|
maxValue: component.maxValue ?? 10000000,
|
||||||
updated: { [weak self] value in
|
updated: { [weak self] value in
|
||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return
|
return
|
||||||
|
|||||||
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/DeletePaid.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/DeletePaid.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "trash_24.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/DeletePaid.imageset/trash_24.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/DeletePaid.imageset/trash_24.pdf
vendored
Normal file
Binary file not shown.
@ -20,6 +20,11 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] settings in
|
|> deliverOnMainQueue).startStandalone(next: { [weak self] settings in
|
||||||
if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
|
if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
|
||||||
|
var enableMultiselection = true
|
||||||
|
if strongSelf.presentationInterfaceState.interfaceState.postSuggestionState != nil {
|
||||||
|
enableMultiselection = false
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.chatDisplayNode.dismissInput()
|
strongSelf.chatDisplayNode.dismissInput()
|
||||||
let controller = mediaPasteboardScreen(
|
let controller = mediaPasteboardScreen(
|
||||||
context: strongSelf.context,
|
context: strongSelf.context,
|
||||||
@ -28,7 +33,7 @@ extension ChatControllerImpl {
|
|||||||
subjects: subjects,
|
subjects: subjects,
|
||||||
presentMediaPicker: { [weak self] subject, saveEditedPhotos, bannedSendPhotos, bannedSendVideos, present in
|
presentMediaPicker: { [weak self] subject, saveEditedPhotos, bannedSendPhotos, bannedSendVideos, present in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.presentMediaPicker(subject: subject, saveEditedPhotos: saveEditedPhotos, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, present: present, updateMediaPickerContext: { _ in }, completion: { [weak self] fromGallery, signals, silentPosting, scheduleTime, parameters, getAnimatedTransitionSource, completion in
|
strongSelf.presentMediaPicker(subject: subject, saveEditedPhotos: saveEditedPhotos, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, enableMultiselection: enableMultiselection, present: present, updateMediaPickerContext: { _ in }, completion: { [weak self] fromGallery, signals, silentPosting, scheduleTime, parameters, getAnimatedTransitionSource, completion in
|
||||||
self?.enqueueMediaMessages(fromGallery: fromGallery, signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
self?.enqueueMediaMessages(fromGallery: fromGallery, signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1173,6 +1173,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
let controller = self.context.sharedContext.makeStarsGiftScreen(context: self.context, message: EngineMessage(message))
|
let controller = self.context.sharedContext.makeStarsGiftScreen(context: self.context, message: EngineMessage(message))
|
||||||
self.push(controller)
|
self.push(controller)
|
||||||
return true
|
return true
|
||||||
|
case let .giftTon(_, _, _, _, transactionId):
|
||||||
|
Task { @MainActor [weak self] in
|
||||||
|
guard let self, let transactionId, let peerId = self.chatLocation.peerId else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let transactionData = await self.context.engine.payments.getStarsTransaction(reference: StarsTransactionReference(peerId: self.context.account.peerId, id: transactionId, isRefund: false)).get()
|
||||||
|
let peer = await self.context.engine.data.get(
|
||||||
|
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
|
||||||
|
).get()
|
||||||
|
if let transactionData, let peer {
|
||||||
|
self.push(self.context.sharedContext.makeStarsTransactionScreen(context: self.context, transaction: transactionData, peer: peer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = transactionId
|
||||||
case let .giftCode(slug, _, _, _, _, _, _, _, _, _, _):
|
case let .giftCode(slug, _, _, _, _, _, _, _, _, _, _):
|
||||||
self.openResolved(result: .premiumGiftCode(slug: slug), sourceMessageId: message.id, progress: params.progress)
|
self.openResolved(result: .premiumGiftCode(slug: slug), sourceMessageId: message.id, progress: params.progress)
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -4472,6 +4472,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var effectivePresentationInterfaceState = self.chatPresentationInterfaceState
|
var effectivePresentationInterfaceState = self.chatPresentationInterfaceState
|
||||||
|
|
||||||
if let textInputPanelNode = self.textInputPanelNode {
|
if let textInputPanelNode = self.textInputPanelNode {
|
||||||
effectivePresentationInterfaceState = effectivePresentationInterfaceState.updatedInterfaceState { $0.withUpdatedEffectiveInputState(textInputPanelNode.inputTextState) }
|
effectivePresentationInterfaceState = effectivePresentationInterfaceState.updatedInterfaceState { $0.withUpdatedEffectiveInputState(textInputPanelNode.inputTextState) }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,6 +61,11 @@ extension ChatControllerImpl {
|
|||||||
var bannedSendVideos: (Int32, Bool)?
|
var bannedSendVideos: (Int32, Bool)?
|
||||||
var bannedSendFiles: (Int32, Bool)?
|
var bannedSendFiles: (Int32, Bool)?
|
||||||
|
|
||||||
|
var enableMultiselection = true
|
||||||
|
if self.presentationInterfaceState.interfaceState.postSuggestionState != nil {
|
||||||
|
enableMultiselection = false
|
||||||
|
}
|
||||||
|
|
||||||
var canSendPolls = true
|
var canSendPolls = true
|
||||||
var canSendTodos = true
|
var canSendTodos = true
|
||||||
if let peer = self.presentationInterfaceState.renderedPeer?.peer {
|
if let peer = self.presentationInterfaceState.renderedPeer?.peer {
|
||||||
@ -336,7 +341,7 @@ extension ChatControllerImpl {
|
|||||||
controller.prepareForReuse()
|
controller.prepareForReuse()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.presentMediaPicker(saveEditedPhotos: dataSettings.storeEditedPhotos, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, present: { controller, mediaPickerContext in
|
strongSelf.presentMediaPicker(saveEditedPhotos: dataSettings.storeEditedPhotos, bannedSendPhotos: bannedSendPhotos, bannedSendVideos: bannedSendVideos, enableMultiselection: enableMultiselection, present: { controller, mediaPickerContext in
|
||||||
let _ = currentMediaController.swap(controller)
|
let _ = currentMediaController.swap(controller)
|
||||||
if !inputText.string.isEmpty {
|
if !inputText.string.isEmpty {
|
||||||
mediaPickerContext?.setCaption(inputText)
|
mediaPickerContext?.setCaption(inputText)
|
||||||
@ -1230,7 +1235,7 @@ extension ChatControllerImpl {
|
|||||||
self.present(actionSheet, in: .window(.root))
|
self.present(actionSheet, in: .window(.root))
|
||||||
}
|
}
|
||||||
|
|
||||||
func presentMediaPicker(subject: MediaPickerScreenImpl.Subject = .assets(nil, .default), saveEditedPhotos: Bool, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, present: @escaping (MediaPickerScreenImpl, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping (Bool, [Any], Bool, Int32?, ChatSendMessageActionSheetController.SendParameters?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) {
|
func presentMediaPicker(subject: MediaPickerScreenImpl.Subject = .assets(nil, .default), saveEditedPhotos: Bool, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, enableMultiselection: Bool, present: @escaping (MediaPickerScreenImpl, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping (Bool, [Any], Bool, Int32?, ChatSendMessageActionSheetController.SendParameters?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) {
|
||||||
var isScheduledMessages = false
|
var isScheduledMessages = false
|
||||||
if case .scheduledMessages = self.presentationInterfaceState.subject {
|
if case .scheduledMessages = self.presentationInterfaceState.subject {
|
||||||
isScheduledMessages = true
|
isScheduledMessages = true
|
||||||
@ -1248,6 +1253,7 @@ extension ChatControllerImpl {
|
|||||||
isScheduledMessages: isScheduledMessages,
|
isScheduledMessages: isScheduledMessages,
|
||||||
bannedSendPhotos: bannedSendPhotos,
|
bannedSendPhotos: bannedSendPhotos,
|
||||||
bannedSendVideos: bannedSendVideos,
|
bannedSendVideos: bannedSendVideos,
|
||||||
|
enableMultiselection: enableMultiselection,
|
||||||
canBoostToUnrestrict: (self.presentationInterfaceState.boostsToUnrestrict ?? 0) > 0 && bannedSendPhotos?.1 != true && bannedSendVideos?.1 != true,
|
canBoostToUnrestrict: (self.presentationInterfaceState.boostsToUnrestrict ?? 0) > 0 && bannedSendPhotos?.1 != true && bannedSendVideos?.1 != true,
|
||||||
paidMediaAllowed: paidMediaAllowed,
|
paidMediaAllowed: paidMediaAllowed,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
|
|||||||
@ -1924,8 +1924,13 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
}
|
}
|
||||||
}), false))
|
}), false))
|
||||||
} else if !isUnremovableAction {
|
} else if !isUnremovableAction {
|
||||||
|
var iconName: String = isSending ? "Chat/Context Menu/Clear" : "Chat/Context Menu/Delete"
|
||||||
|
if message.attributes.contains(where: { $0 is PublishedSuggestedPostMessageAttribute }) {
|
||||||
|
iconName = "Chat/Context Menu/DeletePaid"
|
||||||
|
}
|
||||||
|
|
||||||
actions.append(.action(ContextMenuActionItem(text: title, textColor: .destructive, icon: { theme in
|
actions.append(.action(ContextMenuActionItem(text: title, textColor: .destructive, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: isSending ? "Chat/Context Menu/Clear" : "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: iconName), color: theme.actionSheet.destructiveActionTextColor)
|
||||||
}, action: { controller, f in
|
}, action: { controller, f in
|
||||||
if isEditing {
|
if isEditing {
|
||||||
context.account.pendingUpdateMessageManager.cancel(messageId: message.id)
|
context.account.pendingUpdateMessageManager.cancel(messageId: message.id)
|
||||||
|
|||||||
@ -1523,6 +1523,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
if case let .media(value) = editMessageState.content {
|
if case let .media(value) = editMessageState.content {
|
||||||
isEditingMedia = !value.isEmpty
|
isEditingMedia = !value.isEmpty
|
||||||
isMediaEnabled = !value.isEmpty
|
isMediaEnabled = !value.isEmpty
|
||||||
|
|
||||||
|
if interfaceState.interfaceState.postSuggestionState != nil {
|
||||||
|
if value.contains(.file) {
|
||||||
|
isEditingMedia = false
|
||||||
|
isMediaEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
isMediaEnabled = true
|
isMediaEnabled = true
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user