diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index c84880d009..92f3338d2f 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1419,7 +1419,7 @@ public protocol SharedAccountContext: AnyObject { func openCreateGroupCallUI(context: AccountContext, peerIds: [EnginePeer.Id], parentController: ViewController) - func makeNewContactScreen(context: AccountContext, peer: EnginePeer?, phoneNumber: String?, completion: @escaping (EnginePeer?, DeviceContactStableId?, DeviceContactExtendedData?) -> Void) -> ViewController + func makeNewContactScreen(context: AccountContext, peer: EnginePeer?, phoneNumber: String?, shareViaException: Bool, completion: @escaping (EnginePeer?, DeviceContactStableId?, DeviceContactExtendedData?) -> Void) -> ViewController func navigateToCurrentCall() var hasOngoingCall: ValuePromise { get } diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index 8f6789ff3a..425ca2b7ab 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -745,6 +745,7 @@ public class ContactsController: ViewController { context: strongSelf.context, peer: nil, phoneNumber: nil, + shareViaException: false, completion: { [weak self] peer, stableId, contactData in guard let strongSelf = self else { return diff --git a/submodules/PeerInfoUI/Sources/UserInfoController.swift b/submodules/PeerInfoUI/Sources/UserInfoController.swift index ea1f40bda9..6f8b7694b2 100644 --- a/submodules/PeerInfoUI/Sources/UserInfoController.swift +++ b/submodules/PeerInfoUI/Sources/UserInfoController.swift @@ -59,12 +59,11 @@ public func openAddPersonContactImpl(context: AccountContext, updatedPresentatio shareViaException = statusSettings.contains(.addExceptionWhenAddingContact) } - - let _ = shareViaException let controller = context.sharedContext.makeNewContactScreen( context: context, peer: peer, phoneNumber: user.phone, + shareViaException: shareViaException, completion: { peer, _, _ in if let peer { completion() @@ -75,14 +74,5 @@ public func openAddPersonContactImpl(context: AccountContext, updatedPresentatio } ) pushController(controller) - -// pushController(deviceContactInfoController(context: ShareControllerAppAccountContext(context: context), environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext), updatedPresentationData: updatedPresentationData, subject: .create(peer: user, contactData: contactData, isSharing: true, shareViaException: shareViaException, completion: { peer, stableId, contactData in -// if let peer = peer as? TelegramUser { -// completion() -// -// let presentationData = context.sharedContext.currentPresentationData.with { $0 } -// present(OverlayStatusController(theme: presentationData.theme, type: .genericSuccess(presentationData.strings.AddContact_StatusSuccess(EnginePeer(peer).compactDisplayTitle).string, true)), nil) -// } -// }), completed: nil, cancelled: nil)) }) } diff --git a/submodules/TelegramUI/Components/Contacts/NewContactScreen/BUILD b/submodules/TelegramUI/Components/Contacts/NewContactScreen/BUILD index 42ca757a73..ff2bcf23ef 100644 --- a/submodules/TelegramUI/Components/Contacts/NewContactScreen/BUILD +++ b/submodules/TelegramUI/Components/Contacts/NewContactScreen/BUILD @@ -44,6 +44,7 @@ swift_library( "//submodules/TelegramUI/Components/GlassBarButtonComponent", "//submodules/PhoneNumberFormat", "//submodules/QrCodeUI", + "//submodules/AvatarNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/NewContactScreen.swift b/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/NewContactScreen.swift index ddf9b8bb28..e1d1f7dd7c 100644 --- a/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/NewContactScreen.swift +++ b/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/NewContactScreen.swift @@ -27,6 +27,7 @@ import CountrySelectionUI import PhoneNumberFormat import QrCodeUI import MessageUI +import AvatarNode final class NewContactScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -37,6 +38,7 @@ final class NewContactScreenComponent: Component { let lastName: String let phoneNumber: String let syncContactToPhone: Bool + let addToPrivacyExceptions: Bool let note: NSAttributedString } @@ -67,9 +69,10 @@ final class NewContactScreenComponent: Component { private let nameSection = ComponentView() private let phoneSection = ComponentView() - private let syncContactSection = ComponentView() + private let optionsSection = ComponentView() private let noteSection = ComponentView() private let qrSection = ComponentView() + private var avatarNode: AvatarNode? private let title = ComponentView() private let cancelButton = ComponentView() @@ -94,6 +97,7 @@ final class NewContactScreenComponent: Component { private var updateFocusTag: Any? private var syncContactToPhone = true + private var addToPrivacyExceptions = false private var cachedChevronImage: (UIImage, PresentationTheme)? @@ -166,6 +170,7 @@ final class NewContactScreenComponent: Component { lastName: lastName, phoneNumber: phoneNumber, syncContactToPhone: self.syncContactToPhone, + addToPrivacyExceptions: self.addToPrivacyExceptions, note: note ) } @@ -256,6 +261,10 @@ final class NewContactScreenComponent: Component { self.resolvedPeer = .peer(peer: peer, isContact: false) } + if component.initialData.shareViaException { + self.addToPrivacyExceptions = true + } + let countryCode: Int32 if let phone = component.initialData.phoneNumber { if let (_, code) = lookupCountryIdByNumber(phone, configuration: component.context.currentCountriesConfiguration.with { $0 }), let codeValue = Int32(code.code) { @@ -265,7 +274,10 @@ final class NewContactScreenComponent: Component { } else { countryCode = AuthorizationSequenceCountrySelectionController.defaultCountryCode() } - updateFocusTag = self.firstNameTag + if let _ = component.initialData.peer { + } else { + updateFocusTag = self.firstNameTag + } } else { countryCode = AuthorizationSequenceCountrySelectionController.defaultCountryCode() updateFocusTag = self.phoneTag @@ -302,6 +314,11 @@ final class NewContactScreenComponent: Component { var contentHeight: CGFloat = 0.0 contentHeight += environment.navigationHeight contentHeight += topInset + + var avatarInset: CGFloat = 0.0 + if let _ = component.initialData.peer { + avatarInset = 84.0 + } let nameSectionItems: [AnyComponentWithIdentity] = [ AnyComponentWithIdentity(id: "firstName", component: AnyComponent(ListTextFieldItemComponent( @@ -313,6 +330,7 @@ final class NewContactScreenComponent: Component { autocapitalizationType: .sentences, autocorrectionType: .default, returnKeyType: .next, + contentInsets: UIEdgeInsets(top: 0.0, left: avatarInset, bottom: 0.0, right: 0.0), updated: { value in }, onReturn: { [weak self] in @@ -333,6 +351,7 @@ final class NewContactScreenComponent: Component { autocapitalizationType: .sentences, autocorrectionType: .default, returnKeyType: .next, + contentInsets: UIEdgeInsets(top: 0.0, left: avatarInset, bottom: 0.0, right: 0.0), updated: { value in }, onReturn: { [weak self] in @@ -365,6 +384,20 @@ final class NewContactScreenComponent: Component { } transition.setFrame(view: nameSectionView, frame: nameSectionFrame) } + + if let peer = component.initialData.peer { + let avatarNode: AvatarNode + if let current = self.avatarNode { + avatarNode = current + } else { + avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 28.0)) + avatarNode.setPeer(context: component.context, theme: theme, peer: peer) + self.scrollView.addSubview(avatarNode.view) + self.avatarNode = avatarNode + } + avatarNode.frame = CGRect(origin: CGPoint(x: sideInset + 15.0, y: contentHeight + floorToScreenPixels((nameSectionFrame.height - 66.0) / 2.0)), size: CGSize(width: 66.0, height: 66.0)) + } + contentHeight += nameSectionSize.height contentHeight += sectionSpacing @@ -378,8 +411,46 @@ final class NewContactScreenComponent: Component { phoneAccesory = nil } - let phoneSectionItems: [AnyComponentWithIdentity] = [ - AnyComponentWithIdentity(id: "phone", component: AnyComponent( + var phoneSectionItems: [AnyComponentWithIdentity] = [] + + var phoneFooterComponent: AnyComponent? + + if let peer = component.initialData.peer { + if let phone = component.initialData.phoneNumber { + phoneSectionItems.append(AnyComponentWithIdentity(id: "phone", component: AnyComponent( + ListActionItemComponent( + theme: theme, + style: .glass, + title: AnyComponent(VStack([ + AnyComponentWithIdentity(id: "title", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "mobile", font: Font.regular(14.0), textColor: theme.list.itemPrimaryTextColor))))), + AnyComponentWithIdentity(id: "value", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: formatPhoneNumber(context: component.context, number: phone), font: Font.regular(17.0), textColor: theme.list.itemAccentColor))))) + ], alignment: .left, spacing: 4.0)), + contentInsets: UIEdgeInsets(top: 15.0, left: 0.0, bottom: 15.0, right: 0.0), + accessory: nil, + action: nil + ))) + ) + } else { + phoneSectionItems.append(AnyComponentWithIdentity(id: "phone", component: AnyComponent( + ListActionItemComponent( + theme: theme, + style: .glass, + title: AnyComponent(VStack([ + AnyComponentWithIdentity(id: "title", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "mobile", font: Font.regular(14.0), textColor: theme.list.itemPrimaryTextColor))))), + AnyComponentWithIdentity(id: "value", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.ContactInfo_PhoneNumberHidden, font: Font.regular(17.0), textColor: theme.list.itemAccentColor))))) + ], alignment: .left, spacing: 4.0)), + contentInsets: UIEdgeInsets(top: 15.0, left: 0.0, bottom: 15.0, right: 0.0), + accessory: nil, + action: nil + ))) + ) + phoneFooterComponent = AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: environment.strings.AddContact_ContactWillBeSharedAfterMutual(peer.compactDisplayTitle).string, font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor)), + maximumNumberOfLines: 0 + )) + } + } else { + phoneSectionItems.append(AnyComponentWithIdentity(id: "phone", component: AnyComponent( ListItemComponentAdaptor( itemGenerator: PhoneInputItem( theme: theme, @@ -426,7 +497,7 @@ final class NewContactScreenComponent: Component { if scheduleResolve { self.resolvedPeerDisposable.set( ((Signal.complete() |> delay(resolveDelay, queue: Queue.mainQueue())) - |> then( + |> then( component.context.engine.peers.resolvePeerByPhone(phone: number) |> beforeStarted({ [weak self] in guard let self else { @@ -437,28 +508,28 @@ final class NewContactScreenComponent: Component { self.state?.updated(transition: .easeInOut(duration: 0.2)) } }) - ) - |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let self, let component = self.component else { - return - } - if let peer { - self.resolvedPeerDisposable.set((component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.IsContact(id: peer.id)) |> deliverOnMainQueue).start(next: { [weak self] isContact in - guard let self else { - return - } - self.resolvedPeer = .peer(peer: peer, isContact: isContact) - if !self.isUpdating { - self.state?.updated(transition: .easeInOut(duration: 0.2)) - } - })) - } else { - self.resolvedPeer = .notFound - if !self.isUpdating { - self.state?.updated(transition: .easeInOut(duration: 0.2)) - } - } - }) + ) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let component = self.component else { + return + } + if let peer { + self.resolvedPeerDisposable.set((component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.IsContact(id: peer.id)) |> deliverOnMainQueue).start(next: { [weak self] isContact in + guard let self else { + return + } + self.resolvedPeer = .peer(peer: peer, isContact: isContact) + if !self.isUpdating { + self.state?.updated(transition: .easeInOut(duration: 0.2)) + } + })) + } else { + self.resolvedPeer = .notFound + if !self.isUpdating { + self.state?.updated(transition: .easeInOut(duration: 0.2)) + } + } + }) ) } } @@ -466,59 +537,58 @@ final class NewContactScreenComponent: Component { params: ListViewItemLayoutParams(width: availableSize.width - sideInset * 2.0, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true), tag: self.phoneTag ) - )) - ] - - var phoneFooterComponent: AnyComponent? - if let resolvedPeer = self.resolvedPeer { - if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== environment.theme { - self.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme) - } + ))) - let phoneFooterRawText: String - switch resolvedPeer { - case .resolving: - phoneFooterRawText = "" - case let .peer(_, isContact): - if isContact { - phoneFooterRawText = "This phone number is already in your contacts. [View >]()" - } else { - phoneFooterRawText = "This phone number is on Telegram." + if let resolvedPeer = self.resolvedPeer { + if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== environment.theme { + self.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme) } - case .notFound: - phoneFooterRawText = "This phone number is not on Telegram. [Invite >]()" - } - let phoneFooterText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(phoneFooterRawText, attributes: footerAttributes)) - if let range = phoneFooterText.string.range(of: ">"), let chevronImage = self.cachedChevronImage?.0 { - phoneFooterText.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: phoneFooterText.string)) - } - phoneFooterComponent = AnyComponent(MultilineTextComponent( - text: .plain(phoneFooterText), - maximumNumberOfLines: 0, - highlightColor: environment.theme.list.itemAccentColor.withAlphaComponent(0.1), - highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0), - highlightAction: { attributes in - if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { - return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + + let phoneFooterRawText: String + switch resolvedPeer { + case .resolving: + phoneFooterRawText = "" + case let .peer(_, isContact): + if isContact { + phoneFooterRawText = "This phone number is already in your contacts. [View >]()" } else { - return nil + phoneFooterRawText = "This phone number is on Telegram." } - }, - tapAction: { [weak self] _, _ in - guard let self, let component = self.component else { - return - } - if case let .peer(peer, _) = self.resolvedPeer { - if let infoController = component.context.sharedContext.makePeerInfoController(context: component.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { - if let navigationController = component.context.sharedContext.mainWindow?.viewController as? NavigationController { - navigationController.pushViewController(infoController) - } + case .notFound: + phoneFooterRawText = "This phone number is not on Telegram. [Invite >]()" + } + let phoneFooterText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(phoneFooterRawText, attributes: footerAttributes)) + if let range = phoneFooterText.string.range(of: ">"), let chevronImage = self.cachedChevronImage?.0 { + phoneFooterText.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: phoneFooterText.string)) + } + phoneFooterComponent = AnyComponent(MultilineTextComponent( + text: .plain(phoneFooterText), + maximumNumberOfLines: 0, + highlightColor: environment.theme.list.itemAccentColor.withAlphaComponent(0.1), + highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0), + highlightAction: { attributes in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { + return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + } else { + return nil + } + }, + tapAction: { [weak self] _, _ in + guard let self, let component = self.component else { + return + } + if case let .peer(peer, _) = self.resolvedPeer { + if let infoController = component.context.sharedContext.makePeerInfoController(context: component.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + if let navigationController = component.context.sharedContext.mainWindow?.viewController as? NavigationController { + navigationController.pushViewController(infoController) + } + } + } else { + self.sendInvite() } - } else { - self.sendInvite() } - } - )) + )) + } } let phoneSectionSize = self.phoneSection.update( @@ -548,7 +618,8 @@ final class NewContactScreenComponent: Component { self.updateCountryCode(code: initialCountryCode, name: "") } - let syncContactSectionItems: [AnyComponentWithIdentity] = [ + + var optionsSectionItems: [AnyComponentWithIdentity] = [ AnyComponentWithIdentity(id: "syncContact", component: AnyComponent(ListActionItemComponent( theme: theme, style: .glass, @@ -570,29 +641,59 @@ final class NewContactScreenComponent: Component { action: nil ))) ] - let syncContactSectionSize = self.syncContactSection.update( + var optionsFooterComponent: AnyComponent? + if let peer = component.initialData.peer, component.initialData.shareViaException { + optionsSectionItems.append( + AnyComponentWithIdentity(id: "privacy", component: AnyComponent(ListActionItemComponent( + theme: theme, + style: .glass, + title: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: environment.strings.AddContact_SharedContactException, + font: Font.regular(presentationData.listsFontSize.baseDisplaySize), + textColor: theme.list.itemPrimaryTextColor + )), + maximumNumberOfLines: 1 + )), + accessory: .toggle(ListActionItemComponent.Toggle(style: .regular, isOn: self.addToPrivacyExceptions, action: { [weak self] _ in + guard let self else { + return + } + self.addToPrivacyExceptions = !self.addToPrivacyExceptions + self.state?.updated(transition: .spring(duration: 0.4)) + })), + action: nil + ))) + ) + optionsFooterComponent = AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: environment.strings.AddContact_SharedContactExceptionInfo(peer.compactDisplayTitle).string, font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor)), + maximumNumberOfLines: 0 + )) + } + + let optionsSectionSize = self.optionsSection.update( transition: transition, component: AnyComponent(ListSectionComponent( theme: theme, style: .glass, header: nil, - footer: nil, - items: syncContactSectionItems + footer: optionsFooterComponent, + items: optionsSectionItems )), environment: {}, containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0) ) - let syncContactSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: syncContactSectionSize) - if let syncContactSectionView = self.syncContactSection.view { - if syncContactSectionView.superview == nil { - self.scrollView.addSubview(syncContactSectionView) - self.syncContactSection.parentState = state + let optionsSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: optionsSectionSize) + if let optionsSectionView = self.optionsSection.view { + if optionsSectionView.superview == nil { + self.scrollView.addSubview(optionsSectionView) + self.optionsSection.parentState = state } - transition.setFrame(view: syncContactSectionView, frame: syncContactSectionFrame) + transition.setFrame(view: optionsSectionView, frame: optionsSectionFrame) } - contentHeight += syncContactSectionSize.height + contentHeight += optionsSectionSize.height contentHeight += sectionSpacing - + if case .peer = self.resolvedPeer { if let qrSectionView = self.qrSection.view, qrSectionView.superview != nil { transition.setAlpha(view: qrSectionView, alpha: 0.0, completion: { _ in @@ -647,7 +748,7 @@ final class NewContactScreenComponent: Component { if let noteSectionView = self.noteSection.view { if noteSectionView.superview == nil { self.scrollView.addSubview(noteSectionView) - self.syncContactSection.parentState = state + self.optionsSection.parentState = state noteSectionTransition = .immediate transition.setAlpha(view: noteSectionView, alpha: 1.0) @@ -664,7 +765,7 @@ final class NewContactScreenComponent: Component { } let qrSectionItems: [AnyComponentWithIdentity] = [ - AnyComponentWithIdentity(id: "syncContact", component: AnyComponent(ListActionItemComponent( + AnyComponentWithIdentity(id: "qr", component: AnyComponent(ListActionItemComponent( theme: theme, style: .glass, title: AnyComponent(VStack([ @@ -711,7 +812,7 @@ final class NewContactScreenComponent: Component { var qrSectionTransition = transition if qrSectionView.superview == nil { self.scrollView.addSubview(qrSectionView) - self.syncContactSection.parentState = state + self.optionsSection.parentState = state qrSectionTransition = .immediate transition.setAlpha(view: qrSectionView, alpha: 1.0) @@ -874,17 +975,20 @@ public class NewContactScreen: ViewControllerComponentContainer { fileprivate let firstName: String? fileprivate let lastName: String? fileprivate let phoneNumber: String? + fileprivate let shareViaException: Bool fileprivate init( peer: EnginePeer?, firstName: String?, lastName: String?, - phoneNumber: String? + phoneNumber: String?, + shareViaException: Bool ) { self.peer = peer self.firstName = firstName self.lastName = lastName self.phoneNumber = phoneNumber + self.shareViaException = shareViaException } } @@ -928,21 +1032,24 @@ public class NewContactScreen: ViewControllerComponentContainer { public static func initialData( peer: EnginePeer? = nil, - phoneNumber: String? = nil + phoneNumber: String? = nil, + shareViaException: Bool = false ) -> InitialData { if case let .user(user) = peer { return InitialData( peer: peer, firstName: user.firstName, lastName: user.lastName, - phoneNumber: user.phone ?? phoneNumber + phoneNumber: user.phone ?? phoneNumber, + shareViaException: shareViaException ) } else { return InitialData( peer: nil, firstName: nil, lastName: nil, - phoneNumber: phoneNumber + phoneNumber: phoneNumber, + shareViaException: false ) } } @@ -957,7 +1064,7 @@ public class NewContactScreen: ViewControllerComponentContainer { phoneNumber: result.phoneNumber, noteText: result.note.string, noteEntities: entities, - addToPrivacyExceptions: false + addToPrivacyExceptions: result.addToPrivacyExceptions ).startStandalone(completed: { [weak self] in if !result.syncContactToPhone { self?.completion(result.peer, nil, nil) @@ -974,13 +1081,30 @@ public class NewContactScreen: ViewControllerComponentContainer { } if result.syncContactToPhone, let contactDataManager = self.context.sharedContext.contactDataManager { + var urls: [DeviceContactUrlData] = [] + if let peer = result.peer { + let appProfile = DeviceContactUrlData(appProfile: peer.id) + var found = false + for url in urls { + if url.label == appProfile.label && url.value == appProfile.value { + found = true + break + } + } + if !found { + urls.append(appProfile) + } + } + + var phoneNumbers: [DeviceContactPhoneNumberData] = [] + if !result.phoneNumber.isEmpty { + phoneNumbers.append(DeviceContactPhoneNumberData(label: defaultContactLabel, value: result.phoneNumber)) + } let composedContactData = DeviceContactExtendedData( basicData: DeviceContactBasicData( firstName: result.firstName, lastName: result.lastName, - phoneNumbers: [ - DeviceContactPhoneNumberData(label: "_$!!$_", value: result.phoneNumber) - ] + phoneNumbers: phoneNumbers ), middleName: "", prefix: "", @@ -989,7 +1113,7 @@ public class NewContactScreen: ViewControllerComponentContainer { jobTitle: "", department: "", emailAddresses: [], - urls: [], + urls: urls, addresses: [], birthdayDate: nil, socialProfiles: [], diff --git a/submodules/TelegramUI/Components/ListMultilineTextFieldItemComponent/Sources/ListMultilineTextFieldItemComponent.swift b/submodules/TelegramUI/Components/ListMultilineTextFieldItemComponent/Sources/ListMultilineTextFieldItemComponent.swift index 415ca72422..60dc69c6bd 100644 --- a/submodules/TelegramUI/Components/ListMultilineTextFieldItemComponent/Sources/ListMultilineTextFieldItemComponent.swift +++ b/submodules/TelegramUI/Components/ListMultilineTextFieldItemComponent/Sources/ListMultilineTextFieldItemComponent.swift @@ -12,6 +12,11 @@ import PlainButtonComponent import AccountContext public final class ListMultilineTextFieldItemComponent: Component { + public enum Style { + case glass + case legacy + } + public final class ExternalState { public fileprivate(set) var hasText: Bool = false public fileprivate(set) var text: NSAttributedString = NSAttributedString() @@ -50,6 +55,7 @@ public final class ListMultilineTextFieldItemComponent: Component { } public let externalState: ExternalState? + public let style: Style public let context: AccountContext public let theme: PresentationTheme public let strings: PresentationStrings @@ -73,6 +79,7 @@ public final class ListMultilineTextFieldItemComponent: Component { public init( externalState: ExternalState? = nil, + style: Style = .legacy, context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, @@ -95,6 +102,7 @@ public final class ListMultilineTextFieldItemComponent: Component { tag: AnyObject? = nil ) { self.externalState = externalState + self.style = style self.context = context self.theme = theme self.strings = strings @@ -273,7 +281,14 @@ public final class ListMultilineTextFieldItemComponent: Component { self.component = component self.state = state - let verticalInset: CGFloat = 12.0 + let verticalInset: CGFloat + switch component.style { + case .glass: + verticalInset = 16.0 + case .legacy: + verticalInset = 12.0 + } + let leftInset: CGFloat = 16.0 var rightInset: CGFloat = 16.0 let modeSelectorSize = CGSize(width: 32.0, height: 32.0) diff --git a/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift b/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift index 0db1bf6728..ae8554b1e3 100644 --- a/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift +++ b/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift @@ -34,6 +34,7 @@ public final class ListTextFieldItemComponent: Component { public let autocapitalizationType: UITextAutocapitalizationType public let autocorrectionType: UITextAutocorrectionType public let returnKeyType: UIReturnKeyType + public let contentInsets: UIEdgeInsets public let updated: ((String) -> Void)? public let onReturn: (() -> Void)? public let tag: AnyObject? @@ -47,6 +48,7 @@ public final class ListTextFieldItemComponent: Component { autocapitalizationType: UITextAutocapitalizationType = .sentences, autocorrectionType: UITextAutocorrectionType = .default, returnKeyType: UIReturnKeyType = .default, + contentInsets: UIEdgeInsets = .zero, updated: ((String) -> Void)?, onReturn: (() -> Void)? = nil, tag: AnyObject? = nil @@ -59,6 +61,7 @@ public final class ListTextFieldItemComponent: Component { self.autocapitalizationType = autocapitalizationType self.autocorrectionType = autocorrectionType self.returnKeyType = returnKeyType + self.contentInsets = contentInsets self.updated = updated self.onReturn = onReturn self.tag = tag @@ -89,6 +92,9 @@ public final class ListTextFieldItemComponent: Component { if lhs.returnKeyType != rhs.returnKeyType { return false } + if lhs.contentInsets != rhs.contentInsets { + return false + } if (lhs.updated == nil) != (rhs.updated == nil) { return false } @@ -230,10 +236,10 @@ public final class ListTextFieldItemComponent: Component { text: .plain(NSAttributedString(string: component.placeholder.isEmpty ? " " : component.placeholder, font: Font.regular(17.0), textColor: component.theme.list.itemPlaceholderTextColor)) )), environment: {}, - containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - 30.0, height: 100.0) + containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - 30.0 - component.contentInsets.left - component.contentInsets.right, height: 100.0) ) let contentHeight: CGFloat = placeholderSize.height + verticalInset * 2.0 - let placeholderFrame = CGRect(origin: CGPoint(x: sideInset, y: floor((contentHeight - placeholderSize.height) * 0.5)), size: placeholderSize) + let placeholderFrame = CGRect(origin: CGPoint(x: sideInset + component.contentInsets.left, y: floor((contentHeight - placeholderSize.height) * 0.5)), size: placeholderSize) if let placeholderView = self.placeholder.view { if placeholderView.superview == nil { placeholderView.layer.anchorPoint = CGPoint() @@ -246,7 +252,7 @@ public final class ListTextFieldItemComponent: Component { placeholderView.isHidden = !self.currentText.isEmpty } - transition.setFrame(view: self.textField, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: contentHeight))) + transition.setFrame(view: self.textField, frame: CGRect(origin: CGPoint(x: component.contentInsets.left, y: 0.0), size: CGSize(width: availableSize.width - component.contentInsets.left - component.contentInsets.right, height: contentHeight))) let clearButtonSize = self.clearButton.update( transition: transition, @@ -273,11 +279,11 @@ public final class ListTextFieldItemComponent: Component { if clearButtonView.superview == nil { self.addSubview(clearButtonView) } - transition.setFrame(view: clearButtonView, frame: CGRect(origin: CGPoint(x: availableSize.width - 0.0 - clearButtonSize.width, y: floor((contentHeight - clearButtonSize.height) * 0.5)), size: clearButtonSize)) + transition.setFrame(view: clearButtonView, frame: CGRect(origin: CGPoint(x: availableSize.width - clearButtonSize.width, y: floor((contentHeight - clearButtonSize.height) * 0.5)), size: clearButtonSize)) clearButtonView.isHidden = self.currentText.isEmpty || !self.textField.isFirstResponder } - self.separatorInset = 16.0 + self.separatorInset = 16.0 + component.contentInsets.left return CGSize(width: availableSize.width, height: contentHeight) } diff --git a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift index 800330d209..47251d6b62 100644 --- a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift +++ b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift @@ -1293,7 +1293,8 @@ final class PeerAllowedReactionsScreenComponent: Component { self.allowedReactionCount = index self.state?.updated(transition: .immediate) } - )) + )), + preferNative: true ))) ], displaySeparators: false diff --git a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift index 6acf5283b7..b6c75199ab 100644 --- a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift @@ -918,7 +918,8 @@ final class AffiliateProgramSetupScreenComponent: Component { self.state?.updated(transition: .immediate) } - )) + )), + preferNative: true ))) ], displaySeparators: false @@ -995,7 +996,8 @@ final class AffiliateProgramSetupScreenComponent: Component { self.durationValue = Int(durationItems[value]) self.state?.updated(transition: .immediate) } - )) + )), + preferNative: true ))) ], displaySeparators: false diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift index 98094effb0..0090264bb0 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift @@ -645,7 +645,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode let hasTopCorners = hasCorners && topItem == nil let hasBottomCorners = hasCorners && bottomItem == nil - self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + self.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: true) : nil transition.updateFrame(node: self.maskNode, frame: CGRect(origin: CGPoint(x: safeInsets.left, y: 0.0), size: CGSize(width: width - safeInsets.left - safeInsets.right, height: height))) self.bottomSeparatorNode.isHidden = hasBottomCorners diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift index a263451f0f..9a4d2879b4 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift @@ -1492,7 +1492,8 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { self.inactivityDays = valueList[index] self.state?.updated(transition: .immediate) } - )) + )), + preferNative: true ))) ] )), diff --git a/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/BusinessIntroSetupScreen.swift b/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/BusinessIntroSetupScreen.swift index 61afafa1a6..c3d0fdd748 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/BusinessIntroSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/BusinessIntroSetupScreen.swift @@ -749,6 +749,7 @@ final class BusinessIntroSetupScreenComponent: Component { introSectionItems.append(AnyComponentWithIdentity(id: introSectionItems.count, component: AnyComponent(Rectangle(color: .clear, height: 346.0, tag: self.introPlaceholderTag)))) introSectionItems.append(AnyComponentWithIdentity(id: introSectionItems.count, component: AnyComponent(ListMultilineTextFieldItemComponent( externalState: self.titleInputState, + style: .glass, context: component.context, theme: environment.theme, strings: environment.strings, @@ -780,6 +781,7 @@ final class BusinessIntroSetupScreenComponent: Component { self.resetTitle = nil introSectionItems.append(AnyComponentWithIdentity(id: introSectionItems.count, component: AnyComponent(ListMultilineTextFieldItemComponent( externalState: self.textInputState, + style: .glass, context: component.context, theme: environment.theme, strings: environment.strings, @@ -830,6 +832,7 @@ final class BusinessIntroSetupScreenComponent: Component { introSectionItems.append(AnyComponentWithIdentity(id: introSectionItems.count, component: AnyComponent(ListActionItemComponent( theme: environment.theme, + style: .glass, title: AnyComponent(VStack([ AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent( text: .plain(NSAttributedString( @@ -859,6 +862,7 @@ final class BusinessIntroSetupScreenComponent: Component { transition: transition, component: AnyComponent(ListSectionComponent( theme: environment.theme, + style: .glass, header: AnyComponent(MultilineTextComponent( text: .plain(NSAttributedString( string: environment.strings.Business_Intro_CustomizeSectionHeader, @@ -946,11 +950,13 @@ final class BusinessIntroSetupScreenComponent: Component { transition: transition, component: AnyComponent(ListSectionComponent( theme: environment.theme, + style: .glass, header: nil, footer: nil, items: [ AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent( theme: environment.theme, + style: .glass, title: AnyComponent(VStack([ AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent( text: .plain(NSAttributedString( diff --git a/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift b/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift index c8dbe793e8..f1836fed75 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift @@ -395,6 +395,7 @@ final class BusinessLocationSetupScreenComponent: Component { var addressSectionItems: [AnyComponentWithIdentity] = [] addressSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListMultilineTextFieldItemComponent( externalState: self.addressTextInputState, + style: .glass, context: component.context, theme: environment.theme, strings: environment.strings, diff --git a/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift b/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift index fa6f049d33..c7aa5fd0ba 100644 --- a/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift @@ -742,6 +742,7 @@ final class ChatbotSetupScreenComponent: Component { self.resetQueryText = nil var nameSectionItems: [AnyComponentWithIdentity] = [] nameSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListTextFieldItemComponent( + style: .glass, theme: environment.theme, initialText: "", resetText: resetQueryText.flatMap { ListTextFieldItemComponent.ResetText(value: $0) }, diff --git a/submodules/TelegramUI/Sources/OpenAddContact.swift b/submodules/TelegramUI/Sources/OpenAddContact.swift index 75b4622005..fabba71aaf 100644 --- a/submodules/TelegramUI/Sources/OpenAddContact.swift +++ b/submodules/TelegramUI/Sources/OpenAddContact.swift @@ -14,24 +14,31 @@ func openAddContactImpl(context: AccountContext, firstName: String = "", lastNam |> take(1) |> deliverOnMainQueue).startStandalone(next: { value in switch value { - case .allowed: - let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: [DeviceContactPhoneNumberData(label: label, value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "") - present(deviceContactInfoController(context: ShareControllerAppAccountContext(context: context), environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext), subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in + case .allowed: + let controller = context.sharedContext.makeNewContactScreen( + context: context, + peer: nil, + phoneNumber: phoneNumber, + shareViaException: false, + completion: { peer, stableId, contactData in if let peer = peer { - if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { pushController(infoController) } - } else { + } else if let stableId, let contactData { pushController(deviceContactInfoController(context: ShareControllerAppAccountContext(context: context), environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext), subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil)) } - }), completed: completed, cancelled: nil), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - case .notDetermined: - DeviceAccess.authorizeAccess(to: .contacts) - default: - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - present(textAlertController(context: context, title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { - context.sharedContext.applicationBindings.openSettings() - })]), nil) + completed() + } + ) + pushController(controller) + case .notDetermined: + DeviceAccess.authorizeAccess(to: .contacts) + default: + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + present(textAlertController(context: context, title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { + context.sharedContext.applicationBindings.openSettings() + })]), nil) } }) } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 653198660e..323a854e20 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -3983,8 +3983,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { return ChannelMembersSearchControllerImpl(params: params) } - public func makeNewContactScreen(context: AccountContext, peer: EnginePeer?, phoneNumber: String?, completion: @escaping (EnginePeer?, DeviceContactStableId?, DeviceContactExtendedData?) -> Void) -> ViewController { - return NewContactScreen(context: context, initialData: NewContactScreen.initialData(peer: peer, phoneNumber: phoneNumber), completion: completion) + public func makeNewContactScreen(context: AccountContext, peer: EnginePeer?, phoneNumber: String?, shareViaException: Bool, completion: @escaping (EnginePeer?, DeviceContactStableId?, DeviceContactExtendedData?) -> Void) -> ViewController { + return NewContactScreen(context: context, initialData: NewContactScreen.initialData(peer: peer, phoneNumber: phoneNumber, shareViaException: shareViaException), completion: completion) } }