From 92d95510c7ffa99e918590d3016f77427f434cbd Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 25 Sep 2018 12:29:53 +0100 Subject: [PATCH] no message --- TelegramUI/ChatController.swift | 2 +- .../ChatItemGalleryFooterContentNode.swift | 36 +++- .../ChatRecentActionsControllerNode.swift | 2 +- TelegramUI/GenericEmbedImplementation.swift | 8 +- TelegramUI/GroupInfoController.swift | 2 +- TelegramUI/InstantPageControllerNode.swift | 2 +- .../PeerMediaCollectionController.swift | 2 +- TelegramUI/SecureIdAuthController.swift | 2 +- TelegramUI/SecureIdAuthControllerNode.swift | 47 ++--- TelegramUI/SecureIdAuthFormContentNode.swift | 12 +- TelegramUI/SecureIdAuthFormFieldNode.swift | 198 ++++++++---------- TelegramUI/UniversalVideoCalleryItem.swift | 11 +- TelegramUI/VimeoEmbedImplementation.swift | 7 +- TelegramUI/WebEmbedPlayerNode.swift | 44 ++-- TelegramUI/WebEmbedVideoContent.swift | 11 +- TelegramUI/YoutubeEmbedImplementation.swift | 4 +- .../platform/ios/RMIntroViewController.m | 15 +- 17 files changed, 212 insertions(+), 193 deletions(-) diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index 3834461440..87886a965a 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -738,7 +738,7 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin case let .url(url): var cleanUrl = url var canAddToReadingList = true - let canOpenIn = !availableOpenInOptions(applicationContext: strongSelf.account.telegramApplicationContext, item: .url(url: url)).isEmpty + let canOpenIn = availableOpenInOptions(applicationContext: strongSelf.account.telegramApplicationContext, item: .url(url: url)).count > 1 let mailtoString = "mailto:" let telString = "tel:" var openText = strongSelf.presentationData.strings.Conversation_LinkDialogOpen diff --git a/TelegramUI/ChatItemGalleryFooterContentNode.swift b/TelegramUI/ChatItemGalleryFooterContentNode.swift index ba409fc6fa..70e200cc0c 100644 --- a/TelegramUI/ChatItemGalleryFooterContentNode.swift +++ b/TelegramUI/ChatItemGalleryFooterContentNode.swift @@ -511,19 +511,41 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { break } } - + if messages.count == 1 { var subject: ShareControllerSubject = ShareControllerSubject.messages(messages) for m in messages[0].media { if let image = m as? TelegramMediaImage { subject = .image(image.representations) } else if let webpage = m as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content { - if let file = content.file { - subject = .media(.webPage(webPage: WebpageReference(webpage), media: file)) - preferredAction = .saveToCameraRoll - } else if let image = content.image { - subject = .media(.webPage(webPage: WebpageReference(webpage), media: image)) - preferredAction = .saveToCameraRoll + if content.embedType == "iframe" { + let item = OpenInItem.url(url: content.url) + if availableOpenInOptions(applicationContext: strongSelf.account.telegramApplicationContext, item: item).count > 1 { + preferredAction = .custom(action: ShareControllerAction(title: presentationData.strings.Conversation_FileOpenIn, action: { [weak self] in + if let strongSelf = self { + let openInController = OpenInActionSheetController(postbox: strongSelf.account.postbox, applicationContext: strongSelf.account.telegramApplicationContext, theme: presentationData.theme, strings: presentationData.strings, item: item, additionalAction: nil, openUrl: { [weak self] url in + if let strongSelf = self, let applicationContext = strongSelf.account.applicationContext as? TelegramApplicationContext { + openExternalUrl(account: strongSelf.account, url: url, presentationData: presentationData, applicationContext: applicationContext, navigationController: nil, dismissInput: {}) + } + }) + strongSelf.controllerInteraction?.presentController(openInController, nil) + } + })) + } else { + preferredAction = .custom(action: ShareControllerAction(title: presentationData.strings.Web_OpenExternal, action: { [weak self] in + if let strongSelf = self { + openExternalUrl(account: strongSelf.account, url: content.url, presentationData: presentationData, applicationContext: strongSelf.account.telegramApplicationContext, navigationController: nil, dismissInput: {}) + } + })) + } + } else { + if let file = content.file { + subject = .media(.webPage(webPage: WebpageReference(webpage), media: file)) + preferredAction = .saveToCameraRoll + } else if let image = content.image { + subject = .media(.webPage(webPage: WebpageReference(webpage), media: image)) + preferredAction = .saveToCameraRoll + } } } else if let file = m as? TelegramMediaFile { subject = .media(.message(message: MessageReference(messages[0]), media: file)) diff --git a/TelegramUI/ChatRecentActionsControllerNode.swift b/TelegramUI/ChatRecentActionsControllerNode.swift index 7b60c846e6..404b8a5267 100644 --- a/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/TelegramUI/ChatRecentActionsControllerNode.swift @@ -219,7 +219,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { switch action { case let .url(url): var cleanUrl = url - let canOpenIn = !availableOpenInOptions(applicationContext: strongSelf.account.telegramApplicationContext, item: .url(url: url)).isEmpty + let canOpenIn = availableOpenInOptions(applicationContext: strongSelf.account.telegramApplicationContext, item: .url(url: url)).count > 1 var canAddToReadingList = true let mailtoString = "mailto:" let telString = "tel:" diff --git a/TelegramUI/GenericEmbedImplementation.swift b/TelegramUI/GenericEmbedImplementation.swift index d2e3536bb3..cde7c45932 100644 --- a/TelegramUI/GenericEmbedImplementation.swift +++ b/TelegramUI/GenericEmbedImplementation.swift @@ -6,12 +6,13 @@ final class GenericEmbedImplementation: WebEmbedImplementation { private var evalImpl: ((String) -> Void)? private var updateStatus: ((MediaPlayerStatus) -> Void)? private var onPlaybackStarted: (() -> Void)? + private var status : MediaPlayerStatus private let url: String init(url: String) { self.url = url - //self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true)) + self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true)) } func setup(_ webView: WKWebView, userContentController: WKUserContentController, evaluateJavaScript: @escaping (String) -> Void, updateStatus: @escaping (MediaPlayerStatus) -> Void, onPlaybackStarted: @escaping () -> Void) { @@ -38,7 +39,7 @@ final class GenericEmbedImplementation: WebEmbedImplementation { self.evalImpl = evaluateJavaScript self.updateStatus = updateStatus self.onPlaybackStarted = onPlaybackStarted - //updateStatus(self.status) + updateStatus(self.status) let html = String(format: htmlTemplate, self.url) webView.loadHTMLString(html, baseURL: URL(string: "about:blank")) @@ -59,6 +60,9 @@ final class GenericEmbedImplementation: WebEmbedImplementation { } func pageReady() { + self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .playing) + self.updateStatus?(self.status) + if let onPlaybackStarted = self.onPlaybackStarted { onPlaybackStarted() } diff --git a/TelegramUI/GroupInfoController.swift b/TelegramUI/GroupInfoController.swift index 7301878b14..6dfac7a5a4 100644 --- a/TelegramUI/GroupInfoController.swift +++ b/TelegramUI/GroupInfoController.swift @@ -1932,7 +1932,7 @@ func handlePeerInfoAboutTextAction(account: Account, peerId: PeerId, navigateDis case .longTap: switch itemLink { case let .url(url): - let canOpenIn = !availableOpenInOptions(applicationContext: account.telegramApplicationContext, item: .url(url: url)).isEmpty + let canOpenIn = availableOpenInOptions(applicationContext: account.telegramApplicationContext, item: .url(url: url)).count > 1 let openText = canOpenIn ? presentationData.strings.Conversation_FileOpenIn : presentationData.strings.Conversation_LinkDialogOpen let actionSheet = ActionSheetController(presentationTheme: presentationData.theme) actionSheet.setItemGroups([ActionSheetItemGroup(items: [ diff --git a/TelegramUI/InstantPageControllerNode.swift b/TelegramUI/InstantPageControllerNode.swift index e441cdc7e6..06383e87a4 100644 --- a/TelegramUI/InstantPageControllerNode.swift +++ b/TelegramUI/InstantPageControllerNode.swift @@ -611,7 +611,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } case .longTap: if let url = self.urlForTapLocation(location) { - let canOpenIn = !availableOpenInOptions(applicationContext: self.account.telegramApplicationContext, item: .url(url: url.url)).isEmpty + let canOpenIn = availableOpenInOptions(applicationContext: self.account.telegramApplicationContext, item: .url(url: url.url)).count > 1 let openText = canOpenIn ? self.strings.Conversation_FileOpenIn : self.strings.Conversation_LinkDialogOpen let actionSheet = ActionSheetController(presentationTheme: self.presentationTheme) actionSheet.setItemGroups([ActionSheetItemGroup(items: [ diff --git a/TelegramUI/PeerMediaCollectionController.swift b/TelegramUI/PeerMediaCollectionController.swift index e927fcc9db..4b95718166 100644 --- a/TelegramUI/PeerMediaCollectionController.swift +++ b/TelegramUI/PeerMediaCollectionController.swift @@ -190,7 +190,7 @@ public class PeerMediaCollectionController: TelegramController { if let strongSelf = self { switch content { case let .url(url): - let canOpenIn = !availableOpenInOptions(applicationContext: strongSelf.account.telegramApplicationContext, item: .url(url: url)).isEmpty + let canOpenIn = availableOpenInOptions(applicationContext: strongSelf.account.telegramApplicationContext, item: .url(url: url)).count > 1 let openText = canOpenIn ? strongSelf.presentationData.strings.Conversation_FileOpenIn : strongSelf.presentationData.strings.Conversation_LinkDialogOpen let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme) actionSheet.setItemGroups([ActionSheetItemGroup(items: [ diff --git a/TelegramUI/SecureIdAuthController.swift b/TelegramUI/SecureIdAuthController.swift index 51983b504a..a53d6b95d5 100644 --- a/TelegramUI/SecureIdAuthController.swift +++ b/TelegramUI/SecureIdAuthController.swift @@ -568,7 +568,7 @@ final class SecureIdAuthController: ViewController { switch self.state { case let .form(form): if case let .form(reqForm) = self.mode, let encryptedFormData = form.encryptedFormData, let formData = form.formData { - let values = parseRequestedFormFields(formData.requestedFields, values: formData.values).map({ $0.1 }).flatMap({ $0 }) + let values = parseRequestedFormFields(formData.requestedFields, values: formData.values, primaryLanguageByCountry: encryptedFormData.primaryLanguageByCountry).map({ $0.1 }).flatMap({ $0 }) 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 diff --git a/TelegramUI/SecureIdAuthControllerNode.swift b/TelegramUI/SecureIdAuthControllerNode.swift index 99035e2259..dbdc0c1b97 100644 --- a/TelegramUI/SecureIdAuthControllerNode.swift +++ b/TelegramUI/SecureIdAuthControllerNode.swift @@ -51,11 +51,11 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { self.backgroundColor = presentationData.theme.list.blocksBackgroundColor self.acceptNode.pressed = { [weak self] in - guard let strongSelf = self, let state = strongSelf.state, case let .form(form) = state, let formData = form.formData else { + guard let strongSelf = self, let state = strongSelf.state, case let .form(form) = state, let encryptedFormData = form.encryptedFormData, let formData = form.formData else { return } - for (field, _, filled) in parseRequestedFormFields(formData.requestedFields, values: formData.values) { + for (field, _, filled) in parseRequestedFormFields(formData.requestedFields, values: formData.values, primaryLanguageByCountry: encryptedFormData.primaryLanguageByCountry) { if !filled { if let contentNode = strongSelf.contentNode as? SecureIdAuthFormContentNode { if let rect = contentNode.frameForField(field) { @@ -291,7 +291,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { current.updateValues(formData.values) contentNode = current } else { - let current = SecureIdAuthFormContentNode(theme: self.presentationData.theme, strings: self.presentationData.strings, peer: encryptedFormData.servicePeer, privacyPolicyUrl: encryptedFormData.form.termsUrl, form: formData, openField: { [weak self] field in + let current = SecureIdAuthFormContentNode(theme: self.presentationData.theme, strings: self.presentationData.strings, peer: encryptedFormData.servicePeer, privacyPolicyUrl: encryptedFormData.form.termsUrl, form: formData, primaryLanguageByCountry: encryptedFormData.primaryLanguageByCountry, openField: { [weak self] field in if let strongSelf = self { switch field { case .identity, .address: @@ -438,15 +438,6 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { } } - let hasErrors: (SecureIdValueWithContext, SecureIdValueKey) -> Bool = { value, key in - for error in value.errors { - if case let .value(valueKey) = error.key, valueKey != key { - return value.errors.count > 1 - } - } - return !value.errors.isEmpty - } - switch field { case let .identity(personalDetails, document, selfie, translations): if let document = document { @@ -468,11 +459,8 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { } } case let .oneOf(types): - var chosenByError = false - outer: for type in types { + inner: for type in types.sorted(by: { $0.valueKey.rawValue < $1.valueKey.rawValue }) { if let value = findValue(formData.values, key: type.valueKey)?.1 { - let hasErrors = hasErrors(value, type.valueKey) - let data = extractSecureIdValueAdditionalData(value.value) var dataFilled = true if selfie && !data.selfie { @@ -481,9 +469,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { if translations && !data.translation { dataFilled = false } - - if hasValueType == nil || ((hasErrors || dataFilled) && !chosenByError) { - chosenByError = hasErrors + if hasValueType == nil || dataFilled { switch value.value { case .passport: hasValueType = .passport @@ -496,6 +482,10 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { default: break } + + if dataFilled { + break inner + } } } } @@ -539,41 +529,36 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode { } } case let .oneOf(types): - var chosenByError = false - outer: for type in types { + inner: for type in types.sorted(by: { $0.valueKey.rawValue < $1.valueKey.rawValue }) { if let value = findValue(formData.values, key: type.valueKey)?.1 { - let hasErrors = hasErrors(value, type.valueKey) - let data = extractSecureIdValueAdditionalData(value.value) var dataFilled = true if translation && !data.translation { dataFilled = false } - if hasValueType == nil || ((hasErrors || dataFilled) && !chosenByError) { - chosenByError = hasErrors + if hasValueType == nil || dataFilled { switch value.value { case .utilityBill: hasValueType = .utilityBill - break outer case .bankStatement: hasValueType = .bankStatement - break outer case .rentalAgreement: hasValueType = .rentalAgreement - break outer case .passportRegistration: hasValueType = .passportRegistration - break outer case .temporaryRegistration: hasValueType = .temporaryRegistration - break outer default: break } + + if dataFilled { + break inner + } } } - } + } } if let hasValueType = hasValueType { self.interaction.present(SecureIdDocumentFormController(account: self.account, context: context, requestedData: .address(details: addressDetails, document: hasValueType, translations: translation), primaryLanguageByCountry: encryptedFormData.primaryLanguageByCountry, values: formData.values, updatedValues: { values in diff --git a/TelegramUI/SecureIdAuthFormContentNode.swift b/TelegramUI/SecureIdAuthFormContentNode.swift index 1e50284b21..7e3a86ab6c 100644 --- a/TelegramUI/SecureIdAuthFormContentNode.swift +++ b/TelegramUI/SecureIdAuthFormContentNode.swift @@ -9,6 +9,7 @@ private let passwordFont = Font.regular(16.0) private let buttonFont = Font.regular(17.0) final class SecureIdAuthFormContentNode: ASDisplayNode, SecureIdAuthContentNode, UITextFieldDelegate { + private let primaryLanguageByCountry: [String: String] private let requestedFields: [SecureIdRequestedFormField] private let fieldBackgroundNode: ASDisplayNode private let fieldNodes: [SecureIdAuthFormFieldNode] @@ -18,9 +19,10 @@ final class SecureIdAuthFormContentNode: ASDisplayNode, SecureIdAuthContentNode, private let requestLayout: () -> Void private var validLayout: CGFloat? - init(theme: PresentationTheme, strings: PresentationStrings, peer: Peer, privacyPolicyUrl: String?, form: SecureIdForm, openField: @escaping (SecureIdParsedRequestedFormField) -> Void, openURL: @escaping (String) -> Void, openMention: @escaping (TelegramPeerMention) -> Void, requestLayout: @escaping () -> Void) { + init(theme: PresentationTheme, strings: PresentationStrings, peer: Peer, privacyPolicyUrl: String?, form: SecureIdForm, primaryLanguageByCountry: [String: String], openField: @escaping (SecureIdParsedRequestedFormField) -> Void, openURL: @escaping (String) -> Void, openMention: @escaping (TelegramPeerMention) -> Void, requestLayout: @escaping () -> Void) { self.requestLayout = requestLayout + self.primaryLanguageByCountry = primaryLanguageByCountry self.requestedFields = form.requestedFields self.fieldBackgroundNode = ASDisplayNode() self.fieldBackgroundNode.isLayerBacked = true @@ -28,8 +30,8 @@ final class SecureIdAuthFormContentNode: ASDisplayNode, SecureIdAuthContentNode, var fieldNodes: [SecureIdAuthFormFieldNode] = [] - for (field, fieldValues, _) in parseRequestedFormFields(self.requestedFields, values: form.values) { - fieldNodes.append(SecureIdAuthFormFieldNode(theme: theme, strings: strings, field: field, values: fieldValues, selected: { + for (field, fieldValues, _) in parseRequestedFormFields(self.requestedFields, values: form.values, primaryLanguageByCountry: primaryLanguageByCountry) { + fieldNodes.append(SecureIdAuthFormFieldNode(theme: theme, strings: strings, field: field, values: fieldValues, primaryLanguageByCountry: primaryLanguageByCountry, selected: { openField(field) })) } @@ -88,9 +90,9 @@ final class SecureIdAuthFormContentNode: ASDisplayNode, SecureIdAuthContentNode, func updateValues(_ values: [SecureIdValueWithContext]) { var index = 0 - for (_, fieldValues, _) in parseRequestedFormFields(self.requestedFields, values: values) { + for (_, fieldValues, _) in parseRequestedFormFields(self.requestedFields, values: values, primaryLanguageByCountry: self.primaryLanguageByCountry) { if index < self.fieldNodes.count { - self.fieldNodes[index].updateValues(fieldValues) + self.fieldNodes[index].updateValues(fieldValues, primaryLanguageByCountry: self.primaryLanguageByCountry) } index += 1 } diff --git a/TelegramUI/SecureIdAuthFormFieldNode.swift b/TelegramUI/SecureIdAuthFormFieldNode.swift index 4ac8284902..0d9b550d29 100644 --- a/TelegramUI/SecureIdAuthFormFieldNode.swift +++ b/TelegramUI/SecureIdAuthFormFieldNode.swift @@ -25,24 +25,24 @@ enum SecureIdRequestedIdentityDocument: Int32 { } enum SecureIdRequestedAddressDocument: Int32 { + case utilityBill + case bankStatement + case rentalAgreement case passportRegistration case temporaryRegistration - case bankStatement - case utilityBill - case rentalAgreement var valueKey: SecureIdValueKey { switch self { + case .utilityBill: + return .utilityBill + case .bankStatement: + return .bankStatement + case .rentalAgreement: + return .rentalAgreement case .passportRegistration: return .passportRegistration case .temporaryRegistration: return .temporaryRegistration - case .bankStatement: - return .bankStatement - case .utilityBill: - return .utilityBill - case .rentalAgreement: - return .rentalAgreement } } } @@ -110,7 +110,7 @@ private struct RequestedFieldValues { } } -func parseRequestedFormFields(_ types: [SecureIdRequestedFormField], values: [SecureIdValueWithContext]) -> [(SecureIdParsedRequestedFormField, [SecureIdValueWithContext], Bool)] { +func parseRequestedFormFields(_ types: [SecureIdRequestedFormField], values: [SecureIdValueWithContext], primaryLanguageByCountry: [String: String]) -> [(SecureIdParsedRequestedFormField, [SecureIdValueWithContext], Bool)] { var requestedValues = RequestedFieldValues() for type in types { @@ -191,20 +191,28 @@ func parseRequestedFormFields(_ types: [SecureIdRequestedFormField], values: [Se } return result.map { field in - let (fieldValues, filled) = findValuesForField(field: field, values: values) + let (fieldValues, filled) = findValuesForField(field: field, values: values, primaryLanguageByCountry: primaryLanguageByCountry) return (field, fieldValues, filled) } } -private func findValuesForField(field: SecureIdParsedRequestedFormField, values: [SecureIdValueWithContext]) -> ([SecureIdValueWithContext], Bool) { +private func findValuesForField(field: SecureIdParsedRequestedFormField, values: [SecureIdValueWithContext], primaryLanguageByCountry: [String: String]) -> ([SecureIdValueWithContext], Bool) { switch field { case let .identity(personalDetails, document, selfie, translation): var filled = true var result: [SecureIdValueWithContext] = [] - if personalDetails != nil { + if let personalDetails = personalDetails { if let value = findValue(values, key: .personalDetails)?.1 { result.append(value) - if !value.errors.isEmpty { + if case let .personalDetails(value) = value.value { + let hasNativeNames = value.nativeName?.isComplete() ?? false + let requiresNativeNames = primaryLanguageByCountry[value.residenceCountryCode] != "en" + if personalDetails.nativeNames && !hasNativeNames && requiresNativeNames { + filled = false + } + } + + if errorForErrorKey(.personalDetails, value) != nil { filled = false } } else { @@ -223,44 +231,44 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values: if translation && !data.translation { filled = false } - if !value.errors.isEmpty { + if errorForErrorKey(type.valueKey, value) != nil { filled = false } } else { filled = false } - case let .oneOf(types): - var anyDocument = false - var bestMatchingValue: SecureIdValueWithContext? - inner: for type in types { - if let value = findValue(values, key: type.valueKey)?.1 { - if bestMatchingValue == nil { - bestMatchingValue = value - } - let data = extractSecureIdValueAdditionalData(value.value) - var dataFilled = true - if selfie && !data.selfie { - dataFilled = false - } - if translation && !data.translation { - dataFilled = false - } - if dataFilled { - bestMatchingValue = value - anyDocument = true - break inner + case let .oneOf(types): + var anyDocument = false + var bestMatchingValue: SecureIdValueWithContext? + inner: for type in types.sorted(by: { $0.valueKey.rawValue < $1.valueKey.rawValue }) { + if let value = findValue(values, key: type.valueKey)?.1 { + if bestMatchingValue == nil { + bestMatchingValue = value + } + let data = extractSecureIdValueAdditionalData(value.value) + var dataFilled = true + if selfie && !data.selfie { + dataFilled = false + } + if translation && !data.translation { + dataFilled = false + } + if dataFilled { + bestMatchingValue = value + anyDocument = true + break inner + } } } - } - if !anyDocument { - filled = false - } - if let bestMatchingValue = bestMatchingValue { - result.append(bestMatchingValue) - if !bestMatchingValue.errors.isEmpty { + if !anyDocument { filled = false } - } + if let bestMatchingValue = bestMatchingValue { + result.append(bestMatchingValue) + if errorForErrorKey(bestMatchingValue.value.key, bestMatchingValue) != nil { + filled = false + } + } } } return (result, filled) @@ -270,7 +278,7 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values: if addressDetails { if let value = findValue(values, key: .address)?.1 { result.append(value) - if !value.errors.isEmpty { + if errorForErrorKey(.address, value) != nil { filled = false } } else { @@ -286,7 +294,7 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values: if translation && !data.translation { filled = false } - if !value.errors.isEmpty { + if errorForErrorKey(type.valueKey, value) != nil { filled = false } } else { @@ -295,7 +303,7 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values: case let .oneOf(types): var anyDocument = false var bestMatchingValue: SecureIdValueWithContext? - inner: for type in types { + inner: for type in types.sorted(by: { $0.valueKey.rawValue < $1.valueKey.rawValue }) { if let value = findValue(values, key: type.valueKey)?.1 { if bestMatchingValue == nil { bestMatchingValue = value @@ -317,7 +325,7 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values: } if let bestMatchingValue = bestMatchingValue { result.append(bestMatchingValue) - if !bestMatchingValue.errors.isEmpty { + if errorForErrorKey(bestMatchingValue.value.key, bestMatchingValue) != nil { filled = false } } @@ -587,7 +595,7 @@ private func fieldTitleAndText(field: SecureIdParsedRequestedFormField, strings: title = strings.Passport_FieldOneOf_Or(stringForDocumentType(typesArray[0], strings: strings), stringForDocumentType(typesArray[1], strings: strings)).0 } placeholder = placeholderForDocumentTypes(typesArray, strings: strings) - for type in types { + for type in types.sorted(by: { $0.valueKey.rawValue < $1.valueKey.rawValue }) { if let value = findValue(values, key: type.valueKey)?.1.value { filledDocument = (type, value) break @@ -638,7 +646,7 @@ private func fieldTitleAndText(field: SecureIdParsedRequestedFormField, strings: title = strings.Passport_FieldOneOf_Or(stringForDocumentType(typesArray[0], strings: strings), stringForDocumentType(typesArray[1], strings: strings)).0 } placeholder = placeholderForDocumentTypes(typesArray, strings: strings) - for type in types { + for type in types.sorted(by: { $0.valueKey.rawValue < $1.valueKey.rawValue }) { if let value = findValue(values, key: type.valueKey)?.1.value { filledDocument = (type, value) break @@ -691,76 +699,51 @@ private func fieldTitleAndText(field: SecureIdParsedRequestedFormField, strings: return (title, text.isEmpty ? placeholder : text) } +private func errorForErrorKey(_ key: SecureIdValueKey, _ value: SecureIdValueWithContext) -> String? { + if let error = value.errors[.value(key)] { + return error + } else if let error = value.errors.first { + if case .value = error.key {} else { + return error.value + } + } + return nil +} + private func fieldErrorText(field: SecureIdParsedRequestedFormField, values: [SecureIdValueWithContext]) -> String? { switch field { case let .identity(personalDetails, document, _, _): - if let _ = personalDetails, let value = findValue(values, key: .personalDetails)?.1 { - if let error = value.errors[.value(.personalDetails)] { - return error - } else if let error = value.errors.first { - if case .value = error.key {} else { - return error.value - } - } + if let _ = personalDetails, let value = findValue(values, key: .personalDetails)?.1, let error = errorForErrorKey(.personalDetails, value) { + return error } if let document = document { switch document { case let .just(type): - if let value = findValue(values, key: type.valueKey)?.1 { - if let error = value.errors[.value(type.valueKey)] { - return error - } else if let error = value.errors.first { - if case .value = error.key {} else { - return error.value - } - } + if let value = findValue(values, key: type.valueKey)?.1, let error = errorForErrorKey(type.valueKey, value) { + return error } case let .oneOf(types): - for type in types { - if let value = findValue(values, key: type.valueKey)?.1 { - if let error = value.errors[.value(type.valueKey)] { - return error - } else if let error = value.errors.first { - if case .value = error.key {} else { - return error.value - } - } + for type in types.sorted(by: { $0.valueKey.rawValue < $1.valueKey.rawValue }) { + if let value = findValue(values, key: type.valueKey)?.1, let error = errorForErrorKey(type.valueKey, value) { + return error } } } } case let .address(addressDetails, document, _): - if addressDetails, let value = findValue(values, key: .address)?.1 { - if let error = value.errors[.value(.address)] { - return error - } else if let error = value.errors.first { - if case .value = error.key {} else { - return error.value - } - } + if addressDetails, let value = findValue(values, key: .address)?.1, let error = errorForErrorKey(.address, value) { + return error } if let document = document { switch document { case let .just(type): - if let value = findValue(values, key: type.valueKey)?.1 { - if let error = value.errors[.value(type.valueKey)] { - return error - } else if let error = value.errors.first { - if case .value = error.key {} else { - return error.value - } - } + if let value = findValue(values, key: type.valueKey)?.1, let error = errorForErrorKey(type.valueKey, value) { + return error } case let .oneOf(types): - for type in types { - if let value = findValue(values, key: type.valueKey)?.1 { - if let error = value.errors[.value(type.valueKey)] { - return error - } else if let error = value.errors.first { - if case .value = error.key {} else { - return error.value - } - } + for type in types.sorted(by: { $0.valueKey.rawValue < $1.valueKey.rawValue }) { + if let value = findValue(values, key: type.valueKey)?.1, let error = errorForErrorKey(type.valueKey, value) { + return error } } } @@ -791,7 +774,7 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode { private let theme: PresentationTheme private let strings: PresentationStrings - init(theme: PresentationTheme, strings: PresentationStrings, field: SecureIdParsedRequestedFormField, values: [SecureIdValueWithContext], selected: @escaping () -> Void) { + init(theme: PresentationTheme, strings: PresentationStrings, field: SecureIdParsedRequestedFormField, values: [SecureIdValueWithContext], primaryLanguageByCountry: [String: String], selected: @escaping () -> Void) { self.field = field self.theme = theme self.strings = strings @@ -845,7 +828,7 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode { self.addSubnode(self.checkNode) self.addSubnode(self.buttonNode) - self.updateValues(values) + self.updateValues(values, primaryLanguageByCountry: primaryLanguageByCountry) self.buttonNode.highligthedChanged = { [weak self] highlighted in if let strongSelf = self { @@ -862,7 +845,7 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode { self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) } - func updateValues(_ values: [SecureIdValueWithContext]) { + func updateValues(_ values: [SecureIdValueWithContext], primaryLanguageByCountry: [String: String]) { var (title, text) = fieldTitleAndText(field: self.field, strings: self.strings, values: values) var textColor = self.theme.list.itemSecondaryTextColor @@ -877,10 +860,13 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode { case let .identity(personalDetails, document, selfie, translation): if let personalDetails = personalDetails { if let value = findValue(values, key: .personalDetails)?.1 { - let data = extractSecureIdValueAdditionalData(value.value) - if personalDetails.nativeNames && !data.nativeNames { - filled = false - text = strings.Passport_FieldIdentityDetailsHelp + if case let .personalDetails(value) = value.value { + let hasNativeNames = value.nativeName?.isComplete() ?? false + let requiresNativeNames = primaryLanguageByCountry[value.residenceCountryCode] != "en" + if personalDetails.nativeNames && !hasNativeNames && requiresNativeNames { + filled = false + text = strings.Passport_FieldIdentityDetailsHelp + } } } else { filled = false diff --git a/TelegramUI/UniversalVideoCalleryItem.swift b/TelegramUI/UniversalVideoCalleryItem.swift index 0962ceacfb..334767e745 100644 --- a/TelegramUI/UniversalVideoCalleryItem.swift +++ b/TelegramUI/UniversalVideoCalleryItem.swift @@ -248,11 +248,14 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { self.statusButtonNode.isHidden = true } + var disablePlayerControls = false var isAnimated = false if let content = item.content as? NativeVideoContent { isAnimated = content.fileReference.media.isAnimated } else if let _ = item.content as? SystemVideoContent { self._title.set(.single(item.presentationData.strings.Message_Video)) + } else if let content = item.content as? WebEmbedVideoContent, case .iframe = webEmbedType(content: content.webpageContent) { + disablePlayerControls = true } if let videoNode = self.videoNode { @@ -260,7 +263,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { videoNode.removeFromSupernode() } - if isAnimated { + if isAnimated || disablePlayerControls { self.footerContentNode.scrubberView = nil } @@ -273,7 +276,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } } self.videoNode = videoNode - videoNode.isUserInteractionEnabled = false + videoNode.isUserInteractionEnabled = disablePlayerControls videoNode.backgroundColor = videoNode.ownsContentNode ? UIColor.black : UIColor(rgb: 0x333335) videoNode.canAttachContent = true self.updateDisplayPlaceholder(!videoNode.ownsContentNode) @@ -340,7 +343,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { strongSelf.statusButtonNode.isHidden = !initialBuffering && (strongSelf.didPause || !isPaused || value == nil) } - if isAnimated { + if isAnimated || disablePlayerControls { strongSelf.footerContentNode.content = .info } else if isPaused { @@ -357,7 +360,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { self.zoomableContent = (videoSize, videoNode) - if !isAnimated { + if !isAnimated && !disablePlayerControls { let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed)) self._rightBarButtonItem.set(.single(rightBarButtonItem)) } diff --git a/TelegramUI/VimeoEmbedImplementation.swift b/TelegramUI/VimeoEmbedImplementation.swift index e2434a41b5..66ca0c5952 100644 --- a/TelegramUI/VimeoEmbedImplementation.swift +++ b/TelegramUI/VimeoEmbedImplementation.swift @@ -7,8 +7,9 @@ func extractVimeoVideoIdAndTimestamp(url: String) -> (String, Int)? { return nil } - let domain = "player.vimeo.com" - let match = host == domain || host.contains(".\(domain)") + let match = ["vimeo.com", "player.vimeo.com"].contains(where: { (domain) -> Bool in + return host == domain || host.contains(".\(domain)") + }) guard match else { return nil @@ -39,7 +40,7 @@ func extractVimeoVideoIdAndTimestamp(url: String) -> (String, Int)? { var nextComponentIsVideoId = false for component in pathComponents { - if nextComponentIsVideoId { + if CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: component)) || nextComponentIsVideoId { videoId = component break } else if component == "video" { diff --git a/TelegramUI/WebEmbedPlayerNode.swift b/TelegramUI/WebEmbedPlayerNode.swift index 6a1a177b9b..e1c565bcc4 100644 --- a/TelegramUI/WebEmbedPlayerNode.swift +++ b/TelegramUI/WebEmbedPlayerNode.swift @@ -2,6 +2,7 @@ import Foundation import AsyncDisplayKit import SwiftSignalKit import WebKit +import TelegramCore protocol WebEmbedImplementation { func setup(_ webView: WKWebView, userContentController: WKUserContentController, evaluateJavaScript: @escaping (String) -> Void, updateStatus: @escaping (MediaPlayerStatus) -> Void, onPlaybackStarted: @escaping () -> Void) @@ -15,14 +16,31 @@ protocol WebEmbedImplementation { func callback(url: URL) } -func webEmbedImplementation(embedUrl: String, url: String) -> WebEmbedImplementation { - if let (videoId, timestamp) = extractYoutubeVideoIdAndTimestamp(url: url) { - return YoutubeEmbedImplementation(videoId: videoId, timestamp: timestamp) - } else if let (videoId, timestamp) = extractVimeoVideoIdAndTimestamp(url: url) { - return VimeoEmbedImplementation(videoId: videoId, timestamp: timestamp) +enum WebEmbedType { + case youtube(videoId: String, timestamp: Int) + case vimeo(videoId: String, timestamp: Int) + case iframe(url: String) +} + +func webEmbedType(content: TelegramMediaWebpageLoadedContent) -> WebEmbedType { + if let (videoId, timestamp) = extractYoutubeVideoIdAndTimestamp(url: content.url) { + return .youtube(videoId: videoId, timestamp: timestamp) + } else if let (videoId, timestamp) = extractVimeoVideoIdAndTimestamp(url: content.url) { + return .vimeo(videoId: videoId, timestamp: timestamp) + } else { + return .iframe(url: content.embedUrl ?? content.url) + } +} + +func webEmbedImplementation(for type: WebEmbedType) -> WebEmbedImplementation { + switch type { + case let .youtube(videoId, timestamp): + return YoutubeEmbedImplementation(videoId: videoId, timestamp: timestamp) + case let .vimeo(videoId, timestamp): + return VimeoEmbedImplementation(videoId: videoId, timestamp: timestamp) + case let .iframe(url): + return GenericEmbedImplementation(url: url) } - - return GenericEmbedImplementation(url: url) } final class WebEmbedPlayerNode: ASDisplayNode, WKNavigationDelegate { @@ -84,17 +102,11 @@ final class WebEmbedPlayerNode: ASDisplayNode, WKNavigationDelegate { self.view.addSubview(self.webView) self.impl.setup(self.webView, userContentController: userContentController, evaluateJavaScript: { [weak self] js in - if let strongSelf = self { - strongSelf.evaluateJavaScript(js: js) - } + self?.evaluateJavaScript(js: js) }, updateStatus: { [weak self] status in - if let strongSelf = self { - strongSelf.statusValue.set(status) - } + self?.statusValue.set(status) }, onPlaybackStarted: { [weak self] in - if let strongSelf = self { - strongSelf.readyValue.set(true) - } + self?.readyValue.set(true) }) } diff --git a/TelegramUI/WebEmbedVideoContent.swift b/TelegramUI/WebEmbedVideoContent.swift index 8a140b5a27..83b42b4182 100644 --- a/TelegramUI/WebEmbedVideoContent.swift +++ b/TelegramUI/WebEmbedVideoContent.swift @@ -77,14 +77,9 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte self.imageNode = TransformImageNode() - let embedImpl: WebEmbedImplementation - if let embedUrl = webpageContent.embedUrl { - embedImpl = webEmbedImplementation(embedUrl: embedUrl, url: webpageContent.url) - self.playerNode = WebEmbedPlayerNode(impl: embedImpl, intrinsicDimensions: self.intrinsicDimensions) - } else { - embedImpl = GenericEmbedImplementation(url: webpageContent.url) - self.playerNode = WebEmbedPlayerNode(impl: embedImpl, intrinsicDimensions: self.intrinsicDimensions) - } + let embedType = webEmbedType(content: webpageContent) + let embedImpl = webEmbedImplementation(for: embedType) + self.playerNode = WebEmbedPlayerNode(impl: embedImpl, intrinsicDimensions: self.intrinsicDimensions) super.init() diff --git a/TelegramUI/YoutubeEmbedImplementation.swift b/TelegramUI/YoutubeEmbedImplementation.swift index 59769fa3ad..b4cc4e76d7 100644 --- a/TelegramUI/YoutubeEmbedImplementation.swift +++ b/TelegramUI/YoutubeEmbedImplementation.swift @@ -193,9 +193,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { } self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: self.status.duration, dimensions: self.status.dimensions, timestamp: timestamp, baseRate: 1.0, seekId: self.status.seekId + 1, status: self.status.status) - if let updateStatus = self.updateStatus { - updateStatus(self.status) - } + self.updateStatus?(self.status) self.ignorePosition = 2 } diff --git a/third-party/RMIntro/platform/ios/RMIntroViewController.m b/third-party/RMIntro/platform/ios/RMIntroViewController.m index 7e089972c8..4bdffcdb70 100644 --- a/third-party/RMIntro/platform/ios/RMIntroViewController.m +++ b/third-party/RMIntro/platform/ios/RMIntroViewController.m @@ -25,8 +25,9 @@ typedef enum { Inch4 = 1, Inch47 = 2, Inch55 = 3, - iPad = 4, - iPadPro = 5 + Inch65 = 4, + iPad = 5, + iPadPro = 6 } DeviceScreen; static void TGDispatchOnMainThread(dispatch_block_t block) { @@ -411,6 +412,9 @@ static void TGDispatchOnMainThread(dispatch_block_t block) { case 667: deviceScreen = Inch47; break; + case 896: + deviceScreen = Inch65; + break; default: deviceScreen = Inch55; break; @@ -491,6 +495,13 @@ static void TGDispatchOnMainThread(dispatch_block_t block) { pageControlY = pageY + 160.0f; break; + case Inch65: + glViewY = 62 + 85; + startButtonY = 75 + 30; + pageY = 245 + 125; + pageControlY = pageY + 160.0f; + break; + default: break; }