Various fixes

This commit is contained in:
Ilya Laktyushin 2025-08-23 21:40:21 +04:00
parent 6e3c3940c2
commit e7c698d5f1
19 changed files with 281 additions and 219 deletions

View File

@ -1180,7 +1180,7 @@ public protocol SharedAccountContext: AnyObject {
func navigateToChat(accountId: AccountRecordId, peerId: PeerId, messageId: MessageId?) func navigateToChat(accountId: AccountRecordId, peerId: PeerId, messageId: MessageId?)
func openChatMessage(_ params: OpenChatMessageParams) -> Bool func openChatMessage(_ params: OpenChatMessageParams) -> Bool
func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, tag: HistoryViewInputTag?) -> Signal<(MessageIndex?, Bool), NoError> func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, tag: HistoryViewInputTag?) -> Signal<(MessageIndex?, Bool), NoError>
func makeOverlayAudioPlayerController(context: AccountContext, chatLocation: ChatLocation, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation?, parentNavigationController: NavigationController?, updateMusicSaved: ((FileMediaReference, Bool) -> Void)?, reorderSavedMusic: ((FileMediaReference, FileMediaReference?) -> Void)?) -> ViewController & OverlayAudioPlayerController func makeOverlayAudioPlayerController(context: AccountContext, chatLocation: ChatLocation, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation?, parentNavigationController: NavigationController?) -> ViewController & OverlayAudioPlayerController
func makePeerInfoController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool, requestsContext: PeerInvitationImportersContext?) -> ViewController? func makePeerInfoController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool, requestsContext: PeerInvitationImportersContext?) -> ViewController?
func makeChannelAdminController(context: AccountContext, peerId: PeerId, adminId: PeerId, initialParticipant: ChannelParticipant) -> ViewController? func makeChannelAdminController(context: AccountContext, peerId: PeerId, adminId: PeerId, initialParticipant: ChannelParticipant) -> ViewController?
func makeDeviceContactInfoController(context: ShareControllerAccountContext, environment: ShareControllerEnvironment, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController func makeDeviceContactInfoController(context: ShareControllerAccountContext, environment: ShareControllerEnvironment, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController

View File

@ -69,7 +69,7 @@ public enum PeerMessagesPlaylistLocation: Equatable, SharedMediaPlaylistLocation
} }
for file in state.files { for file in state.files {
let stableId = UInt32(clamping: file.fileId.id % Int64(Int32.max)) let stableId = UInt32(clamping: file.fileId.id % Int64(Int32.max))
messages.append(Message(stableId: stableId, stableVersion: 0, id: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: Int32(stableId)), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [.music], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])) messages.append(Message(stableId: stableId, stableVersion: 0, id: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: Int32(stableId)), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: stableId, flags: [], tags: [.music], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]))
} }
var canLoadMore = false var canLoadMore = false

View File

