mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-17 03:40:18 +00:00
no message
This commit is contained in:
parent
c2149ecd2e
commit
8407cd1c5f
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "ModernMessageSelectionChecked@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB |
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "ModernMessageSelectionUnchecked@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB |
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "ModernContactSelectionChecked@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 795 B |
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "ModernContactSelectionEmpty@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 665 B |
@ -268,7 +268,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if let codeLength = codeLength, let text = textField.text, text.characters.count == Int(codeLength) {
|
if let codeLength = codeLength, let text = textField.text, text.count == Int(codeLength) {
|
||||||
self.loginWithCode?(text)
|
self.loginWithCode?(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -182,7 +182,7 @@ func callListNodeEntriesForView(_ view: CallListView, state: CallListNodeState,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if showSettings {
|
if showSettings {
|
||||||
result.append(.displayTabInfo(state.theme, state.strings.Calls_CallTabDescription))
|
result.append(.displayTabInfo(state.theme, state.strings.CallSettings_TabIconDescription))
|
||||||
result.append(.displayTab(state.theme, state.strings.Calls_CallTabTitle, showCallsTab))
|
result.append(.displayTab(state.theme, state.strings.Calls_CallTabTitle, showCallsTab))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|||||||
@ -3525,7 +3525,7 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin
|
|||||||
strongSelf.controllerNavigationDisposable.set((dataSignal
|
strongSelf.controllerNavigationDisposable.set((dataSignal
|
||||||
|> deliverOnMainQueue).start(next: { peerAndContactData in
|
|> deliverOnMainQueue).start(next: { peerAndContactData in
|
||||||
if let strongSelf = self, let contactData = peerAndContactData.1, contactData.basicData.phoneNumbers.count != 0 {
|
if let strongSelf = self, let contactData = peerAndContactData.1, contactData.basicData.phoneNumbers.count != 0 {
|
||||||
if contactData.basicData.phoneNumbers.count == 1, false {
|
if contactData.isPrimitive {
|
||||||
let phone = contactData.basicData.phoneNumbers[0].value
|
let phone = contactData.basicData.phoneNumbers[0].value
|
||||||
let media = TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: peerAndContactData.0?.id, vCardData: nil)
|
let media = TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: peerAndContactData.0?.id, vCardData: nil)
|
||||||
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
|
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
|
||||||
|
|||||||
@ -707,7 +707,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
isPinned = item.index.pinningIndex != nil
|
isPinned = item.index.pinningIndex != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.enableContextActions && !isAd {
|
if item.enableContextActions && !item.editing && !isAd {
|
||||||
peerRevealOptions = revealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isPinned: isPinned, isMuted: item.account.peerId != item.index.messageIndex.id.peerId ? (currentMutedIconImage != nil) : nil, hasPeerGroupId: hasPeerGroupId, canDelete: true)
|
peerRevealOptions = revealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isPinned: isPinned, isMuted: item.account.peerId != item.index.messageIndex.id.peerId ? (currentMutedIconImage != nil) : nil, hasPeerGroupId: hasPeerGroupId, canDelete: true)
|
||||||
if itemPeer.peerId != item.account.peerId {
|
if itemPeer.peerId != item.account.peerId {
|
||||||
peerLeftRevealOptions = leftRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isUnread: unreadCount.unread)
|
peerLeftRevealOptions = leftRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isUnread: unreadCount.unread)
|
||||||
@ -721,7 +721,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
case .groupReference:
|
case .groupReference:
|
||||||
let isPinned = item.index.pinningIndex != nil
|
let isPinned = item.index.pinningIndex != nil
|
||||||
|
|
||||||
if item.enableContextActions {
|
if item.enableContextActions && !item.editing {
|
||||||
peerRevealOptions = revealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isPinned: isPinned, isMuted: nil, hasPeerGroupId: nil, canDelete: false)
|
peerRevealOptions = revealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isPinned: isPinned, isMuted: nil, hasPeerGroupId: nil, canDelete: false)
|
||||||
} else {
|
} else {
|
||||||
peerRevealOptions = []
|
peerRevealOptions = []
|
||||||
|
|||||||
@ -703,8 +703,9 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
|||||||
let previousEntries = previousRecentItems.swap(entries)
|
let previousEntries = previousRecentItems.swap(entries)
|
||||||
|
|
||||||
let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, account: account, filter: filter, peerSelected: { peer in
|
let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, account: account, filter: filter, peerSelected: { peer in
|
||||||
self?.recentListNode.clearHighlightAnimated(true)
|
|
||||||
openPeer(peer, true)
|
openPeer(peer, true)
|
||||||
|
let _ = addRecentlySearchedPeer(postbox: account.postbox, peerId: peer.id).start()
|
||||||
|
self?.recentListNode.clearHighlightAnimated(true)
|
||||||
}, peerLongTapped: { peer in
|
}, peerLongTapped: { peer in
|
||||||
openRecentPeerOptions(peer)
|
openRecentPeerOptions(peer)
|
||||||
}, clearRecentlySearchedPeers: {
|
}, clearRecentlySearchedPeers: {
|
||||||
|
|||||||
@ -734,7 +734,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
|||||||
adminBadgeString = NSAttributedString(string: " \(item.presentationData.strings.Conversation_Admin)", font: inlineBotPrefixFont, textColor: incoming ? item.presentationData.theme.theme.chat.bubble.incomingSecondaryTextColor : item.presentationData.theme.theme.chat.bubble.outgoingSecondaryTextColor)
|
adminBadgeString = NSAttributedString(string: " \(item.presentationData.strings.Conversation_Admin)", font: inlineBotPrefixFont, textColor: incoming ? item.presentationData.theme.theme.chat.bubble.incomingSecondaryTextColor : item.presentationData.theme.theme.chat.bubble.outgoingSecondaryTextColor)
|
||||||
}
|
}
|
||||||
if let authorNameString = authorNameString, let authorNameColor = authorNameColor, let inlineBotNameString = inlineBotNameString {
|
if let authorNameString = authorNameString, let authorNameColor = authorNameColor, let inlineBotNameString = inlineBotNameString {
|
||||||
|
|
||||||
let mutableString = NSMutableAttributedString(string: "\(authorNameString) ", attributes: [NSAttributedStringKey.font: nameFont, NSAttributedStringKey.foregroundColor: authorNameColor])
|
let mutableString = NSMutableAttributedString(string: "\(authorNameString) ", attributes: [NSAttributedStringKey.font: nameFont, NSAttributedStringKey.foregroundColor: authorNameColor])
|
||||||
let bodyAttributes = MarkdownAttributeSet(font: nameFont, textColor: inlineBotNameColor)
|
let bodyAttributes = MarkdownAttributeSet(font: nameFont, textColor: inlineBotNameColor)
|
||||||
let boldAttributes = MarkdownAttributeSet(font: inlineBotPrefixFont, textColor: inlineBotNameColor)
|
let boldAttributes = MarkdownAttributeSet(font: inlineBotPrefixFont, textColor: inlineBotNameColor)
|
||||||
@ -1501,6 +1500,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
|||||||
item.controllerInteraction.updateInputState { textInputState in
|
item.controllerInteraction.updateInputState { textInputState in
|
||||||
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
|
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
|
||||||
}
|
}
|
||||||
|
item.controllerInteraction.updateInputMode { _ in
|
||||||
|
return .text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,9 +11,6 @@ private let titleBoldFont = Font.medium(17.0)
|
|||||||
private let statusFont = Font.regular(13.0)
|
private let statusFont = Font.regular(13.0)
|
||||||
private let badgeFont = Font.regular(14.0)
|
private let badgeFont = Font.regular(14.0)
|
||||||
|
|
||||||
private let selectedImage = UIImage(bundleImageName: "Contact List/SelectionChecked")?.precomposed()
|
|
||||||
private let selectableImage = UIImage(bundleImageName: "Contact List/SelectionUnchecked")?.precomposed()
|
|
||||||
|
|
||||||
enum ContactsPeerItemStatus {
|
enum ContactsPeerItemStatus {
|
||||||
case none
|
case none
|
||||||
case presence(PeerPresence, PresentationTimeFormat)
|
case presence(PeerPresence, PresentationTimeFormat)
|
||||||
|
|||||||
@ -13,10 +13,14 @@ private enum CreatePasswordField {
|
|||||||
|
|
||||||
private final class CreatePasswordControllerArguments {
|
private final class CreatePasswordControllerArguments {
|
||||||
let updateFieldText: (CreatePasswordField, String) -> Void
|
let updateFieldText: (CreatePasswordField, String) -> Void
|
||||||
|
let selectNextInputItem: (CreatePasswordEntryTag) -> Void
|
||||||
|
let save: () -> Void
|
||||||
let cancelEmailConfirmation: () -> Void
|
let cancelEmailConfirmation: () -> Void
|
||||||
|
|
||||||
init(updateFieldText: @escaping (CreatePasswordField, String) -> Void, cancelEmailConfirmation: @escaping () -> Void) {
|
init(updateFieldText: @escaping (CreatePasswordField, String) -> Void, selectNextInputItem: @escaping (CreatePasswordEntryTag) -> Void, save: @escaping () -> Void, cancelEmailConfirmation: @escaping () -> Void) {
|
||||||
self.updateFieldText = updateFieldText
|
self.updateFieldText = updateFieldText
|
||||||
|
self.selectNextInputItem = selectNextInputItem
|
||||||
|
self.save = save
|
||||||
self.cancelEmailConfirmation = cancelEmailConfirmation
|
self.cancelEmailConfirmation = cancelEmailConfirmation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,7 +54,7 @@ private enum CreatePasswordEntry: ItemListNodeEntry, Equatable {
|
|||||||
case passwordInfo(PresentationTheme, String)
|
case passwordInfo(PresentationTheme, String)
|
||||||
|
|
||||||
case hintHeader(PresentationTheme, String)
|
case hintHeader(PresentationTheme, String)
|
||||||
case hint(PresentationTheme, String, String)
|
case hint(PresentationTheme, String, String, Bool)
|
||||||
case hintInfo(PresentationTheme, String)
|
case hintInfo(PresentationTheme, String)
|
||||||
|
|
||||||
case emailHeader(PresentationTheme, String)
|
case emailHeader(PresentationTheme, String)
|
||||||
@ -83,14 +87,12 @@ private enum CreatePasswordEntry: ItemListNodeEntry, Equatable {
|
|||||||
return 2
|
return 2
|
||||||
case .passwordInfo:
|
case .passwordInfo:
|
||||||
return 3
|
return 3
|
||||||
|
|
||||||
case .hintHeader:
|
case .hintHeader:
|
||||||
return 4
|
return 4
|
||||||
case .hint:
|
case .hint:
|
||||||
return 5
|
return 5
|
||||||
case .hintInfo:
|
case .hintInfo:
|
||||||
return 6
|
return 6
|
||||||
|
|
||||||
case .emailHeader:
|
case .emailHeader:
|
||||||
return 7
|
return 7
|
||||||
case .email:
|
case .email:
|
||||||
@ -113,32 +115,40 @@ private enum CreatePasswordEntry: ItemListNodeEntry, Equatable {
|
|||||||
case let .passwordHeader(theme, text):
|
case let .passwordHeader(theme, text):
|
||||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||||
case let .password(theme, text, value):
|
case let .password(theme, text, value):
|
||||||
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(), text: value, placeholder: text, type: .password, spacing: 0.0, tag: CreatePasswordEntryTag.password, sectionId: self.section, textUpdated: { updatedText in
|
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(), text: value, placeholder: text, type: .password, returnKeyType: .next, spacing: 0.0, tag: CreatePasswordEntryTag.password, sectionId: self.section, textUpdated: { updatedText in
|
||||||
arguments.updateFieldText(.password, updatedText)
|
arguments.updateFieldText(.password, updatedText)
|
||||||
}, action: {
|
}, action: {
|
||||||
|
arguments.selectNextInputItem(CreatePasswordEntryTag.password)
|
||||||
})
|
})
|
||||||
case let .passwordConfirmation(theme, text, value):
|
case let .passwordConfirmation(theme, text, value):
|
||||||
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(), text: value, placeholder: text, type: .password, spacing: 0.0, tag: CreatePasswordEntryTag.passwordConfirmation, sectionId: self.section, textUpdated: { updatedText in
|
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(), text: value, placeholder: text, type: .password, returnKeyType: .next, spacing: 0.0, tag: CreatePasswordEntryTag.passwordConfirmation, sectionId: self.section, textUpdated: { updatedText in
|
||||||
arguments.updateFieldText(.passwordConfirmation, updatedText)
|
arguments.updateFieldText(.passwordConfirmation, updatedText)
|
||||||
}, action: {
|
}, action: {
|
||||||
|
arguments.selectNextInputItem(CreatePasswordEntryTag.passwordConfirmation)
|
||||||
})
|
})
|
||||||
case let .passwordInfo(theme, text):
|
case let .passwordInfo(theme, text):
|
||||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||||
case let .hintHeader(theme, text):
|
case let .hintHeader(theme, text):
|
||||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||||
case let .hint(theme, text, value):
|
case let .hint(theme, text, value, last):
|
||||||
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(), text: value, placeholder: text, type: .regular(capitalization: true, autocorrection: false), spacing: 0.0, tag: CreatePasswordEntryTag.hint, sectionId: self.section, textUpdated: { updatedText in
|
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(), text: value, placeholder: text, type: .regular(capitalization: true, autocorrection: false), returnKeyType: last ? .done : .next, spacing: 0.0, tag: CreatePasswordEntryTag.hint, sectionId: self.section, textUpdated: { updatedText in
|
||||||
arguments.updateFieldText(.hint, updatedText)
|
arguments.updateFieldText(.hint, updatedText)
|
||||||
}, action: {
|
}, action: {
|
||||||
|
if last {
|
||||||
|
arguments.save()
|
||||||
|
} else {
|
||||||
|
arguments.selectNextInputItem(CreatePasswordEntryTag.hint)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
case let .hintInfo(theme, text):
|
case let .hintInfo(theme, text):
|
||||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||||
case let .emailHeader(theme, text):
|
case let .emailHeader(theme, text):
|
||||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||||
case let .email(theme, text, value):
|
case let .email(theme, text, value):
|
||||||
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(), text: value, placeholder: text, type: .email, spacing: 0.0, tag: CreatePasswordEntryTag.email, sectionId: self.section, textUpdated: { updatedText in
|
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(), text: value, placeholder: text, type: .email, returnKeyType: .done, spacing: 0.0, tag: CreatePasswordEntryTag.email, sectionId: self.section, textUpdated: { updatedText in
|
||||||
arguments.updateFieldText(.email, updatedText)
|
arguments.updateFieldText(.email, updatedText)
|
||||||
}, action: {
|
}, action: {
|
||||||
|
arguments.save()
|
||||||
})
|
})
|
||||||
case let .emailInfo(theme, text):
|
case let .emailInfo(theme, text):
|
||||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||||
@ -175,11 +185,13 @@ private func createPasswordControllerEntries(presentationData: PresentationData,
|
|||||||
entries.append(.passwordConfirmation(presentationData.theme, presentationData.strings.FastTwoStepSetup_PasswordConfirmationPlaceholder, state.passwordConfirmationText))
|
entries.append(.passwordConfirmation(presentationData.theme, presentationData.strings.FastTwoStepSetup_PasswordConfirmationPlaceholder, state.passwordConfirmationText))
|
||||||
entries.append(.passwordInfo(presentationData.theme, presentationData.strings.FastTwoStepSetup_PasswordHelp))
|
entries.append(.passwordInfo(presentationData.theme, presentationData.strings.FastTwoStepSetup_PasswordHelp))
|
||||||
|
|
||||||
|
let showEmail = currentPassword == nil
|
||||||
|
|
||||||
entries.append(.hintHeader(presentationData.theme, presentationData.strings.FastTwoStepSetup_HintSection))
|
entries.append(.hintHeader(presentationData.theme, presentationData.strings.FastTwoStepSetup_HintSection))
|
||||||
entries.append(.hint(presentationData.theme, presentationData.strings.FastTwoStepSetup_HintPlaceholder, state.hintText))
|
entries.append(.hint(presentationData.theme, presentationData.strings.FastTwoStepSetup_HintPlaceholder, state.hintText, !showEmail))
|
||||||
entries.append(.hintInfo(presentationData.theme, presentationData.strings.FastTwoStepSetup_HintHelp))
|
entries.append(.hintInfo(presentationData.theme, presentationData.strings.FastTwoStepSetup_HintHelp))
|
||||||
|
|
||||||
if currentPassword == nil {
|
if showEmail {
|
||||||
entries.append(.emailHeader(presentationData.theme, presentationData.strings.FastTwoStepSetup_EmailSection))
|
entries.append(.emailHeader(presentationData.theme, presentationData.strings.FastTwoStepSetup_EmailSection))
|
||||||
entries.append(.email(presentationData.theme, presentationData.strings.FastTwoStepSetup_EmailPlaceholder, state.emailText))
|
entries.append(.email(presentationData.theme, presentationData.strings.FastTwoStepSetup_EmailPlaceholder, state.emailText))
|
||||||
entries.append(.emailInfo(presentationData.theme, presentationData.strings.FastTwoStepSetup_EmailHelp))
|
entries.append(.emailInfo(presentationData.theme, presentationData.strings.FastTwoStepSetup_EmailHelp))
|
||||||
@ -212,78 +224,20 @@ func createPasswordController(account: Account, state: CreatePasswordState, comp
|
|||||||
let saveDisposable = MetaDisposable()
|
let saveDisposable = MetaDisposable()
|
||||||
actionsDisposable.add(saveDisposable)
|
actionsDisposable.add(saveDisposable)
|
||||||
|
|
||||||
let arguments = CreatePasswordControllerArguments(updateFieldText: { field, updatedText in
|
|
||||||
updateState { state in
|
|
||||||
var state = state
|
|
||||||
switch field {
|
|
||||||
case .password:
|
|
||||||
state.passwordText = updatedText
|
|
||||||
case .passwordConfirmation:
|
|
||||||
state.passwordConfirmationText = updatedText
|
|
||||||
case .hint:
|
|
||||||
state.hintText = updatedText
|
|
||||||
case .email:
|
|
||||||
state.emailText = updatedText
|
|
||||||
}
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}, cancelEmailConfirmation: {
|
|
||||||
var currentPassword: String?
|
|
||||||
updateState { state in
|
|
||||||
var state = state
|
|
||||||
switch state.state {
|
|
||||||
case let .setup(password):
|
|
||||||
currentPassword = password
|
|
||||||
case .pendingVerification:
|
|
||||||
currentPassword = nil
|
|
||||||
}
|
|
||||||
state.saving = true
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
saveDisposable.set((updateTwoStepVerificationPassword(network: account.network, currentPassword: currentPassword, updatedPassword: .none)
|
|
||||||
|> deliverOnMainQueue).start(next: { _ in
|
|
||||||
updateState { state in
|
|
||||||
var state = state
|
|
||||||
state.saving = false
|
|
||||||
state.state = .setup(currentPassword: nil)
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
updatePasswordEmailConfirmation(nil)
|
|
||||||
}, error: { _ in
|
|
||||||
updateState { state in
|
|
||||||
var state = state
|
|
||||||
state.saving = false
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
var initialFocusImpl: (() -> Void)?
|
var initialFocusImpl: (() -> Void)?
|
||||||
|
|
||||||
let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, statePromise.get())
|
var selectNextInputItemImpl: ((CreatePasswordEntryTag) -> Void)?
|
||||||
|> deliverOnMainQueue
|
|
||||||
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState<CreatePasswordEntry>, CreatePasswordEntry.ItemGenerationArguments)) in
|
|
||||||
|
|
||||||
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
let saveImpl = {
|
||||||
dismissImpl?()
|
|
||||||
})
|
|
||||||
var rightNavigationButton: ItemListNavigationButton?
|
|
||||||
if state.saving {
|
|
||||||
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
|
|
||||||
} else {
|
|
||||||
switch state.state {
|
|
||||||
case .setup:
|
|
||||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: !state.passwordText.isEmpty, action: {
|
|
||||||
var state: CreatePasswordControllerState?
|
var state: CreatePasswordControllerState?
|
||||||
updateState { s in
|
updateState { s in
|
||||||
state = s
|
state = s
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
if let state = state {
|
if let state = state {
|
||||||
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||||
if state.passwordText.isEmpty {
|
if state.passwordText.isEmpty {
|
||||||
} else if state.passwordText != state.passwordConfirmationText {
|
} else if state.passwordText != state.passwordConfirmationText {
|
||||||
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
|
||||||
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||||
} else {
|
} else {
|
||||||
let saveImpl: () -> Void = {
|
let saveImpl: () -> Void = {
|
||||||
@ -349,6 +303,73 @@ func createPasswordController(account: Account, state: CreatePasswordState, comp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let arguments = CreatePasswordControllerArguments(updateFieldText: { field, updatedText in
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
switch field {
|
||||||
|
case .password:
|
||||||
|
state.passwordText = updatedText
|
||||||
|
case .passwordConfirmation:
|
||||||
|
state.passwordConfirmationText = updatedText
|
||||||
|
case .hint:
|
||||||
|
state.hintText = updatedText
|
||||||
|
case .email:
|
||||||
|
state.emailText = updatedText
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}, selectNextInputItem: { tag in
|
||||||
|
selectNextInputItemImpl?(tag)
|
||||||
|
}, save: {
|
||||||
|
saveImpl()
|
||||||
|
}, cancelEmailConfirmation: {
|
||||||
|
var currentPassword: String?
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
switch state.state {
|
||||||
|
case let .setup(password):
|
||||||
|
currentPassword = password
|
||||||
|
case .pendingVerification:
|
||||||
|
currentPassword = nil
|
||||||
|
}
|
||||||
|
state.saving = true
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
saveDisposable.set((updateTwoStepVerificationPassword(network: account.network, currentPassword: currentPassword, updatedPassword: .none)
|
||||||
|
|> deliverOnMainQueue).start(next: { _ in
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
state.saving = false
|
||||||
|
state.state = .setup(currentPassword: nil)
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
updatePasswordEmailConfirmation(nil)
|
||||||
|
}, error: { _ in
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
state.saving = false
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, statePromise.get())
|
||||||
|
|> deliverOnMainQueue
|
||||||
|
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState<CreatePasswordEntry>, CreatePasswordEntry.ItemGenerationArguments)) in
|
||||||
|
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||||
|
dismissImpl?()
|
||||||
|
})
|
||||||
|
var rightNavigationButton: ItemListNavigationButton?
|
||||||
|
if state.saving {
|
||||||
|
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
|
||||||
|
} else {
|
||||||
|
switch state.state {
|
||||||
|
case .setup:
|
||||||
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: !state.passwordText.isEmpty, action: {
|
||||||
|
saveImpl()
|
||||||
})
|
})
|
||||||
case .pendingVerification:
|
case .pendingVerification:
|
||||||
break
|
break
|
||||||
@ -402,6 +423,28 @@ func createPasswordController(account: Account, state: CreatePasswordState, comp
|
|||||||
resultItemNode.focus()
|
resultItemNode.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
selectNextInputItemImpl = { [weak controller] currentTag in
|
||||||
|
guard let controller = controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultItemNode: ItemListSingleLineInputItemNode?
|
||||||
|
var focusOnNext = false
|
||||||
|
let _ = controller.frameForItemNode({ itemNode in
|
||||||
|
if let itemNode = itemNode as? ItemListSingleLineInputItemNode, let tag = itemNode.tag {
|
||||||
|
if focusOnNext && resultItemNode == nil {
|
||||||
|
resultItemNode = itemNode
|
||||||
|
return true
|
||||||
|
} else if currentTag.isEqual(to: tag) {
|
||||||
|
focusOnNext = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
if let resultItemNode = resultItemNode {
|
||||||
|
resultItemNode.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
controller.didAppear = { firstTime in
|
controller.didAppear = { firstTime in
|
||||||
if !firstTime {
|
if !firstTime {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -390,6 +390,40 @@ public extension DeviceContactExtendedData {
|
|||||||
let basicData = DeviceContactBasicData(firstName: contact.givenName, lastName: contact.familyName, phoneNumbers: phoneNumbers)
|
let basicData = DeviceContactBasicData(firstName: contact.givenName, lastName: contact.familyName, phoneNumbers: phoneNumbers)
|
||||||
self.init(basicData: basicData, middleName: contact.middleName, prefix: contact.namePrefix, suffix: contact.nameSuffix, organization: contact.organizationName, jobTitle: contact.jobTitle, department: contact.departmentName, emailAddresses: emailAddresses, urls: urls, addresses: addresses, birthdayDate: birthdayDate, socialProfiles: socialProfiles, instantMessagingProfiles: instantMessagingProfiles)
|
self.init(basicData: basicData, middleName: contact.middleName, prefix: contact.namePrefix, suffix: contact.nameSuffix, organization: contact.organizationName, jobTitle: contact.jobTitle, department: contact.departmentName, emailAddresses: emailAddresses, urls: urls, addresses: addresses, birthdayDate: birthdayDate, socialProfiles: socialProfiles, instantMessagingProfiles: instantMessagingProfiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var isPrimitive: Bool {
|
||||||
|
if self.basicData.phoneNumbers.count > 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !self.organization.isEmpty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !self.jobTitle.isEmpty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !self.department.isEmpty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !self.emailAddresses.isEmpty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !self.urls.isEmpty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !self.addresses.isEmpty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if self.birthdayDate != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !self.socialProfiles.isEmpty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !self.instantMessagingProfiles.isEmpty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension DeviceContactExtendedData {
|
extension DeviceContactExtendedData {
|
||||||
|
|||||||
@ -288,7 +288,7 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate {
|
|||||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: nodeHeight - separatorHeight), size: CGSize(width: width, height: separatorHeight)))
|
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: nodeHeight - separatorHeight), size: CGSize(width: width, height: separatorHeight)))
|
||||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: nodeHeight)))
|
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: nodeHeight)))
|
||||||
|
|
||||||
if !fabs(previousContentHeight - contentHeight).isLess(than: CGFloat.ulpOfOne) {
|
if !abs(previousContentHeight - contentHeight).isLess(than: CGFloat.ulpOfOne) {
|
||||||
let contentOffset = CGPoint(x: 0, y: max(0, contentHeight - nodeHeight))
|
let contentOffset = CGPoint(x: 0, y: max(0, contentHeight - nodeHeight))
|
||||||
if case .immediate = transition {
|
if case .immediate = transition {
|
||||||
self.scrollNode.view.contentOffset = contentOffset
|
self.scrollNode.view.contentOffset = contentOffset
|
||||||
|
|||||||
@ -339,6 +339,14 @@ class FormControllerNode<InitParams, InnerState: FormControllerInnerState>: View
|
|||||||
let contentOffset = CGPoint(x: 0.0, y: -insets.top)
|
let contentOffset = CGPoint(x: 0.0, y: -insets.top)
|
||||||
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: contentOffset.y), size: layout.size))
|
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: contentOffset.y), size: layout.size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if previousLayout == nil {
|
||||||
|
self.didAppear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func didAppear() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateIn() {
|
func animateIn() {
|
||||||
|
|||||||
@ -91,7 +91,7 @@ final class FormControllerTextInputItemNode: FormBlockItemNode<FormControllerTex
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (FormControllerItemPreLayout(aligningInset: aligningInset), { params in
|
return (FormControllerItemPreLayout(aligningInset: aligningInset), { params in
|
||||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset, y: 11.0), size: titleSize))
|
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: leftInset, y: 12.0), size: titleSize))
|
||||||
|
|
||||||
let capitalizationType: UITextAutocapitalizationType
|
let capitalizationType: UITextAutocapitalizationType
|
||||||
let autocorrectionType: UITextAutocorrectionType
|
let autocorrectionType: UITextAutocorrectionType
|
||||||
|
|||||||
@ -165,11 +165,12 @@ class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem {
|
|||||||
let updatingImage: ItemListAvatarAndNameInfoItemUpdatingAvatar?
|
let updatingImage: ItemListAvatarAndNameInfoItemUpdatingAvatar?
|
||||||
let call: (() -> Void)?
|
let call: (() -> Void)?
|
||||||
let action: (() -> Void)?
|
let action: (() -> Void)?
|
||||||
|
let longTapAction: (() -> Void)?
|
||||||
let tag: ItemListItemTag?
|
let tag: ItemListItemTag?
|
||||||
|
|
||||||
let selectable: Bool
|
let selectable: Bool
|
||||||
|
|
||||||
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, mode: ItemListAvatarAndNameInfoItemMode, peer: Peer?, presence: PeerPresence?, label: String? = nil, cachedData: CachedPeerData?, state: ItemListAvatarAndNameInfoItemState, sectionId: ItemListSectionId, style: ItemListAvatarAndNameInfoItemStyle, editingNameUpdated: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, avatarTapped: @escaping () -> Void, context: ItemListAvatarAndNameInfoItemContext? = nil, updatingImage: ItemListAvatarAndNameInfoItemUpdatingAvatar? = nil, call: (() -> Void)? = nil, action: (() -> Void)? = nil, tag: ItemListItemTag? = nil) {
|
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, mode: ItemListAvatarAndNameInfoItemMode, peer: Peer?, presence: PeerPresence?, label: String? = nil, cachedData: CachedPeerData?, state: ItemListAvatarAndNameInfoItemState, sectionId: ItemListSectionId, style: ItemListAvatarAndNameInfoItemStyle, editingNameUpdated: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, avatarTapped: @escaping () -> Void, context: ItemListAvatarAndNameInfoItemContext? = nil, updatingImage: ItemListAvatarAndNameInfoItemUpdatingAvatar? = nil, call: (() -> Void)? = nil, action: (() -> Void)? = nil, longTapAction: (() -> Void)? = nil, tag: ItemListItemTag? = nil) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
@ -187,6 +188,7 @@ class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem {
|
|||||||
self.updatingImage = updatingImage
|
self.updatingImage = updatingImage
|
||||||
self.call = call
|
self.call = call
|
||||||
self.action = action
|
self.action = action
|
||||||
|
self.longTapAction = longTapAction
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
|
|
||||||
if case .settings = mode {
|
if case .settings = mode {
|
||||||
@ -909,4 +911,12 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
|
|||||||
func focus() {
|
func focus() {
|
||||||
self.inputFirstField?.becomeFirstResponder()
|
self.inputFirstField?.becomeFirstResponder()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func longTapped() {
|
||||||
|
self.item?.longTapAction?()
|
||||||
|
}
|
||||||
|
|
||||||
|
override var canBeLongTapped: Bool {
|
||||||
|
return self.item?.longTapAction != nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,7 @@ final class ItemListRevealOptionsGestureRecognizer: UIPanGestureRecognizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ItemListRevealOptionsItemNode: ListViewItemNode {
|
class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelegate {
|
||||||
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
||||||
|
|
||||||
private var leftRevealNode: ItemListRevealOptionsNode?
|
private var leftRevealNode: ItemListRevealOptionsNode?
|
||||||
@ -64,6 +64,7 @@ class ItemListRevealOptionsItemNode: ListViewItemNode {
|
|||||||
private(set) var revealOffset: CGFloat = 0.0
|
private(set) var revealOffset: CGFloat = 0.0
|
||||||
|
|
||||||
private var recognizer: ItemListRevealOptionsGestureRecognizer?
|
private var recognizer: ItemListRevealOptionsGestureRecognizer?
|
||||||
|
private var tapRecognizer: UITapGestureRecognizer?
|
||||||
private var hapticFeedback: HapticFeedback?
|
private var hapticFeedback: HapticFeedback?
|
||||||
|
|
||||||
private var allowAnyDirection = false
|
private var allowAnyDirection = false
|
||||||
@ -88,6 +89,11 @@ class ItemListRevealOptionsItemNode: ListViewItemNode {
|
|||||||
recognizer.allowAnyDirection = self.allowAnyDirection
|
recognizer.allowAnyDirection = self.allowAnyDirection
|
||||||
self.view.addGestureRecognizer(recognizer)
|
self.view.addGestureRecognizer(recognizer)
|
||||||
|
|
||||||
|
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.revealTapGesture(_:)))
|
||||||
|
self.tapRecognizer = tapRecognizer
|
||||||
|
tapRecognizer.delegate = self
|
||||||
|
self.view.addGestureRecognizer(tapRecognizer)
|
||||||
|
|
||||||
self.view.disablesInteractiveTransitionGestureRecognizer = self.allowAnyDirection
|
self.view.disablesInteractiveTransitionGestureRecognizer = self.allowAnyDirection
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,6 +142,24 @@ class ItemListRevealOptionsItemNode: ListViewItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
if let recognizer = self.recognizer, gestureRecognizer == self.tapRecognizer {
|
||||||
|
return abs(self.revealOffset) > 0.0 && !recognizer.validatedGesture
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func revealTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||||
|
if case .ended = recognizer.state {
|
||||||
|
self.updateRevealOffsetInternal(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc func revealGesture(_ recognizer: ItemListRevealOptionsGestureRecognizer) {
|
@objc func revealGesture(_ recognizer: ItemListRevealOptionsGestureRecognizer) {
|
||||||
guard let (size, _, _) = self.validLayout else {
|
guard let (size, _, _) = self.validLayout else {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -186,9 +186,9 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
self.titleNode.frame = titleFrame
|
self.titleNode.frame = titleFrame
|
||||||
|
|
||||||
if (fabs(revealFactor) >= 0.4) {
|
if (abs(revealFactor) >= 0.4) {
|
||||||
animationNode.play()
|
animationNode.play()
|
||||||
} else if fabs(revealFactor) < CGFloat.ulpOfOne {
|
} else if abs(revealFactor) < CGFloat.ulpOfOne {
|
||||||
animationNode.reset()
|
animationNode.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ class ItemListSingleLineInputItem: ListViewItem, ItemListItem {
|
|||||||
let text: String
|
let text: String
|
||||||
let placeholder: String
|
let placeholder: String
|
||||||
let type: ItemListSingleLineInputItemType
|
let type: ItemListSingleLineInputItemType
|
||||||
|
let returnKeyType: UIReturnKeyType
|
||||||
let spacing: CGFloat
|
let spacing: CGFloat
|
||||||
let clearButton: Bool
|
let clearButton: Bool
|
||||||
let sectionId: ItemListSectionId
|
let sectionId: ItemListSectionId
|
||||||
@ -25,12 +26,13 @@ class ItemListSingleLineInputItem: ListViewItem, ItemListItem {
|
|||||||
let processPaste: ((String) -> String)?
|
let processPaste: ((String) -> String)?
|
||||||
let tag: ItemListItemTag?
|
let tag: ItemListItemTag?
|
||||||
|
|
||||||
init(theme: PresentationTheme, title: NSAttributedString, text: String, placeholder: String, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), spacing: CGFloat = 0.0, clearButton: Bool = false, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, processPaste: ((String) -> String)? = nil, action: @escaping () -> Void) {
|
init(theme: PresentationTheme, title: NSAttributedString, text: String, placeholder: String, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), returnKeyType: UIReturnKeyType = .`default`, spacing: CGFloat = 0.0, clearButton: Bool = false, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, processPaste: ((String) -> String)? = nil, action: @escaping () -> Void) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.title = title
|
self.title = title
|
||||||
self.text = text
|
self.text = text
|
||||||
self.placeholder = placeholder
|
self.placeholder = placeholder
|
||||||
self.type = type
|
self.type = type
|
||||||
|
self.returnKeyType = returnKeyType
|
||||||
self.spacing = spacing
|
self.spacing = spacing
|
||||||
self.clearButton = clearButton
|
self.clearButton = clearButton
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
@ -244,6 +246,9 @@ class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDelegate, It
|
|||||||
if strongSelf.textNode.textField.autocorrectionType != autocorrectionType {
|
if strongSelf.textNode.textField.autocorrectionType != autocorrectionType {
|
||||||
strongSelf.textNode.textField.autocorrectionType = autocorrectionType
|
strongSelf.textNode.textField.autocorrectionType = autocorrectionType
|
||||||
}
|
}
|
||||||
|
if strongSelf.textNode.textField.returnKeyType != item.returnKeyType {
|
||||||
|
strongSelf.textNode.textField.returnKeyType = item.returnKeyType
|
||||||
|
}
|
||||||
|
|
||||||
if let currentText = strongSelf.textNode.textField.text {
|
if let currentText = strongSelf.textNode.textField.text {
|
||||||
if currentText != item.text {
|
if currentText != item.text {
|
||||||
|
|||||||
@ -27,6 +27,7 @@ func legacySecureIdScanController(theme: PresentationTheme, strings: Presentatio
|
|||||||
legacyController?.dismiss()
|
legacyController?.dismiss()
|
||||||
}, rootController: nil)
|
}, rootController: nil)
|
||||||
|
|
||||||
|
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||||
legacyController.bind(controller: navigationController)
|
legacyController.bind(controller: navigationController)
|
||||||
|
|
||||||
return legacyController
|
return legacyController
|
||||||
|
|||||||
@ -280,7 +280,7 @@ final class MediaNavigationAccessoryHeaderNode: ASDisplayNode {
|
|||||||
let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0))
|
let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0))
|
||||||
transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 18.0 - closeButtonSize.width, y: minimizedTitleFrame.minY + 8.0), size: closeButtonSize))
|
transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 18.0 - closeButtonSize.width, y: minimizedTitleFrame.minY + 8.0), size: closeButtonSize))
|
||||||
let rateButtonSize = CGSize(width: 24.0, height: minHeight)
|
let rateButtonSize = CGSize(width: 24.0, height: minHeight)
|
||||||
transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 18.0 - closeButtonSize.width - 8.0 - rateButtonSize.width, y: 0.0), size: rateButtonSize))
|
transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 18.0 - closeButtonSize.width - 18.0 - rateButtonSize.width, y: 0.0), size: rateButtonSize))
|
||||||
transition.updateFrame(node: self.actionPlayNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 40.0, height: 37.0)))
|
transition.updateFrame(node: self.actionPlayNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 40.0, height: 37.0)))
|
||||||
transition.updateFrame(node: self.actionPauseNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 40.0, height: 37.0)))
|
transition.updateFrame(node: self.actionPauseNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 40.0, height: 37.0)))
|
||||||
transition.updateFrame(node: self.actionButton, frame: CGRect(origin: CGPoint(x: 0.0, y: minimizedTitleFrame.minY - 4.0), size: CGSize(width: 40.0, height: 37.0)))
|
transition.updateFrame(node: self.actionButton, frame: CGRect(origin: CGPoint(x: 0.0, y: minimizedTitleFrame.minY - 4.0), size: CGSize(width: 40.0, height: 37.0)))
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import TelegramCore
|
|||||||
import Postbox
|
import Postbox
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
|
|
||||||
public func openExternalUrl(account: Account, url: String, presentationData: PresentationData, applicationContext: TelegramApplicationContext, navigationController: NavigationController?, dismissInput: @escaping () -> Void) {
|
public func openExternalUrl(account: Account, url: String, forceExternal: Bool = false, presentationData: PresentationData, applicationContext: TelegramApplicationContext, navigationController: NavigationController?, dismissInput: @escaping () -> Void) {
|
||||||
if url.lowercased().hasPrefix("tel:") || url.lowercased().hasPrefix("calshow:") {
|
if forceExternal || url.lowercased().hasPrefix("tel:") || url.lowercased().hasPrefix("calshow:") {
|
||||||
applicationContext.applicationBindings.openUrl(url)
|
applicationContext.applicationBindings.openUrl(url)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -198,6 +198,7 @@ public func openExternalUrl(account: Account, url: String, presentationData: Pre
|
|||||||
var botId: Int32?
|
var botId: Int32?
|
||||||
var scope: String?
|
var scope: String?
|
||||||
var publicKey: String?
|
var publicKey: String?
|
||||||
|
var callbackUrl: String?
|
||||||
var opaquePayload = Data()
|
var opaquePayload = Data()
|
||||||
var opaqueNonce = Data()
|
var opaqueNonce = Data()
|
||||||
if let queryItems = components.queryItems {
|
if let queryItems = components.queryItems {
|
||||||
@ -211,6 +212,8 @@ public func openExternalUrl(account: Account, url: String, presentationData: Pre
|
|||||||
scope = value
|
scope = value
|
||||||
} else if queryItem.name == "public_key" {
|
} else if queryItem.name == "public_key" {
|
||||||
publicKey = value
|
publicKey = value
|
||||||
|
} else if queryItem.name == "callback_url" {
|
||||||
|
callbackUrl = value
|
||||||
} else if queryItem.name == "payload" {
|
} else if queryItem.name == "payload" {
|
||||||
if let data = value.data(using: .utf8) {
|
if let data = value.data(using: .utf8) {
|
||||||
opaquePayload = data
|
opaquePayload = data
|
||||||
@ -236,7 +239,7 @@ public func openExternalUrl(account: Account, url: String, presentationData: Pre
|
|||||||
}
|
}
|
||||||
|
|
||||||
if valid && GlobalExperimentalSettings.enablePassport {
|
if valid && GlobalExperimentalSettings.enablePassport {
|
||||||
if let botId = botId, let scope = scope, let publicKey = publicKey {
|
if let botId = botId, let scope = scope, let publicKey = publicKey, let callbackUrl = callbackUrl {
|
||||||
if scope.hasPrefix("{") && scope.hasSuffix("}") {
|
if scope.hasPrefix("{") && scope.hasSuffix("}") {
|
||||||
opaquePayload = Data()
|
opaquePayload = Data()
|
||||||
if opaqueNonce.isEmpty {
|
if opaqueNonce.isEmpty {
|
||||||
@ -245,7 +248,7 @@ public func openExternalUrl(account: Account, url: String, presentationData: Pre
|
|||||||
} else if opaquePayload.isEmpty {
|
} else if opaquePayload.isEmpty {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = SecureIdAuthController(account: account, mode: .form(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: botId), scope: scope, publicKey: publicKey, opaquePayload: opaquePayload, opaqueNonce: opaqueNonce))
|
let controller = SecureIdAuthController(account: account, mode: .form(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: botId), scope: scope, publicKey: publicKey, callbackUrl: callbackUrl, opaquePayload: opaquePayload, opaqueNonce: opaqueNonce))
|
||||||
|
|
||||||
if let navigationController = navigationController {
|
if let navigationController = navigationController {
|
||||||
navigationController.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
|
navigationController.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
|
||||||
|
|||||||
@ -24,7 +24,7 @@ func generatePlayerRateIcon(_ color: UIColor) -> UIImage? {
|
|||||||
context.setStrokeColor(color.cgColor)
|
context.setStrokeColor(color.cgColor)
|
||||||
context.setLineWidth(4.0)
|
context.setLineWidth(4.0)
|
||||||
context.scaleBy(x: 0.3333, y: 0.3333)
|
context.scaleBy(x: 0.3333, y: 0.3333)
|
||||||
let _ = try? drawSvgPath(context, path: "M15.3637695,32.1972656 L23.7749023,32.1972656 C24.6127972,32.1972656 25.2519509,32.3691389 25.6923828,32.7128906 C26.1328147,33.0566423 26.3530273,33.5239228 26.3530273,34.1147461 C26.3530273,34.6411159 26.1784685,35.0869122 25.8293457,35.4521484 C25.4802229,35.8173846 24.9511754,36 24.2421875,36 L12.3828125,36 C11.5771444,36 10.9487327,35.7771018 10.4975586,35.3312988 C10.0463845,34.8854958 9.82080078,34.3618194 9.82080078,33.7602539 C9.82080078,33.3735332 9.96581886,32.8605989 10.2558594,32.2214355 C10.5458999,31.5822722 10.8627913,31.08008 11.206543,30.7148438 C12.635261,29.2324145 13.9243107,27.9621635 15.0737305,26.9040527 C16.2231503,25.845942 17.0449194,25.1503923 17.5390625,24.8173828 C18.4199263,24.1943328 19.1530732,23.5686067 19.7385254,22.9401855 C20.3239775,22.3117644 20.7697739,21.6672396 21.0759277,21.0065918 C21.3820816,20.345944 21.5351562,19.6987336 21.5351562,19.0649414 C21.5351562,18.377438 21.3713395,17.7624539 21.0437012,17.2199707 C20.7160628,16.6774875 20.2702665,16.2558609 19.7062988,15.9550781 C19.1423312,15.6542954 18.5273471,15.5039062 17.8613281,15.5039062 C16.4540945,15.5039062 15.3476603,16.1215759 14.5419922,17.3569336 C14.4345698,17.5180672 14.2546399,17.9584925 14.0021973,18.6782227 C13.7497546,19.3979528 13.4650895,19.9511699 13.1481934,20.3378906 C12.8312972,20.7246113 12.3667023,20.9179688 11.7543945,20.9179688 C11.2172825,20.9179688 10.7714861,20.7407244 10.4169922,20.3862305 C10.0624982,20.0317365 9.88525391,19.5483429 9.88525391,18.9360352 C9.88525391,18.1948205 10.0517561,17.4213907 10.3847656,16.6157227 C10.7177751,15.8100546 11.2145963,15.0795931 11.8752441,14.4243164 C12.535892,13.7690397 13.3737742,13.2399922 14.388916,12.8371582 C15.4040578,12.4343242 16.5937432,12.2329102 17.9580078,12.2329102 C19.6015707,12.2329102 21.0034122,12.4907201 22.1635742,13.0063477 C22.9155311,13.3500994 23.576169,13.8227509 24.1455078,14.4243164 C24.7148466,15.0258819 25.1579574,15.7214316 25.4748535,16.5109863 C25.7917496,17.3005411 25.9501953,18.1196247 25.9501953,18.9682617 C25.9501953,20.3002996 25.6198764,21.5114692 24.9592285,22.6018066 C24.2985807,23.6921441 23.6245152,24.5461395 22.9370117,25.1638184 C22.2495083,25.7814972 21.0974202,26.75097 19.4807129,28.0722656 C17.8640056,29.3935613 16.7548858,30.4194299 16.1533203,31.1499023 C15.8955065,31.4399429 15.6323256,31.7890605 15.3637695,32.1972656 Z M28.8464425,31.4077148 L34.1315987,23.6894531 L29.6843331,16.8251953 C29.2653857,16.1591764 28.9511799,15.5871606 28.7417062,15.1091309 C28.5322325,14.6311011 28.4274972,14.1718772 28.4274972,13.7314453 C28.4274972,13.2802712 28.6289112,12.8747577 29.0317452,12.5148926 C29.4345793,12.1550275 29.9260294,11.9750977 30.5061105,11.9750977 C31.1721294,11.9750977 31.6904348,12.1711406 32.0610421,12.5632324 C32.4316494,12.9553242 32.9445837,13.6831002 33.5998605,14.746582 L37.1447823,20.4829102 L40.9314034,14.746582 C41.2429284,14.2631812 41.5087949,13.8496111 41.7290109,13.5058594 C41.9492268,13.1621077 42.1613829,12.8774425 42.3654855,12.6518555 C42.569588,12.4262684 42.7978572,12.2570806 43.0502999,12.1442871 C43.3027426,12.0314936 43.5954643,11.9750977 43.9284737,11.9750977 C44.5300392,11.9750977 45.0214894,12.1550275 45.402839,12.5148926 C45.7841885,12.8747577 45.9748605,13.3017553 45.9748605,13.7958984 C45.9748605,14.5156286 45.5612904,15.4931579 44.7341378,16.7285156 L40.0773995,23.6894531 L45.08863,31.4077148 C45.5398041,32.084476 45.8674376,32.6457497 46.0715401,33.0915527 C46.2756427,33.5373557 46.3776925,33.9589824 46.3776925,34.3564453 C46.3776925,34.7324238 46.2863848,35.0761703 46.1037667,35.3876953 C45.9211486,35.6992203 45.6633387,35.9462881 45.3303292,36.1289062 C44.9973197,36.3115244 44.6213469,36.402832 44.2023995,36.402832 C43.7512254,36.402832 43.3698815,36.3088388 43.0583566,36.1208496 C42.7468316,35.9328604 42.4943927,35.6992201 42.3010323,35.4199219 C42.107672,35.1406236 41.7478123,34.5981486 41.2214425,33.7924805 L37.0642159,27.2504883 L32.6491769,33.9858398 C32.3054251,34.5229519 32.0610428,34.8989247 31.9160226,35.1137695 C31.7710023,35.3286144 31.5964435,35.5380849 31.3923409,35.7421875 C31.1882383,35.9462901 30.9465415,36.1074213 30.6672433,36.2255859 C30.387945,36.3437506 30.0603116,36.402832 29.6843331,36.402832 C29.1042521,36.402832 28.623544,36.2255877 28.2421944,35.8710938 C27.8608449,35.5165998 27.670173,35.0009799 27.670173,34.3242188 C27.670173,33.5292929 28.0622589,32.5571347 28.8464425,31.4077148 Z M8,2 C4.6862915,2 2,4.6862915 2,8 L2,40 C2,43.3137085 4.6862915,46 8,46 L48,46 C51.3137085,46 54,43.3137085 54,40 L54,8 C54,4.6862915 51.3137085,2 48,2 L8,2 S ")
|
let _ = try? drawSvgPath(context, path: "M15.3637695,32.1972656 L23.7749023,32.1972656 C24.6127972,32.1972656 25.2519509,32.3691389 25.6923828,32.7128906 C26.1328147,33.0566423 26.3530273,33.5239228 26.3530273,34.1147461 C26.3530273,34.6411159 26.1784685,35.0869122 25.8293457,35.4521484 C25.4802229,35.8173846 24.9511754,36 24.2421875,36 L12.3828125,36 C11.5771444,36 10.9487327,35.7771018 10.4975586,35.3312988 C10.0463845,34.8854958 9.82080078,34.3618194 9.82080078,33.7602539 C9.82080078,33.3735332 9.96581886,32.8605989 10.2558594,32.2214355 C10.5458999,31.5822722 10.8627913,31.08008 11.206543,30.7148438 C12.635261,29.2324145 13.9243107,27.9621635 15.0737305,26.9040527 C16.2231503,25.845942 17.0449194,25.1503923 17.5390625,24.8173828 C18.4199263,24.1943328 19.1530732,23.5686067 19.7385254,22.9401855 C20.3239775,22.3117644 20.7697739,21.6672396 21.0759277,21.0065918 C21.3820816,20.345944 21.5351562,19.6987336 21.5351562,19.0649414 C21.5351562,18.377438 21.3713395,17.7624539 21.0437012,17.2199707 C20.7160628,16.6774875 20.2702665,16.2558609 19.7062988,15.9550781 C19.1423312,15.6542954 18.5273471,15.5039062 17.8613281,15.5039062 C16.4540945,15.5039062 15.3476603,16.1215759 14.5419922,17.3569336 C14.4345698,17.5180672 14.2546399,17.9584925 14.0021973,18.6782227 C13.7497546,19.3979528 13.4650895,19.9511699 13.1481934,20.3378906 C12.8312972,20.7246113 12.3667023,20.9179688 11.7543945,20.9179688 C11.2172825,20.9179688 10.7714861,20.7407244 10.4169922,20.3862305 C10.0624982,20.0317365 9.88525391,19.5483429 9.88525391,18.9360352 C9.88525391,18.1948205 10.0517561,17.4213907 10.3847656,16.6157227 C10.7177751,15.8100546 11.2145963,15.0795931 11.8752441,14.4243164 C12.535892,13.7690397 13.3737742,13.2399922 14.388916,12.8371582 C15.4040578,12.4343242 16.5937432,12.2329102 17.9580078,12.2329102 C19.6015707,12.2329102 21.0034122,12.4907201 22.1635742,13.0063477 C22.9155311,13.3500994 23.576169,13.8227509 24.1455078,14.4243164 C24.7148466,15.0258819 25.1579574,15.7214316 25.4748535,16.5109863 C25.7917496,17.3005411 25.9501953,18.1196247 25.9501953,18.9682617 C25.9501953,20.3002996 25.6198764,21.5114692 24.9592285,22.6018066 C24.2985807,23.6921441 23.6245152,24.5461395 22.9370117,25.1638184 C22.2495083,25.7814972 21.0974202,26.75097 19.4807129,28.0722656 C17.8640056,29.3935613 16.7548858,30.4194299 16.1533203,31.1499023 C15.8955065,31.4399429 15.6323256,31.7890605 15.3637695,32.1972656 Z M28.8464425,31.4077148 L34.1315987,23.6894531 L29.6843331,16.8251953 C29.2653857,16.1591764 28.9511799,15.5871606 28.7417062,15.1091309 C28.5322325,14.6311011 28.4274972,14.1718772 28.4274972,13.7314453 C28.4274972,13.2802712 28.6289112,12.8747577 29.0317452,12.5148926 C29.4345793,12.1550275 29.9260294,11.9750977 30.5061105,11.9750977 C31.1721294,11.9750977 31.6904348,12.1711406 32.0610421,12.5632324 C32.4316494,12.9553242 32.9445837,13.6831002 33.5998605,14.746582 L37.1447823,20.4829102 L40.9314034,14.746582 C41.2429284,14.2631812 41.5087949,13.8496111 41.7290109,13.5058594 C41.9492268,13.1621077 42.1613829,12.8774425 42.3654855,12.6518555 C42.569588,12.4262684 42.7978572,12.2570806 43.0502999,12.1442871 C43.3027426,12.0314936 43.5954643,11.9750977 43.9284737,11.9750977 C44.5300392,11.9750977 45.0214894,12.1550275 45.402839,12.5148926 C45.7841885,12.8747577 45.9748605,13.3017553 45.9748605,13.7958984 C45.9748605,14.5156286 45.5612904,15.4931579 44.7341378,16.7285156 L40.0773995,23.6894531 L45.08863,31.4077148 C45.5398041,32.084476 45.8674376,32.6457497 46.0715401,33.0915527 C46.2756427,33.5373557 46.3776925,33.9589824 46.3776925,34.3564453 C46.3776925,34.7324238 46.2863848,35.0761703 46.1037667,35.3876953 C45.9211486,35.6992203 45.6633387,35.9462881 45.3303292,36.1289062 C44.9973197,36.3115244 44.6213469,36.402832 44.2023995,36.402832 C43.7512254,36.402832 43.3698815,36.3088388 43.0583566,36.1208496 C42.7468316,35.9328604 42.4943927,35.6992201 42.3010323,35.4199219 C42.107672,35.1406236 41.7478123,34.5981486 41.2214425,33.7924805 L37.0642159,27.2504883 L32.6491769,33.9858398 C32.3054251,34.5229519 32.0610428,34.8989247 31.9160226,35.1137695 C31.7710023,35.3286144 31.5964435,35.5380849 31.3923409,35.7421875 C31.1882383,35.9462901 30.9465415,36.1074213 30.6672433,36.2255859 C30.387945,36.3437506 30.0603116,36.402832 29.6843331,36.402832 C29.1042521,36.402832 28.623544,36.2255877 28.2421944,35.8710938 C27.8608449,35.5165998 27.670173,35.0009799 27.670173,34.3242188 C27.670173,33.5292929 28.0622589,32.5571347 28.8464425,31.4077148 Z M8,2 C4.6862915,2 2,4.6862915 2,8 L2,40 C2,43.3137085 4.6862915,46 8,46 L48,46 C51.3137085,46 54,43.3137085 54,40 L54,8 C54,4.6862915 51.3137085,2 48,2 L8,2 S")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,30 @@ import SwiftSignalKit
|
|||||||
import Postbox
|
import Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
|
|
||||||
|
public enum SecureIdRequestResult: String {
|
||||||
|
case success = "success"
|
||||||
|
case cancel = "cancel"
|
||||||
|
case error = "error"
|
||||||
|
}
|
||||||
|
|
||||||
|
public func secureIdCallbackUrl(with baseUrl: String, peerId: PeerId, result: SecureIdRequestResult, parameters: [String : String]) -> String {
|
||||||
|
var query = (parameters.compactMap({ (key, value) -> String in
|
||||||
|
return "\(key)=\(value)"
|
||||||
|
}) as Array).joined(separator: "&")
|
||||||
|
|
||||||
|
if !query.isEmpty {
|
||||||
|
query = "?" + query
|
||||||
|
}
|
||||||
|
|
||||||
|
let url: String
|
||||||
|
if baseUrl.hasPrefix("tgbot") {
|
||||||
|
url = "tgbot\(peerId.id)://passport/" + result.rawValue + query
|
||||||
|
} else {
|
||||||
|
url = baseUrl + (baseUrl.range(of: "?") != nil ? "&" : "?") + "tg_passport=" + result.rawValue + query
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
final class SecureIdAuthControllerInteraction {
|
final class SecureIdAuthControllerInteraction {
|
||||||
let updateState: ((SecureIdAuthControllerState) -> SecureIdAuthControllerState) -> Void
|
let updateState: ((SecureIdAuthControllerState) -> SecureIdAuthControllerState) -> Void
|
||||||
let present: (ViewController, Any?) -> Void
|
let present: (ViewController, Any?) -> Void
|
||||||
@ -30,7 +54,7 @@ final class SecureIdAuthControllerInteraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum SecureIdAuthControllerMode {
|
enum SecureIdAuthControllerMode {
|
||||||
case form(peerId: PeerId, scope: String, publicKey: String, opaquePayload: Data, opaqueNonce: Data)
|
case form(peerId: PeerId, scope: String, publicKey: String, callbackUrl: String, opaquePayload: Data, opaqueNonce: Data)
|
||||||
case list
|
case list
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,9 +87,9 @@ final class SecureIdAuthController: ViewController {
|
|||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .form:
|
case .form:
|
||||||
self.state = .form(SecureIdAuthControllerFormState(encryptedFormData: nil, formData: nil, verificationState: nil, removingValues: false))
|
self.state = .form(SecureIdAuthControllerFormState(twoStepEmail: nil, encryptedFormData: nil, formData: nil, verificationState: nil, removingValues: false))
|
||||||
case .list:
|
case .list:
|
||||||
self.state = .list(SecureIdAuthControllerListState(accountPeer: nil, verificationState: nil, encryptedValues: nil, primaryLanguageByCountry: [:], values: nil, removingValues: false))
|
self.state = .list(SecureIdAuthControllerListState(accountPeer: nil, twoStepEmail: nil, verificationState: nil, encryptedValues: nil, primaryLanguageByCountry: [:], values: nil, removingValues: false))
|
||||||
}
|
}
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||||
@ -90,6 +114,7 @@ final class SecureIdAuthController: ViewController {
|
|||||||
strongSelf.updateState(animated: true, { state in
|
strongSelf.updateState(animated: true, { state in
|
||||||
var state = state
|
var state = state
|
||||||
state.verificationState = .verified(context.context)
|
state.verificationState = .verified(context.context)
|
||||||
|
state.twoStepEmail = context.settings.email
|
||||||
switch state {
|
switch state {
|
||||||
case var .form(form):
|
case var .form(form):
|
||||||
form.formData = form.encryptedFormData.flatMap({ decryptedSecureIdForm(context: context.context, form: $0.form) })
|
form.formData = form.encryptedFormData.flatMap({ decryptedSecureIdForm(context: context.context, form: $0.form) })
|
||||||
@ -126,8 +151,44 @@ final class SecureIdAuthController: ViewController {
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
let handleError: (Any, String?, PeerId?) -> Void = { [weak self] error, callbackUrl, peerId in
|
||||||
|
if let strongSelf = self {
|
||||||
|
var passError: String?
|
||||||
|
var appUpdateRequired = false
|
||||||
|
switch error {
|
||||||
|
case let error as RequestSecureIdFormError:
|
||||||
|
if case let .serverError(error) = error, ["BOT_INVALID", "PUBLIC_KEY_REQUIRED", "PUBLIC_KEY_INVALID", "SCOPE_EMPTY", "PAYLOAD_EMPTY", "NONCE_EMPTY"].contains(error) {
|
||||||
|
passError = error
|
||||||
|
} else if case .versionOutdated = error {
|
||||||
|
appUpdateRequired = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case let error as GetAllSecureIdValuesError:
|
||||||
|
if case .versionOutdated = error {
|
||||||
|
appUpdateRequired = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if appUpdateRequired {
|
||||||
|
let errorText = strongSelf.presentationData.strings.Passport_UpdateRequiredError
|
||||||
|
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Application_Update, action: {})]), in: .window(.root))
|
||||||
|
} else if let callbackUrl = callbackUrl, let peerId = peerId {
|
||||||
|
let errorText = strongSelf.presentationData.strings.Login_UnknownError
|
||||||
|
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||||
|
if let error = passError {
|
||||||
|
strongSelf.openUrl(secureIdCallbackUrl(with: callbackUrl, peerId: peerId, result: .error, parameters: ["error": error]))
|
||||||
|
}
|
||||||
|
})]), in: .window(.root))
|
||||||
|
}
|
||||||
|
strongSelf.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch self.mode {
|
switch self.mode {
|
||||||
case let .form(peerId, scope, publicKey, _, _):
|
case let .form(peerId, scope, publicKey, callbackUrl, _, _):
|
||||||
self.formDisposable = (combineLatest(requestSecureIdForm(postbox: account.postbox, network: account.network, peerId: peerId, scope: scope, publicKey: publicKey), secureIdConfiguration(postbox: account.postbox, network: account.network) |> introduceError(RequestSecureIdFormError.self))
|
self.formDisposable = (combineLatest(requestSecureIdForm(postbox: account.postbox, network: account.network, peerId: peerId, scope: scope, publicKey: publicKey), secureIdConfiguration(postbox: account.postbox, network: account.network) |> introduceError(RequestSecureIdFormError.self))
|
||||||
|> mapToSignal { form, configuration -> Signal<SecureIdEncryptedFormData, RequestSecureIdFormError> in
|
|> mapToSignal { form, configuration -> Signal<SecureIdEncryptedFormData, RequestSecureIdFormError> in
|
||||||
return account.postbox.transaction { transaction -> Signal<SecureIdEncryptedFormData, RequestSecureIdFormError> in
|
return account.postbox.transaction { transaction -> Signal<SecureIdEncryptedFormData, RequestSecureIdFormError> in
|
||||||
@ -155,12 +216,8 @@ final class SecureIdAuthController: ViewController {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, error: { [weak self] _ in
|
}, error: { error in
|
||||||
if let strongSelf = self {
|
handleError(error, callbackUrl, peerId)
|
||||||
let errorText = strongSelf.presentationData.strings.Login_UnknownError
|
|
||||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
|
||||||
strongSelf.dismiss()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
case .list:
|
case .list:
|
||||||
self.formDisposable = (combineLatest(getAllSecureIdValues(network: self.account.network), secureIdConfiguration(postbox: account.postbox, network: account.network) |> introduceError(GetAllSecureIdValuesError.self), account.postbox.transaction { transaction -> Signal<Peer, GetAllSecureIdValuesError> in
|
self.formDisposable = (combineLatest(getAllSecureIdValues(network: self.account.network), secureIdConfiguration(postbox: account.postbox, network: account.network) |> introduceError(GetAllSecureIdValuesError.self), account.postbox.transaction { transaction -> Signal<Peer, GetAllSecureIdValuesError> in
|
||||||
@ -190,6 +247,8 @@ final class SecureIdAuthController: ViewController {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, error: { error in
|
||||||
|
handleError(error, nil, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,8 +382,18 @@ final class SecureIdAuthController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func openUrl(_ url: String) {
|
||||||
|
openExternalUrl(account: self.account, url: url, forceExternal: true, presentationData: self.presentationData, applicationContext: self.account.telegramApplicationContext, navigationController: nil, dismissInput: { [weak self] in
|
||||||
|
self?.view.endEditing(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func cancelPressed() {
|
@objc private func cancelPressed() {
|
||||||
self.dismiss()
|
self.dismiss()
|
||||||
|
|
||||||
|
if case let .form(reqForm) = self.mode {
|
||||||
|
self.openUrl(secureIdCallbackUrl(with: reqForm.callbackUrl, peerId: reqForm.peerId, result: .cancel, parameters: [:]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func checkPassword(password: String, inBackground: Bool, completion: @escaping () -> Void) {
|
@objc private func checkPassword(password: String, inBackground: Bool, completion: @escaping () -> Void) {
|
||||||
@ -349,6 +418,7 @@ final class SecureIdAuthController: ViewController {
|
|||||||
strongSelf.updateState(animated: !inBackground, { state in
|
strongSelf.updateState(animated: !inBackground, { state in
|
||||||
var state = state
|
var state = state
|
||||||
state.verificationState = .verified(context.context)
|
state.verificationState = .verified(context.context)
|
||||||
|
state.twoStepEmail = context.settings.email
|
||||||
switch state {
|
switch state {
|
||||||
case var .form(form):
|
case var .form(form):
|
||||||
form.formData = form.encryptedFormData.flatMap({ decryptedSecureIdForm(context: context.context, form: $0.form) })
|
form.formData = form.encryptedFormData.flatMap({ decryptedSecureIdForm(context: context.context, form: $0.form) })
|
||||||
@ -492,6 +562,7 @@ final class SecureIdAuthController: ViewController {
|
|||||||
let _ = (grantSecureIdAccess(network: self.account.network, peerId: encryptedFormData.servicePeer.id, publicKey: reqForm.publicKey, scope: reqForm.scope, opaquePayload: reqForm.opaquePayload, opaqueNonce: reqForm.opaqueNonce, values: values, requestedFields: formData.requestedFields)
|
let _ = (grantSecureIdAccess(network: self.account.network, peerId: encryptedFormData.servicePeer.id, publicKey: reqForm.publicKey, scope: reqForm.scope, opaquePayload: reqForm.opaquePayload, opaqueNonce: reqForm.opaqueNonce, values: values, requestedFields: formData.requestedFields)
|
||||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||||
self?.dismiss()
|
self?.dismiss()
|
||||||
|
self?.openUrl(secureIdCallbackUrl(with: reqForm.callbackUrl, peerId: reqForm.peerId, result: .success, parameters: [:]))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case .list:
|
case .list:
|
||||||
|
|||||||
@ -421,7 +421,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
return !touchedKeys.contains(value.value.key)
|
return !touchedKeys.contains(value.value.key)
|
||||||
}
|
}
|
||||||
values.append(contentsOf: updatedValues)
|
values.append(contentsOf: updatedValues)
|
||||||
return .form(SecureIdAuthControllerFormState(encryptedFormData: form.encryptedFormData, formData: SecureIdForm(peerId: formData.peerId, requestedFields: formData.requestedFields, values: values), verificationState: form.verificationState, removingValues: form.removingValues))
|
return .form(SecureIdAuthControllerFormState(twoStepEmail: form.twoStepEmail, encryptedFormData: form.encryptedFormData, formData: SecureIdForm(peerId: formData.peerId, requestedFields: formData.requestedFields, values: values), verificationState: form.verificationState, removingValues: form.removingValues))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,6 +594,9 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
currentValue = findValue(formData.values, key: .phone)?.1
|
currentValue = findValue(formData.values, key: .phone)?.1
|
||||||
case .email:
|
case .email:
|
||||||
|
if let email = form.twoStepEmail {
|
||||||
|
immediatelyAvailableValue = .email(SecureIdEmailValue(email: email))
|
||||||
|
}
|
||||||
currentValue = findValue(formData.values, key: .email)?.1
|
currentValue = findValue(formData.values, key: .email)?.1
|
||||||
}
|
}
|
||||||
let openForm: () -> Void = { [weak self] in
|
let openForm: () -> Void = { [weak self] in
|
||||||
@ -618,7 +621,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
if let valueWithContext = valueWithContext {
|
if let valueWithContext = valueWithContext {
|
||||||
values.append(valueWithContext)
|
values.append(valueWithContext)
|
||||||
}
|
}
|
||||||
return .form(SecureIdAuthControllerFormState(encryptedFormData: form.encryptedFormData, formData: SecureIdForm(peerId: formData.peerId, requestedFields: formData.requestedFields, values: values), verificationState: form.verificationState, removingValues: form.removingValues))
|
return .form(SecureIdAuthControllerFormState(twoStepEmail: form.twoStepEmail, encryptedFormData: form.encryptedFormData, formData: SecureIdForm(peerId: formData.peerId, requestedFields: formData.requestedFields, values: values), verificationState: form.verificationState, removingValues: form.removingValues))
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
@ -742,6 +745,58 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let deleteField: (SecureIdValueKey) -> Void = { [weak self] field in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = ActionSheetController(presentationTheme: strongSelf.presentationData.theme)
|
||||||
|
let dismissAction: () -> Void = { [weak controller] in
|
||||||
|
controller?.dismissAnimated()
|
||||||
|
}
|
||||||
|
let text: String
|
||||||
|
switch field {
|
||||||
|
case .phone:
|
||||||
|
text = strongSelf.presentationData.strings.Passport_Phone_Delete
|
||||||
|
default:
|
||||||
|
text = strongSelf.presentationData.strings.Passport_Email_Delete
|
||||||
|
}
|
||||||
|
controller.setItemGroups([
|
||||||
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: text, color: .destructive, action: { [weak self] in
|
||||||
|
dismissAction()
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.interaction.updateState { state in
|
||||||
|
if case var .list(list) = state {
|
||||||
|
list.removingValues = true
|
||||||
|
return .list(list)
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
strongSelf.deleteValueDisposable.set((deleteSecureIdValues(network: strongSelf.account.network, keys: Set([field]))
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.interaction.updateState { state in
|
||||||
|
if case var .list(list) = state , let values = list.values {
|
||||||
|
list.removingValues = false
|
||||||
|
list.values = values.filter {
|
||||||
|
$0.value.key != field
|
||||||
|
}
|
||||||
|
return .list(list)
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
})]),
|
||||||
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||||
|
])
|
||||||
|
strongSelf.view.endEditing(true)
|
||||||
|
strongSelf.interaction.present(controller, nil)
|
||||||
|
}
|
||||||
|
|
||||||
switch field {
|
switch field {
|
||||||
case .identity, .address:
|
case .identity, .address:
|
||||||
let keys: [(SecureIdValueKey, String, String)]
|
let keys: [(SecureIdValueKey, String, String)]
|
||||||
@ -782,6 +837,9 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
self.view.endEditing(true)
|
self.view.endEditing(true)
|
||||||
self.interaction.present(controller, nil)
|
self.interaction.present(controller, nil)
|
||||||
case .phone:
|
case .phone:
|
||||||
|
if findValue(values, key: .phone) != nil {
|
||||||
|
deleteField(.phone)
|
||||||
|
} else {
|
||||||
var immediatelyAvailableValue: SecureIdValue?
|
var immediatelyAvailableValue: SecureIdValue?
|
||||||
if let peer = list.accountPeer as? TelegramUser, let phone = peer.phone, !phone.isEmpty {
|
if let peer = list.accountPeer as? TelegramUser, let phone = peer.phone, !phone.isEmpty {
|
||||||
immediatelyAvailableValue = .phone(SecureIdPhoneValue(phone: phone))
|
immediatelyAvailableValue = .phone(SecureIdPhoneValue(phone: phone))
|
||||||
@ -789,12 +847,21 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
self.interaction.present(SecureIdPlaintextFormController(account: self.account, context: context, type: .phone, immediatelyAvailableValue: immediatelyAvailableValue, updatedValue: { value in
|
self.interaction.present(SecureIdPlaintextFormController(account: self.account, context: context, type: .phone, immediatelyAvailableValue: immediatelyAvailableValue, updatedValue: { value in
|
||||||
updatedValues(.phone)(value.flatMap({ [$0] }) ?? [])
|
updatedValues(.phone)(value.flatMap({ [$0] }) ?? [])
|
||||||
}), nil)
|
}), nil)
|
||||||
|
}
|
||||||
case .email:
|
case .email:
|
||||||
self.interaction.present(SecureIdPlaintextFormController(account: self.account, context: context, type: .email, immediatelyAvailableValue: nil, updatedValue: { value in
|
if findValue(values, key: .email) != nil {
|
||||||
|
deleteField(.email)
|
||||||
|
} else {
|
||||||
|
var immediatelyAvailableValue: SecureIdValue?
|
||||||
|
if let email = list.twoStepEmail {
|
||||||
|
immediatelyAvailableValue = .email(SecureIdEmailValue(email: email))
|
||||||
|
}
|
||||||
|
self.interaction.present(SecureIdPlaintextFormController(account: self.account, context: context, type: .email, immediatelyAvailableValue: immediatelyAvailableValue, updatedValue: { value in
|
||||||
updatedValues(.email)(value.flatMap({ [$0] }) ?? [])
|
updatedValues(.email)(value.flatMap({ [$0] }) ?? [])
|
||||||
}), nil)
|
}), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func deleteAllValues() {
|
private func deleteAllValues() {
|
||||||
let controller = ActionSheetController(presentationTheme: self.presentationData.theme)
|
let controller = ActionSheetController(presentationTheme: self.presentationData.theme)
|
||||||
|
|||||||
@ -22,20 +22,24 @@ enum SecureIdAuthControllerVerificationState: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct SecureIdAuthControllerFormState: Equatable {
|
struct SecureIdAuthControllerFormState: Equatable {
|
||||||
|
var twoStepEmail: String?
|
||||||
var encryptedFormData: SecureIdEncryptedFormData?
|
var encryptedFormData: SecureIdEncryptedFormData?
|
||||||
var formData: SecureIdForm?
|
var formData: SecureIdForm?
|
||||||
var verificationState: SecureIdAuthControllerVerificationState?
|
var verificationState: SecureIdAuthControllerVerificationState?
|
||||||
var removingValues: Bool = false
|
var removingValues: Bool = false
|
||||||
|
|
||||||
static func ==(lhs: SecureIdAuthControllerFormState, rhs: SecureIdAuthControllerFormState) -> Bool {
|
static func ==(lhs: SecureIdAuthControllerFormState, rhs: SecureIdAuthControllerFormState) -> Bool {
|
||||||
if (lhs.formData != nil) != (rhs.formData != nil) {
|
if let lhsTwoStepEmail = lhs.twoStepEmail, let rhsTwoStepEmail = rhs.twoStepEmail, lhsTwoStepEmail != rhsTwoStepEmail {
|
||||||
|
return false
|
||||||
|
} else if (lhs.twoStepEmail != nil) != (rhs.twoStepEmail != nil) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lhs.encryptedFormData != nil) != (rhs.encryptedFormData != nil) {
|
if (lhs.encryptedFormData != nil) != (rhs.encryptedFormData != nil) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if (lhs.formData != nil) != (rhs.formData != nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if let lhsFormData = lhs.formData, let rhsFormData = rhs.formData {
|
if let lhsFormData = lhs.formData, let rhsFormData = rhs.formData {
|
||||||
if lhsFormData != rhsFormData {
|
if lhsFormData != rhsFormData {
|
||||||
return false
|
return false
|
||||||
@ -43,21 +47,19 @@ struct SecureIdAuthControllerFormState: Equatable {
|
|||||||
} else if (lhs.formData != nil) != (rhs.formData != nil) {
|
} else if (lhs.formData != nil) != (rhs.formData != nil) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if lhs.verificationState != rhs.verificationState {
|
if lhs.verificationState != rhs.verificationState {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if lhs.removingValues != rhs.removingValues {
|
if lhs.removingValues != rhs.removingValues {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SecureIdAuthControllerListState: Equatable {
|
struct SecureIdAuthControllerListState: Equatable {
|
||||||
var accountPeer: Peer?
|
var accountPeer: Peer?
|
||||||
|
var twoStepEmail: String?
|
||||||
var verificationState: SecureIdAuthControllerVerificationState?
|
var verificationState: SecureIdAuthControllerVerificationState?
|
||||||
var encryptedValues: EncryptedAllSecureIdValues?
|
var encryptedValues: EncryptedAllSecureIdValues?
|
||||||
var primaryLanguageByCountry: [String: String]?
|
var primaryLanguageByCountry: [String: String]?
|
||||||
@ -65,6 +67,14 @@ struct SecureIdAuthControllerListState: Equatable {
|
|||||||
var removingValues: Bool = false
|
var removingValues: Bool = false
|
||||||
|
|
||||||
static func ==(lhs: SecureIdAuthControllerListState, rhs: SecureIdAuthControllerListState) -> Bool {
|
static func ==(lhs: SecureIdAuthControllerListState, rhs: SecureIdAuthControllerListState) -> Bool {
|
||||||
|
if !arePeersEqual(lhs.accountPeer, rhs.accountPeer) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if let lhsTwoStepEmail = lhs.twoStepEmail, let rhsTwoStepEmail = rhs.twoStepEmail, lhsTwoStepEmail != rhsTwoStepEmail {
|
||||||
|
return false
|
||||||
|
} else if (lhs.twoStepEmail != nil) != (rhs.twoStepEmail != nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.verificationState != rhs.verificationState {
|
if lhs.verificationState != rhs.verificationState {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -88,6 +98,26 @@ enum SecureIdAuthControllerState: Equatable {
|
|||||||
case form(SecureIdAuthControllerFormState)
|
case form(SecureIdAuthControllerFormState)
|
||||||
case list(SecureIdAuthControllerListState)
|
case list(SecureIdAuthControllerListState)
|
||||||
|
|
||||||
|
var twoStepEmail: String? {
|
||||||
|
get {
|
||||||
|
switch self {
|
||||||
|
case let .form(form):
|
||||||
|
return form.twoStepEmail
|
||||||
|
case let .list(list):
|
||||||
|
return list.twoStepEmail
|
||||||
|
}
|
||||||
|
} set(value) {
|
||||||
|
switch self {
|
||||||
|
case var .form(form):
|
||||||
|
form.twoStepEmail = value
|
||||||
|
self = .form(form)
|
||||||
|
case var .list(list):
|
||||||
|
list.twoStepEmail = value
|
||||||
|
self = .list(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var verificationState: SecureIdAuthControllerVerificationState? {
|
var verificationState: SecureIdAuthControllerVerificationState? {
|
||||||
get {
|
get {
|
||||||
switch self {
|
switch self {
|
||||||
|
|||||||
@ -532,12 +532,18 @@ private func fieldTitleAndText(field: SecureIdParsedRequestedFormField, strings:
|
|||||||
placeholder = strings.Passport_FieldIdentityDetailsHelp
|
placeholder = strings.Passport_FieldIdentityDetailsHelp
|
||||||
}
|
}
|
||||||
|
|
||||||
if personalDetails != nil {
|
if let personalDetails = personalDetails {
|
||||||
if let value = findValue(values, key: .personalDetails), case let .personalDetails(personalDetailsValue) = value.1.value {
|
if let value = findValue(values, key: .personalDetails), case let .personalDetails(personalDetailsValue) = value.1.value {
|
||||||
if !text.isEmpty {
|
if !text.isEmpty {
|
||||||
text.append(", ")
|
text.append(", ")
|
||||||
}
|
}
|
||||||
text.append(fieldsText(personalDetailsValue.latinName.firstName, personalDetailsValue.latinName.lastName, countryName(code: personalDetailsValue.countryCode, strings: strings)))
|
let fullName: String
|
||||||
|
if let nativeName = personalDetailsValue.nativeName, !nativeName.firstName.isEmpty, personalDetails.nativeNames {
|
||||||
|
fullName = nativeName.firstName + " " + nativeName.lastName
|
||||||
|
} else {
|
||||||
|
fullName = personalDetailsValue.latinName.firstName + " " + personalDetailsValue.latinName.lastName
|
||||||
|
}
|
||||||
|
text.append(fieldsText(fullName, countryName(code: personalDetailsValue.countryCode, strings: strings)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .address(addressDetails, document, _):
|
case let .address(addressDetails, document, _):
|
||||||
@ -567,7 +573,7 @@ private func fieldTitleAndText(field: SecureIdParsedRequestedFormField, strings:
|
|||||||
if !text.isEmpty {
|
if !text.isEmpty {
|
||||||
text.append(", ")
|
text.append(", ")
|
||||||
}
|
}
|
||||||
text.append(fieldsText(addressValue.postcode, addressValue.street1, addressValue.street2, addressValue.city))
|
text.append(fieldsText(addressValue.street1, addressValue.street2, addressValue.city, addressValue.state, addressValue.postcode, countryName(code: addressValue.countryCode, strings: strings)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .phone:
|
case .phone:
|
||||||
@ -596,6 +602,7 @@ private func fieldTitleAndText(field: SecureIdParsedRequestedFormField, strings:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private struct ValueAdditionalData {
|
private struct ValueAdditionalData {
|
||||||
|
var nativeNames: Bool = false
|
||||||
var selfie: Bool = false
|
var selfie: Bool = false
|
||||||
var translation: Bool = false
|
var translation: Bool = false
|
||||||
}
|
}
|
||||||
@ -603,6 +610,8 @@ private struct ValueAdditionalData {
|
|||||||
private func extractValueAdditionalData(_ value: SecureIdValue) -> ValueAdditionalData {
|
private func extractValueAdditionalData(_ value: SecureIdValue) -> ValueAdditionalData {
|
||||||
var data = ValueAdditionalData()
|
var data = ValueAdditionalData()
|
||||||
switch value {
|
switch value {
|
||||||
|
case let .personalDetails(value):
|
||||||
|
data.nativeNames = value.nativeName != nil
|
||||||
case let .passport(value):
|
case let .passport(value):
|
||||||
data.selfie = value.selfieDocument != nil
|
data.selfie = value.selfieDocument != nil
|
||||||
data.translation = !value.translations.isEmpty
|
data.translation = !value.translations.isEmpty
|
||||||
@ -724,7 +733,7 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode {
|
|||||||
|
|
||||||
func updateValues(_ values: [SecureIdValueWithContext]) {
|
func updateValues(_ values: [SecureIdValueWithContext]) {
|
||||||
var (title, text) = fieldTitleAndText(field: self.field, strings: self.strings, values: values)
|
var (title, text) = fieldTitleAndText(field: self.field, strings: self.strings, values: values)
|
||||||
var textColor = self.theme.list.itemSecondaryTextColor
|
let textColor = self.theme.list.itemSecondaryTextColor
|
||||||
/*switch self.field {
|
/*switch self.field {
|
||||||
case .identity:
|
case .identity:
|
||||||
if let error = errors[.personalDetails]?.first {
|
if let error = errors[.personalDetails]?.first {
|
||||||
@ -734,14 +743,18 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode {
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}*/
|
}*/
|
||||||
self.titleNode.attributedText = NSAttributedString(string: title, font: titleFont, textColor: self.theme.list.itemPrimaryTextColor)
|
|
||||||
self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: textColor)
|
|
||||||
|
|
||||||
var filled = true
|
var filled = true
|
||||||
switch self.field {
|
switch self.field {
|
||||||
case let .identity(personalDetails, document, selfie, translation):
|
case let .identity(personalDetails, document, selfie, translation):
|
||||||
if personalDetails != nil {
|
if let personalDetails = personalDetails {
|
||||||
if findValue(values, key: .personalDetails) == nil {
|
if let value = findValue(values, key: .personalDetails)?.1 {
|
||||||
|
let data = extractValueAdditionalData(value.value)
|
||||||
|
if personalDetails.nativeNames && !data.nativeNames {
|
||||||
|
filled = false
|
||||||
|
text = strings.Passport_FieldIdentityDetailsHelp
|
||||||
|
}
|
||||||
|
} else {
|
||||||
filled = false
|
filled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -752,9 +765,11 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode {
|
|||||||
let data = extractValueAdditionalData(value.value)
|
let data = extractValueAdditionalData(value.value)
|
||||||
if selfie && !data.selfie {
|
if selfie && !data.selfie {
|
||||||
filled = false
|
filled = false
|
||||||
|
text = strings.Passport_FieldIdentitySelfieHelp
|
||||||
}
|
}
|
||||||
if translation && !data.translation {
|
if translation && !data.translation {
|
||||||
filled = false
|
filled = false
|
||||||
|
text = strings.Passport_FieldIdentityTranslationHelp
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
filled = false
|
filled = false
|
||||||
@ -794,6 +809,7 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode {
|
|||||||
let data = extractValueAdditionalData(value.value)
|
let data = extractValueAdditionalData(value.value)
|
||||||
if translation && !data.translation {
|
if translation && !data.translation {
|
||||||
filled = false
|
filled = false
|
||||||
|
text = strings.Passport_FieldAddressTranslationHelp
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
filled = false
|
filled = false
|
||||||
@ -827,6 +843,9 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.titleNode.attributedText = NSAttributedString(string: title, font: titleFont, textColor: self.theme.list.itemPrimaryTextColor)
|
||||||
|
self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: textColor)
|
||||||
|
|
||||||
self.checkNode.isHidden = !filled
|
self.checkNode.isHidden = !filled
|
||||||
self.disclosureNode.isHidden = filled
|
self.disclosureNode.isHidden = filled
|
||||||
|
|
||||||
|
|||||||
@ -45,9 +45,9 @@ private func fieldTitleAndText(field: SecureIdAuthListContentField, strings: Pre
|
|||||||
let keyList: [(SecureIdValueKey, String)] = [
|
let keyList: [(SecureIdValueKey, String)] = [
|
||||||
(.personalDetails, strings.Passport_Identity_TypePersonalDetails),
|
(.personalDetails, strings.Passport_Identity_TypePersonalDetails),
|
||||||
(.passport, strings.Passport_Identity_TypePassport),
|
(.passport, strings.Passport_Identity_TypePassport),
|
||||||
(.internalPassport, strings.Passport_Identity_TypeInternalPassport),
|
(.idCard, strings.Passport_Identity_TypeIdentityCard),
|
||||||
(.driversLicense, strings.Passport_Identity_TypeDriversLicense),
|
(.driversLicense, strings.Passport_Identity_TypeDriversLicense),
|
||||||
(.idCard, strings.Passport_Identity_TypeIdentityCard)
|
(.internalPassport, strings.Passport_Identity_TypeInternalPassport)
|
||||||
]
|
]
|
||||||
|
|
||||||
var fields: [String] = []
|
var fields: [String] = []
|
||||||
@ -66,11 +66,11 @@ private func fieldTitleAndText(field: SecureIdAuthListContentField, strings: Pre
|
|||||||
|
|
||||||
let keyList: [(SecureIdValueKey, String)] = [
|
let keyList: [(SecureIdValueKey, String)] = [
|
||||||
(.address, strings.Passport_Address_TypeResidentialAddress),
|
(.address, strings.Passport_Address_TypeResidentialAddress),
|
||||||
(.passportRegistration, strings.Passport_Address_TypePassportRegistration),
|
|
||||||
(.temporaryRegistration, strings.Passport_Address_TypeTemporaryRegistration),
|
|
||||||
(.utilityBill, strings.Passport_Address_TypeUtilityBill),
|
(.utilityBill, strings.Passport_Address_TypeUtilityBill),
|
||||||
(.bankStatement, strings.Passport_Address_TypeBankStatement),
|
(.bankStatement, strings.Passport_Address_TypeBankStatement),
|
||||||
(.rentalAgreement, strings.Passport_Address_TypeRentalAgreement)
|
(.rentalAgreement, strings.Passport_Address_TypeRentalAgreement),
|
||||||
|
(.passportRegistration, strings.Passport_Address_TypePassportRegistration),
|
||||||
|
(.temporaryRegistration, strings.Passport_Address_TypeTemporaryRegistration)
|
||||||
]
|
]
|
||||||
|
|
||||||
var fields: [String] = []
|
var fields: [String] = []
|
||||||
@ -161,7 +161,7 @@ final class SecureIdAuthListFieldNode: ASDisplayNode {
|
|||||||
self.textNode = ImmediateTextNode()
|
self.textNode = ImmediateTextNode()
|
||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = false
|
||||||
self.textNode.isLayerBacked = true
|
self.textNode.isLayerBacked = true
|
||||||
self.textNode.maximumNumberOfLines = 1
|
self.textNode.maximumNumberOfLines = 4
|
||||||
|
|
||||||
self.disclosureNode = ASImageNode()
|
self.disclosureNode = ASImageNode()
|
||||||
self.disclosureNode.isLayerBacked = true
|
self.disclosureNode.isLayerBacked = true
|
||||||
@ -215,14 +215,13 @@ final class SecureIdAuthListFieldNode: ASDisplayNode {
|
|||||||
self.validLayout = (width, hasPrevious, hasNext)
|
self.validLayout = (width, hasPrevious, hasNext)
|
||||||
let leftInset: CGFloat = 16.0
|
let leftInset: CGFloat = 16.0
|
||||||
let rightInset: CGFloat = 16.0
|
let rightInset: CGFloat = 16.0
|
||||||
let height: CGFloat = 64.0
|
|
||||||
|
|
||||||
let rightTextInset = rightInset + 24.0
|
let rightTextInset = rightInset + 24.0
|
||||||
|
|
||||||
let titleTextSpacing: CGFloat = 5.0
|
let titleTextSpacing: CGFloat = 5.0
|
||||||
|
|
||||||
let titleSize = self.titleNode.updateLayout(CGSize(width: width - leftInset - rightTextInset, height: 100.0))
|
let titleSize = self.titleNode.updateLayout(CGSize(width: width - leftInset - rightTextInset, height: 100.0))
|
||||||
let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightTextInset, height: 100.0))
|
let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightTextInset, height: 100.0))
|
||||||
|
let height = max(64.0, 11.0 + titleSize.height + titleTextSpacing + textSize.height + 11.0)
|
||||||
|
|
||||||
let textOrigin = floor((height - titleSize.height - titleTextSpacing - textSize.height) / 2.0)
|
let textOrigin = floor((height - titleSize.height - titleTextSpacing - textSize.height) / 2.0)
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: textOrigin), size: titleSize)
|
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: textOrigin), size: titleSize)
|
||||||
|
|||||||
@ -331,6 +331,56 @@ private enum SecureIdDocumentFormDocumentState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SecureIdDocumentFormDocumentState {
|
||||||
|
mutating func updateWithRecognizedData(_ data: SecureIdRecognizedDocumentData) {
|
||||||
|
if case var .identity(state) = self {
|
||||||
|
if var details = state.details {
|
||||||
|
if details.firstName.isEmpty {
|
||||||
|
details.firstName = data.firstName ?? ""
|
||||||
|
}
|
||||||
|
if details.lastName.isEmpty {
|
||||||
|
details.lastName = data.lastName ?? ""
|
||||||
|
}
|
||||||
|
if details.birthdate == nil, let birthdate = data.birthDate {
|
||||||
|
details.birthdate = SecureIdDate(timestamp: Int32(birthdate.timeIntervalSince1970))
|
||||||
|
}
|
||||||
|
if details.gender == nil, let gender = data.gender {
|
||||||
|
if gender == "M" {
|
||||||
|
details.gender = .male
|
||||||
|
} else {
|
||||||
|
details.gender = .female
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if details.countryCode.isEmpty {
|
||||||
|
details.countryCode = data.issuingCountry ?? ""
|
||||||
|
}
|
||||||
|
state.details = details
|
||||||
|
}
|
||||||
|
|
||||||
|
if var document = state.document {
|
||||||
|
switch document.type {
|
||||||
|
case .passport:
|
||||||
|
break
|
||||||
|
case .internalPassport:
|
||||||
|
break
|
||||||
|
case .driversLicense:
|
||||||
|
break
|
||||||
|
case .idCard:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if document.identifier.isEmpty {
|
||||||
|
document.identifier = data.documentNumber ?? ""
|
||||||
|
}
|
||||||
|
if document.expiryDate == nil, let expiryDate = data.expiryDate {
|
||||||
|
document.expiryDate = SecureIdDate(timestamp: Int32(expiryDate.timeIntervalSince1970))
|
||||||
|
}
|
||||||
|
state.document = document
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private enum SecureIdDocumentFormActionState {
|
private enum SecureIdDocumentFormActionState {
|
||||||
case none
|
case none
|
||||||
case saving
|
case saving
|
||||||
@ -670,14 +720,35 @@ struct SecureIdDocumentFormState: FormControllerInnerState {
|
|||||||
} else {
|
} else {
|
||||||
result.append(.spacer)
|
result.append(.spacer)
|
||||||
}
|
}
|
||||||
result.append(.entry(SecureIdDocumentFormEntry.deleteDocument))
|
result.append(.entry(SecureIdDocumentFormEntry.deleteDocument(.identity, identity.document != nil)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
case let .address(address):
|
case let .address(address):
|
||||||
var result: [FormControllerItemEntry<SecureIdDocumentFormEntry>] = []
|
var result: [FormControllerItemEntry<SecureIdDocumentFormEntry>] = []
|
||||||
var errorIndex = 0
|
var errorIndex = 0
|
||||||
|
|
||||||
|
if let details = address.details {
|
||||||
|
result.append(.entry(SecureIdDocumentFormEntry.infoHeader(.address)))
|
||||||
|
|
||||||
|
let previousValue: SecureIdValueWithContext? = self.previousValues[.address]
|
||||||
|
let valueErrorKey: SecureIdValueContentErrorKey = .value(.address)
|
||||||
|
if let previousValue = previousValue {
|
||||||
|
maybeAddError(key: valueErrorKey, value: previousValue, entries: &result, errorIndex: &errorIndex)
|
||||||
|
}
|
||||||
|
result.append(.entry(SecureIdDocumentFormEntry.street1(details.street1, self.previousValues[.address]?.errors[.field(.address(.streetLine1))])))
|
||||||
|
result.append(.entry(SecureIdDocumentFormEntry.street2(details.street2, self.previousValues[.address]?.errors[.field(.address(.streetLine2))])))
|
||||||
|
result.append(.entry(SecureIdDocumentFormEntry.city(details.city, self.previousValues[.address]?.errors[.field(.address(.city))])))
|
||||||
|
result.append(.entry(SecureIdDocumentFormEntry.state(details.state, self.previousValues[.address]?.errors[.field(.address(.state))])))
|
||||||
|
result.append(.entry(SecureIdDocumentFormEntry.countryCode(.address, details.countryCode, self.previousValues[.address]?.errors[.field(.address(.countryCode))])))
|
||||||
|
result.append(.entry(SecureIdDocumentFormEntry.postcode(details.postcode, self.previousValues[.address]?.errors[.field(.address(.postCode))])))
|
||||||
|
}
|
||||||
|
|
||||||
if let document = address.document {
|
if let document = address.document {
|
||||||
|
if let last = result.last, case .spacer = last {
|
||||||
|
} else {
|
||||||
|
result.append(.spacer)
|
||||||
|
}
|
||||||
result.append(.entry(SecureIdDocumentFormEntry.scansHeader))
|
result.append(.entry(SecureIdDocumentFormEntry.scansHeader))
|
||||||
|
|
||||||
let filesType: SecureIdValueKey
|
let filesType: SecureIdValueKey
|
||||||
@ -755,25 +826,12 @@ struct SecureIdDocumentFormState: FormControllerInnerState {
|
|||||||
result.append(.spacer)
|
result.append(.spacer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let details = address.details {
|
|
||||||
|
|
||||||
result.append(.entry(SecureIdDocumentFormEntry.infoHeader(.address)))
|
|
||||||
|
|
||||||
let previousValue: SecureIdValueWithContext? = self.previousValues[.address]
|
|
||||||
let valueErrorKey: SecureIdValueContentErrorKey = .value(.address)
|
|
||||||
if let previousValue = previousValue {
|
|
||||||
maybeAddError(key: valueErrorKey, value: previousValue, entries: &result, errorIndex: &errorIndex)
|
|
||||||
}
|
|
||||||
result.append(.entry(SecureIdDocumentFormEntry.street1(details.street1, self.previousValues[.address]?.errors[.field(.address(.streetLine1))])))
|
|
||||||
result.append(.entry(SecureIdDocumentFormEntry.street2(details.street2, self.previousValues[.address]?.errors[.field(.address(.streetLine2))])))
|
|
||||||
result.append(.entry(SecureIdDocumentFormEntry.city(details.city, self.previousValues[.address]?.errors[.field(.address(.city))])))
|
|
||||||
result.append(.entry(SecureIdDocumentFormEntry.state(details.state, self.previousValues[.address]?.errors[.field(.address(.state))])))
|
|
||||||
result.append(.entry(SecureIdDocumentFormEntry.countryCode(.address, details.countryCode, self.previousValues[.address]?.errors[.field(.address(.countryCode))])))
|
|
||||||
result.append(.entry(SecureIdDocumentFormEntry.postcode(details.postcode, self.previousValues[.address]?.errors[.field(.address(.postCode))])))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.translationsRequired, let document = address.document {
|
if self.translationsRequired, let document = address.document {
|
||||||
|
if let last = result.last, case .spacer = last {
|
||||||
|
} else {
|
||||||
result.append(.spacer)
|
result.append(.spacer)
|
||||||
|
}
|
||||||
|
|
||||||
result.append(.entry(SecureIdDocumentFormEntry.translationsHeader))
|
result.append(.entry(SecureIdDocumentFormEntry.translationsHeader))
|
||||||
|
|
||||||
let filesType: SecureIdValueKey
|
let filesType: SecureIdValueKey
|
||||||
@ -845,7 +903,7 @@ struct SecureIdDocumentFormState: FormControllerInnerState {
|
|||||||
} else {
|
} else {
|
||||||
result.append(.spacer)
|
result.append(.spacer)
|
||||||
}
|
}
|
||||||
result.append(.entry(SecureIdDocumentFormEntry.deleteDocument))
|
result.append(.entry(SecureIdDocumentFormEntry.deleteDocument(.address, address.document != nil)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -1318,7 +1376,7 @@ enum SecureIdDocumentFormEntry: FormControllerEntry {
|
|||||||
case residenceCountryCode(String, String?)
|
case residenceCountryCode(String, String?)
|
||||||
case birthdate(SecureIdDate?, String?)
|
case birthdate(SecureIdDate?, String?)
|
||||||
case expiryDate(SecureIdDate?, String?)
|
case expiryDate(SecureIdDate?, String?)
|
||||||
case deleteDocument
|
case deleteDocument(SecureIdDocumentFormEntryCategory, Bool)
|
||||||
case requestedDocumentsHeader
|
case requestedDocumentsHeader
|
||||||
case selfie(Int, SecureIdVerificationDocument?, String?)
|
case selfie(Int, SecureIdVerificationDocument?, String?)
|
||||||
case frontSide(Int, SecureIdRequestedIdentityDocument?, SecureIdVerificationDocument?, String?)
|
case frontSide(Int, SecureIdRequestedIdentityDocument?, SecureIdVerificationDocument?, String?)
|
||||||
@ -1543,8 +1601,8 @@ enum SecureIdDocumentFormEntry: FormControllerEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case .deleteDocument:
|
case let .deleteDocument(lhsCategory, lhsHasDocument):
|
||||||
if case .deleteDocument = to {
|
if case let .deleteDocument(rhsCategory, rhsHasDocument) = to, lhsCategory == rhsCategory, lhsHasDocument == rhsHasDocument {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -1776,8 +1834,17 @@ enum SecureIdDocumentFormEntry: FormControllerEntry {
|
|||||||
return FormControllerDetailActionItem(title: strings.Passport_Identity_ExpiryDate, text: value.flatMap({ stringForDate(timestamp: $0.timestamp, strings: strings) }) ?? strings.Passport_Identity_ExpiryDateNone, placeholder: strings.Passport_Identity_ExpiryDatePlaceholder, error: error, activated: {
|
return FormControllerDetailActionItem(title: strings.Passport_Identity_ExpiryDate, text: value.flatMap({ stringForDate(timestamp: $0.timestamp, strings: strings) }) ?? strings.Passport_Identity_ExpiryDateNone, placeholder: strings.Passport_Identity_ExpiryDatePlaceholder, error: error, activated: {
|
||||||
params.activateSelection(.date(value?.timestamp, .expiry))
|
params.activateSelection(.date(value?.timestamp, .expiry))
|
||||||
})
|
})
|
||||||
case .deleteDocument:
|
case let .deleteDocument(category, hasDocument):
|
||||||
return FormControllerActionItem(type: .destructive, title: strings.Passport_DeleteDocument, activated: {
|
var title = strings.Passport_DeleteDocument
|
||||||
|
if !hasDocument {
|
||||||
|
switch category {
|
||||||
|
case .identity:
|
||||||
|
title = strings.Passport_DeletePersonalDetails
|
||||||
|
case .address:
|
||||||
|
title = strings.Passport_DeleteAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FormControllerActionItem(type: .destructive, title: title, activated: {
|
||||||
params.deleteValue()
|
params.deleteValue()
|
||||||
})
|
})
|
||||||
case let .street1(value, error):
|
case let .street1(value, error):
|
||||||
@ -2196,7 +2263,10 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode<SecureIdDocum
|
|||||||
}, scanPassport: { [weak self] in
|
}, scanPassport: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let controller = legacySecureIdScanController(theme: theme, strings: strings, finished: { recognizedData in
|
let controller = legacySecureIdScanController(theme: theme, strings: strings, finished: { recognizedData in
|
||||||
|
if let strongSelf = self, let recognizedData = recognizedData, var innerState = strongSelf.innerState {
|
||||||
|
innerState.documentState.updateWithRecognizedData(recognizedData)
|
||||||
|
strongSelf.updateInnerState(transition: .immediate, with: innerState)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
strongSelf.present(controller, nil)
|
strongSelf.present(controller, nil)
|
||||||
}
|
}
|
||||||
@ -2393,55 +2463,7 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode<SecureIdDocum
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let recognizedData = recognizedData {
|
if let recognizedData = recognizedData {
|
||||||
switch innerState.documentState {
|
innerState.documentState.updateWithRecognizedData(recognizedData)
|
||||||
case var .identity(identity):
|
|
||||||
if var document = identity.document {
|
|
||||||
switch document.type {
|
|
||||||
case .passport:
|
|
||||||
break
|
|
||||||
case .internalPassport:
|
|
||||||
break
|
|
||||||
case .driversLicense:
|
|
||||||
break
|
|
||||||
case .idCard:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if var details = identity.details {
|
|
||||||
if details.firstName.isEmpty {
|
|
||||||
details.firstName = recognizedData.firstName ?? ""
|
|
||||||
}
|
|
||||||
if details.lastName.isEmpty {
|
|
||||||
details.lastName = recognizedData.lastName ?? ""
|
|
||||||
}
|
|
||||||
if details.birthdate == nil, let birthdate = recognizedData.birthDate {
|
|
||||||
details.birthdate = SecureIdDate(timestamp: Int32(birthdate.timeIntervalSince1970))
|
|
||||||
}
|
|
||||||
if details.gender == nil, let gender = recognizedData.gender {
|
|
||||||
if gender == "M" {
|
|
||||||
details.gender = .male
|
|
||||||
} else {
|
|
||||||
details.gender = .female
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if details.countryCode.isEmpty {
|
|
||||||
|
|
||||||
details.countryCode = recognizedData.issuingCountry ?? ""
|
|
||||||
}
|
|
||||||
identity.details = details
|
|
||||||
}
|
|
||||||
if document.identifier.isEmpty {
|
|
||||||
document.identifier = recognizedData.documentNumber ?? ""
|
|
||||||
}
|
|
||||||
if document.expiryDate == nil, let expiryDate = recognizedData.expiryDate {
|
|
||||||
document.expiryDate = SecureIdDate(timestamp: Int32(expiryDate.timeIntervalSince1970))
|
|
||||||
}
|
|
||||||
identity.document = document
|
|
||||||
innerState.documentState = .identity(identity)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.updateInnerState(transition: .immediate, with: innerState)
|
self.updateInnerState(transition: .immediate, with: innerState)
|
||||||
}
|
}
|
||||||
@ -2656,7 +2678,7 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode<SecureIdDocum
|
|||||||
strongSelf.presentAssetPicker(target, replaceDocumentId: document.id)
|
strongSelf.presentAssetPicker(target, replaceDocumentId: document.id)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
ActionSheetButtonItem(title: strings.Common_Delete, action: { [weak self] in
|
ActionSheetButtonItem(title: strings.Common_Delete, color: .destructive, action: { [weak self] in
|
||||||
dismissAction()
|
dismissAction()
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -2684,37 +2706,52 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode<SecureIdDocum
|
|||||||
var entries: [SecureIdDocumentGalleryEntry] = []
|
var entries: [SecureIdDocumentGalleryEntry] = []
|
||||||
var index = 0
|
var index = 0
|
||||||
var centralIndex = 0
|
var centralIndex = 0
|
||||||
if let selfieDocument = innerState.selfieDocument, selfieDocument.id == document.id {
|
var totalCount: Int32 = 0
|
||||||
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: selfieDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: 1), error: ""))
|
if innerState.frontSideDocument != nil {
|
||||||
|
totalCount += 1
|
||||||
|
}
|
||||||
|
if innerState.backSideDocument != nil {
|
||||||
|
totalCount += 1
|
||||||
|
}
|
||||||
|
if innerState.selfieDocument != nil {
|
||||||
|
totalCount += 1
|
||||||
|
}
|
||||||
|
totalCount += Int32(innerState.documents.count)
|
||||||
|
totalCount += Int32(innerState.translations.count)
|
||||||
|
|
||||||
|
if let frontSideDocument = innerState.frontSideDocument {
|
||||||
|
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: frontSideDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: totalCount), error: ""))
|
||||||
centralIndex = index
|
centralIndex = index
|
||||||
index += 1
|
index += 1
|
||||||
} else if let frontSideDocument = innerState.frontSideDocument, frontSideDocument.id == document.id {
|
}
|
||||||
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: frontSideDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: 1), error: ""))
|
if let backSideDocument = innerState.backSideDocument {
|
||||||
|
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: backSideDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: totalCount), error: ""))
|
||||||
centralIndex = index
|
centralIndex = index
|
||||||
index += 1
|
index += 1
|
||||||
} else if let backSideDocument = innerState.backSideDocument, backSideDocument.id == document.id {
|
}
|
||||||
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: backSideDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: 1), error: ""))
|
if let selfieDocument = innerState.selfieDocument {
|
||||||
|
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: selfieDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: totalCount), error: ""))
|
||||||
centralIndex = index
|
centralIndex = index
|
||||||
index += 1
|
index += 1
|
||||||
} else {
|
}
|
||||||
if let _ = innerState.documents.index(where: { $0.id == document.id }) {
|
if let _ = innerState.documents.index(where: { $0.id == document.id }) {
|
||||||
for itemDocument in innerState.documents {
|
for itemDocument in innerState.documents {
|
||||||
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: itemDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: Int32(innerState.documents.count)), error: ""))
|
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: itemDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: totalCount), error: ""))
|
||||||
if document.id == itemDocument.id {
|
if document.id == itemDocument.id {
|
||||||
centralIndex = index
|
centralIndex = index
|
||||||
}
|
}
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
} else if let _ = innerState.translations.index(where: { $0.id == document.id }) {
|
}
|
||||||
|
if let _ = innerState.translations.index(where: { $0.id == document.id }) {
|
||||||
for itemDocument in innerState.translations {
|
for itemDocument in innerState.translations {
|
||||||
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: itemDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: Int32(innerState.documents.count)), error: ""))
|
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: itemDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: totalCount), error: ""))
|
||||||
if document.id == itemDocument.id {
|
if document.id == itemDocument.id {
|
||||||
centralIndex = index
|
centralIndex = index
|
||||||
}
|
}
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let galleryController = SecureIdDocumentGalleryController(account: self.account, context: self.context, entries: entries, centralIndex: centralIndex, replaceRootController: { _, _ in
|
let galleryController = SecureIdDocumentGalleryController(account: self.account, context: self.context, entries: entries, centralIndex: centralIndex, replaceRootController: { _, _ in
|
||||||
|
|
||||||
|
|||||||
@ -23,12 +23,14 @@ final class SecureIdPlaintextFormParams {
|
|||||||
fileprivate let updateTextField: (SecureIdPlaintextFormTextField, String) -> Void
|
fileprivate let updateTextField: (SecureIdPlaintextFormTextField, String) -> Void
|
||||||
fileprivate let usePhone: (String) -> Void
|
fileprivate let usePhone: (String) -> Void
|
||||||
fileprivate let useEmailAddress: (String) -> Void
|
fileprivate let useEmailAddress: (String) -> Void
|
||||||
|
fileprivate let save: () -> Void
|
||||||
|
|
||||||
fileprivate init(openCountrySelection: @escaping () -> Void, updateTextField: @escaping (SecureIdPlaintextFormTextField, String) -> Void, usePhone: @escaping (String) -> Void, useEmailAddress: @escaping (String) -> Void) {
|
fileprivate init(openCountrySelection: @escaping () -> Void, updateTextField: @escaping (SecureIdPlaintextFormTextField, String) -> Void, usePhone: @escaping (String) -> Void, useEmailAddress: @escaping (String) -> Void, save: @escaping () -> Void) {
|
||||||
self.openCountrySelection = openCountrySelection
|
self.openCountrySelection = openCountrySelection
|
||||||
self.updateTextField = updateTextField
|
self.updateTextField = updateTextField
|
||||||
self.usePhone = usePhone
|
self.usePhone = usePhone
|
||||||
self.useEmailAddress = useEmailAddress
|
self.useEmailAddress = useEmailAddress
|
||||||
|
self.save = save
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +289,18 @@ struct SecureIdPlaintextFormInnerState: FormControllerInnerState {
|
|||||||
result.append(.entry(SecureIdPlaintextFormEntry.numberInputInfo))
|
result.append(.entry(SecureIdPlaintextFormEntry.numberInputInfo))
|
||||||
case let .verify(verify):
|
case let .verify(verify):
|
||||||
result.append(.spacer)
|
result.append(.spacer)
|
||||||
result.append(.entry(SecureIdPlaintextFormEntry.numberCode(verify.code)))
|
var codeLength: Int32 = 5
|
||||||
|
switch verify.payload.type {
|
||||||
|
case let .sms(length):
|
||||||
|
codeLength = length
|
||||||
|
case let .call(length):
|
||||||
|
codeLength = length
|
||||||
|
case let .otherSession(length):
|
||||||
|
codeLength = length
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
result.append(.entry(SecureIdPlaintextFormEntry.numberCode(verify.code, codeLength)))
|
||||||
result.append(.entry(SecureIdPlaintextFormEntry.numberVerifyInfo))
|
result.append(.entry(SecureIdPlaintextFormEntry.numberVerifyInfo))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@ -308,7 +321,7 @@ struct SecureIdPlaintextFormInnerState: FormControllerInnerState {
|
|||||||
result.append(.entry(SecureIdPlaintextFormEntry.emailInputInfo))
|
result.append(.entry(SecureIdPlaintextFormEntry.emailInputInfo))
|
||||||
case let .verify(verify):
|
case let .verify(verify):
|
||||||
result.append(.spacer)
|
result.append(.spacer)
|
||||||
result.append(.entry(SecureIdPlaintextFormEntry.numberCode(verify.code)))
|
result.append(.entry(SecureIdPlaintextFormEntry.numberCode(verify.code, verify.payload.length)))
|
||||||
result.append(.entry(SecureIdPlaintextFormEntry.emailVerifyInfo(verify.email)))
|
result.append(.entry(SecureIdPlaintextFormEntry.emailVerifyInfo(verify.email)))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@ -414,7 +427,7 @@ enum SecureIdPlaintextFormEntry: FormControllerEntry {
|
|||||||
case numberInputHeader
|
case numberInputHeader
|
||||||
case numberInput(countryCode: String, number: String)
|
case numberInput(countryCode: String, number: String)
|
||||||
case numberInputInfo
|
case numberInputInfo
|
||||||
case numberCode(String)
|
case numberCode(String, Int32)
|
||||||
case numberVerifyInfo
|
case numberVerifyInfo
|
||||||
case immediatelyAvailableEmail(String)
|
case immediatelyAvailableEmail(String)
|
||||||
case immediatelyAvailableEmailInfo
|
case immediatelyAvailableEmailInfo
|
||||||
@ -489,8 +502,8 @@ enum SecureIdPlaintextFormEntry: FormControllerEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .numberCode(code):
|
case let .numberCode(code, length):
|
||||||
if case .numberCode(code) = to {
|
if case .numberCode(code, length) = to {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -570,9 +583,12 @@ enum SecureIdPlaintextFormEntry: FormControllerEntry {
|
|||||||
})
|
})
|
||||||
case .numberInputInfo:
|
case .numberInputInfo:
|
||||||
return FormControllerTextItem(text: strings.Passport_Phone_Help)
|
return FormControllerTextItem(text: strings.Passport_Phone_Help)
|
||||||
case let .numberCode(code):
|
case let .numberCode(code, length):
|
||||||
return FormControllerTextInputItem(title: strings.ChangePhoneNumberCode_CodePlaceholder, text: code, placeholder: strings.ChangePhoneNumberCode_CodePlaceholder, type: .number, textUpdated: { value in
|
return FormControllerTextInputItem(title: strings.ChangePhoneNumberCode_CodePlaceholder, text: code, placeholder: strings.ChangePhoneNumberCode_CodePlaceholder, type: .number, textUpdated: { value in
|
||||||
params.updateTextField(.code, value)
|
params.updateTextField(.code, value)
|
||||||
|
if value.count == length {
|
||||||
|
params.save()
|
||||||
|
}
|
||||||
}, returnPressed: {
|
}, returnPressed: {
|
||||||
|
|
||||||
})
|
})
|
||||||
@ -663,6 +679,8 @@ final class SecureIdPlaintextFormControllerNode: FormControllerNode<SecureIdPlai
|
|||||||
self?.savePhone(value)
|
self?.savePhone(value)
|
||||||
}, useEmailAddress: { [weak self] value in
|
}, useEmailAddress: { [weak self] value in
|
||||||
self?.saveEmailAddress(value)
|
self?.saveEmailAddress(value)
|
||||||
|
}, save: { [weak self] in
|
||||||
|
self?.save()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,6 +698,29 @@ final class SecureIdPlaintextFormControllerNode: FormControllerNode<SecureIdPlai
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func activateMainInput() {
|
||||||
|
self.enumerateItemsAndEntries({ itemEntry, itemNode in
|
||||||
|
switch itemEntry {
|
||||||
|
case .emailAddress, .numberCode, .emailCode:
|
||||||
|
if let inputNode = itemNode as? FormControllerTextInputItemNode {
|
||||||
|
inputNode.activate()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
case .numberInput:
|
||||||
|
if let inputNode = itemNode as? SecureIdValueFormPhoneItemNode {
|
||||||
|
inputNode.activate()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didAppear() {
|
||||||
|
self.activateMainInput()
|
||||||
|
}
|
||||||
|
|
||||||
func save() {
|
func save() {
|
||||||
guard var innerState = self.innerState else {
|
guard var innerState = self.innerState else {
|
||||||
return
|
return
|
||||||
@ -813,6 +854,7 @@ final class SecureIdPlaintextFormControllerNode: FormControllerNode<SecureIdPlai
|
|||||||
innerState.actionState = .none
|
innerState.actionState = .none
|
||||||
innerState.data = .phone(.verify(PhoneVerifyState(phone: inputPhone, payload: result, code: "")))
|
innerState.data = .phone(.verify(PhoneVerifyState(phone: inputPhone, payload: result, code: "")))
|
||||||
strongSelf.updateInnerState(transition: .immediate, with: innerState)
|
strongSelf.updateInnerState(transition: .immediate, with: innerState)
|
||||||
|
strongSelf.activateMainInput()
|
||||||
}
|
}
|
||||||
}, error: { [weak self] error in
|
}, error: { [weak self] error in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -858,6 +900,7 @@ final class SecureIdPlaintextFormControllerNode: FormControllerNode<SecureIdPlai
|
|||||||
innerState.actionState = .none
|
innerState.actionState = .none
|
||||||
innerState.data = .email(.verify(EmailVerifyState(email: value, payload: result, code: "")))
|
innerState.data = .email(.verify(EmailVerifyState(email: value, payload: result, code: "")))
|
||||||
strongSelf.updateInnerState(transition: .immediate, with: innerState)
|
strongSelf.updateInnerState(transition: .immediate, with: innerState)
|
||||||
|
strongSelf.activateMainInput()
|
||||||
}
|
}
|
||||||
}, error: { [weak self] error in
|
}, error: { [weak self] error in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
|||||||
@ -170,4 +170,8 @@ final class SecureIdValueFormPhoneItemNode: FormBlockItemNode<SecureIdValueFormP
|
|||||||
private func numberTextUpdated(_ value: String) {
|
private func numberTextUpdated(_ value: String) {
|
||||||
self.item?.updateNumber(value)
|
self.item?.updateNumber(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func activate() {
|
||||||
|
self.phoneInputNode.numberField.becomeFirstResponder()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,7 @@ private struct SettingsItemArguments {
|
|||||||
let openFaq: () -> Void
|
let openFaq: () -> Void
|
||||||
let openEditing: () -> Void
|
let openEditing: () -> Void
|
||||||
let updateArchivedPacks: ([ArchivedStickerPackItem]?) -> Void
|
let updateArchivedPacks: ([ArchivedStickerPackItem]?) -> Void
|
||||||
|
let displayCopyContextMenu: () -> Void
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum SettingsSection: Int32 {
|
private enum SettingsSection: Int32 {
|
||||||
@ -263,6 +264,8 @@ private enum SettingsEntry: ItemListNodeEntry {
|
|||||||
arguments.avatarTapAction()
|
arguments.avatarTapAction()
|
||||||
}, context: arguments.avatarAndNameInfoContext, updatingImage: updatingImage, action: {
|
}, context: arguments.avatarAndNameInfoContext, updatingImage: updatingImage, action: {
|
||||||
arguments.openEditing()
|
arguments.openEditing()
|
||||||
|
}, longTapAction: {
|
||||||
|
arguments.displayCopyContextMenu()
|
||||||
})
|
})
|
||||||
case let .setProfilePhoto(theme, text):
|
case let .setProfilePhoto(theme, text):
|
||||||
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||||
@ -432,6 +435,7 @@ public func settingsController(account: Account, accountManager: AccountManager)
|
|||||||
var updateHiddenAvatarImpl: (() -> Void)?
|
var updateHiddenAvatarImpl: (() -> Void)?
|
||||||
var changeProfilePhotoImpl: (() -> Void)?
|
var changeProfilePhotoImpl: (() -> Void)?
|
||||||
var openSavedMessagesImpl: (() -> Void)?
|
var openSavedMessagesImpl: (() -> Void)?
|
||||||
|
var displayCopyContextMenuImpl: ((Peer) -> Void)?
|
||||||
|
|
||||||
let archivedPacks = Promise<[ArchivedStickerPackItem]?>()
|
let archivedPacks = Promise<[ArchivedStickerPackItem]?>()
|
||||||
|
|
||||||
@ -544,6 +548,14 @@ public func settingsController(account: Account, accountManager: AccountManager)
|
|||||||
})
|
})
|
||||||
}, updateArchivedPacks: { packs in
|
}, updateArchivedPacks: { packs in
|
||||||
archivedPacks.set(.single(packs))
|
archivedPacks.set(.single(packs))
|
||||||
|
}, displayCopyContextMenu: {
|
||||||
|
let _ = (account.postbox.transaction { transaction -> (Peer?) in
|
||||||
|
return transaction.getPeer(account.peerId)
|
||||||
|
} |> deliverOnMainQueue).start(next: { peer in
|
||||||
|
if let peer = peer {
|
||||||
|
displayCopyContextMenuImpl?(peer)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
changeProfilePhotoImpl = {
|
changeProfilePhotoImpl = {
|
||||||
@ -715,6 +727,45 @@ public func settingsController(account: Account, accountManager: AccountManager)
|
|||||||
controller.tabBarItemDebugTapAction = {
|
controller.tabBarItemDebugTapAction = {
|
||||||
pushControllerImpl?(debugController(account: account, accountManager: accountManager))
|
pushControllerImpl?(debugController(account: account, accountManager: accountManager))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
displayCopyContextMenuImpl = { [weak controller] peer in
|
||||||
|
if let strongController = controller {
|
||||||
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||||
|
var resultItemNode: ListViewItemNode?
|
||||||
|
let _ = strongController.frameForItemNode({ itemNode in
|
||||||
|
if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode {
|
||||||
|
resultItemNode = itemNode
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
if let resultItemNode = resultItemNode, let user = peer as? TelegramUser {
|
||||||
|
var actions: [ContextMenuAction] = []
|
||||||
|
|
||||||
|
if let phone = user.phone, !phone.isEmpty {
|
||||||
|
actions.append(ContextMenuAction(content: .text(presentationData.strings.Settings_CopyPhoneNumber), action: {
|
||||||
|
UIPasteboard.general.string = formatPhoneNumber(phone)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
if let username = user.username, !username.isEmpty {
|
||||||
|
actions.append(ContextMenuAction(content: .text(presentationData.strings.Settings_CopyUsername), action: {
|
||||||
|
UIPasteboard.general.string = username
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
let contextMenuController = ContextMenuController(actions: actions)
|
||||||
|
strongController.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak resultItemNode] in
|
||||||
|
if let strongController = controller, let resultItemNode = resultItemNode {
|
||||||
|
return (resultItemNode, resultItemNode.contentBounds.insetBy(dx: 0.0, dy: -2.0), strongController.displayNode, strongController.view.bounds)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
controller.didAppear = { _ in
|
controller.didAppear = { _ in
|
||||||
updatePassport()
|
updatePassport()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -540,7 +540,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { peers in
|
|> deliverOnMainQueue).start(next: { peers in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let searchContentNode = ShareSearchContainerNode(account: strongSelf.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, controllerInteraction: strongSelf.controllerInteraction!, recentPeers: peers.map({ $0.peer }))
|
let searchContentNode = ShareSearchContainerNode(account: strongSelf.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, controllerInteraction: strongSelf.controllerInteraction!, recentPeers: peers.filter({ $0.peer.peerId.namespace != Namespaces.Peer.SecretChat }).map({ $0.peer }))
|
||||||
searchContentNode.cancel = {
|
searchContentNode.cancel = {
|
||||||
if let strongSelf = self, let peersContentNode = strongSelf.peersContentNode {
|
if let strongSelf = self, let peersContentNode = strongSelf.peersContentNode {
|
||||||
strongSelf.transitionToContentNode(peersContentNode)
|
strongSelf.transitionToContentNode(peersContentNode)
|
||||||
|
|||||||
@ -262,7 +262,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for renderedPeer in foundLocalPeers {
|
for renderedPeer in foundLocalPeers {
|
||||||
if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != accountPeer.id {
|
if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != accountPeer.id, peer.id.namespace != Namespaces.Peer.SecretChat {
|
||||||
if !existingPeerIds.contains(peer.id) {
|
if !existingPeerIds.contains(peer.id) {
|
||||||
existingPeerIds.insert(peer.id)
|
existingPeerIds.insert(peer.id)
|
||||||
var associatedPeer: Peer?
|
var associatedPeer: Peer?
|
||||||
|
|||||||
@ -163,6 +163,7 @@
|
|||||||
switch (screenSize)
|
switch (screenSize)
|
||||||
{
|
{
|
||||||
case 812:
|
case 812:
|
||||||
|
case 896:
|
||||||
titleY = 445 + 44;
|
titleY = 445 + 44;
|
||||||
imageY = 141 + 44;
|
imageY = 141 + 44;
|
||||||
descY = 490 + 44;
|
descY = 490 + 44;
|
||||||
|
|||||||
@ -1145,7 +1145,7 @@ public func userInfoController(account: Account, peerId: PeerId) -> ViewControll
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
shareMyContactImpl = {
|
shareMyContactImpl = { [weak controller] in
|
||||||
let _ = (peerView.get()
|
let _ = (peerView.get()
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { view in
|
|> deliverOnMainQueue).start(next: { view in
|
||||||
@ -1154,6 +1154,12 @@ public func userInfoController(account: Account, peerId: PeerId) -> ViewControll
|
|||||||
}
|
}
|
||||||
|
|
||||||
let contact = TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)
|
let contact = TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)
|
||||||
|
|
||||||
|
let _ = (enqueueMessages(account: account, peerId: peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: contact), replyToMessageId: nil, localGroupingKey: nil)])
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak controller] _ in
|
||||||
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||||
|
controller?.present(OverlayStatusController(theme: presentationData.theme, type: .success), in: .window(.root))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
startSecretChatImpl = { [weak controller] in
|
startSecretChatImpl = { [weak controller] in
|
||||||
|
|||||||
@ -66,9 +66,6 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte
|
|||||||
private let imageNode: TransformImageNode
|
private let imageNode: TransformImageNode
|
||||||
private let playerNode: WebEmbedPlayerNode
|
private let playerNode: WebEmbedPlayerNode
|
||||||
|
|
||||||
private let thumbnail = Promise<UIImage?>()
|
|
||||||
private var thumbnailDisposable: Disposable?
|
|
||||||
|
|
||||||
private var loadProgressDisposable: Disposable?
|
private var loadProgressDisposable: Disposable?
|
||||||
private var statusDisposable: Disposable?
|
private var statusDisposable: Disposable?
|
||||||
|
|
||||||
@ -83,6 +80,7 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.imageNode = TransformImageNode()
|
self.imageNode = TransformImageNode()
|
||||||
|
|
||||||
if let embedUrl = webpageContent.embedUrl {
|
if let embedUrl = webpageContent.embedUrl {
|
||||||
let impl = webEmbedImplementation(embedUrl: embedUrl, url: webpageContent.url)
|
let impl = webEmbedImplementation(embedUrl: embedUrl, url: webpageContent.url)
|
||||||
self.playerNode = WebEmbedPlayerNode(impl: impl, intrinsicDimensions: self.intrinsicDimensions)
|
self.playerNode = WebEmbedPlayerNode(impl: impl, intrinsicDimensions: self.intrinsicDimensions)
|
||||||
@ -100,14 +98,9 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte
|
|||||||
|
|
||||||
if let image = webpageContent.image {
|
if let image = webpageContent.image {
|
||||||
self.imageNode.setSignal(chatMessagePhoto(postbox: postbox, photoReference: .webPage(webPage: WebpageReference(webPage), media: image)))
|
self.imageNode.setSignal(chatMessagePhoto(postbox: postbox, photoReference: .webPage(webPage: WebpageReference(webPage), media: image)))
|
||||||
|
self.imageNode.imageUpdated = { [weak self] in
|
||||||
self.thumbnailDisposable = (rawMessagePhoto(postbox: postbox, photoReference: .webPage(webPage: WebpageReference(webPage), media: image))
|
self?._ready.set(.single(Void()))
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] image in
|
|
||||||
if let strongSelf = self {
|
|
||||||
strongSelf.thumbnail.set(.single(image))
|
|
||||||
strongSelf._ready.set(.single(Void()))
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
self._ready.set(.single(Void()))
|
self._ready.set(.single(Void()))
|
||||||
}
|
}
|
||||||
@ -152,9 +145,7 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte
|
|||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.audioSessionDisposable.dispose()
|
self.audioSessionDisposable.dispose()
|
||||||
|
|
||||||
self.loadProgressDisposable?.dispose()
|
self.loadProgressDisposable?.dispose()
|
||||||
self.thumbnailDisposable?.dispose()
|
|
||||||
self.statusDisposable?.dispose()
|
self.statusDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,31 +154,22 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte
|
|||||||
transition.updateTransformScale(node: self.playerNode, scale: size.width / self.intrinsicDimensions.width)
|
transition.updateTransformScale(node: self.playerNode, scale: size.width / self.intrinsicDimensions.width)
|
||||||
|
|
||||||
transition.updateFrame(node: self.imageNode, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(node: self.imageNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
let makeImageLayout = self.imageNode.asyncLayout()
|
||||||
|
let applyImageLayout = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets()))
|
||||||
|
applyImageLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
func play() {
|
func play() {
|
||||||
assert(Queue.mainQueue().isCurrent())
|
assert(Queue.mainQueue().isCurrent())
|
||||||
|
|
||||||
self.playerNode.play()
|
self.playerNode.play()
|
||||||
|
|
||||||
// if !self.initializedStatus {
|
|
||||||
// self._status.set(MediaPlayerStatus(generationTimestamp: 0.0, duration: Double(self.approximateDuration), dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: self.seekId, status: .buffering(initial: true, whilePlaying: true)))
|
|
||||||
// } else {
|
|
||||||
// self.playerView.playVideo()
|
|
||||||
// }
|
|
||||||
//>>>>>>> 368a96b2910b01bf361ed88aefd8662804f55f0a
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func pause() {
|
func pause() {
|
||||||
assert(Queue.mainQueue().isCurrent())
|
assert(Queue.mainQueue().isCurrent())
|
||||||
|
|
||||||
self.playerNode.pause()
|
self.playerNode.pause()
|
||||||
|
|
||||||
// if !self.initializedStatus {
|
|
||||||
// self._status.set(MediaPlayerStatus(generationTimestamp: 0.0, duration: Double(self.approximateDuration), dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: self.seekId, status: .paused))
|
|
||||||
// }
|
|
||||||
// self.playerView.pauseVideo()
|
|
||||||
//>>>>>>> 368a96b2910b01bf361ed88aefd8662804f55f0a
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func togglePlayPause() {
|
func togglePlayPause() {
|
||||||
@ -197,18 +179,12 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte
|
|||||||
|
|
||||||
func setSoundEnabled(_ value: Bool) {
|
func setSoundEnabled(_ value: Bool) {
|
||||||
assert(Queue.mainQueue().isCurrent())
|
assert(Queue.mainQueue().isCurrent())
|
||||||
/*if value {
|
|
||||||
self.player.playOnceWithSound()
|
|
||||||
} else {
|
|
||||||
self.player.continuePlayingWithoutSound()
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func seek(_ timestamp: Double) {
|
func seek(_ timestamp: Double) {
|
||||||
assert(Queue.mainQueue().isCurrent())
|
assert(Queue.mainQueue().isCurrent())
|
||||||
self.seekId += 1
|
self.seekId += 1
|
||||||
self.playerNode.seek(timestamp: timestamp)
|
self.playerNode.seek(timestamp: timestamp)
|
||||||
//self.playerView.seek(toPosition: timestamp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func playOnceWithSound(playAndRecord: Bool) {
|
func playOnceWithSound(playAndRecord: Bool) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user