Various improvements

This commit is contained in:
Isaac 2025-04-25 10:54:34 +01:00
parent 9b0bcee0a7
commit 6ff961d250
10 changed files with 276 additions and 75 deletions

View File

@ -995,6 +995,11 @@ public enum SendInviteLinkScreenSubject {
case groupCall(link: String) case groupCall(link: String)
} }
public enum StarsWithdrawalScreenSubject {
case withdraw
case enterAmount(current: StarsAmount)
}
public protocol SharedAccountContext: AnyObject { public protocol SharedAccountContext: AnyObject {
var sharedContainerPath: String { get } var sharedContainerPath: String { get }
var basePath: String { get } var basePath: String { get }
@ -1179,7 +1184,7 @@ public protocol SharedAccountContext: AnyObject {
func makeStarsStatisticsScreen(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) -> ViewController func makeStarsStatisticsScreen(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) -> ViewController
func makeStarsAmountScreen(context: AccountContext, initialValue: Int64?, completion: @escaping (Int64) -> Void) -> ViewController func makeStarsAmountScreen(context: AccountContext, initialValue: Int64?, completion: @escaping (Int64) -> Void) -> ViewController
func makeStarsWithdrawalScreen(context: AccountContext, stats: StarsRevenueStats, completion: @escaping (Int64) -> Void) -> ViewController func makeStarsWithdrawalScreen(context: AccountContext, stats: StarsRevenueStats, completion: @escaping (Int64) -> Void) -> ViewController
func makeStarsWithdrawalScreen(context: AccountContext, completion: @escaping (Int64) -> Void) -> ViewController func makeStarsWithdrawalScreen(context: AccountContext, subject: StarsWithdrawalScreenSubject, completion: @escaping (Int64) -> Void) -> ViewController
func makeStarGiftResellScreen(context: AccountContext, update: Bool, completion: @escaping (Int64) -> Void) -> ViewController func makeStarGiftResellScreen(context: AccountContext, update: Bool, completion: @escaping (Int64) -> Void) -> ViewController
func makeStarsGiftScreen(context: AccountContext, message: EngineMessage) -> ViewController func makeStarsGiftScreen(context: AccountContext, message: EngineMessage) -> ViewController
func makeStarsGiveawayBoostScreen(context: AccountContext, peerId: EnginePeer.Id, boost: ChannelBoostersContext.State.Boost) -> ViewController func makeStarsGiveawayBoostScreen(context: AccountContext, peerId: EnginePeer.Id, boost: ChannelBoostersContext.State.Boost) -> ViewController

View File

@ -38,9 +38,10 @@ private final class ChannelPermissionsControllerArguments {
let updateSlowmode: (Int32) -> Void let updateSlowmode: (Int32) -> Void
let updateUnrestrictBoosters: (Int32) -> Void let updateUnrestrictBoosters: (Int32) -> Void
let updateStarsAmount: (StarsAmount?, Bool) -> Void let updateStarsAmount: (StarsAmount?, Bool) -> Void
let openSetCustomStarsAmount: () -> Void
let toggleIsOptionExpanded: (TelegramChatBannedRightsFlags) -> Void let toggleIsOptionExpanded: (TelegramChatBannedRightsFlags) -> Void
init(context: AccountContext, updatePermission: @escaping (TelegramChatBannedRightsFlags, Bool) -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, addPeer: @escaping () -> Void, removePeer: @escaping (EnginePeer.Id) -> Void, openPeer: @escaping (ChannelParticipant) -> Void, openPeerInfo: @escaping (EnginePeer) -> Void, openKicked: @escaping () -> Void, presentRestrictedPermissionAlert: @escaping (TelegramChatBannedRightsFlags) -> Void, presentConversionToBroadcastGroup: @escaping () -> Void, openChannelExample: @escaping () -> Void, updateSlowmode: @escaping (Int32) -> Void, updateUnrestrictBoosters: @escaping (Int32) -> Void, updateStarsAmount: @escaping (StarsAmount?, Bool) -> Void, toggleIsOptionExpanded: @escaping (TelegramChatBannedRightsFlags) -> Void) { init(context: AccountContext, updatePermission: @escaping (TelegramChatBannedRightsFlags, Bool) -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, addPeer: @escaping () -> Void, removePeer: @escaping (EnginePeer.Id) -> Void, openPeer: @escaping (ChannelParticipant) -> Void, openPeerInfo: @escaping (EnginePeer) -> Void, openKicked: @escaping () -> Void, presentRestrictedPermissionAlert: @escaping (TelegramChatBannedRightsFlags) -> Void, presentConversionToBroadcastGroup: @escaping () -> Void, openChannelExample: @escaping () -> Void, updateSlowmode: @escaping (Int32) -> Void, updateUnrestrictBoosters: @escaping (Int32) -> Void, updateStarsAmount: @escaping (StarsAmount?, Bool) -> Void, openSetCustomStarsAmount: @escaping () -> Void, toggleIsOptionExpanded: @escaping (TelegramChatBannedRightsFlags) -> Void) {
self.context = context self.context = context
self.updatePermission = updatePermission self.updatePermission = updatePermission
self.addPeer = addPeer self.addPeer = addPeer
@ -55,6 +56,7 @@ private final class ChannelPermissionsControllerArguments {
self.updateSlowmode = updateSlowmode self.updateSlowmode = updateSlowmode
self.updateUnrestrictBoosters = updateUnrestrictBoosters self.updateUnrestrictBoosters = updateUnrestrictBoosters
self.updateStarsAmount = updateStarsAmount self.updateStarsAmount = updateStarsAmount
self.openSetCustomStarsAmount = openSetCustomStarsAmount
self.toggleIsOptionExpanded = toggleIsOptionExpanded self.toggleIsOptionExpanded = toggleIsOptionExpanded
} }
} }
@ -427,7 +429,7 @@ private enum ChannelPermissionsEntry: ItemListNodeEntry {
case let .messagePrice(_, value, maxValue, price): case let .messagePrice(_, value, maxValue, price):
return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, isEnabled: true, minValue: 1, maxValue: maxValue, value: value, price: price, sectionId: self.section, updated: { value, apply in return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, isEnabled: true, minValue: 1, maxValue: maxValue, value: value, price: price, sectionId: self.section, updated: { value, apply in
arguments.updateStarsAmount(StarsAmount(value: value, nanos: 0), apply) arguments.updateStarsAmount(StarsAmount(value: value, nanos: 0), apply)
}) }, openSetCustom: nil)
case let .messagePriceInfo(_, value): case let .messagePriceInfo(_, value):
return ItemListTextItem(presentationData: presentationData, text: .plain(value), sectionId: self.section) return ItemListTextItem(presentationData: presentationData, text: .plain(value), sectionId: self.section)
case let .unrestrictBoostersSwitch(_, title, value): case let .unrestrictBoostersSwitch(_, title, value):
@ -1267,6 +1269,7 @@ public func channelPermissionsController(context: AccountContext, updatedPresent
|> deliverOnMainQueue).start()) |> deliverOnMainQueue).start())
}) })
} }
}, openSetCustomStarsAmount: {
}, toggleIsOptionExpanded: { flags in }, toggleIsOptionExpanded: { flags in
updateState { state in updateState { state in
var state = state var state = state

View File

@ -20,6 +20,7 @@ private final class IncomingMessagePrivacyScreenArguments {
let infoLinkAction: () -> Void let infoLinkAction: () -> Void
let openExceptions: () -> Void let openExceptions: () -> Void
let openPremiumInfo: () -> Void let openPremiumInfo: () -> Void
let openSetCustomStarsAmount: () -> Void
init( init(
context: AccountContext, context: AccountContext,
@ -27,7 +28,8 @@ private final class IncomingMessagePrivacyScreenArguments {
disabledValuePressed: @escaping () -> Void, disabledValuePressed: @escaping () -> Void,
infoLinkAction: @escaping () -> Void, infoLinkAction: @escaping () -> Void,
openExceptions: @escaping () -> Void, openExceptions: @escaping () -> Void,
openPremiumInfo: @escaping () -> Void openPremiumInfo: @escaping () -> Void,
openSetCustomStarsAmount: @escaping () -> Void
) { ) {
self.context = context self.context = context
self.updateValue = updateValue self.updateValue = updateValue
@ -35,6 +37,7 @@ private final class IncomingMessagePrivacyScreenArguments {
self.infoLinkAction = infoLinkAction self.infoLinkAction = infoLinkAction
self.openExceptions = openExceptions self.openExceptions = openExceptions
self.openPremiumInfo = openPremiumInfo self.openPremiumInfo = openPremiumInfo
self.openSetCustomStarsAmount = openSetCustomStarsAmount
} }
} }
@ -151,6 +154,8 @@ private enum GlobalAutoremoveEntry: ItemListNodeEntry {
case let .price(value, maxValue, price, isEnabled): case let .price(value, maxValue, price, isEnabled):
return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, isEnabled: isEnabled, minValue: 1, maxValue: maxValue, value: value, price: price, sectionId: self.section, updated: { value, _ in return MessagePriceItem(theme: presentationData.theme, strings: presentationData.strings, isEnabled: isEnabled, minValue: 1, maxValue: maxValue, 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)))
}, openSetCustom: {
arguments.openSetCustomStarsAmount()
}, openPremiumInfo: { }, openPremiumInfo: {
arguments.openPremiumInfo() arguments.openPremiumInfo()
}) })
@ -365,6 +370,20 @@ public func incomingMessagePrivacyScreen(context: AccountContext, value: GlobalP
controller?.replace(with: c) controller?.replace(with: c)
} }
pushControllerImpl?(controller) pushControllerImpl?(controller)
},
openSetCustomStarsAmount: {
var currentAmount: StarsAmount = StarsAmount(value: 1, nanos: 0)
if case let .paidMessages(value) = stateValue.with({ $0 }).updatedValue {
currentAmount = value
}
let starsScreen = context.sharedContext.makeStarsWithdrawalScreen(context: context, subject: .enterAmount(current: currentAmount), completion: { amount in
updateState { state in
var state = state
state.updatedValue = .paidMessages(StarsAmount(value: amount, nanos: 0))
return state
}
})
pushControllerImpl?(starsScreen)
} }
) )