@ -1305,7 +1305,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
} }
controllers.append(self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService, displayCancel: displayCancel)) controllers.append(self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService, displayCancel: displayCancel))
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty) self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
case let .payment(number, codeHash, storeProduct, _): case let .payment(number, codeHash, storeProduct, _, _, _):
var controllers: [ViewController] = [] var controllers: [ViewController] = []
if !self.otherAccountPhoneNumbers.1.isEmpty { if !self.otherAccountPhoneNumbers.1.isEmpty {
controllers.append(self.splashController()) controllers.append(self.splashController())

View File

@ -4934,7 +4934,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
} else { } else {
controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account) controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account)
} }
let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: .peer(id: id.messageId.peerId), type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: playlistLocation, parentNavigationController: navigationController, updateMusicSaved: nil, reorderSavedMusic: nil) let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: .peer(id: id.messageId.peerId), type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: playlistLocation, parentNavigationController: navigationController)
strongSelf.interaction.dismissInput() strongSelf.interaction.dismissInput()
strongSelf.interaction.present(controller, nil) strongSelf.interaction.present(controller, nil)
} else if case let .messages(chatLocation, _, _) = playlistLocation { } else if case let .messages(chatLocation, _, _) = playlistLocation {
@ -4975,7 +4975,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
} else { } else {
controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account) controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account)
} }
let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: chatLocation, type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: nil, parentNavigationController: navigationController, updateMusicSaved: nil, reorderSavedMusic: nil) let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: chatLocation, type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: nil, parentNavigationController: navigationController)
strongSelf.interaction.dismissInput() strongSelf.interaction.dismissInput()
strongSelf.interaction.present(controller, nil) strongSelf.interaction.present(controller, nil)
} else if index.1 { } else if index.1 {

View File

@ -1269,7 +1269,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[957176926] = { return Api.auth.LoginToken.parse_loginTokenSuccess($0) } dict[957176926] = { return Api.auth.LoginToken.parse_loginTokenSuccess($0) }
dict[326715557] = { return Api.auth.PasswordRecovery.parse_passwordRecovery($0) } dict[326715557] = { return Api.auth.PasswordRecovery.parse_passwordRecovery($0) }
dict[1577067778] = { return Api.auth.SentCode.parse_sentCode($0) } dict[1577067778] = { return Api.auth.SentCode.parse_sentCode($0) }
dict[-674301568] = { return Api.auth.SentCode.parse_sentCodePaymentRequired($0) } dict[-677184263] = { return Api.auth.SentCode.parse_sentCodePaymentRequired($0) }
dict[596704836] = { return Api.auth.SentCode.parse_sentCodeSuccess($0) } dict[596704836] = { return Api.auth.SentCode.parse_sentCodeSuccess($0) }
dict[1035688326] = { return Api.auth.SentCodeType.parse_sentCodeTypeApp($0) } dict[1035688326] = { return Api.auth.SentCodeType.parse_sentCodeTypeApp($0) }
dict[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($0) } dict[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($0) }

View File

@ -643,7 +643,7 @@ public extension Api.auth {
public extension Api.auth { public extension Api.auth {
enum SentCode: TypeConstructorDescription { enum SentCode: TypeConstructorDescription {
case sentCode(flags: Int32, type: Api.auth.SentCodeType, phoneCodeHash: String, nextType: Api.auth.CodeType?, timeout: Int32?) case sentCode(flags: Int32, type: Api.auth.SentCodeType, phoneCodeHash: String, nextType: Api.auth.CodeType?, timeout: Int32?)
case sentCodePaymentRequired(storeProduct: String, phoneCodeHash: String) case sentCodePaymentRequired(storeProduct: String, phoneCodeHash: String, supportEmailAddress: String, supportEmailSubject: String)
case sentCodeSuccess(authorization: Api.auth.Authorization) case sentCodeSuccess(authorization: Api.auth.Authorization)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
@ -658,12 +658,14 @@ public extension Api.auth {
if Int(flags) & Int(1 << 1) != 0 {nextType!.serialize(buffer, true)} if Int(flags) & Int(1 << 1) != 0 {nextType!.serialize(buffer, true)}
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 2) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)}
break break
case .sentCodePaymentRequired(let storeProduct, let phoneCodeHash): case .sentCodePaymentRequired(let storeProduct, let phoneCodeHash, let supportEmailAddress, let supportEmailSubject):
if boxed { if boxed {
buffer.appendInt32(-674301568) buffer.appendInt32(-677184263)
} }
serializeString(storeProduct, buffer: buffer, boxed: false) serializeString(storeProduct, buffer: buffer, boxed: false)
serializeString(phoneCodeHash, buffer: buffer, boxed: false) serializeString(phoneCodeHash, buffer: buffer, boxed: false)
serializeString(supportEmailAddress, buffer: buffer, boxed: false)
serializeString(supportEmailSubject, buffer: buffer, boxed: false)
break break
case .sentCodeSuccess(let authorization): case .sentCodeSuccess(let authorization):
if boxed { if boxed {
@ -678,8 +680,8 @@ public extension Api.auth {
switch self { switch self {
case .sentCode(let flags, let type, let phoneCodeHash, let nextType, let timeout): case .sentCode(let flags, let type, let phoneCodeHash, let nextType, let timeout):
return ("sentCode", [("flags", flags as Any), ("type", type as Any), ("phoneCodeHash", phoneCodeHash as Any), ("nextType", nextType as Any), ("timeout", timeout as Any)]) return ("sentCode", [("flags", flags as Any), ("type", type as Any), ("phoneCodeHash", phoneCodeHash as Any), ("nextType", nextType as Any), ("timeout", timeout as Any)])
case .sentCodePaymentRequired(let storeProduct, let phoneCodeHash): case .sentCodePaymentRequired(let storeProduct, let phoneCodeHash, let supportEmailAddress, let supportEmailSubject):
return ("sentCodePaymentRequired", [("storeProduct", storeProduct as Any), ("phoneCodeHash", phoneCodeHash as Any)]) return ("sentCodePaymentRequired", [("storeProduct", storeProduct as Any), ("phoneCodeHash", phoneCodeHash as Any), ("supportEmailAddress", supportEmailAddress as Any), ("supportEmailSubject", supportEmailSubject as Any)])
case .sentCodeSuccess(let authorization): case .sentCodeSuccess(let authorization):
return ("sentCodeSuccess", [("authorization", authorization as Any)]) return ("sentCodeSuccess", [("authorization", authorization as Any)])
} }
@ -717,10 +719,16 @@ public extension Api.auth {
_1 = parseString(reader) _1 = parseString(reader)
var _2: String? var _2: String?
_2 = parseString(reader) _2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: String?
_4 = parseString(reader)
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
if _c1 && _c2 { let _c3 = _3 != nil
return Api.auth.SentCode.sentCodePaymentRequired(storeProduct: _1!, phoneCodeHash: _2!) let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.auth.SentCode.sentCodePaymentRequired(storeProduct: _1!, phoneCodeHash: _2!, supportEmailAddress: _3!, supportEmailSubject: _4!)
} }
else { else {
return nil return nil

View File

@ -861,7 +861,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
} else { } else {
controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account) controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account)
} }
let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: .peer(id: id.messageId.peerId), type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: playlistLocation, parentNavigationController: strongSelf.navigationController as? NavigationController, updateMusicSaved: nil, reorderSavedMusic: nil) let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: .peer(id: id.messageId.peerId), type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: playlistLocation, parentNavigationController: strongSelf.navigationController as? NavigationController)
strongSelf.displayNode.view.window?.endEditing(true) strongSelf.displayNode.view.window?.endEditing(true)
strongSelf.present(controller, in: .window(.root)) strongSelf.present(controller, in: .window(.root))
case let .messages(chatLocation, _, _): case let .messages(chatLocation, _, _):
@ -902,7 +902,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
} else { } else {
controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account) controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account)
} }
let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: chatLocation, type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: nil, parentNavigationController: strongSelf.navigationController as? NavigationController, updateMusicSaved: nil, reorderSavedMusic: nil) let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: chatLocation, type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: nil, parentNavigationController: strongSelf.navigationController as? NavigationController)
strongSelf.displayNode.view.window?.endEditing(true) strongSelf.displayNode.view.window?.endEditing(true)
strongSelf.present(controller, in: .window(.root)) strongSelf.present(controller, in: .window(.root))
} else if index.1 { } else if index.1 {

View File

@ -127,7 +127,7 @@ public class UnauthorizedAccount {
if let nextType = nextType { if let nextType = nextType {
parsedNextType = AuthorizationCodeNextType(apiType: nextType) parsedNextType = AuthorizationCodeNextType(apiType: nextType)
} }
if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(phoneNumber, _, _, syncContacts) = state.contents { if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(phoneNumber, _, _, _, _, syncContacts) = state.contents {
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: testingEnvironment, masterDatacenterId: masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false))) transaction.setState(UnauthorizedAccountState(isTestingEnvironment: testingEnvironment, masterDatacenterId: masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false)))
} }
}).start() }).start()
@ -136,7 +136,7 @@ public class UnauthorizedAccount {
case let .authorization(_, _, _, futureAuthToken, user): case let .authorization(_, _, _, futureAuthToken, user):
let _ = postbox.transaction({ [weak self] transaction in let _ = postbox.transaction({ [weak self] transaction in
var syncContacts = true var syncContacts = true
if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(_, _, _, syncContactsValue) = state.contents { if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(_, _, _, _, _, syncContactsValue) = state.contents {
syncContacts = syncContactsValue syncContacts = syncContactsValue
} }
@ -162,7 +162,7 @@ public class UnauthorizedAccount {
case let .authorizationSignUpRequired(_, termsOfService): case let .authorizationSignUpRequired(_, termsOfService):
let _ = postbox.transaction({ [weak self] transaction in let _ = postbox.transaction({ [weak self] transaction in
if let self { if let self {
if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(number, codeHash, _, syncContacts) = state.contents { if let state = transaction.getState() as? UnauthorizedAccountState, case let .payment(number, codeHash, _, _, _, syncContacts) = state.contents {
let _ = beginSignUp( let _ = beginSignUp(
account: self, account: self,
data: AuthorizationSignUpData( data: AuthorizationSignUpData(

View File

@ -518,8 +518,8 @@ private func internalResendAuthorizationCode(accountManager: AccountManager<Tele
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false))) transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
return .single(.sentCode(account)) return .single(.sentCode(account))
case let .sentCodePaymentRequired(storeProduct, codeHash): case let .sentCodePaymentRequired(storeProduct, codeHash, supportEmailAddress, supportEmailSubject):
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: number, codeHash: codeHash, storeProduct: storeProduct, syncContacts: syncContacts))) transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: number, codeHash: codeHash, storeProduct: storeProduct, supportEmailAddress: supportEmailAddress, supportEmailSubject: supportEmailSubject, syncContacts: syncContacts)))
return .single(.sentCode(account)) return .single(.sentCode(account))
case .sentCodeSuccess: case .sentCodeSuccess:
return .single(.loggedIn) return .single(.loggedIn)
@ -626,8 +626,8 @@ public func resendAuthorizationCode(accountManager: AccountManager<TelegramAccou
} }
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: parsedType, hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false))) transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: parsedType, hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
case let .sentCodePaymentRequired(storeProduct, codeHash): case let .sentCodePaymentRequired(storeProduct, codeHash, supportEmailAddress, supportEmailSubject):
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: number, codeHash: codeHash, storeProduct: storeProduct, syncContacts: syncContacts))) transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: number, codeHash: codeHash, storeProduct: storeProduct, supportEmailAddress: supportEmailAddress, supportEmailSubject: supportEmailSubject, syncContacts: syncContacts)))
case .sentCodeSuccess: case .sentCodeSuccess:
break break
} }
@ -905,8 +905,8 @@ public func verifyLoginEmailSetup(account: UnauthorizedAccount, code: Authorizat
} }
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false))) transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false)))
case let .sentCodePaymentRequired(storeProduct, codeHash): case let .sentCodePaymentRequired(storeProduct, codeHash, supportEmailAddress, supportEmailSubject):
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: codeHash, storeProduct: storeProduct, syncContacts: syncContacts))) transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: codeHash, storeProduct: storeProduct, supportEmailAddress: supportEmailAddress, supportEmailSubject: supportEmailSubject, syncContacts: syncContacts)))
case .sentCodeSuccess: case .sentCodeSuccess:
break break
} }
@ -970,8 +970,8 @@ public func resetLoginEmail(account: UnauthorizedAccount, phoneNumber: String, p
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false))) transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false)))
return .complete() return .complete()
case let .sentCodePaymentRequired(storeProduct, codeHash): case let .sentCodePaymentRequired(storeProduct, codeHash, supportEmailAddress, supportEmailSubject):
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: codeHash, storeProduct: storeProduct, syncContacts: syncContacts))) transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .payment(number: phoneNumber, codeHash: codeHash, storeProduct: storeProduct, supportEmailAddress: supportEmailAddress, supportEmailSubject: supportEmailSubject, syncContacts: syncContacts)))
return .complete() return .complete()
case .sentCodeSuccess: case .sentCodeSuccess:
return .complete() return .complete()

