mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-04 13:38:21 +00:00
no message
This commit is contained in:
parent
f3c966308f
commit
92d95510c7
@ -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
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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:"
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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: [
|
||||
|
||||
@ -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: [
|
||||
|
||||
@ -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: [
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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" {
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user