mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-04 18:41:00 +00:00
Merge commit 'dfe1dbd825ea321903a7d63e19577109c2cfa35b'
This commit is contained in:
commit
5d3e3b9ebe
@ -6339,3 +6339,25 @@ Sorry for the inconvenience.";
|
|||||||
"VoiceChat.UnpinVideo" = "Unpin Video";
|
"VoiceChat.UnpinVideo" = "Unpin Video";
|
||||||
|
|
||||||
"Notification.VoiceChatScheduled" = "Voice chat scheduled";
|
"Notification.VoiceChatScheduled" = "Voice chat scheduled";
|
||||||
|
|
||||||
|
"VoiceChat.EditStartTime" = "Edit Start Time";
|
||||||
|
"VoiceChat.StartsIn" = "Starts in";
|
||||||
|
|
||||||
|
"VoiceChat.SetReminder" = "Set Reminder";
|
||||||
|
"VoiceChat.CancelReminder" = "Cancel Reminder";
|
||||||
|
|
||||||
|
"VoiceChat.ShareShort" = "share";
|
||||||
|
|
||||||
|
"ChannelInfo.ScheduleVoiceChat" = "Schedule Voice Chat";
|
||||||
|
|
||||||
|
"ScheduleVoiceChat.Title" = "Schedule Voice Chat";
|
||||||
|
"ScheduleVoiceChat.GroupText" = "The members of the group will be notified that the voice chat will start in %@.";
|
||||||
|
"ScheduleVoiceChat.ChannelText" = "The members of the channel will be notified that the voice chat will start in %@.";
|
||||||
|
|
||||||
|
"ScheduleVoiceChat.ScheduleToday" = "Remind today at %@";
|
||||||
|
"ScheduleVoiceChat.ScheduleTomorrow" = "Remind tomorrow at %@";
|
||||||
|
"ScheduleVoiceChat.ScheduleOn" = "Remind on %@ at %@";
|
||||||
|
|
||||||
|
"Conversation.ScheduledVoiceChat" = "Scheduled Voice Chat";
|
||||||
|
"Conversation.ScheduledVoiceChatStartsInShort" = "Voice chat starts %@";
|
||||||
|
"Conversation.ScheduledVoiceChatStartsInShort" = "Starts %@";
|
||||||
|
@ -12,8 +12,10 @@ import AlertUI
|
|||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import CountrySelectionUI
|
import CountrySelectionUI
|
||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
|
import CoreTelephony
|
||||||
|
import MessageUI
|
||||||
|
|
||||||
final class ChangePhoneNumberController: ViewController {
|
final class ChangePhoneNumberController: ViewController, MFMailComposeViewControllerDelegate {
|
||||||
private var controllerNode: ChangePhoneNumberControllerNode {
|
private var controllerNode: ChangePhoneNumberControllerNode {
|
||||||
return self.displayNode as! ChangePhoneNumberControllerNode
|
return self.displayNode as! ChangePhoneNumberControllerNode
|
||||||
}
|
}
|
||||||
@ -133,6 +135,9 @@ final class ChangePhoneNumberController: ViewController {
|
|||||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
let text: String
|
let text: String
|
||||||
|
var actions: [TextAlertAction] = [
|
||||||
|
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
|
||||||
|
]
|
||||||
switch error {
|
switch error {
|
||||||
case .limitExceeded:
|
case .limitExceeded:
|
||||||
text = presentationData.strings.Login_CodeFloodError
|
text = presentationData.strings.Login_CodeFloodError
|
||||||
@ -140,11 +145,26 @@ final class ChangePhoneNumberController: ViewController {
|
|||||||
text = presentationData.strings.Login_InvalidPhoneError
|
text = presentationData.strings.Login_InvalidPhoneError
|
||||||
case .phoneNumberOccupied:
|
case .phoneNumberOccupied:
|
||||||
text = presentationData.strings.ChangePhone_ErrorOccupied(formatPhoneNumber(phoneNumber)).0
|
text = presentationData.strings.ChangePhone_ErrorOccupied(formatPhoneNumber(phoneNumber)).0
|
||||||
|
case .phoneBanned:
|
||||||
|
text = presentationData.strings.Login_PhoneBannedError
|
||||||
|
actions.append(TextAlertAction(type: .defaultAction, title: presentationData.strings.Login_PhoneNumberHelp, action: { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let formattedNumber = formatPhoneNumber(number)
|
||||||
|
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
|
||||||
|
let systemVersion = UIDevice.current.systemVersion
|
||||||
|
let locale = Locale.current.identifier
|
||||||
|
let carrier = CTCarrier()
|
||||||
|
let mnc = carrier.mobileNetworkCode ?? "none"
|
||||||
|
|
||||||
|
strongSelf.presentEmailComposeController(address: "login@stel.com", subject: presentationData.strings.Login_PhoneBannedEmailSubject(formattedNumber).0, body: presentationData.strings.Login_PhoneBannedEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).0)
|
||||||
|
}))
|
||||||
case .generic:
|
case .generic:
|
||||||
text = presentationData.strings.Login_UnknownError
|
text = presentationData.strings.Login_UnknownError
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: actions), in: .window(.root))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
@ -152,4 +172,22 @@ final class ChangePhoneNumberController: ViewController {
|
|||||||
self.controllerNode.animateError()
|
self.controllerNode.animateError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func presentEmailComposeController(address: String, subject: String, body: String) {
|
||||||
|
if MFMailComposeViewController.canSendMail() {
|
||||||
|
let composeController = MFMailComposeViewController()
|
||||||
|
composeController.setToRecipients([address])
|
||||||
|
composeController.setSubject(subject)
|
||||||
|
composeController.setMessageBody(body, isHTML: false)
|
||||||
|
composeController.mailComposeDelegate = self
|
||||||
|
|
||||||
|
self.view.window?.rootViewController?.present(composeController, animated: true, completion: nil)
|
||||||
|
} else {
|
||||||
|
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||||
|
controller.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1322,7 +1322,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
maxBioLength = 100
|
maxBioLength = 100
|
||||||
}
|
}
|
||||||
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_EditBioTitle, text: presentationData.strings.VoiceChat_EditBioText, placeholder: presentationData.strings.VoiceChat_EditBioPlaceholder, doneButtonTitle: presentationData.strings.VoiceChat_EditBioSave, value: entry.about, maxLength: maxBioLength, apply: { bio in
|
let controller = voiceChatTitleEditController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_EditBioTitle, text: presentationData.strings.VoiceChat_EditBioText, placeholder: presentationData.strings.VoiceChat_EditBioPlaceholder, doneButtonTitle: presentationData.strings.VoiceChat_EditBioSave, value: entry.about, maxLength: maxBioLength, apply: { bio in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self, let bio = bio {
|
||||||
let _ = (updateAbout(account: strongSelf.context.account, about: bio)
|
let _ = (updateAbout(account: strongSelf.context.account, about: bio)
|
||||||
|> `catch` { _ -> Signal<Void, NoError> in
|
|> `catch` { _ -> Signal<Void, NoError> in
|
||||||
return .complete()
|
return .complete()
|
||||||
@ -1345,8 +1345,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
f(.default)
|
f(.default)
|
||||||
|
|
||||||
Queue.mainQueue().after(0.1) {
|
Queue.mainQueue().after(0.1) {
|
||||||
let controller = voiceChatUserNameController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_ChangeNameTitle, firstNamePlaceholder: presentationData.strings.UserInfo_FirstNamePlaceholder, lastNamePlaceholder: presentationData.strings.UserInfo_LastNamePlaceholder, doneButtonTitle: presentationData.strings.VoiceChat_EditBioSave, firstName: peer.firstName, lastName: peer.lastName, maxLength: 128, apply: { firstName, lastName in
|
let controller = voiceChatUserNameController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, forceTheme: strongSelf.darkTheme, title: presentationData.strings.VoiceChat_ChangeNameTitle, firstNamePlaceholder: presentationData.strings.UserInfo_FirstNamePlaceholder, lastNamePlaceholder: presentationData.strings.UserInfo_LastNamePlaceholder, doneButtonTitle: presentationData.strings.VoiceChat_EditBioSave, firstName: peer.firstName, lastName: peer.lastName, maxLength: 128, apply: { firstAndLastName in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self, let (firstName, lastName) = firstAndLastName {
|
||||||
let _ = updateAccountPeerName(account: context.account, firstName: firstName, lastName: lastName).start()
|
let _ = updateAccountPeerName(account: context.account, firstName: firstName, lastName: lastName).start()
|
||||||
|
|
||||||
strongSelf.presentUndoOverlay(content: .info(text: strongSelf.presentationData.strings.VoiceChat_EditNameSuccess), action: { _ in return false })
|
strongSelf.presentUndoOverlay(content: .info(text: strongSelf.presentationData.strings.VoiceChat_EditNameSuccess), action: { _ in return false })
|
||||||
@ -1994,6 +1994,15 @@ public final class VoiceChatController: ViewController {
|
|||||||
c.setItems(strongSelf.contextMenuPermissionItems())
|
c.setItems(strongSelf.contextMenuPermissionItems())
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_EditPermissions, icon: { theme -> UIImage? in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.actionSheet.primaryTextColor)
|
||||||
|
}, action: { c, _ in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.setItems(strongSelf.contextMenuPermissionItems())
|
||||||
|
})))
|
||||||
|
|
||||||
if let inviteLinks = inviteLinks {
|
if let inviteLinks = inviteLinks {
|
||||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_Share, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_Share, icon: { theme in
|
||||||
@ -3748,7 +3757,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in
|
mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in
|
||||||
if let image = image, let asset = asset {
|
if let image = image, let asset = asset {
|
||||||
completion()
|
completion()
|
||||||
// self?.updateProfileVideo(image, asset: asset, adjustments: adjustments)
|
self?.updateProfileVideo(image, asset: asset, adjustments: adjustments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mixin.didFinishWithDelete = {
|
mixin.didFinishWithDelete = {
|
||||||
@ -3756,55 +3765,42 @@ public final class VoiceChatController: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// let proceed = {
|
let proceed = {
|
||||||
// if let item = item {
|
let _ = strongSelf.currentAvatarMixin.swap(nil)
|
||||||
// strongSelf.deleteAvatar(item, remove: false)
|
let postbox = strongSelf.context.account.postbox
|
||||||
// }
|
strongSelf.updateAvatarDisposable.set((updatePeerPhoto(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, stateManager: strongSelf.context.account.stateManager, accountPeerId: strongSelf.context.account.peerId, peerId: peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in
|
||||||
//
|
return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
|
||||||
// let _ = strongSelf.currentAvatarMixin.swap(nil)
|
})
|
||||||
// if let _ = peer.smallProfileImage {
|
|> deliverOnMainQueue).start(next: { result in
|
||||||
// strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
|
guard let strongSelf = self else {
|
||||||
// if let (layout, navigationHeight) = strongSelf.validLayout {
|
return
|
||||||
// strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
|
}
|
||||||
// }
|
switch result {
|
||||||
// }
|
case .complete:
|
||||||
// let postbox = strongSelf.context.account.postbox
|
break
|
||||||
// strongSelf.updateAvatarDisposable.set((updatePeerPhoto(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, stateManager: strongSelf.context.account.stateManager, accountPeerId: strongSelf.context.account.peerId, peerId: strongSelf.peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in
|
case .progress:
|
||||||
// return mapResourceToAvatarSizes(postbox: postbox, resource: resource, representations: representations)
|
break
|
||||||
// })
|
}
|
||||||
// |> deliverOnMainQueue).start(next: { result in
|
}))
|
||||||
// guard let strongSelf = self else {
|
}
|
||||||
// return
|
|
||||||
// }
|
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||||
// switch result {
|
let items: [ActionSheetItem] = [
|
||||||
// case .complete:
|
ActionSheetButtonItem(title: presentationData.strings.Settings_RemoveConfirmation, color: .destructive, action: { [weak actionSheet] in
|
||||||
// strongSelf.state = strongSelf.state.withUpdatingAvatar(nil)
|
actionSheet?.dismissAnimated()
|
||||||
// if let (layout, navigationHeight) = strongSelf.validLayout {
|
proceed()
|
||||||
// strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
|
})
|
||||||
// }
|
]
|
||||||
// case .progress:
|
|
||||||
// break
|
actionSheet.setItemGroups([
|
||||||
// }
|
ActionSheetItemGroup(items: items),
|
||||||
// }))
|
ActionSheetItemGroup(items: [
|
||||||
// }
|
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||||
//
|
actionSheet?.dismissAnimated()
|
||||||
// let actionSheet = ActionSheetController(presentationData: presentationData)
|
})
|
||||||
// let items: [ActionSheetItem] = [
|
])
|
||||||
// ActionSheetButtonItem(title: presentationData.strings.Settings_RemoveConfirmation, color: .destructive, action: { [weak actionSheet] in
|
])
|
||||||
// actionSheet?.dismissAnimated()
|
strongSelf.controller?.present(actionSheet, in: .window(.root))
|
||||||
// proceed()
|
|
||||||
// })
|
|
||||||
// ]
|
|
||||||
//
|
|
||||||
// actionSheet.setItemGroups([
|
|
||||||
// ActionSheetItemGroup(items: items),
|
|
||||||
// ActionSheetItemGroup(items: [
|
|
||||||
// ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
|
||||||
// actionSheet?.dismissAnimated()
|
|
||||||
// })
|
|
||||||
// ])
|
|
||||||
// ])
|
|
||||||
// strongSelf.controller?.present(actionSheet, in: .window(.root))
|
|
||||||
}
|
}
|
||||||
mixin.didDismiss = { [weak legacyController] in
|
mixin.didDismiss = { [weak legacyController] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -3856,6 +3852,125 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? ([], nil), invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
|
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? ([], nil), invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateProfileVideo(_ image: UIImage, asset: Any?, adjustments: TGVideoEditAdjustments?) {
|
||||||
|
guard let data = image.jpegData(compressionQuality: 0.6), let peerId = self.callState?.myPeerId else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let photoResource = LocalFileMediaResource(fileId: arc4random64())
|
||||||
|
self.context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
|
||||||
|
let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil)
|
||||||
|
|
||||||
|
self.currentUpdatingAvatar = representation
|
||||||
|
self.updateAvatarPromise.set(.single((representation, 0.0)))
|
||||||
|
|
||||||
|
var videoStartTimestamp: Double? = nil
|
||||||
|
if let adjustments = adjustments, adjustments.videoStartValue > 0.0 {
|
||||||
|
videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue
|
||||||
|
}
|
||||||
|
|
||||||
|
let account = self.context.account
|
||||||
|
let signal = Signal<TelegramMediaResource, UploadPeerPhotoError> { [weak self] subscriber in
|
||||||
|
let entityRenderer: LegacyPaintEntityRenderer? = adjustments.flatMap { adjustments in
|
||||||
|
if let paintingData = adjustments.paintingData, paintingData.hasAnimation {
|
||||||
|
return LegacyPaintEntityRenderer(account: account, adjustments: adjustments)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let uploadInterface = LegacyLiveUploadInterface(account: account)
|
||||||
|
let signal: SSignal
|
||||||
|
if let asset = asset as? AVAsset {
|
||||||
|
signal = TGMediaVideoConverter.convert(asset, adjustments: adjustments, watcher: uploadInterface, entityRenderer: entityRenderer)!
|
||||||
|
} else if let url = asset as? URL, let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer {
|
||||||
|
let durationSignal: SSignal = SSignal(generator: { subscriber in
|
||||||
|
let disposable = (entityRenderer.duration()).start(next: { duration in
|
||||||
|
subscriber?.putNext(duration)
|
||||||
|
subscriber?.putCompletion()
|
||||||
|
})
|
||||||
|
|
||||||
|
return SBlockDisposable(block: {
|
||||||
|
disposable.dispose()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
signal = durationSignal.map(toSignal: { duration -> SSignal? in
|
||||||
|
if let duration = duration as? Double {
|
||||||
|
return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)!
|
||||||
|
} else {
|
||||||
|
return SSignal.single(nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
signal = SSignal.complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
let signalDisposable = signal.start(next: { next in
|
||||||
|
if let result = next as? TGMediaVideoConversionResult {
|
||||||
|
if let image = result.coverImage, let data = image.jpegData(compressionQuality: 0.7) {
|
||||||
|
account.postbox.mediaBox.storeResourceData(photoResource.id, data: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let timestamp = videoStartTimestamp {
|
||||||
|
videoStartTimestamp = max(0.0, min(timestamp, result.duration - 0.05))
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = stat()
|
||||||
|
if stat(result.fileURL.path, &value) == 0 {
|
||||||
|
if let data = try? Data(contentsOf: result.fileURL) {
|
||||||
|
let resource: TelegramMediaResource
|
||||||
|
if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult {
|
||||||
|
resource = LocalFileMediaResource(fileId: liveUploadData.id)
|
||||||
|
} else {
|
||||||
|
resource = LocalFileMediaResource(fileId: arc4random64())
|
||||||
|
}
|
||||||
|
account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
|
||||||
|
subscriber.putNext(resource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subscriber.putCompletion()
|
||||||
|
} else if let strongSelf = self, let progress = next as? NSNumber {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
strongSelf.updateAvatarPromise.set(.single((representation, Float(truncating: progress) * 0.25)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, error: { _ in
|
||||||
|
}, completed: nil)
|
||||||
|
|
||||||
|
let disposable = ActionDisposable {
|
||||||
|
signalDisposable?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateAvatarDisposable.set((signal
|
||||||
|
|> mapToSignal { videoResource -> Signal<UpdatePeerPhotoStatus, UploadPeerPhotoError> in
|
||||||
|
if peerId.namespace == Namespaces.Peer.CloudUser {
|
||||||
|
return updateAccountPhoto(account: account, resource: photoResource, videoResource: videoResource, videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { resource, representations in
|
||||||
|
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: peerId, photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: photoResource), video: uploadedPeerVideo(postbox: account.postbox, network: account.network, messageMediaPreuploadManager: account.messageMediaPreuploadManager, resource: videoResource) |> map(Optional.init), videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { resource, representations in
|
||||||
|
return mapResourceToAvatarSizes(postbox: account.postbox, resource: resource, representations: representations)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch result {
|
||||||
|
case .complete:
|
||||||
|
strongSelf.updateAvatarPromise.set(.single(nil))
|
||||||
|
case let .progress(value):
|
||||||
|
strongSelf.updateAvatarPromise.set(.single((representation, 0.25 + value * 0.75)))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let sharedContext: SharedAccountContext
|
private let sharedContext: SharedAccountContext
|
||||||
|
@ -710,7 +710,7 @@ private final class VoiceChatUserNameEditAlertContentNode: AlertContentNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func voiceChatUserNameController(sharedContext: SharedAccountContext, account: Account, forceTheme: PresentationTheme?, title: String, firstNamePlaceholder: String, lastNamePlaceholder: String, doneButtonTitle: String? = nil, firstName: String?, lastName: String?, maxLength: Int, apply: @escaping (String, String) -> Void) -> AlertController {
|
func voiceChatUserNameController(sharedContext: SharedAccountContext, account: Account, forceTheme: PresentationTheme?, title: String, firstNamePlaceholder: String, lastNamePlaceholder: String, doneButtonTitle: String? = nil, firstName: String?, lastName: String?, maxLength: Int, apply: @escaping ((String, String)?) -> Void) -> AlertController {
|
||||||
var presentationData = sharedContext.currentPresentationData.with { $0 }
|
var presentationData = sharedContext.currentPresentationData.with { $0 }
|
||||||
if let forceTheme = forceTheme {
|
if let forceTheme = forceTheme {
|
||||||
presentationData = presentationData.withUpdated(theme: forceTheme)
|
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||||
@ -733,14 +733,24 @@ func voiceChatUserNameController(sharedContext: SharedAccountContext, account: A
|
|||||||
guard let contentNode = contentNode else {
|
guard let contentNode = contentNode else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dismissImpl?(true)
|
|
||||||
|
|
||||||
let previousFirstName = firstName ?? ""
|
let previousFirstName = firstName ?? ""
|
||||||
let previousLastName = firstName ?? ""
|
let previousLastName = lastName ?? ""
|
||||||
|
|
||||||
let newFirstName = contentNode.firstName.trimmingCharacters(in: .whitespacesAndNewlines)
|
let newFirstName = contentNode.firstName.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
let newLastName = contentNode.lastName.trimmingCharacters(in: .whitespacesAndNewlines)
|
let newLastName = contentNode.lastName.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
apply(newFirstName, newLastName)
|
|
||||||
|
if newFirstName.isEmpty {
|
||||||
|
contentNode.animateError()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dismissImpl?(true)
|
||||||
|
|
||||||
|
if previousFirstName != newFirstName || previousLastName != newLastName {
|
||||||
|
apply((newFirstName, newLastName))
|
||||||
|
} else {
|
||||||
|
apply(nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
|
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
|
||||||
|
@ -33,6 +33,7 @@ public enum RequestChangeAccountPhoneNumberVerificationError {
|
|||||||
case invalidPhoneNumber
|
case invalidPhoneNumber
|
||||||
case limitExceeded
|
case limitExceeded
|
||||||
case phoneNumberOccupied
|
case phoneNumberOccupied
|
||||||
|
case phoneBanned
|
||||||
case generic
|
case generic
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +46,8 @@ public func requestChangeAccountPhoneNumberVerification(account: Account, phoneN
|
|||||||
return .invalidPhoneNumber
|
return .invalidPhoneNumber
|
||||||
} else if error.errorDescription == "PHONE_NUMBER_OCCUPIED" {
|
} else if error.errorDescription == "PHONE_NUMBER_OCCUPIED" {
|
||||||
return .phoneNumberOccupied
|
return .phoneNumberOccupied
|
||||||
|
} else if error.errorDescription == "PHONE_NUMBER_BANNED" {
|
||||||
|
return .phoneBanned
|
||||||
} else {
|
} else {
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -54,7 +54,7 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
|||||||
let referenceNode: ContextReferenceContentNode
|
let referenceNode: ContextReferenceContentNode
|
||||||
let containerNode: ContextControllerSourceNode
|
let containerNode: ContextControllerSourceNode
|
||||||
private let backgroundNode: ASImageNode
|
private let backgroundNode: ASImageNode
|
||||||
private let backgroundWithIconNode: ASImageNode
|
private let iconNode: ASImageNode
|
||||||
private let textNode: ImmediateTextNode
|
private let textNode: ImmediateTextNode
|
||||||
private var animationNode: AnimationNode?
|
private var animationNode: AnimationNode?
|
||||||
|
|
||||||
@ -73,11 +73,10 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
|||||||
self.backgroundNode = ASImageNode()
|
self.backgroundNode = ASImageNode()
|
||||||
self.backgroundNode.displaysAsynchronously = false
|
self.backgroundNode.displaysAsynchronously = false
|
||||||
self.backgroundNode.displayWithoutProcessing = true
|
self.backgroundNode.displayWithoutProcessing = true
|
||||||
self.backgroundNode.isHidden = true
|
|
||||||
|
|
||||||
self.backgroundWithIconNode = ASImageNode()
|
self.iconNode = ASImageNode()
|
||||||
self.backgroundWithIconNode.displaysAsynchronously = false
|
self.iconNode.displaysAsynchronously = false
|
||||||
self.backgroundWithIconNode.displayWithoutProcessing = true
|
self.iconNode.displayWithoutProcessing = true
|
||||||
|
|
||||||
self.textNode = ImmediateTextNode()
|
self.textNode = ImmediateTextNode()
|
||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = false
|
||||||
@ -88,7 +87,7 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
|||||||
|
|
||||||
self.containerNode.addSubnode(self.referenceNode)
|
self.containerNode.addSubnode(self.referenceNode)
|
||||||
self.referenceNode.addSubnode(self.backgroundNode)
|
self.referenceNode.addSubnode(self.backgroundNode)
|
||||||
self.referenceNode.addSubnode(self.backgroundWithIconNode)
|
self.referenceNode.addSubnode(self.iconNode)
|
||||||
self.addSubnode(self.containerNode)
|
self.addSubnode(self.containerNode)
|
||||||
self.addSubnode(self.textNode)
|
self.addSubnode(self.textNode)
|
||||||
|
|
||||||
@ -126,13 +125,12 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
|||||||
func update(size: CGSize, text: String, icon: PeerInfoHeaderButtonIcon, isActive: Bool, isExpanded: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
func update(size: CGSize, text: String, icon: PeerInfoHeaderButtonIcon, isActive: Bool, isExpanded: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
||||||
let previousIcon = self.icon
|
let previousIcon = self.icon
|
||||||
let iconUpdated = self.icon != icon
|
let iconUpdated = self.icon != icon
|
||||||
if self.theme != presentationData.theme || self.icon != icon || self.isActive != isActive {
|
let isActiveUpdated = self.isActive != isActive
|
||||||
|
self.isActive = isActive
|
||||||
|
if self.theme != presentationData.theme || self.icon != icon {
|
||||||
self.theme = presentationData.theme
|
self.theme = presentationData.theme
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
|
|
||||||
let isActiveUpdated = self.isActive != isActive
|
|
||||||
self.isActive = isActive
|
|
||||||
|
|
||||||
var isGestureEnabled = false
|
var isGestureEnabled = false
|
||||||
if [.mute, .voiceChat, .more].contains(icon) {
|
if [.mute, .voiceChat, .more].contains(icon) {
|
||||||
isGestureEnabled = true
|
isGestureEnabled = true
|
||||||
@ -195,17 +193,13 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
animationNode = AnimationNode(animation: animationName, colors: colors, scale: 1.0)
|
animationNode = AnimationNode(animation: animationName, colors: colors, scale: 1.0)
|
||||||
self.addSubnode(animationNode)
|
self.referenceNode.addSubnode(animationNode)
|
||||||
self.animationNode = animationNode
|
self.animationNode = animationNode
|
||||||
}
|
}
|
||||||
animationNode.frame = CGRect(origin: CGPoint(), size: size)
|
animationNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||||
self.backgroundWithIconNode.isHidden = true
|
|
||||||
self.backgroundNode.isHidden = false
|
|
||||||
} else if let animationNode = self.animationNode {
|
} else if let animationNode = self.animationNode {
|
||||||
self.animationNode = nil
|
self.animationNode = nil
|
||||||
animationNode.removeFromSupernode()
|
animationNode.removeFromSupernode()
|
||||||
self.backgroundWithIconNode.isHidden = false
|
|
||||||
self.backgroundNode.isHidden = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if playOnce {
|
if playOnce {
|
||||||
@ -213,35 +207,10 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
|||||||
} else if seekToEnd {
|
} else if seekToEnd {
|
||||||
self.animationNode?.seekToEnd()
|
self.animationNode?.seekToEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
if isActiveUpdated, !self.containerNode.alpha.isZero {
|
self.backgroundNode.image = generateFilledCircleImage(diameter: 40.0, color: presentationData.theme.list.itemAccentColor)
|
||||||
let backgroundNode = !self.backgroundNode.isHidden ? self.backgroundNode : self.backgroundWithIconNode
|
self.iconNode.image = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in
|
||||||
if let snapshotView = backgroundNode.view.snapshotContentTree() {
|
|
||||||
snapshotView.frame = backgroundNode.view.frame
|
|
||||||
if let animationNode = self.animationNode {
|
|
||||||
self.view.insertSubview(snapshotView, belowSubview: animationNode.view)
|
|
||||||
} else {
|
|
||||||
self.view.addSubview(snapshotView)
|
|
||||||
}
|
|
||||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
|
||||||
snapshotView?.removeFromSuperview()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if !isExpanded, let snapshotView = self.textNode.view.snapshotContentTree() {
|
|
||||||
snapshotView.frame = self.textNode.view.frame
|
|
||||||
self.view.addSubview(snapshotView)
|
|
||||||
|
|
||||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
|
||||||
snapshotView?.removeFromSuperview()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.backgroundNode.image = generateFilledCircleImage(diameter: 40.0, color: isActive ? presentationData.theme.list.itemAccentColor : presentationData.theme.list.itemDisabledTextColor)
|
|
||||||
self.backgroundWithIconNode.image = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in
|
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
context.setFillColor(isActive ? presentationData.theme.list.itemAccentColor.cgColor : presentationData.theme.list.itemDisabledTextColor.cgColor)
|
|
||||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
|
||||||
context.setBlendMode(.normal)
|
context.setBlendMode(.normal)
|
||||||
context.setFillColor(presentationData.theme.list.itemCheckColors.foregroundColor.cgColor)
|
context.setFillColor(presentationData.theme.list.itemCheckColors.foregroundColor.cgColor)
|
||||||
let imageName: String?
|
let imageName: String?
|
||||||
@ -275,15 +244,22 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(12.0), textColor: isActive ? presentationData.theme.list.itemAccentColor : presentationData.theme.list.itemDisabledTextColor)
|
let alpha: CGFloat = isActive ? 1.0 : 0.3
|
||||||
|
if isActiveUpdated, !self.containerNode.alpha.isZero {
|
||||||
|
let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
||||||
|
alphaTransition.updateAlpha(node: self.backgroundNode, alpha: isActive ? 1.0 : 0.3)
|
||||||
|
alphaTransition.updateAlpha(node: self.textNode, alpha: isActive ? 1.0 : 0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(12.0), textColor: presentationData.theme.list.itemAccentColor)
|
||||||
self.accessibilityLabel = text
|
self.accessibilityLabel = text
|
||||||
let titleSize = self.textNode.updateLayout(CGSize(width: 120.0, height: .greatestFiniteMagnitude))
|
let titleSize = self.textNode.updateLayout(CGSize(width: 120.0, height: .greatestFiniteMagnitude))
|
||||||
|
|
||||||
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
transition.updateFrame(node: self.backgroundWithIconNode, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
transition.updateFrameAdditiveToCenter(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: size.height + 6.0), size: titleSize))
|
transition.updateFrameAdditiveToCenter(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: size.height + 6.0), size: titleSize))
|
||||||
transition.updateAlpha(node: self.textNode, alpha: isExpanded ? 0.0 : 1.0)
|
transition.updateAlpha(node: self.textNode, alpha: isExpanded ? 0.0 : alpha)
|
||||||
|
|
||||||
self.referenceNode.frame = self.containerNode.bounds
|
self.referenceNode.frame = self.containerNode.bounds
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user