View File

@ -121,6 +121,7 @@ swift_library(
"//submodules/FastBlur", "//submodules/FastBlur",
"//submodules/InviteLinksUI", "//submodules/InviteLinksUI",
"//third-party/td:TdBinding", "//third-party/td:TdBinding",
"//submodules/TelegramUI/Components/AnimatedTextComponent",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -26,6 +26,7 @@ import TooltipUI
import BlurredBackgroundComponent import BlurredBackgroundComponent
import CallsEmoji import CallsEmoji
import InviteLinksUI import InviteLinksUI
import AnimatedTextComponent
extension VideoChatCall { extension VideoChatCall {
var myAudioLevelAndSpeaking: Signal<(Float, Bool), NoError> { var myAudioLevelAndSpeaking: Signal<(Float, Bool), NoError> {
@ -262,6 +263,9 @@ final class VideoChatScreenComponent: Component {
var invitedPeers: [InvitedPeer] = [] var invitedPeers: [InvitedPeer] = []
var invitedPeersDisposable: Disposable? var invitedPeersDisposable: Disposable?
var lastTitleEvent: String?
var lastTitleEventTimer: Foundation.Timer?
var speakingParticipantPeers: [EnginePeer] = [] var speakingParticipantPeers: [EnginePeer] = []
var visibleParticipants: Set<EnginePeer.Id> = Set() var visibleParticipants: Set<EnginePeer.Id> = Set()
@ -320,6 +324,7 @@ final class VideoChatScreenComponent: Component {
self.inviteDisposable.dispose() self.inviteDisposable.dispose()
self.conferenceCallStateDisposable?.dispose() self.conferenceCallStateDisposable?.dispose()
self.encryptionKeyEmojiDisposable?.dispose() self.encryptionKeyEmojiDisposable?.dispose()
self.lastTitleEventTimer?.invalidate()
} }
func animateIn() { func animateIn() {
@ -1600,12 +1605,13 @@ final class VideoChatScreenComponent: Component {
}) })
self.memberEventsDisposable?.dispose() self.memberEventsDisposable?.dispose()
if groupCall.peerId != nil {
self.memberEventsDisposable = (groupCall.memberEvents self.memberEventsDisposable = (groupCall.memberEvents
|> deliverOnMainQueue).start(next: { [weak self] event in |> deliverOnMainQueue).start(next: { [weak self] event in
guard let self, let members = self.members, let environment = self.environment, case let .group(groupCall) = self.currentCall else { guard let self, let members = self.members, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
return return
} }
if groupCall.peerId != nil {
if event.joined { if event.joined {
var displayEvent = false var displayEvent = false
if case let .channel(channel) = self.peer, case .broadcast = channel.info { if case let .channel(channel) = self.peer, case .broadcast = channel.info {
@ -1624,8 +1630,30 @@ final class VideoChatScreenComponent: Component {
self.presentUndoOverlay(content: .invitedToVoiceChat(context: groupCall.accountContext, peer: event.peer, title: nil, text: text, action: nil, duration: 3), action: { _ in return false }) self.presentUndoOverlay(content: .invitedToVoiceChat(context: groupCall.accountContext, peer: event.peer, title: nil, text: text, action: nil, duration: 3), action: { _ in return false })
} }
} }
} else {
if event.joined {
self.lastTitleEvent = "\(event.peer.compactDisplayTitle) joined"
} else {
self.lastTitleEvent = "\(event.peer.compactDisplayTitle) left"
}
if !self.isUpdating {
self.state?.updated(transition: .spring(duration: 0.4))
}
self.lastTitleEventTimer?.invalidate()
self.lastTitleEventTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 3.5, repeats: false, block: { [weak self] _ in
guard let self else {
return
}
self.lastTitleEventTimer = nil
self.lastTitleEvent = nil
if !self.isUpdating {
self.state?.updated(transition: .spring(duration: 0.4))
}
}) })
} }
})
case let .conferenceSource(conferenceSource): case let .conferenceSource(conferenceSource):
self.membersDisposable?.dispose() self.membersDisposable?.dispose()
self.membersDisposable = (View.groupCallMembersForConferenceSource(conferenceSource: conferenceSource) self.membersDisposable = (View.groupCallMembersForConferenceSource(conferenceSource: conferenceSource)
@ -1887,14 +1915,17 @@ final class VideoChatScreenComponent: Component {
let maxSingleColumnWidth: CGFloat = 620.0 let maxSingleColumnWidth: CGFloat = 620.0
let isTwoColumnLayout: Bool let isTwoColumnLayout: Bool
let isLandscape: Bool
if availableSize.width > maxSingleColumnWidth { if availableSize.width > maxSingleColumnWidth {
if let mappedParticipants, mappedParticipants.participants.contains(where: { $0.videoDescription != nil || $0.presentationDescription != nil }) { if let mappedParticipants, mappedParticipants.participants.contains(where: { $0.videoDescription != nil || $0.presentationDescription != nil }) {
isTwoColumnLayout = true isTwoColumnLayout = true
} else { } else {
isTwoColumnLayout = false isTwoColumnLayout = false
} }
isLandscape = true
} else { } else {
isTwoColumnLayout = false isTwoColumnLayout = false
isLandscape = false
} }
var containerOffset: CGFloat = 0.0 var containerOffset: CGFloat = 0.0
@ -1935,24 +1966,32 @@ final class VideoChatScreenComponent: Component {
} }
}) })
let landscapeControlsWidth: CGFloat = 88.0 let landscapeControlsWidth: CGFloat = 104.0
let landscapeControlsOffsetX: CGFloat = 18.0 var landscapeControlsOffsetX: CGFloat = 0.0
let landscapeControlsSpacing: CGFloat = 30.0 let landscapeControlsSpacing: CGFloat = 30.0
let leftInset: CGFloat = max(environment.safeInsets.left, 14.0) var leftInset: CGFloat = max(environment.safeInsets.left, 14.0)
var rightInset: CGFloat = max(environment.safeInsets.right, 14.0) var rightInset: CGFloat = max(environment.safeInsets.right, 14.0)
var buttonsOnTheSide = false var buttonsOnTheSide = false
if availableSize.width > maxSingleColumnWidth && !environment.metrics.isTablet { if availableSize.width > maxSingleColumnWidth && !environment.metrics.isTablet {
leftInset += 2.0
rightInset += 2.0
buttonsOnTheSide = true buttonsOnTheSide = true
rightInset += landscapeControlsWidth if case .landscapeLeft = environment.orientation {
rightInset = max(rightInset, environment.safeInsets.left + landscapeControlsWidth)
landscapeControlsOffsetX = -environment.safeInsets.left
} else {
rightInset = max(rightInset, landscapeControlsWidth)
}
} }
let topInset: CGFloat = environment.statusBarHeight + 2.0 let topInset: CGFloat = environment.statusBarHeight + 2.0
let navigationBarHeight: CGFloat = 61.0 let navigationBarHeight: CGFloat = 61.0
var navigationHeight = topInset + navigationBarHeight var navigationHeight = topInset + navigationBarHeight
let navigationButtonAreaWidth: CGFloat = 40.0 let navigationButtonAreaWidth: CGFloat = 34.0
let navigationButtonDiameter: CGFloat = 28.0 let navigationButtonDiameter: CGFloat = 28.0
let navigationLeftButtonSize = self.navigationLeftButton.update( let navigationLeftButtonSize = self.navigationLeftButton.update(
@ -2013,7 +2052,10 @@ final class VideoChatScreenComponent: Component {
alphaTransition.setAlpha(view: navigationLeftButtonView, alpha: self.isAnimatedOutFromPrivateCall ? 0.0 : 1.0) alphaTransition.setAlpha(view: navigationLeftButtonView, alpha: self.isAnimatedOutFromPrivateCall ? 0.0 : 1.0)
} }
let navigationRightButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - rightInset - navigationButtonAreaWidth + floor((navigationButtonAreaWidth - navigationRightButtonSize.width) * 0.5), y: topInset + floor((navigationBarHeight - navigationRightButtonSize.height) * 0.5)), size: navigationRightButtonSize) var navigationRightButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - rightInset - navigationButtonAreaWidth + floor((navigationButtonAreaWidth - navigationRightButtonSize.width) * 0.5), y: topInset + floor((navigationBarHeight - navigationRightButtonSize.height) * 0.5)), size: navigationRightButtonSize)
if buttonsOnTheSide {
navigationRightButtonFrame.origin.x += 42.0
}
if let navigationRightButtonView = self.navigationRightButton.view { if let navigationRightButtonView = self.navigationRightButton.view {
if navigationRightButtonView.superview == nil { if navigationRightButtonView.superview == nil {
self.containerView.addSubview(navigationRightButtonView) self.containerView.addSubview(navigationRightButtonView)
@ -2022,6 +2064,7 @@ final class VideoChatScreenComponent: Component {
alphaTransition.setAlpha(view: navigationRightButtonView, alpha: self.isAnimatedOutFromPrivateCall ? 0.0 : 1.0) alphaTransition.setAlpha(view: navigationRightButtonView, alpha: self.isAnimatedOutFromPrivateCall ? 0.0 : 1.0)
} }
var navigationSidebarButtonFrame: CGRect?
if isTwoColumnLayout { if isTwoColumnLayout {
var navigationSidebarButtonTransition = transition var navigationSidebarButtonTransition = transition
let navigationSidebarButton: ComponentView<Empty> let navigationSidebarButton: ComponentView<Empty>
@ -2057,7 +2100,8 @@ final class VideoChatScreenComponent: Component {
environment: {}, environment: {},
containerSize: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter) containerSize: CGSize(width: navigationButtonDiameter, height: navigationButtonDiameter)
) )
let navigationSidebarButtonFrame = CGRect(origin: CGPoint(x: navigationRightButtonFrame.minX - 32.0 - navigationSidebarButtonSize.width, y: topInset + floor((navigationBarHeight - navigationSidebarButtonSize.height) * 0.5)), size: navigationSidebarButtonSize) let navigationSidebarButtonFrameValue = CGRect(origin: CGPoint(x: navigationRightButtonFrame.minX - 21.0 - navigationSidebarButtonSize.width, y: topInset + floor((navigationBarHeight - navigationSidebarButtonSize.height) * 0.5)), size: navigationSidebarButtonSize)
navigationSidebarButtonFrame = navigationSidebarButtonFrameValue
if let navigationSidebarButtonView = navigationSidebarButton.view { if let navigationSidebarButtonView = navigationSidebarButton.view {
var animateIn = false var animateIn = false
if navigationSidebarButtonView.superview == nil { if navigationSidebarButtonView.superview == nil {
@ -2066,7 +2110,7 @@ final class VideoChatScreenComponent: Component {
self.containerView.insertSubview(navigationSidebarButtonView, aboveSubview: navigationRightButtonView) self.containerView.insertSubview(navigationSidebarButtonView, aboveSubview: navigationRightButtonView)
} }
} }
navigationSidebarButtonTransition.setFrame(view: navigationSidebarButtonView, frame: navigationSidebarButtonFrame) navigationSidebarButtonTransition.setFrame(view: navigationSidebarButtonView, frame: navigationSidebarButtonFrameValue)
if animateIn { if animateIn {
transition.animateScale(view: navigationSidebarButtonView, from: 0.001, to: 1.0) transition.animateScale(view: navigationSidebarButtonView, from: 0.001, to: 1.0)
transition.animateAlpha(view: navigationSidebarButtonView, from: 0.0, to: 1.0) transition.animateAlpha(view: navigationSidebarButtonView, from: 0.0, to: 1.0)
@ -2082,17 +2126,27 @@ final class VideoChatScreenComponent: Component {
} }
} }
let idleTitleStatusText: String var idleTitleStatusText: [AnimatedTextComponent.Item] = []
if let callState = self.callState { if let callState = self.callState {
if callState.networkState == .connected, let members = self.members { if callState.networkState == .connected, let members = self.members {
idleTitleStatusText = environment.strings.VoiceChat_Panel_Members(Int32(max(1, members.totalCount))) //TODO:localize
let totalCount = max(1, members.totalCount)
idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(0), isUnbreakable: false, content: .number(totalCount, minDigits: 0)))
idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(1), isUnbreakable: false, content: .text(totalCount == 1 ? " participant" : " participants")))
if let lastTitleEvent = self.lastTitleEvent {
idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(6), isUnbreakable: false, content: .text(", \(lastTitleEvent)")))
} else if !self.invitedPeers.isEmpty {
idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(3), isUnbreakable: true, content: .text(", ")))
idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(4), isUnbreakable: false, content: .number(self.invitedPeers.count, minDigits: 0)))
idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(5), isUnbreakable: false, content: .text(" invited")))
}
} else if callState.scheduleTimestamp != nil { } else if callState.scheduleTimestamp != nil {
idleTitleStatusText = environment.strings.VoiceChat_Scheduled idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(0), isUnbreakable: false, content: .text(environment.strings.VoiceChat_Scheduled)))
} else { } else {
idleTitleStatusText = environment.strings.VoiceChat_Connecting idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(0), isUnbreakable: false, content: .text(environment.strings.VoiceChat_Connecting)))
} }
} else { } else {
idleTitleStatusText = " " idleTitleStatusText.append(AnimatedTextComponent.Item(id: AnyHashable(0), isUnbreakable: false, content: .text(" ")))
} }
let canManageCall = self.callState?.canManageCall ?? false let canManageCall = self.callState?.canManageCall ?? false
@ -2108,6 +2162,7 @@ final class VideoChatScreenComponent: Component {
title: self.callState?.title ?? self.peer?.debugDisplayTitle ?? environment.strings.VideoChat_GroupCallTitle, title: self.callState?.title ?? self.peer?.debugDisplayTitle ?? environment.strings.VideoChat_GroupCallTitle,
status: idleTitleStatusText, status: idleTitleStatusText,
isRecording: self.callState?.recordingStartTimestamp != nil, isRecording: self.callState?.recordingStartTimestamp != nil,
isLandscape: isLandscape,
strings: environment.strings, strings: environment.strings,
tapAction: self.callState?.recordingStartTimestamp != nil ? { [weak self] in tapAction: self.callState?.recordingStartTimestamp != nil ? { [weak self] in
guard let self, let environment = self.environment, let currentCall = self.currentCall else { guard let self, let environment = self.environment, let currentCall = self.currentCall else {
@ -2146,7 +2201,12 @@ final class VideoChatScreenComponent: Component {
environment: {}, environment: {},
containerSize: CGSize(width: maxTitleWidth, height: 100.0) containerSize: CGSize(width: maxTitleWidth, height: 100.0)
) )
let titleFrame = CGRect(origin: CGPoint(x: leftInset + floor((availableSize.width - leftInset - rightInset - titleSize.width) * 0.5), y: topInset + floor((navigationBarHeight - titleSize.height) * 0.5)), size: titleSize) var titleFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset + floor((navigationBarHeight - titleSize.height) * 0.5)), size: titleSize)
if isLandscape {
titleFrame.origin.x = navigationLeftButtonFrame.maxX + 20.0
} else {
titleFrame.origin.x = leftInset + floor((availableSize.width - leftInset - rightInset - titleSize.width) * 0.5)
}
if let titleView = self.title.view { if let titleView = self.title.view {
if titleView.superview == nil { if titleView.superview == nil {
self.containerView.addSubview(titleView) self.containerView.addSubview(titleView)
@ -2155,6 +2215,27 @@ final class VideoChatScreenComponent: Component {
alphaTransition.setAlpha(view: titleView, alpha: self.isAnimatedOutFromPrivateCall ? 0.0 : 1.0) alphaTransition.setAlpha(view: titleView, alpha: self.isAnimatedOutFromPrivateCall ? 0.0 : 1.0)
} }
let areButtonsCollapsed: Bool
let mainColumnWidth: CGFloat
let mainColumnSideInset: CGFloat
if isTwoColumnLayout {
areButtonsCollapsed = false
mainColumnWidth = min(isLandscape ? 340.0 : 320.0, availableSize.width - leftInset - rightInset - 340.0)
mainColumnSideInset = 0.0
} else {
areButtonsCollapsed = self.expandedParticipantsVideoState != nil
if availableSize.width > maxSingleColumnWidth {
mainColumnWidth = 420.0
mainColumnSideInset = 0.0
} else {
mainColumnWidth = availableSize.width
mainColumnSideInset = max(leftInset, rightInset)
}
}
var encryptionKeyFrame: CGRect? var encryptionKeyFrame: CGRect?
var isConference = false var isConference = false
if case let .group(groupCall) = self.currentCall { if case let .group(groupCall) = self.currentCall {
@ -2163,7 +2244,9 @@ final class VideoChatScreenComponent: Component {
isConference = true isConference = true
} }
if isConference { if isConference {
if !isLandscape {
navigationHeight -= 2.0 navigationHeight -= 2.0
}
let encryptionKey: ComponentView<Empty> let encryptionKey: ComponentView<Empty>
var encryptionKeyTransition = transition var encryptionKeyTransition = transition
if let current = self.encryptionKey { if let current = self.encryptionKey {
@ -2194,11 +2277,34 @@ final class VideoChatScreenComponent: Component {
environment: {}, environment: {},
containerSize: CGSize(width: min(400.0, availableSize.width - leftInset - rightInset - 16.0 * 2.0), height: 10000.0) containerSize: CGSize(width: min(400.0, availableSize.width - leftInset - rightInset - 16.0 * 2.0), height: 10000.0)
) )
let encryptionKeyFrameValue = CGRect(origin: CGPoint(x: leftInset + floor((availableSize.width - leftInset - rightInset - encryptionKeySize.width) * 0.5), y: navigationHeight), size: encryptionKeySize) var encryptionKeyFrameValue = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: encryptionKeySize)
if isLandscape {
let maxEncryptionKeyX: CGFloat
if let navigationSidebarButtonFrame {
maxEncryptionKeyX = navigationSidebarButtonFrame.minX - 8.0 - encryptionKeySize.width
} else {
maxEncryptionKeyX = navigationRightButtonFrame.minX - 8.0 - encryptionKeySize.width
}
let idealEncryptionKeyX: CGFloat
if isTwoColumnLayout {
idealEncryptionKeyX = availableSize.width - rightInset - mainColumnWidth
} else {
idealEncryptionKeyX = maxEncryptionKeyX - 13.0
}
encryptionKeyFrameValue.origin.x = min(idealEncryptionKeyX, maxEncryptionKeyX)
encryptionKeyFrameValue.origin.y = navigationLeftButtonFrame.minY + floorToScreenPixels((navigationLeftButtonFrame.height - encryptionKeySize.height) * 0.5)
} else {
encryptionKeyFrameValue.origin.x = leftInset + floor((availableSize.width - leftInset - rightInset - encryptionKeySize.width) * 0.5)
encryptionKeyFrameValue.origin.y = navigationHeight
}
encryptionKeyFrame = encryptionKeyFrameValue encryptionKeyFrame = encryptionKeyFrameValue
if !isLandscape {
navigationHeight += encryptionKeySize.height navigationHeight += encryptionKeySize.height
navigationHeight += 16.0 navigationHeight += 16.0
}
} else if let encryptionKey = self.encryptionKey { } else if let encryptionKey = self.encryptionKey {
self.encryptionKey = nil self.encryptionKey = nil
encryptionKey.view?.removeFromSuperview() encryptionKey.view?.removeFromSuperview()
@ -2207,27 +2313,6 @@ final class VideoChatScreenComponent: Component {
self.encryptionKeyBackground = nil self.encryptionKeyBackground = nil
} }
let areButtonsCollapsed: Bool
let mainColumnWidth: CGFloat
let mainColumnSideInset: CGFloat
if isTwoColumnLayout {
areButtonsCollapsed = false
mainColumnWidth = 320.0
mainColumnSideInset = 0.0
} else {
areButtonsCollapsed = self.expandedParticipantsVideoState != nil
if availableSize.width > maxSingleColumnWidth {
mainColumnWidth = 420.0
mainColumnSideInset = 0.0
} else {
mainColumnWidth = availableSize.width
mainColumnSideInset = max(leftInset, rightInset)
}
}
let actionButtonDiameter: CGFloat = 56.0 let actionButtonDiameter: CGFloat = 56.0
let expandedMicrophoneButtonDiameter: CGFloat = actionButtonDiameter let expandedMicrophoneButtonDiameter: CGFloat = actionButtonDiameter
var collapsedMicrophoneButtonDiameter: CGFloat = 116.0 var collapsedMicrophoneButtonDiameter: CGFloat = 116.0
@ -2285,7 +2370,7 @@ final class VideoChatScreenComponent: Component {
if buttonsOnTheSide { if buttonsOnTheSide {
collapsedMicrophoneButtonFrame.origin.y = floor((availableSize.height - actionButtonDiameter) * 0.5) collapsedMicrophoneButtonFrame.origin.y = floor((availableSize.height - actionButtonDiameter) * 0.5)
collapsedMicrophoneButtonFrame.origin.x = availableSize.width - environment.safeInsets.right - landscapeControlsWidth + landscapeControlsOffsetX collapsedMicrophoneButtonFrame.origin.x = availableSize.width - landscapeControlsWidth + landscapeControlsOffsetX + floor((landscapeControlsWidth - actionButtonDiameter) * 0.5)
if isMainColumnHidden { if isMainColumnHidden {
collapsedMicrophoneButtonFrame.origin.x += mainColumnWidth + landscapeControlsWidth collapsedMicrophoneButtonFrame.origin.x += mainColumnWidth + landscapeControlsWidth

View File

@ -6,19 +6,22 @@ import MultilineTextComponent
import TelegramPresentationData import TelegramPresentationData
import HierarchyTrackingLayer import HierarchyTrackingLayer
import ChatTitleActivityNode import ChatTitleActivityNode
import AnimatedTextComponent
final class VideoChatTitleComponent: Component { final class VideoChatTitleComponent: Component {
let title: String let title: String
let status: String let status: [AnimatedTextComponent.Item]
let isRecording: Bool let isRecording: Bool
let isLandscape: Bool
let strings: PresentationStrings let strings: PresentationStrings
let tapAction: (() -> Void)? let tapAction: (() -> Void)?
let longTapAction: (() -> Void)? let longTapAction: (() -> Void)?
init( init(
title: String, title: String,
status: String, status: [AnimatedTextComponent.Item],
isRecording: Bool, isRecording: Bool,
isLandscape: Bool,
strings: PresentationStrings, strings: PresentationStrings,
tapAction: (() -> Void)?, tapAction: (() -> Void)?,
longTapAction: (() -> Void)? longTapAction: (() -> Void)?
@ -26,6 +29,7 @@ final class VideoChatTitleComponent: Component {
self.title = title self.title = title
self.status = status self.status = status
self.isRecording = isRecording self.isRecording = isRecording
self.isLandscape = isLandscape
self.strings = strings self.strings = strings
self.tapAction = tapAction self.tapAction = tapAction
self.longTapAction = longTapAction self.longTapAction = longTapAction
@ -41,6 +45,9 @@ final class VideoChatTitleComponent: Component {
if lhs.isRecording != rhs.isRecording { if lhs.isRecording != rhs.isRecording {
return false return false
} }
if lhs.isLandscape != rhs.isLandscape {
return false
}
if lhs.strings !== rhs.strings { if lhs.strings !== rhs.strings {
return false return false
} }
@ -211,12 +218,14 @@ final class VideoChatTitleComponent: Component {
) )
let statusComponent: AnyComponent<Empty> let statusComponent: AnyComponent<Empty>
statusComponent = AnyComponent(MultilineTextComponent( statusComponent = AnyComponent(AnimatedTextComponent(
text: .plain(NSAttributedString(string: component.status, font: Font.regular(13.0), textColor: UIColor(white: 1.0, alpha: 0.5))) font: Font.regular(13.0),
color: UIColor(white: 1.0, alpha: 0.5),
items: component.status
)) ))
let statusSize = self.status.update( let statusSize = self.status.update(
transition: .immediate, transition: transition,
component: statusComponent, component: statusComponent,
environment: {}, environment: {},
containerSize: CGSize(width: availableSize.width, height: 100.0) containerSize: CGSize(width: availableSize.width, height: 100.0)
@ -224,7 +233,10 @@ final class VideoChatTitleComponent: Component {
let size = CGSize(width: availableSize.width, height: titleSize.height + spacing + statusSize.height) let size = CGSize(width: availableSize.width, height: titleSize.height + spacing + statusSize.height)
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) * 0.5), y: 0.0), size: titleSize) var titleFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: titleSize)
if !component.isLandscape {
titleFrame.origin.x = floor((size.width - titleSize.width) * 0.5)
}
if let titleView = self.title.view { if let titleView = self.title.view {
if titleView.superview == nil { if titleView.superview == nil {
titleView.layer.anchorPoint = CGPoint() titleView.layer.anchorPoint = CGPoint()
@ -235,13 +247,17 @@ final class VideoChatTitleComponent: Component {
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size) titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
} }
let statusFrame = CGRect(origin: CGPoint(x: floor((size.width - statusSize.width) * 0.5), y: titleFrame.maxY + spacing), size: statusSize) var statusFrame = CGRect(origin: CGPoint(x: 0.0, y: titleFrame.maxY + spacing), size: statusSize)
if !component.isLandscape {
statusFrame.origin.x = floor((size.width - statusSize.width) * 0.5)
}
if let statusView = self.status.view { if let statusView = self.status.view {
if statusView.superview == nil { if statusView.superview == nil {
statusView.layer.anchorPoint = CGPoint()
statusView.isUserInteractionEnabled = false statusView.isUserInteractionEnabled = false
self.addSubview(statusView) self.addSubview(statusView)
} }
transition.setPosition(view: statusView, position: statusFrame.center) transition.setPosition(view: statusView, position: statusFrame.origin)
statusView.bounds = CGRect(origin: CGPoint(), size: statusFrame.size) statusView.bounds = CGRect(origin: CGPoint(), size: statusFrame.size)
} }

View File

@ -53,9 +53,6 @@ func closeButtonImage(dark: Bool) -> UIImage? {
return generateImage(CGSize(width: 28.0, height: 28.0), contextGenerator: { size, context in return generateImage(CGSize(width: 28.0, height: 28.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size)) context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor(rgb: dark ? 0x1c1c1e : 0x2c2c2e).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setLineWidth(2.0) context.setLineWidth(2.0)
context.setLineCap(.round) context.setLineCap(.round)
context.setStrokeColor(UIColor.white.cgColor) context.setStrokeColor(UIColor.white.cgColor)

View File

@ -26,9 +26,10 @@ public final class MessagePriceItem: ListViewItem, ItemListItem {
let price: String let price: String
public let sectionId: ItemListSectionId public let sectionId: ItemListSectionId
let updated: (Int64, Bool) -> Void let updated: (Int64, Bool) -> Void
let openSetCustom: (() -> Void)?
let openPremiumInfo: (() -> Void)? let openPremiumInfo: (() -> Void)?
public init(theme: PresentationTheme, strings: PresentationStrings, isEnabled: Bool, minValue: Int64, maxValue: Int64, value: Int64, price: String, sectionId: ItemListSectionId, updated: @escaping (Int64, Bool) -> Void, openPremiumInfo: (() -> Void)? = nil) { public init(theme: PresentationTheme, strings: PresentationStrings, isEnabled: Bool, minValue: Int64, maxValue: Int64, value: Int64, price: String, sectionId: ItemListSectionId, updated: @escaping (Int64, Bool) -> Void, openSetCustom: (() -> Void)? = nil, openPremiumInfo: (() -> Void)? = nil) {
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
self.isEnabled = isEnabled self.isEnabled = isEnabled
@ -38,6 +39,7 @@ public final class MessagePriceItem: ListViewItem, ItemListItem {
self.price = price self.price = price
self.sectionId = sectionId self.sectionId = sectionId
self.updated = updated self.updated = updated
self.openSetCustom = openSetCustom
self.openPremiumInfo = openPremiumInfo self.openPremiumInfo = openPremiumInfo
} }
@ -161,6 +163,8 @@ private class MessagePriceItemNode: ListViewItemNode {
private var sliderView: TGPhotoEditorSliderView? private var sliderView: TGPhotoEditorSliderView?
private let leftTextNode: ImmediateTextNode private let leftTextNode: ImmediateTextNode
private let rightTextNode: ImmediateTextNode private let rightTextNode: ImmediateTextNode
private let centerTextButtonNode: HighlightableButtonNode
private let centerTextButtonBackground: UIImageView
private let centerLeftTextNode: ImmediateTextNode private let centerLeftTextNode: ImmediateTextNode
private let centerRightTextNode: ImmediateTextNode private let centerRightTextNode: ImmediateTextNode
private let lockIconNode: ASImageNode private let lockIconNode: ASImageNode
@ -186,8 +190,13 @@ private class MessagePriceItemNode: ListViewItemNode {
self.leftTextNode = ImmediateTextNode() self.leftTextNode = ImmediateTextNode()
self.rightTextNode = ImmediateTextNode() self.rightTextNode = ImmediateTextNode()
self.centerTextButtonNode = HighlightableButtonNode()
self.centerTextButtonBackground = UIImageView()
self.centerLeftTextNode = ImmediateTextNode() self.centerLeftTextNode = ImmediateTextNode()
self.centerLeftTextNode.isUserInteractionEnabled = false
self.centerRightTextNode = ImmediateTextNode() self.centerRightTextNode = ImmediateTextNode()
self.centerRightTextNode.isUserInteractionEnabled = false
self.lockIconNode = ASImageNode() self.lockIconNode = ASImageNode()
self.lockIconNode.displaysAsynchronously = false self.lockIconNode.displaysAsynchronously = false
@ -198,9 +207,13 @@ private class MessagePriceItemNode: ListViewItemNode {
self.addSubnode(self.leftTextNode) self.addSubnode(self.leftTextNode)
self.addSubnode(self.rightTextNode) self.addSubnode(self.rightTextNode)
self.addSubnode(self.centerLeftTextNode) self.addSubnode(self.centerTextButtonNode)
self.addSubnode(self.centerRightTextNode) self.centerTextButtonNode.view.addSubview(self.centerTextButtonBackground)
self.centerTextButtonNode.addSubnode(self.centerLeftTextNode)
self.centerTextButtonNode.addSubnode(self.centerRightTextNode)
self.addSubnode(self.lockIconNode) self.addSubnode(self.lockIconNode)
self.centerTextButtonNode.addTarget(self, action: #selector(self.centerTextButtonPressed), forControlEvents: .touchUpInside)
} }
override func didLoad() { override func didLoad() {
@ -232,6 +245,10 @@ private class MessagePriceItemNode: ListViewItemNode {
self.sliderView = sliderView self.sliderView = sliderView
} }
@objc private func centerTextButtonPressed() {
self.item?.openSetCustom?()
}
func asyncLayout() -> (_ item: MessagePriceItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { func asyncLayout() -> (_ item: MessagePriceItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
let currentItem = self.item let currentItem = self.item
@ -312,8 +329,8 @@ private class MessagePriceItemNode: ListViewItemNode {
strongSelf.rightTextNode.attributedText = NSAttributedString(string: "\(item.maxValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor) strongSelf.rightTextNode.attributedText = NSAttributedString(string: "\(item.maxValue)", font: Font.regular(13.0), textColor: item.theme.list.itemSecondaryTextColor)
let centralLeftText = item.strings.Privacy_Messages_Stars(Int32(item.value)) let centralLeftText = item.strings.Privacy_Messages_Stars(Int32(item.value))
strongSelf.centerLeftTextNode.attributedText = NSAttributedString(string: centralLeftText, font: textFont, textColor: item.theme.list.itemPrimaryTextColor) strongSelf.centerLeftTextNode.attributedText = NSAttributedString(string: centralLeftText, font: textFont, textColor: item.openSetCustom != nil ? item.theme.list.itemAccentColor : item.theme.list.itemPrimaryTextColor)
strongSelf.centerRightTextNode.attributedText = NSAttributedString(string: item.price, font: smallTextFont, textColor: item.theme.list.itemSecondaryTextColor) strongSelf.centerRightTextNode.attributedText = NSAttributedString(string: item.price, font: smallTextFont, textColor: item.openSetCustom != nil ? item.theme.list.itemAccentColor.withMultipliedAlpha(0.5) : item.theme.list.itemSecondaryTextColor)
let leftTextSize = strongSelf.leftTextNode.updateLayout(CGSize(width: 100.0, height: 100.0)) let leftTextSize = strongSelf.leftTextNode.updateLayout(CGSize(width: 100.0, height: 100.0))
let rightTextSize = strongSelf.rightTextNode.updateLayout(CGSize(width: 100.0, height: 100.0)) let rightTextSize = strongSelf.rightTextNode.updateLayout(CGSize(width: 100.0, height: 100.0))
@ -328,10 +345,28 @@ private class MessagePriceItemNode: ListViewItemNode {
let totalCenterWidth = centerLeftTextSize.width + centerSpacing + centerRightTextSize.width let totalCenterWidth = centerLeftTextSize.width + centerSpacing + centerRightTextSize.width
let centerLeftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - totalCenterWidth) / 2.0), y: 11.0), size: centerLeftTextSize) let centerLeftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - totalCenterWidth) / 2.0), y: 11.0), size: centerLeftTextSize)
strongSelf.centerLeftTextNode.frame = centerLeftFrame
let centerRightFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - totalCenterWidth) / 2.0) + totalCenterWidth - centerRightTextSize.width, y: 14.0 - UIScreenPixel), size: centerRightTextSize) let centerRightFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - totalCenterWidth) / 2.0) + totalCenterWidth - centerRightTextSize.width, y: 14.0 - UIScreenPixel), size: centerRightTextSize)
strongSelf.centerRightTextNode.frame = centerRightFrame
let centerButtonFrame = CGRect(origin: CGPoint(x: centerLeftFrame.minX, y: centerLeftFrame.minY), size: CGSize(width: centerRightFrame.maxX - centerLeftFrame.minX, height: centerLeftFrame.height)).insetBy(dx: -8.0, dy: -4.0)
strongSelf.centerTextButtonNode.frame = centerButtonFrame
strongSelf.centerTextButtonBackground.frame = CGRect(origin: CGPoint(x: 0.0, y: UIScreenPixel), size: centerButtonFrame.size)
if strongSelf.centerTextButtonBackground.image == nil {
strongSelf.centerTextButtonBackground.image = generateStretchableFilledCircleImage(diameter: 16.0, color: .white)?.withRenderingMode(.alwaysTemplate)
}
strongSelf.centerTextButtonBackground.tintColor = item.theme.list.itemAccentColor.withMultipliedAlpha(0.1)
if item.openSetCustom != nil {
strongSelf.centerTextButtonNode.isEnabled = true
strongSelf.centerTextButtonBackground.isHidden = false
} else {
strongSelf.centerTextButtonNode.isEnabled = false
strongSelf.centerTextButtonBackground.isHidden = true
}
strongSelf.centerLeftTextNode.frame = centerLeftFrame.offsetBy(dx: -centerButtonFrame.minX, dy: -centerButtonFrame.minY)
strongSelf.centerRightTextNode.frame = centerRightFrame.offsetBy(dx: -centerButtonFrame.minX, dy: -centerButtonFrame.minY)
if let sliderView = strongSelf.sliderView { if let sliderView = strongSelf.sliderView {
if themeUpdated { if themeUpdated {
@ -343,12 +378,17 @@ private class MessagePriceItemNode: ListViewItemNode {
sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + 18.0, y: 36.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 18.0 * 2.0, height: 44.0)) sliderView.frame = CGRect(origin: CGPoint(x: params.leftInset + 18.0, y: 36.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 18.0 * 2.0, height: 44.0))
sliderView.interactionEnded = { [weak self] in sliderView.interactionEnded = {
guard let self else { guard let self else {
return return
} }
self.item?.updated(Int64(self.amount.realValue), true) self.item?.updated(Int64(self.amount.realValue), true)
} }
if !sliderView.isTracking {
strongSelf.amount = Amount(realValue: Int(item.value), maxRealValue: Int(item.maxValue), maxSliderValue: 999, isLogarithmic: true)
sliderView.value = CGFloat(strongSelf.amount.sliderValue)
}
} }
strongSelf.lockIconNode.isHidden = item.isEnabled strongSelf.lockIconNode.isHidden = item.isEnabled

View File

@ -156,6 +156,15 @@ private final class SheetContent: CombinedComponent {
minAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMinAmount, nanos: 0) minAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMinAmount, nanos: 0)
maxAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMaxAmount, nanos: 0) maxAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMaxAmount, nanos: 0)
amountLabel = nil amountLabel = nil
case .paidMessages:
//TODO:localize
titleString = "Price per Message"
amountTitle = "PRICE IN STARS"
amountPlaceholder = "Enter Price"
minAmount = StarsAmount(value: 1, nanos: 0)
maxAmount = StarsAmount(value: resaleConfiguration.paidMessageMaxAmount, nanos: 0)
amountLabel = nil
} }
let title = title.update( let title = title.update(
@ -280,6 +289,19 @@ private final class SheetContent: CombinedComponent {
text: .plain(amountInfoString), text: .plain(amountInfoString),
maximumNumberOfLines: 0 maximumNumberOfLines: 0
)) ))
case .paidMessages:
let amountInfoString: NSAttributedString
if let value = state.amount?.value, value > 0 {
let fullValue: Int64 = Int64(value) * 1_000_000_000 * 80 / 100
let amountValue = StarsAmount(value: fullValue / 1_000_000_000, nanos: Int32(fullValue % 1_000_000_000))
amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString("You will receive **\(amountValue) Stars**.", attributes: amountMarkdownAttributes, textAlignment: .natural))
} else {
amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString("You will receive **80%**.", attributes: amountMarkdownAttributes, textAlignment: .natural))
}
amountFooter = AnyComponent(MultilineTextComponent(
text: .plain(amountInfoString),
maximumNumberOfLines: 0
))
default: default:
amountFooter = nil amountFooter = nil
} }
@ -340,6 +362,9 @@ private final class SheetContent: CombinedComponent {
} else { } else {
buttonString = "Sell" buttonString = "Sell"
} }
} else if case .paidMessages = component.mode {
//TODO:localize
buttonString = "OK"
} else if let amount = state.amount { } else if let amount = state.amount {
buttonString = "\(environment.strings.Stars_Withdraw_Withdraw) # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))" buttonString = "\(environment.strings.Stars_Withdraw_Withdraw) # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))"
} else { } else {
@ -432,6 +457,8 @@ private final class SheetContent: CombinedComponent {
amount = nil amount = nil
case .starGiftResell: case .starGiftResell:
amount = nil amount = nil
case let .paidMessages(initialValue):
amount = StarsAmount(value: initialValue, nanos: 0)
} }
self.amount = amount self.amount = amount
@ -553,6 +580,7 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer {
case paidMedia(Int64?) case paidMedia(Int64?)
case reaction(Int64?) case reaction(Int64?)
case starGiftResell(Bool) case starGiftResell(Bool)
case paidMessages(Int64)
} }
private let context: AccountContext private let context: AccountContext

View File

@ -3664,8 +3664,15 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return StarsWithdrawScreen(context: context, mode: .withdraw(stats), completion: completion) return StarsWithdrawScreen(context: context, mode: .withdraw(stats), completion: completion)
} }
public func makeStarsWithdrawalScreen(context: AccountContext, completion: @escaping (Int64) -> Void) -> ViewController { public func makeStarsWithdrawalScreen(context: AccountContext, subject: StarsWithdrawalScreenSubject, completion: @escaping (Int64) -> Void) -> ViewController {
return StarsWithdrawScreen(context: context, mode: .accountWithdraw, completion: completion) let mode: StarsWithdrawScreen.Mode
switch subject {
case .withdraw:
mode = .accountWithdraw
case let .enterAmount(current):
mode = .paidMessages(current.value)
}
return StarsWithdrawScreen(context: context, mode: mode, completion: completion)
} }
public func makeStarGiftResellScreen(context: AccountContext, update: Bool, completion: @escaping (Int64) -> Void) -> ViewController { public func makeStarGiftResellScreen(context: AccountContext, update: Bool, completion: @escaping (Int64) -> Void) -> ViewController {