View File

@ -182,7 +182,7 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable
case passwordRecovery(hint: String, number: String?, code: AuthorizationCode?, emailPattern: String, syncContacts: Bool) case passwordRecovery(hint: String, number: String?, code: AuthorizationCode?, emailPattern: String, syncContacts: Bool)
case awaitingAccountReset(protectedUntil: Int32, number: String?, syncContacts: Bool) case awaitingAccountReset(protectedUntil: Int32, number: String?, syncContacts: Bool)
case signUp(number: String, codeHash: String, firstName: String, lastName: String, termsOfService: UnauthorizedAccountTermsOfService?, syncContacts: Bool) case signUp(number: String, codeHash: String, firstName: String, lastName: String, termsOfService: UnauthorizedAccountTermsOfService?, syncContacts: Bool)
case payment(number: String, codeHash: String, storeProduct: String, syncContacts: Bool) case payment(number: String, codeHash: String, storeProduct: String, supportEmailAddress: String, supportEmailSubject: String, syncContacts: Bool)
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("v", orElse: 0) { switch decoder.decodeInt32ForKey("v", orElse: 0) {
@ -217,7 +217,7 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable
case UnauthorizedAccountStateContentsValue.signUp.rawValue: case UnauthorizedAccountStateContentsValue.signUp.rawValue:
self = .signUp(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), firstName: decoder.decodeStringForKey("f", orElse: ""), lastName: decoder.decodeStringForKey("l", orElse: ""), termsOfService: decoder.decodeObjectForKey("tos", decoder: { UnauthorizedAccountTermsOfService(decoder: $0) }) as? UnauthorizedAccountTermsOfService, syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0) self = .signUp(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), firstName: decoder.decodeStringForKey("f", orElse: ""), lastName: decoder.decodeStringForKey("l", orElse: ""), termsOfService: decoder.decodeObjectForKey("tos", decoder: { UnauthorizedAccountTermsOfService(decoder: $0) }) as? UnauthorizedAccountTermsOfService, syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0)
case UnauthorizedAccountStateContentsValue.payment.rawValue: case UnauthorizedAccountStateContentsValue.payment.rawValue:
self = .payment(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), storeProduct: decoder.decodeStringForKey("storeProduct", orElse: ""), syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0) self = .payment(number: decoder.decodeStringForKey("n", orElse: ""), codeHash: decoder.decodeStringForKey("h", orElse: ""), storeProduct: decoder.decodeStringForKey("storeProduct", orElse: ""), supportEmailAddress: decoder.decodeStringForKey("supportEmailAddress", orElse: ""), supportEmailSubject: decoder.decodeStringForKey("supportEmailSubject", orElse: ""), syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0)
default: default:
assertionFailure() assertionFailure()
self = .empty self = .empty
@ -307,11 +307,13 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable
encoder.encodeNil(forKey: "tos") encoder.encodeNil(forKey: "tos")
} }
encoder.encodeInt32(syncContacts ? 1 : 0, forKey: "syncContacts") encoder.encodeInt32(syncContacts ? 1 : 0, forKey: "syncContacts")
case let .payment(number, codeHash, storeProduct, syncContacts): case let .payment(number, codeHash, storeProduct, supportEmailAddress, supportEmailSubject, syncContacts):
encoder.encodeInt32(UnauthorizedAccountStateContentsValue.payment.rawValue, forKey: "v") encoder.encodeInt32(UnauthorizedAccountStateContentsValue.payment.rawValue, forKey: "v")
encoder.encodeString(number, forKey: "n") encoder.encodeString(number, forKey: "n")
encoder.encodeString(codeHash, forKey: "h") encoder.encodeString(codeHash, forKey: "h")
encoder.encodeString(storeProduct, forKey: "storeProduct") encoder.encodeString(storeProduct, forKey: "storeProduct")
encoder.encodeString(supportEmailAddress, forKey: "supportEmailAddress")
encoder.encodeString(supportEmailSubject, forKey: "supportEmailSubject")
encoder.encodeInt32(syncContacts ? 1 : 0, forKey: "syncContacts") encoder.encodeInt32(syncContacts ? 1 : 0, forKey: "syncContacts")
} }
} }
@ -384,8 +386,8 @@ public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable
} else { } else {
return false return false
} }
case let .payment(number, codeHash, storeProduct, syncContacts): case let .payment(number, codeHash, storeProduct, supportEmailAddress, supportEmailSubject, syncContacts):
if case .payment(number, codeHash, storeProduct, syncContacts) = rhs { if case .payment(number, codeHash, storeProduct, supportEmailAddress, supportEmailSubject, syncContacts) = rhs {
return true return true
} else { } else {
return false return false

View File

@ -143,6 +143,8 @@ final class GiftOptionsScreenComponent: Component {
private var starsFilter: StarsFilter = .all private var starsFilter: StarsFilter = .all
private var switchingFilter = false private var switchingFilter = false
private var loadingGiftId: Int64?
private var _effectiveStarGifts: ([StarGift], StarsFilter)? private var _effectiveStarGifts: ([StarGift], StarsFilter)?
private var effectiveStarGifts: [StarGift]? { private var effectiveStarGifts: [StarGift]? {
get { get {
@ -275,6 +277,122 @@ final class GiftOptionsScreenComponent: Component {
self.updateScrolling(interactive: true, transition: self.nextScrollTransition ?? .immediate) self.updateScrolling(interactive: true, transition: self.nextScrollTransition ?? .immediate)
} }
private func openGift(gift: StarGift, skipDateCheck: Bool = false) {
guard let component = self.component, let environment = self.environment else {
return
}
let context = component.context
let strings = environment.strings
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
let starGift = gift
if let controller = environment.controller() as? GiftOptionsScreen {
let mainController: ViewController
if let parentController = controller.parentController() {
mainController = parentController
} else {
mainController = controller
}
if case let .generic(gift) = gift {
if let lockedUntilDate = gift.lockedUntilDate, currentTime < lockedUntilDate {
self.loadingGiftId = gift.id
Queue.mainQueue().after(0.25) {
if self.loadingGiftId != nil {
self.state?.updated()
}
}
let _ = (component.context.engine.payments.checkCanSendStarGift(giftId: gift.id)
|> deliverOnMainQueue).start(next: { [weak self, weak controller] result in
guard let self, let controller else {
return
}
self.loadingGiftId = nil
self.state?.updated()
switch result {
case .available:
self.openGift(gift: starGift, skipDateCheck: true)
case let .unavailable(text, entities):
let theme = AlertControllerTheme(presentationData: component.context.sharedContext.currentPresentationData.with { $0 })
let font = Font.regular(floor(theme.baseFontSize * 13.0 / 17.0))
let boldFont = Font.semibold(floor(theme.baseFontSize * 13.0 / 17.0))
let attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: theme.primaryColor, linkColor: theme.accentColor, baseFont: font, linkFont: font, boldFont: boldFont, italicFont: font, boldItalicFont: font, fixedFont: font, blockQuoteFont: font, message: nil, paragraphAlignment: .center)
var dismissImpl: (() -> Void)?
let alertController = textAlertController(theme: theme, title: NSAttributedString(string: strings.Gift_Options_GiftLocked_Title, font: Font.semibold(theme.baseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center), text: attributedText, actions: [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {
dismissImpl?()
})], actionLayout: .horizontal, dismissOnOutsideTap: true, linkAction: { [weak controller] attributes, _ in
if let value = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
dismissImpl?()
if let navigationController = controller?.navigationController as? NavigationController {
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: value, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: {})
}
}
})
dismissImpl = { [weak alertController] in
alertController?.dismissAnimated()
}
controller.present(alertController, in: .window(.root))
case .failed:
break
}
})
return
}
if let perUserLimit = gift.perUserLimit, perUserLimit.remains == 0 {
let text = environment.strings.Gift_Options_Gift_BuyLimitReached(perUserLimit.total)
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: component.context, file: gift.file, loop: true, title: nil, text: text, undoText: nil, customAction: nil), action: { _ in return false })
mainController.present(controller, in: .current)
return
}
if gift.flags.contains(.requiresPremium) && !component.context.isPremium {
let controller = component.context.sharedContext.makePremiumIntroController(context: component.context, source: .premiumGift(gift.file), forceDark: false, dismissed: nil)
mainController.push(controller)
return
}
if let availability = gift.availability, availability.remains == 0 {
if availability.resale > 0 {
let storeController = component.context.sharedContext.makeGiftStoreController(
context: component.context,
peerId: component.peerId,
gift: gift
)
mainController.push(storeController)
} else {
let giftController = GiftViewScreen(
context: component.context,
subject: .soldOutGift(gift)
)
mainController.push(giftController)
}
} else {
var forceUnique: Bool?
if let disallowedGifts = self.state?.disallowedGifts {
if disallowedGifts.contains(.limited) && !disallowedGifts.contains(.unique) {
forceUnique = true
} else if !disallowedGifts.contains(.limited) && disallowedGifts.contains(.unique) {
forceUnique = false
}
}
let giftController = GiftSetupScreen(
context: component.context,
peerId: component.peerId,
subject: .starGift(gift, forceUnique),
completion: component.completion
)
mainController.push(giftController)
}
} else if case let .unique(gift) = gift {
self.transferGift(gift)
}
}
}
private func updateScrolling(interactive: Bool = false, transition: ComponentTransition) { private func updateScrolling(interactive: Bool = false, transition: ComponentTransition) {
guard let environment = self.environment, let component = self.component else { guard let environment = self.environment, let component = self.component else {
return return
@ -282,7 +400,6 @@ final class GiftOptionsScreenComponent: Component {
let availableWidth = self.scrollView.bounds.width let availableWidth = self.scrollView.bounds.width
let contentOffset = self.scrollView.contentOffset.y let contentOffset = self.scrollView.contentOffset.y
let strings = environment.strings
let topPanelAlpha = min(20.0, max(0.0, contentOffset - 95.0)) / 20.0 let topPanelAlpha = min(20.0, max(0.0, contentOffset - 95.0)) / 20.0
if let topPanelView = self.topPanel.view, let topSeparator = self.topSeparator.view { if let topPanelView = self.topPanel.view, let topSeparator = self.topSeparator.view {
@ -345,8 +462,6 @@ final class GiftOptionsScreenComponent: Component {
var validIds: [AnyHashable] = [] var validIds: [AnyHashable] = []
var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: self.starsItemsOrigin), size: starsOptionSize) var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: self.starsItemsOrigin), size: starsOptionSize)
let controller = environment.controller
for gift in starGifts { for gift in starGifts {
var isVisible = false var isVisible = false
if visibleBounds.intersects(itemFrame) { if visibleBounds.intersects(itemFrame) {
@ -372,6 +487,7 @@ final class GiftOptionsScreenComponent: Component {
var ribbon: GiftItemComponent.Ribbon? var ribbon: GiftItemComponent.Ribbon?
var outline: GiftItemComponent.Outline? var outline: GiftItemComponent.Outline?
var isSoldOut = false var isSoldOut = false
var isLoading = false
switch gift { switch gift {
case let .generic(gift): case let .generic(gift):
if let _ = gift.soldOut { if let _ = gift.soldOut {
@ -400,6 +516,10 @@ final class GiftOptionsScreenComponent: Component {
) )
outline = .orange outline = .orange
} }
if gift.id == self.loadingGiftId {
isLoading = true
}
case let .unique(gift): case let .unique(gift):
var ribbonColor: GiftItemComponent.Ribbon.Color = .blue var ribbonColor: GiftItemComponent.Ribbon.Color = .blue
for attribute in gift.attributes { for attribute in gift.attributes {
@ -415,7 +535,7 @@ final class GiftOptionsScreenComponent: Component {
) )
} }
var isDateLocked = false var lockedUntilDate: Int32?
let subject: GiftItemComponent.Subject let subject: GiftItemComponent.Subject
switch gift { switch gift {
case let .generic(gift): case let .generic(gift):
@ -429,12 +549,11 @@ final class GiftOptionsScreenComponent: Component {
} else { } else {
subject = .starGift(gift: gift, price: "# \(presentationStringsFormattedNumber(Int32(gift.price), environment.dateTimeFormat.groupingSeparator))") subject = .starGift(gift: gift, price: "# \(presentationStringsFormattedNumber(Int32(gift.price), environment.dateTimeFormat.groupingSeparator))")
} }
isDateLocked = gift.lockedUntilDate != nil lockedUntilDate = gift.lockedUntilDate
case let .unique(gift): case let .unique(gift):
subject = .uniqueGift(gift: gift, price: nil) subject = .uniqueGift(gift: gift, price: nil)
} }
let context = component.context
let _ = visibleItem.update( let _ = visibleItem.update(
transition: itemTransition, transition: itemTransition,
component: AnyComponent( component: AnyComponent(
@ -448,104 +567,17 @@ final class GiftOptionsScreenComponent: Component {
subject: subject, subject: subject,
ribbon: ribbon, ribbon: ribbon,
outline: outline, outline: outline,
isLoading: isLoading,
isSoldOut: isSoldOut, isSoldOut: isSoldOut,
isDateLocked: isDateLocked isDateLocked: lockedUntilDate != nil
) )
), ),
effectAlignment: .center, effectAlignment: .center,
action: { [weak self] in action: { [weak self] in
if let self, let component = self.component { guard let self else {
if let controller = controller() as? GiftOptionsScreen { return
let mainController: ViewController
if let parentController = controller.parentController() {
mainController = parentController
} else {
mainController = controller
}
if case let .generic(gift) = gift {
if isDateLocked {
let _ = (component.context.engine.payments.checkCanSendStarGift(giftId: gift.id)
|> deliverOnMainQueue).start(next: { [weak controller] result in
guard let controller else {
return
}
if case let .unavailable(text, entities) = result {
let theme = AlertControllerTheme(presentationData: component.context.sharedContext.currentPresentationData.with { $0 })
let font = Font.regular(floor(theme.baseFontSize * 13.0 / 17.0))
let boldFont = Font.semibold(floor(theme.baseFontSize * 13.0 / 17.0))
let attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: theme.primaryColor, linkColor: theme.accentColor, baseFont: font, linkFont: font, boldFont: boldFont, italicFont: font, boldItalicFont: font, fixedFont: font, blockQuoteFont: font, message: nil, paragraphAlignment: .center)
var dismissImpl: (() -> Void)?
let alertController = textAlertController(theme: theme, title: NSAttributedString(string: strings.Gift_Options_GiftLocked_Title, font: Font.semibold(theme.baseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center), text: attributedText, actions: [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {
dismissImpl?()
})], actionLayout: .horizontal, dismissOnOutsideTap: true, linkAction: { [weak controller] attributes, _ in
if let value = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
dismissImpl?()
if let navigationController = controller?.navigationController as? NavigationController {
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: value, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: {})
}
}
})
dismissImpl = { [weak alertController] in
alertController?.dismissAnimated()
}
controller.present(alertController, in: .window(.root))
}
})
return
}
if let perUserLimit = gift.perUserLimit, perUserLimit.remains == 0 {
let text = environment.strings.Gift_Options_Gift_BuyLimitReached(perUserLimit.total)
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: component.context, file: gift.file, loop: true, title: nil, text: text, undoText: nil, customAction: nil), action: { _ in return false })
mainController.present(controller, in: .current)
return
}
if gift.flags.contains(.requiresPremium) && !component.context.isPremium {
let controller = component.context.sharedContext.makePremiumIntroController(context: component.context, source: .premiumGift(gift.file), forceDark: false, dismissed: nil)
mainController.push(controller)
return
}
if let availability = gift.availability, availability.remains == 0 {
if availability.resale > 0 {
let storeController = component.context.sharedContext.makeGiftStoreController(
context: component.context,
peerId: component.peerId,
gift: gift
)
mainController.push(storeController)
} else {
let giftController = GiftViewScreen(
context: component.context,
subject: .soldOutGift(gift)
)
mainController.push(giftController)
}
} else {
var forceUnique: Bool?
if let disallowedGifts = self.state?.disallowedGifts {
if disallowedGifts.contains(.limited) && !disallowedGifts.contains(.unique) {
forceUnique = true
} else if !disallowedGifts.contains(.limited) && disallowedGifts.contains(.unique) {
forceUnique = false
}
}
let giftController = GiftSetupScreen(
context: component.context,
peerId: component.peerId,
subject: .starGift(gift, forceUnique),
completion: component.completion
)
mainController.push(giftController)
}
} else if case let .unique(gift) = gift {
self.transferGift(gift)
}
}
} }
self.openGift(gift: gift)
}, },
animateAlpha: false animateAlpha: false
) )

View File

@ -446,7 +446,7 @@ private final class GiftValueSheetContent: CombinedComponent {
) )
let percentage = Int32(floor(Double(lastSalePrice) / Double(component.valueInfo.initialSalePrice) * 100.0 - 100.0)) let percentage = Int32(floor(Double(lastSalePrice) / Double(component.valueInfo.initialSalePrice) * 100.0 - 100.0))
let percentageString = percentage > 0 ? "+\(percentage)" : "\(percentage)" let percentageString = (percentage > 0 ? "+\(percentage)" : "\(percentage)") + "%%"
items.append(AnyComponentWithIdentity( items.append(AnyComponentWithIdentity(
id: AnyHashable(1), id: AnyHashable(1),

View File

@ -1206,7 +1206,7 @@ private final class GiftViewSheetContent: CombinedComponent {
self.inUpgradePreview = true self.inUpgradePreview = true
self.updated(transition: .spring(duration: 0.4)) self.updated(transition: .spring(duration: 0.4))
if let controller = self.getController() as? GiftViewScreen { if let controller = self.getController() as? GiftViewScreen, self.upgradeForm != nil {
controller.showBalance = true controller.showBalance = true
} }
} }
@ -1625,6 +1625,38 @@ private final class GiftViewSheetContent: CombinedComponent {
} }
self.justUpgraded = true self.justUpgraded = true
self.revealedNumberDigits = -1
if case let .unique(uniqueGift) = result.gift {
for i in 0 ..< "\(uniqueGift.number)".count {
Queue.mainQueue().after(0.2 + Double(i) * 0.3) {
self.revealedNumberDigits += 1
self.updated(transition: .immediate)
}
}
}
Queue.mainQueue().after(1.2) {
self.revealedAttributes.insert(.backdrop)
self.updated(transition: .immediate)
Queue.mainQueue().after(0.7) {
self.revealedAttributes.insert(.pattern)
self.updated(transition: .immediate)
Queue.mainQueue().after(0.7) {
self.revealedAttributes.insert(.model)
self.updated(transition: .immediate)
Queue.mainQueue().after(0.6) {
if let controller = self.getController() as? GiftViewScreen {
controller.animateSuccess()
}
}
}
}
}
self.subject = .profileGift(peerId, result) self.subject = .profileGift(peerId, result)
controller.animateSuccess() controller.animateSuccess()
self.updated(transition: .spring(duration: 0.4)) self.updated(transition: .spring(duration: 0.4))
@ -1714,7 +1746,6 @@ private final class GiftViewSheetContent: CombinedComponent {
guard let self else { guard let self else {
return return
} }
self.dismiss(animated: true)
Queue.mainQueue().after(0.5) { Queue.mainQueue().after(0.5) {
starsContext?.load(force: true) starsContext?.load(force: true)
} }
@ -4433,9 +4464,15 @@ public class GiftViewScreen: ViewControllerComponentContainer {
} }
fileprivate func switchToNextUpgradable() { fileprivate func switchToNextUpgradable() {
guard let upgradableGifts = self.upgradableGifts, case let .profileGift(peerId, _) = self.subject else { guard let upgradableGifts = self.upgradableGifts else {
return return
} }
let peerId: EnginePeer.Id
if case let .profileGift(peerIdValue, _) = self.subject {
peerId = peerIdValue
} else {
peerId = self.context.account.peerId
}
var effectiveUpgradableGifts: [ProfileGiftsContext.State.StarGift] = [] var effectiveUpgradableGifts: [ProfileGiftsContext.State.StarGift] = []
for gift in upgradableGifts { for gift in upgradableGifts {
if let reference = gift.reference { if let reference = gift.reference {
@ -4613,7 +4650,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
} }
func formatPercentage(_ value: Float) -> String { func formatPercentage(_ value: Float) -> String {
return String(format: "%0.1f%%", value).replacingOccurrences(of: ".0%", with: "%").replacingOccurrences(of: ",0%", with: "%") return String(format: "%0.1f", value).replacingOccurrences(of: ".0", with: "").replacingOccurrences(of: ",0", with: "") + "%%"
} }
private final class PeerCellComponent: Component { private final class PeerCellComponent: Component {

View File

@ -406,7 +406,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
} else { } else {
controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account) controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account)
} }
let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: chatLocation, type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: nil, parentNavigationController: strongSelf.chatControllerInteraction.navigationController(), updateMusicSaved: nil, reorderSavedMusic: nil) let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: chatLocation, type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: nil, parentNavigationController: strongSelf.chatControllerInteraction.navigationController())
strongSelf.view.window?.endEditing(true) strongSelf.view.window?.endEditing(true)
strongSelf.chatControllerInteraction.presentController(controller, nil) strongSelf.chatControllerInteraction.presentController(controller, nil)
} else if index.1 { } else if index.1 {

View File

@ -557,6 +557,36 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} }
} }
let actionButtonKeys: [PeerInfoHeaderButtonKey] = (self.isSettings || self.isMyProfile) ? [] : peerInfoHeaderActionButtons(peer: peer, isSecretChat: isSecretChat, isContact: isContact)
let buttonKeys: [PeerInfoHeaderButtonKey] = (self.isSettings || self.isMyProfile) ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: true, videoCallsEnabled: width > 320.0 && self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact, threadInfo: threadData?.info)
let backgroundCoverSubject: PeerInfoCoverComponent.Subject?
var backgroundCoverAnimateIn = false
var backgroundDefaultHeight: CGFloat = 254.0
var hasBackground = false
if let status = peer?.emojiStatus, case .starGift = status.content {
backgroundCoverSubject = .status(status)
if !self.didSetupBackgroundCover {
if !self.isSettings {
backgroundCoverAnimateIn = true
}
self.didSetupBackgroundCover = true
}
if !buttonKeys.isEmpty {
backgroundDefaultHeight = 327.0
if metrics.isTablet {
backgroundDefaultHeight += 60.0
}
}
hasBackground = true
} else if let peer {
backgroundCoverSubject = .peer(EnginePeer(peer))
if peer.profileColor != nil {
hasBackground = true
}
} else {
backgroundCoverSubject = nil
}
var currentSavedMusic: TelegramMediaFile? var currentSavedMusic: TelegramMediaFile?
if !self.isSettings, let screenData { if !self.isSettings, let screenData {
@ -566,8 +596,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
currentSavedMusic = cachedUserData.savedMusic currentSavedMusic = cachedUserData.savedMusic
} }
} }
let musicHeight: CGFloat = 24.0 let musicHeight: CGFloat = hasBackground ? 24.0 : 16.0
let bottomInset: CGFloat = currentSavedMusic != nil ? 24.0 : 0.0 let bottomInset: CGFloat = currentSavedMusic != nil ? musicHeight : 0.0
let isLandscape = containerInset > 16.0 let isLandscape = containerInset > 16.0
@ -1188,9 +1218,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight) let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight)
let actionButtonKeys: [PeerInfoHeaderButtonKey] = (self.isSettings || self.isMyProfile) ? [] : peerInfoHeaderActionButtons(peer: peer, isSecretChat: isSecretChat, isContact: isContact)
let buttonKeys: [PeerInfoHeaderButtonKey] = (self.isSettings || self.isMyProfile) ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: true, videoCallsEnabled: width > 320.0 && self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact, threadInfo: threadData?.info)
var isPremium = false var isPremium = false
var isVerified = false var isVerified = false
var isFake = false var isFake = false
@ -2460,34 +2487,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transition.updateFrame(view: self.backgroundBannerView, frame: bannerFrame) transition.updateFrame(view: self.backgroundBannerView, frame: bannerFrame)
} }
let backgroundCoverSubject: PeerInfoCoverComponent.Subject?
var backgroundCoverAnimateIn = false
var backgroundDefaultHeight: CGFloat = 254.0
var hasBackground = false
if let status = peer?.emojiStatus, case .starGift = status.content {
backgroundCoverSubject = .status(status)
if !self.didSetupBackgroundCover {
if !self.isSettings {
backgroundCoverAnimateIn = true
}
self.didSetupBackgroundCover = true
}
if !buttonKeys.isEmpty {
backgroundDefaultHeight = 327.0
if metrics.isTablet {
backgroundDefaultHeight += 60.0
}
}
hasBackground = true
} else if let peer {
backgroundCoverSubject = .peer(EnginePeer(peer))
if peer.profileColor != nil {
hasBackground = true
}
} else {
backgroundCoverSubject = nil
}
let backgroundCoverSize = self.backgroundCover.update( let backgroundCoverSize = self.backgroundCover.update(
transition: ComponentTransition(transition), transition: ComponentTransition(transition),
component: AnyComponent(PeerInfoCoverComponent( component: AnyComponent(PeerInfoCoverComponent(
@ -2699,7 +2698,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
environment: {}, environment: {},
containerSize: CGSize(width: backgroundFrame.width, height: musicHeight) containerSize: CGSize(width: backgroundFrame.width, height: musicHeight)
) )
let musicFrame = CGRect(origin: CGPoint(x: 0.0, y: (apparentBackgroundHeight - backgroundHeight) + backgroundHeight - musicHeight), size: musicSize) let musicFrame = CGRect(origin: CGPoint(x: 0.0, y: (apparentBackgroundHeight - backgroundHeight) + backgroundHeight - musicHeight - (hasBackground ? 0.0 : 4.0)), size: musicSize)
if let musicView = music.view { if let musicView = music.view {
if musicView.superview == nil { if musicView.superview == nil {
self.regularContentNode.view.addSubview(musicView) self.regularContentNode.view.addSubview(musicView)

View File

@ -6050,23 +6050,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
initialMessageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: initialId), initialMessageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: initialId),
initialOrder: .regular, initialOrder: .regular,
playlistLocation: playlistLocation, playlistLocation: playlistLocation,
parentNavigationController: self.controller?.navigationController as? NavigationController, parentNavigationController: self.controller?.navigationController as? NavigationController
updateMusicSaved: self.peerId == self.context.account.peerId ? { [weak savedMusicContext] file, isSaved in
guard let savedMusicContext else {
return
}
if isSaved {
let _ = savedMusicContext.addMusic(file: file).start()
} else {
let _ = savedMusicContext.removeMusic(file: file).start()
}
} : nil,
reorderSavedMusic: { [weak savedMusicContext] file, afterFile in
guard let savedMusicContext else {
return
}
let _ = savedMusicContext.addMusic(file: file, afterFile: afterFile, apply: true).start()
}
) )
self.controller?.present(musicController, in: .window(.root)) self.controller?.present(musicController, in: .window(.root))
} }

View File

@ -18,8 +18,6 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer
let playlistLocation: SharedMediaPlaylistLocation? let playlistLocation: SharedMediaPlaylistLocation?
private(set) weak var parentNavigationController: NavigationController? private(set) weak var parentNavigationController: NavigationController?
private let updateMusicSaved: ((FileMediaReference, Bool) -> Void)?
let reorderSavedMusic: ((FileMediaReference, FileMediaReference?) -> Void)?
private var animatedIn = false private var animatedIn = false
@ -36,9 +34,7 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer
initialMessageId: MessageId, initialMessageId: MessageId,
initialOrder: MusicPlaybackSettingsOrder, initialOrder: MusicPlaybackSettingsOrder,
playlistLocation: SharedMediaPlaylistLocation? = nil, playlistLocation: SharedMediaPlaylistLocation? = nil,
parentNavigationController: NavigationController?, parentNavigationController: NavigationController?
updateMusicSaved: ((FileMediaReference, Bool) -> Void)? = nil,
reorderSavedMusic: ((FileMediaReference, FileMediaReference?) -> Void)? = nil
) { ) {
self.context = context self.context = context
self.chatLocation = chatLocation self.chatLocation = chatLocation
@ -47,8 +43,6 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer
self.initialOrder = initialOrder self.initialOrder = initialOrder
self.playlistLocation = playlistLocation self.playlistLocation = playlistLocation
self.parentNavigationController = parentNavigationController self.parentNavigationController = parentNavigationController
self.updateMusicSaved = updateMusicSaved
self.reorderSavedMusic = reorderSavedMusic
super.init(navigationBarPresentationData: nil) super.init(navigationBarPresentationData: nil)
@ -146,18 +140,6 @@ final class OverlayAudioPlayerControllerImpl: ViewController, OverlayAudioPlayer
strongSelf.context.sharedContext.openSearch(filter: .music, query: artist) strongSelf.context.sharedContext.openSearch(filter: .music, query: artist)
strongSelf.dismiss() strongSelf.dismiss()
} }
}, updateMusicSaved: { [weak self] file, isSaved in
if let self {
if let updateMusicSaved = self.updateMusicSaved {
updateMusicSaved(file, isSaved)
} else {
if isSaved {
let _ = self.context.engine.peers.addSavedMusic(file: file).start()
} else {
let _ = self.context.engine.peers.removeSavedMusic(file: file).start()
}
}
}
}, },
getParentController: { [weak self] in getParentController: { [weak self] in
return self return self

View File

@ -27,7 +27,6 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
private let requestDismiss: () -> Void private let requestDismiss: () -> Void
private let requestShare: (ShareControllerSubject) -> Void private let requestShare: (ShareControllerSubject) -> Void
private let requestSearchByArtist: (String) -> Void private let requestSearchByArtist: (String) -> Void
private let updateMusicSaved: (FileMediaReference, Bool) -> Void
private let playlistLocation: SharedMediaPlaylistLocation? private let playlistLocation: SharedMediaPlaylistLocation?
private let isGlobalSearch: Bool private let isGlobalSearch: Bool
@ -68,7 +67,6 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
requestDismiss: @escaping () -> Void, requestDismiss: @escaping () -> Void,
requestShare: @escaping (ShareControllerSubject) -> Void, requestShare: @escaping (ShareControllerSubject) -> Void,
requestSearchByArtist: @escaping (String) -> Void, requestSearchByArtist: @escaping (String) -> Void,
updateMusicSaved: @escaping (FileMediaReference, Bool) -> Void,
getParentController: @escaping () -> ViewController? getParentController: @escaping () -> ViewController?
) { ) {
self.context = context self.context = context
@ -79,7 +77,6 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
self.requestShare = requestShare self.requestShare = requestShare
self.requestSearchByArtist = requestSearchByArtist self.requestSearchByArtist = requestSearchByArtist
self.playlistLocation = playlistLocation self.playlistLocation = playlistLocation
self.updateMusicSaved = updateMusicSaved
self.getParentController = getParentController self.getParentController = getParentController
if let playlistLocation = playlistLocation as? PeerMessagesPlaylistLocation, case let .custom(messages, canReorder, at, loadMore) = playlistLocation.effectiveLocation(context: context) { if let playlistLocation = playlistLocation as? PeerMessagesPlaylistLocation, case let .custom(messages, canReorder, at, loadMore) = playlistLocation.effectiveLocation(context: context) {
@ -456,10 +453,10 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
} }
private func setupReordering() { private func setupReordering() {
guard let controller = self.getParentController() as? OverlayAudioPlayerControllerImpl, let reorderSavedMusic = controller.reorderSavedMusic, case let .peer(peerId) = self.chatLocation else { guard let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(savedMusicContext, _, canReorder) = playlistLocation, canReorder else {
return return
} }
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: savedMusicContext.peerId))
|> deliverOnMainQueue).start(next: { [weak self] peer in |> deliverOnMainQueue).start(next: { [weak self] peer in
guard let self, let peer = peer.flatMap({ PeerReference($0._asPeer()) }) else { guard let self, let peer = peer.flatMap({ PeerReference($0._asPeer()) }) else {
return return
@ -491,7 +488,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
return .single(false) return .single(false)
} }
reorderSavedMusic(.savedMusic(peer: peer, media: fromFile), toFile.flatMap { .savedMusic(peer: peer, media: $0) }) let _ = savedMusicContext.addMusic(file: .savedMusic(peer: peer, media: fromFile), afterFile: toFile.flatMap { .savedMusic(peer: peer, media: $0) }).start()
return .single(true) return .single(true)
} }
@ -538,6 +535,25 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
self.getParentController()?.present(controller, in: .window(.root)) self.getParentController()?.present(controller, in: .window(.root))
} }
private func updateMusicSaved(file: FileMediaReference, isSaved: Bool) {
guard let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(savedMusicContext, _, _) = playlistLocation else {
return
}
if savedMusicContext.peerId == self.context.account.peerId {
if isSaved {
let _ = savedMusicContext.addMusic(file: file).start()
} else {
let _ = savedMusicContext.removeMusic(file: file).start()
}
} else {
if isSaved {
let _ = self.context.engine.peers.addSavedMusic(file: file).start()
} else {
let _ = self.context.engine.peers.removeSavedMusic(file: file).start()
}
}
}
func addToSavedMusic(file: FileMediaReference) { func addToSavedMusic(file: FileMediaReference) {
self.dismissAllTooltips() self.dismissAllTooltips()
@ -585,7 +601,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
) )
self.getParentController()?.present(controller, in: .window(.root)) self.getParentController()?.present(controller, in: .window(.root))
self.updateMusicSaved(file, true) self.updateMusicSaved(file: file, isSaved: true)
} }
func removeFromSavedMusic(file: FileMediaReference) { func removeFromSavedMusic(file: FileMediaReference) {
@ -618,7 +634,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
self.getParentController()?.present(controller, in: .window(.root)) self.getParentController()?.present(controller, in: .window(.root))
} }
self.updateMusicSaved(file, false) self.updateMusicSaved(file: file, isSaved: false)
} }
private var isSaved: Bool? { private var isSaved: Bool? {
@ -1106,8 +1122,10 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
// ) // )
var canDelete = false var canDelete = false
if case let .custom(_, _, _, _, canReorder, _) = self.source, canReorder { if case .custom = self.source {
canDelete = true if self.savedIds?.contains(file.fileId.id) == true {
canDelete = true
}
} else if let peer = message.peers[message.id.peerId] { } else if let peer = message.peers[message.id.peerId] {
if peer is TelegramUser || peer is TelegramSecretChat { if peer is TelegramUser || peer is TelegramSecretChat {
canDelete = true canDelete = true

View File

@ -1786,8 +1786,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}) })
} }
public func makeOverlayAudioPlayerController(context: AccountContext, chatLocation: ChatLocation, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation?, parentNavigationController: NavigationController?, updateMusicSaved: ((FileMediaReference, Bool) -> Void)?, reorderSavedMusic: ((FileMediaReference, FileMediaReference?) -> Void)?) -> ViewController & OverlayAudioPlayerController { public func makeOverlayAudioPlayerController(context: AccountContext, chatLocation: ChatLocation, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, playlistLocation: SharedMediaPlaylistLocation?, parentNavigationController: NavigationController?) -> ViewController & OverlayAudioPlayerController {
return OverlayAudioPlayerControllerImpl(context: context, chatLocation: chatLocation, type: type, initialMessageId: initialMessageId, initialOrder: initialOrder, playlistLocation: playlistLocation, parentNavigationController: parentNavigationController, updateMusicSaved: updateMusicSaved, reorderSavedMusic: reorderSavedMusic) return OverlayAudioPlayerControllerImpl(context: context, chatLocation: chatLocation, type: type, initialMessageId: initialMessageId, initialOrder: initialOrder, playlistLocation: playlistLocation, parentNavigationController: parentNavigationController)
} }
public func makeTempAccountContext(account: Account) -> AccountContext { public func makeTempAccountContext(account: Account) -> AccountContext {