mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-04 21:41:45 +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):
|
case let .url(url):
|
||||||
var cleanUrl = url
|
var cleanUrl = url
|
||||||
var canAddToReadingList = true
|
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 mailtoString = "mailto:"
|
||||||
let telString = "tel:"
|
let telString = "tel:"
|
||||||
var openText = strongSelf.presentationData.strings.Conversation_LinkDialogOpen
|
var openText = strongSelf.presentationData.strings.Conversation_LinkDialogOpen
|
||||||
|
|||||||
@ -518,6 +518,27 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
|||||||
if let image = m as? TelegramMediaImage {
|
if let image = m as? TelegramMediaImage {
|
||||||
subject = .image(image.representations)
|
subject = .image(image.representations)
|
||||||
} else if let webpage = m as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
} else if let webpage = m as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
||||||
|
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 {
|
if let file = content.file {
|
||||||
subject = .media(.webPage(webPage: WebpageReference(webpage), media: file))
|
subject = .media(.webPage(webPage: WebpageReference(webpage), media: file))
|
||||||
preferredAction = .saveToCameraRoll
|
preferredAction = .saveToCameraRoll
|
||||||
@ -525,6 +546,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
|||||||
subject = .media(.webPage(webPage: WebpageReference(webpage), media: image))
|
subject = .media(.webPage(webPage: WebpageReference(webpage), media: image))
|
||||||
preferredAction = .saveToCameraRoll
|
preferredAction = .saveToCameraRoll
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if let file = m as? TelegramMediaFile {
|
} else if let file = m as? TelegramMediaFile {
|
||||||
subject = .media(.message(message: MessageReference(messages[0]), media: file))
|
subject = .media(.message(message: MessageReference(messages[0]), media: file))
|
||||||
if file.isAnimated {
|
if file.isAnimated {
|
||||||
|
|||||||
@ -219,7 +219,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
switch action {
|
switch action {
|
||||||
case let .url(url):
|
case let .url(url):
|
||||||
var cleanUrl = 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
|
var canAddToReadingList = true
|
||||||
let mailtoString = "mailto:"
|
let mailtoString = "mailto:"
|
||||||
let telString = "tel:"
|
let telString = "tel:"
|
||||||
|
|||||||
@ -6,12 +6,13 @@ final class GenericEmbedImplementation: WebEmbedImplementation {
|
|||||||
private var evalImpl: ((String) -> Void)?
|
private var evalImpl: ((String) -> Void)?
|
||||||
private var updateStatus: ((MediaPlayerStatus) -> Void)?
|
private var updateStatus: ((MediaPlayerStatus) -> Void)?
|
||||||
private var onPlaybackStarted: (() -> Void)?
|
private var onPlaybackStarted: (() -> Void)?
|
||||||
|
private var status : MediaPlayerStatus
|
||||||
|
|
||||||
private let url: String
|
private let url: String
|
||||||
|
|
||||||
init(url: String) {
|
init(url: String) {
|
||||||
self.url = url
|
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) {
|
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.evalImpl = evaluateJavaScript
|
||||||
self.updateStatus = updateStatus
|
self.updateStatus = updateStatus
|
||||||
self.onPlaybackStarted = onPlaybackStarted
|
self.onPlaybackStarted = onPlaybackStarted
|
||||||
//updateStatus(self.status)
|
updateStatus(self.status)
|
||||||
|
|
||||||
let html = String(format: htmlTemplate, self.url)
|
let html = String(format: htmlTemplate, self.url)
|
||||||
webView.loadHTMLString(html, baseURL: URL(string: "about:blank"))
|
webView.loadHTMLString(html, baseURL: URL(string: "about:blank"))
|
||||||
@ -59,6 +60,9 @@ final class GenericEmbedImplementation: WebEmbedImplementation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func pageReady() {
|
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 {
|
if let onPlaybackStarted = self.onPlaybackStarted {
|
||||||
onPlaybackStarted()
|
onPlaybackStarted()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1932,7 +1932,7 @@ func handlePeerInfoAboutTextAction(account: Account, peerId: PeerId, navigateDis
|
|||||||
case .longTap:
|
case .longTap:
|
||||||
switch itemLink {
|
switch itemLink {
|
||||||
case let .url(url):
|
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 openText = canOpenIn ? presentationData.strings.Conversation_FileOpenIn : presentationData.strings.Conversation_LinkDialogOpen
|
||||||
let actionSheet = ActionSheetController(presentationTheme: presentationData.theme)
|
let actionSheet = ActionSheetController(presentationTheme: presentationData.theme)
|
||||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
||||||
|
|||||||
@ -611,7 +611,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
case .longTap:
|
case .longTap:
|
||||||
if let url = self.urlForTapLocation(location) {
|
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 openText = canOpenIn ? self.strings.Conversation_FileOpenIn : self.strings.Conversation_LinkDialogOpen
|
||||||
let actionSheet = ActionSheetController(presentationTheme: self.presentationTheme)
|
let actionSheet = ActionSheetController(presentationTheme: self.presentationTheme)
|
||||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
||||||
|
|||||||
@ -190,7 +190,7 @@ public class PeerMediaCollectionController: TelegramController {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
switch content {
|
switch content {
|
||||||
case let .url(url):
|
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 openText = canOpenIn ? strongSelf.presentationData.strings.Conversation_FileOpenIn : strongSelf.presentationData.strings.Conversation_LinkDialogOpen
|
||||||
let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme)
|
let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme)
|
||||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
||||||
|
|||||||
@ -568,7 +568,7 @@ final class SecureIdAuthController: ViewController {
|
|||||||
switch self.state {
|
switch self.state {
|
||||||
case let .form(form):
|
case let .form(form):
|
||||||
if case let .form(reqForm) = self.mode, let encryptedFormData = form.encryptedFormData, let formData = form.formData {
|
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)
|
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
|
||||||
|
|||||||
@ -51,11 +51,11 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
self.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
self.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||||
self.acceptNode.pressed = { [weak self] in
|
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
|
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 !filled {
|
||||||
if let contentNode = strongSelf.contentNode as? SecureIdAuthFormContentNode {
|
if let contentNode = strongSelf.contentNode as? SecureIdAuthFormContentNode {
|
||||||
if let rect = contentNode.frameForField(field) {
|
if let rect = contentNode.frameForField(field) {
|
||||||
@ -291,7 +291,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
current.updateValues(formData.values)
|
current.updateValues(formData.values)
|
||||||
contentNode = current
|
contentNode = current
|
||||||
} else {
|
} 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 {
|
if let strongSelf = self {
|
||||||
switch field {
|
switch field {
|
||||||
case .identity, .address:
|
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 {
|
switch field {
|
||||||
case let .identity(personalDetails, document, selfie, translations):
|
case let .identity(personalDetails, document, selfie, translations):
|
||||||
if let document = document {
|
if let document = document {
|
||||||
@ -468,11 +459,8 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .oneOf(types):
|
case let .oneOf(types):
|
||||||
var chosenByError = false
|
inner: for type in types.sorted(by: { $0.valueKey.rawValue < $1.valueKey.rawValue }) {
|
||||||
outer: for type in types {
|
|
||||||
if let value = findValue(formData.values, key: type.valueKey)?.1 {
|
if let value = findValue(formData.values, key: type.valueKey)?.1 {
|
||||||
let hasErrors = hasErrors(value, type.valueKey)
|
|
||||||
|
|
||||||
let data = extractSecureIdValueAdditionalData(value.value)
|
let data = extractSecureIdValueAdditionalData(value.value)
|
||||||
var dataFilled = true
|
var dataFilled = true
|
||||||
if selfie && !data.selfie {
|
if selfie && !data.selfie {
|
||||||
@ -481,9 +469,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
if translations && !data.translation {
|
if translations && !data.translation {
|
||||||
dataFilled = false
|
dataFilled = false
|
||||||
}
|
}
|
||||||
|
if hasValueType == nil || dataFilled {
|
||||||
if hasValueType == nil || ((hasErrors || dataFilled) && !chosenByError) {
|
|
||||||
chosenByError = hasErrors
|
|
||||||
switch value.value {
|
switch value.value {
|
||||||
case .passport:
|
case .passport:
|
||||||
hasValueType = .passport
|
hasValueType = .passport
|
||||||
@ -496,6 +482,10 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dataFilled {
|
||||||
|
break inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -539,38 +529,33 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .oneOf(types):
|
case let .oneOf(types):
|
||||||
var chosenByError = false
|
inner: for type in types.sorted(by: { $0.valueKey.rawValue < $1.valueKey.rawValue }) {
|
||||||
outer: for type in types {
|
|
||||||
if let value = findValue(formData.values, key: type.valueKey)?.1 {
|
if let value = findValue(formData.values, key: type.valueKey)?.1 {
|
||||||
let hasErrors = hasErrors(value, type.valueKey)
|
|
||||||
|
|
||||||
let data = extractSecureIdValueAdditionalData(value.value)
|
let data = extractSecureIdValueAdditionalData(value.value)
|
||||||
var dataFilled = true
|
var dataFilled = true
|
||||||
if translation && !data.translation {
|
if translation && !data.translation {
|
||||||
dataFilled = false
|
dataFilled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasValueType == nil || ((hasErrors || dataFilled) && !chosenByError) {
|
if hasValueType == nil || dataFilled {
|
||||||
chosenByError = hasErrors
|
|
||||||
switch value.value {
|
switch value.value {
|
||||||
case .utilityBill:
|
case .utilityBill:
|
||||||
hasValueType = .utilityBill
|
hasValueType = .utilityBill
|
||||||
break outer
|
|
||||||
case .bankStatement:
|
case .bankStatement:
|
||||||
hasValueType = .bankStatement
|
hasValueType = .bankStatement
|
||||||
break outer
|
|
||||||
case .rentalAgreement:
|
case .rentalAgreement:
|
||||||
hasValueType = .rentalAgreement
|
hasValueType = .rentalAgreement
|
||||||
break outer
|
|
||||||
case .passportRegistration:
|
case .passportRegistration:
|
||||||
hasValueType = .passportRegistration
|
hasValueType = .passportRegistration
|
||||||
break outer
|
|
||||||
case .temporaryRegistration:
|
case .temporaryRegistration:
|
||||||
hasValueType = .temporaryRegistration
|
hasValueType = .temporaryRegistration
|
||||||
break outer
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dataFilled {
|
||||||
|
break inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ private let passwordFont = Font.regular(16.0)
|
|||||||
private let buttonFont = Font.regular(17.0)
|
private let buttonFont = Font.regular(17.0)
|
||||||
|
|
||||||
final class SecureIdAuthFormContentNode: ASDisplayNode, SecureIdAuthContentNode, UITextFieldDelegate {
|
final class SecureIdAuthFormContentNode: ASDisplayNode, SecureIdAuthContentNode, UITextFieldDelegate {
|
||||||
|
private let primaryLanguageByCountry: [String: String]
|
||||||
private let requestedFields: [SecureIdRequestedFormField]
|
private let requestedFields: [SecureIdRequestedFormField]
|
||||||
private let fieldBackgroundNode: ASDisplayNode
|
private let fieldBackgroundNode: ASDisplayNode
|
||||||
private let fieldNodes: [SecureIdAuthFormFieldNode]
|
private let fieldNodes: [SecureIdAuthFormFieldNode]
|
||||||
@ -18,9 +19,10 @@ final class SecureIdAuthFormContentNode: ASDisplayNode, SecureIdAuthContentNode,
|
|||||||
private let requestLayout: () -> Void
|
private let requestLayout: () -> Void
|
||||||
private var validLayout: CGFloat?
|
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.requestLayout = requestLayout
|
||||||
|
|
||||||
|
self.primaryLanguageByCountry = primaryLanguageByCountry
|
||||||
self.requestedFields = form.requestedFields
|
self.requestedFields = form.requestedFields
|
||||||
self.fieldBackgroundNode = ASDisplayNode()
|
self.fieldBackgroundNode = ASDisplayNode()
|
||||||
self.fieldBackgroundNode.isLayerBacked = true
|
self.fieldBackgroundNode.isLayerBacked = true
|
||||||
@ -28,8 +30,8 @@ final class SecureIdAuthFormContentNode: ASDisplayNode, SecureIdAuthContentNode,
|
|||||||
|
|
||||||
var fieldNodes: [SecureIdAuthFormFieldNode] = []
|
var fieldNodes: [SecureIdAuthFormFieldNode] = []
|
||||||
|
|
||||||
for (field, fieldValues, _) in parseRequestedFormFields(self.requestedFields, values: form.values) {
|
for (field, fieldValues, _) in parseRequestedFormFields(self.requestedFields, values: form.values, primaryLanguageByCountry: primaryLanguageByCountry) {
|
||||||
fieldNodes.append(SecureIdAuthFormFieldNode(theme: theme, strings: strings, field: field, values: fieldValues, selected: {
|
fieldNodes.append(SecureIdAuthFormFieldNode(theme: theme, strings: strings, field: field, values: fieldValues, primaryLanguageByCountry: primaryLanguageByCountry, selected: {
|
||||||
openField(field)
|
openField(field)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -88,9 +90,9 @@ final class SecureIdAuthFormContentNode: ASDisplayNode, SecureIdAuthContentNode,
|
|||||||
|
|
||||||
func updateValues(_ values: [SecureIdValueWithContext]) {
|
func updateValues(_ values: [SecureIdValueWithContext]) {
|
||||||
var index = 0
|
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 {
|
if index < self.fieldNodes.count {
|
||||||
self.fieldNodes[index].updateValues(fieldValues)
|
self.fieldNodes[index].updateValues(fieldValues, primaryLanguageByCountry: self.primaryLanguageByCountry)
|
||||||
}
|
}
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,24 +25,24 @@ enum SecureIdRequestedIdentityDocument: Int32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum SecureIdRequestedAddressDocument: Int32 {
|
enum SecureIdRequestedAddressDocument: Int32 {
|
||||||
|
case utilityBill
|
||||||
|
case bankStatement
|
||||||
|
case rentalAgreement
|
||||||
case passportRegistration
|
case passportRegistration
|
||||||
case temporaryRegistration
|
case temporaryRegistration
|
||||||
case bankStatement
|
|
||||||
case utilityBill
|
|
||||||
case rentalAgreement
|
|
||||||
|
|
||||||
var valueKey: SecureIdValueKey {
|
var valueKey: SecureIdValueKey {
|
||||||
switch self {
|
switch self {
|
||||||
|
case .utilityBill:
|
||||||
|
return .utilityBill
|
||||||
|
case .bankStatement:
|
||||||
|
return .bankStatement
|
||||||
|
case .rentalAgreement:
|
||||||
|
return .rentalAgreement
|
||||||
case .passportRegistration:
|
case .passportRegistration:
|
||||||
return .passportRegistration
|
return .passportRegistration
|
||||||
case .temporaryRegistration:
|
case .temporaryRegistration:
|
||||||
return .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()
|
var requestedValues = RequestedFieldValues()
|
||||||
|
|
||||||
for type in types {
|
for type in types {
|
||||||
@ -191,20 +191,28 @@ func parseRequestedFormFields(_ types: [SecureIdRequestedFormField], values: [Se
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result.map { field in
|
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)
|
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 {
|
switch field {
|
||||||
case let .identity(personalDetails, document, selfie, translation):
|
case let .identity(personalDetails, document, selfie, translation):
|
||||||
var filled = true
|
var filled = true
|
||||||
var result: [SecureIdValueWithContext] = []
|
var result: [SecureIdValueWithContext] = []
|
||||||
if personalDetails != nil {
|
if let personalDetails = personalDetails {
|
||||||
if let value = findValue(values, key: .personalDetails)?.1 {
|
if let value = findValue(values, key: .personalDetails)?.1 {
|
||||||
result.append(value)
|
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
|
filled = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -223,7 +231,7 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values:
|
|||||||
if translation && !data.translation {
|
if translation && !data.translation {
|
||||||
filled = false
|
filled = false
|
||||||
}
|
}
|
||||||
if !value.errors.isEmpty {
|
if errorForErrorKey(type.valueKey, value) != nil {
|
||||||
filled = false
|
filled = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -232,7 +240,7 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values:
|
|||||||
case let .oneOf(types):
|
case let .oneOf(types):
|
||||||
var anyDocument = false
|
var anyDocument = false
|
||||||
var bestMatchingValue: SecureIdValueWithContext?
|
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 let value = findValue(values, key: type.valueKey)?.1 {
|
||||||
if bestMatchingValue == nil {
|
if bestMatchingValue == nil {
|
||||||
bestMatchingValue = value
|
bestMatchingValue = value
|
||||||
@ -257,7 +265,7 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values:
|
|||||||
}
|
}
|
||||||
if let bestMatchingValue = bestMatchingValue {
|
if let bestMatchingValue = bestMatchingValue {
|
||||||
result.append(bestMatchingValue)
|
result.append(bestMatchingValue)
|
||||||
if !bestMatchingValue.errors.isEmpty {
|
if errorForErrorKey(bestMatchingValue.value.key, bestMatchingValue) != nil {
|
||||||
filled = false
|
filled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,7 +278,7 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values:
|
|||||||
if addressDetails {
|
if addressDetails {
|
||||||
if let value = findValue(values, key: .address)?.1 {
|
if let value = findValue(values, key: .address)?.1 {
|
||||||
result.append(value)
|
result.append(value)
|
||||||
if !value.errors.isEmpty {
|
if errorForErrorKey(.address, value) != nil {
|
||||||
filled = false
|
filled = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -286,7 +294,7 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values:
|
|||||||
if translation && !data.translation {
|
if translation && !data.translation {
|
||||||
filled = false
|
filled = false
|
||||||
}
|
}
|
||||||
if !value.errors.isEmpty {
|
if errorForErrorKey(type.valueKey, value) != nil {
|
||||||
filled = false
|
filled = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -295,7 +303,7 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values:
|
|||||||
case let .oneOf(types):
|
case let .oneOf(types):
|
||||||
var anyDocument = false
|
var anyDocument = false
|
||||||
var bestMatchingValue: SecureIdValueWithContext?
|
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 let value = findValue(values, key: type.valueKey)?.1 {
|
||||||
if bestMatchingValue == nil {
|
if bestMatchingValue == nil {
|
||||||
bestMatchingValue = value
|
bestMatchingValue = value
|
||||||
@ -317,7 +325,7 @@ private func findValuesForField(field: SecureIdParsedRequestedFormField, values:
|
|||||||
}
|
}
|
||||||
if let bestMatchingValue = bestMatchingValue {
|
if let bestMatchingValue = bestMatchingValue {
|
||||||
result.append(bestMatchingValue)
|
result.append(bestMatchingValue)
|
||||||
if !bestMatchingValue.errors.isEmpty {
|
if errorForErrorKey(bestMatchingValue.value.key, bestMatchingValue) != nil {
|
||||||
filled = false
|
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
|
title = strings.Passport_FieldOneOf_Or(stringForDocumentType(typesArray[0], strings: strings), stringForDocumentType(typesArray[1], strings: strings)).0
|
||||||
}
|
}
|
||||||
placeholder = placeholderForDocumentTypes(typesArray, strings: strings)
|
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 {
|
if let value = findValue(values, key: type.valueKey)?.1.value {
|
||||||
filledDocument = (type, value)
|
filledDocument = (type, value)
|
||||||
break
|
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
|
title = strings.Passport_FieldOneOf_Or(stringForDocumentType(typesArray[0], strings: strings), stringForDocumentType(typesArray[1], strings: strings)).0
|
||||||
}
|
}
|
||||||
placeholder = placeholderForDocumentTypes(typesArray, strings: strings)
|
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 {
|
if let value = findValue(values, key: type.valueKey)?.1.value {
|
||||||
filledDocument = (type, value)
|
filledDocument = (type, value)
|
||||||
break
|
break
|
||||||
@ -691,76 +699,51 @@ private func fieldTitleAndText(field: SecureIdParsedRequestedFormField, strings:
|
|||||||
return (title, text.isEmpty ? placeholder : text)
|
return (title, text.isEmpty ? placeholder : text)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fieldErrorText(field: SecureIdParsedRequestedFormField, values: [SecureIdValueWithContext]) -> String? {
|
private func errorForErrorKey(_ key: SecureIdValueKey, _ value: SecureIdValueWithContext) -> String? {
|
||||||
switch field {
|
if let error = value.errors[.value(key)] {
|
||||||
case let .identity(personalDetails, document, _, _):
|
|
||||||
if let _ = personalDetails, let value = findValue(values, key: .personalDetails)?.1 {
|
|
||||||
if let error = value.errors[.value(.personalDetails)] {
|
|
||||||
return error
|
return error
|
||||||
} else if let error = value.errors.first {
|
} else if let error = value.errors.first {
|
||||||
if case .value = error.key {} else {
|
if case .value = error.key {} else {
|
||||||
return error.value
|
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, let error = errorForErrorKey(.personalDetails, value) {
|
||||||
|
return error
|
||||||
}
|
}
|
||||||
if let document = document {
|
if let document = document {
|
||||||
switch document {
|
switch document {
|
||||||
case let .just(type):
|
case let .just(type):
|
||||||
if let value = findValue(values, key: type.valueKey)?.1 {
|
if let value = findValue(values, key: type.valueKey)?.1, let error = errorForErrorKey(type.valueKey, value) {
|
||||||
if let error = value.errors[.value(type.valueKey)] {
|
|
||||||
return error
|
return error
|
||||||
} else if let error = value.errors.first {
|
|
||||||
if case .value = error.key {} else {
|
|
||||||
return error.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case let .oneOf(types):
|
case let .oneOf(types):
|
||||||
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 {
|
if let value = findValue(values, key: type.valueKey)?.1, let error = errorForErrorKey(type.valueKey, value) {
|
||||||
if let error = value.errors[.value(type.valueKey)] {
|
|
||||||
return error
|
return error
|
||||||
} else if let error = value.errors.first {
|
|
||||||
if case .value = error.key {} else {
|
|
||||||
return error.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .address(addressDetails, document, _):
|
case let .address(addressDetails, document, _):
|
||||||
if addressDetails, let value = findValue(values, key: .address)?.1 {
|
if addressDetails, let value = findValue(values, key: .address)?.1, let error = errorForErrorKey(.address, value) {
|
||||||
if let error = value.errors[.value(.address)] {
|
|
||||||
return error
|
return error
|
||||||
} else if let error = value.errors.first {
|
|
||||||
if case .value = error.key {} else {
|
|
||||||
return error.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let document = document {
|
if let document = document {
|
||||||
switch document {
|
switch document {
|
||||||
case let .just(type):
|
case let .just(type):
|
||||||
if let value = findValue(values, key: type.valueKey)?.1 {
|
if let value = findValue(values, key: type.valueKey)?.1, let error = errorForErrorKey(type.valueKey, value) {
|
||||||
if let error = value.errors[.value(type.valueKey)] {
|
|
||||||
return error
|
return error
|
||||||
} else if let error = value.errors.first {
|
|
||||||
if case .value = error.key {} else {
|
|
||||||
return error.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case let .oneOf(types):
|
case let .oneOf(types):
|
||||||
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 {
|
if let value = findValue(values, key: type.valueKey)?.1, let error = errorForErrorKey(type.valueKey, value) {
|
||||||
if let error = value.errors[.value(type.valueKey)] {
|
|
||||||
return error
|
return error
|
||||||
} else if let error = value.errors.first {
|
|
||||||
if case .value = error.key {} else {
|
|
||||||
return error.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -791,7 +774,7 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode {
|
|||||||
private let theme: PresentationTheme
|
private let theme: PresentationTheme
|
||||||
private let strings: PresentationStrings
|
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.field = field
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
@ -845,7 +828,7 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode {
|
|||||||
self.addSubnode(self.checkNode)
|
self.addSubnode(self.checkNode)
|
||||||
self.addSubnode(self.buttonNode)
|
self.addSubnode(self.buttonNode)
|
||||||
|
|
||||||
self.updateValues(values)
|
self.updateValues(values, primaryLanguageByCountry: primaryLanguageByCountry)
|
||||||
|
|
||||||
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -862,7 +845,7 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode {
|
|||||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
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 (title, text) = fieldTitleAndText(field: self.field, strings: self.strings, values: values)
|
||||||
var textColor = self.theme.list.itemSecondaryTextColor
|
var textColor = self.theme.list.itemSecondaryTextColor
|
||||||
|
|
||||||
@ -877,11 +860,14 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode {
|
|||||||
case let .identity(personalDetails, document, selfie, translation):
|
case let .identity(personalDetails, document, selfie, translation):
|
||||||
if let personalDetails = personalDetails {
|
if let personalDetails = personalDetails {
|
||||||
if let value = findValue(values, key: .personalDetails)?.1 {
|
if let value = findValue(values, key: .personalDetails)?.1 {
|
||||||
let data = extractSecureIdValueAdditionalData(value.value)
|
if case let .personalDetails(value) = value.value {
|
||||||
if personalDetails.nativeNames && !data.nativeNames {
|
let hasNativeNames = value.nativeName?.isComplete() ?? false
|
||||||
|
let requiresNativeNames = primaryLanguageByCountry[value.residenceCountryCode] != "en"
|
||||||
|
if personalDetails.nativeNames && !hasNativeNames && requiresNativeNames {
|
||||||
filled = false
|
filled = false
|
||||||
text = strings.Passport_FieldIdentityDetailsHelp
|
text = strings.Passport_FieldIdentityDetailsHelp
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
filled = false
|
filled = false
|
||||||
text = strings.Passport_FieldIdentityDetailsHelp
|
text = strings.Passport_FieldIdentityDetailsHelp
|
||||||
|
|||||||
@ -248,11 +248,14 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
self.statusButtonNode.isHidden = true
|
self.statusButtonNode.isHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var disablePlayerControls = false
|
||||||
var isAnimated = false
|
var isAnimated = false
|
||||||
if let content = item.content as? NativeVideoContent {
|
if let content = item.content as? NativeVideoContent {
|
||||||
isAnimated = content.fileReference.media.isAnimated
|
isAnimated = content.fileReference.media.isAnimated
|
||||||
} else if let _ = item.content as? SystemVideoContent {
|
} else if let _ = item.content as? SystemVideoContent {
|
||||||
self._title.set(.single(item.presentationData.strings.Message_Video))
|
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 {
|
if let videoNode = self.videoNode {
|
||||||
@ -260,7 +263,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
videoNode.removeFromSupernode()
|
videoNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
|
|
||||||
if isAnimated {
|
if isAnimated || disablePlayerControls {
|
||||||
self.footerContentNode.scrubberView = nil
|
self.footerContentNode.scrubberView = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +276,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.videoNode = videoNode
|
self.videoNode = videoNode
|
||||||
videoNode.isUserInteractionEnabled = false
|
videoNode.isUserInteractionEnabled = disablePlayerControls
|
||||||
videoNode.backgroundColor = videoNode.ownsContentNode ? UIColor.black : UIColor(rgb: 0x333335)
|
videoNode.backgroundColor = videoNode.ownsContentNode ? UIColor.black : UIColor(rgb: 0x333335)
|
||||||
videoNode.canAttachContent = true
|
videoNode.canAttachContent = true
|
||||||
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
|
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
|
||||||
@ -340,7 +343,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
strongSelf.statusButtonNode.isHidden = !initialBuffering && (strongSelf.didPause || !isPaused || value == nil)
|
strongSelf.statusButtonNode.isHidden = !initialBuffering && (strongSelf.didPause || !isPaused || value == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isAnimated {
|
if isAnimated || disablePlayerControls {
|
||||||
strongSelf.footerContentNode.content = .info
|
strongSelf.footerContentNode.content = .info
|
||||||
}
|
}
|
||||||
else if isPaused {
|
else if isPaused {
|
||||||
@ -357,7 +360,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
self.zoomableContent = (videoSize, videoNode)
|
self.zoomableContent = (videoSize, videoNode)
|
||||||
|
|
||||||
if !isAnimated {
|
if !isAnimated && !disablePlayerControls {
|
||||||
let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed))
|
let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed))
|
||||||
self._rightBarButtonItem.set(.single(rightBarButtonItem))
|
self._rightBarButtonItem.set(.single(rightBarButtonItem))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,9 @@ func extractVimeoVideoIdAndTimestamp(url: String) -> (String, Int)? {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let domain = "player.vimeo.com"
|
let match = ["vimeo.com", "player.vimeo.com"].contains(where: { (domain) -> Bool in
|
||||||
let match = host == domain || host.contains(".\(domain)")
|
return host == domain || host.contains(".\(domain)")
|
||||||
|
})
|
||||||
|
|
||||||
guard match else {
|
guard match else {
|
||||||
return nil
|
return nil
|
||||||
@ -39,7 +40,7 @@ func extractVimeoVideoIdAndTimestamp(url: String) -> (String, Int)? {
|
|||||||
var nextComponentIsVideoId = false
|
var nextComponentIsVideoId = false
|
||||||
|
|
||||||
for component in pathComponents {
|
for component in pathComponents {
|
||||||
if nextComponentIsVideoId {
|
if CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: component)) || nextComponentIsVideoId {
|
||||||
videoId = component
|
videoId = component
|
||||||
break
|
break
|
||||||
} else if component == "video" {
|
} else if component == "video" {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import Foundation
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import WebKit
|
import WebKit
|
||||||
|
import TelegramCore
|
||||||
|
|
||||||
protocol WebEmbedImplementation {
|
protocol WebEmbedImplementation {
|
||||||
func setup(_ webView: WKWebView, userContentController: WKUserContentController, evaluateJavaScript: @escaping (String) -> Void, updateStatus: @escaping (MediaPlayerStatus) -> Void, onPlaybackStarted: @escaping () -> Void)
|
func setup(_ webView: WKWebView, userContentController: WKUserContentController, evaluateJavaScript: @escaping (String) -> Void, updateStatus: @escaping (MediaPlayerStatus) -> Void, onPlaybackStarted: @escaping () -> Void)
|
||||||
@ -15,15 +16,32 @@ protocol WebEmbedImplementation {
|
|||||||
func callback(url: URL)
|
func callback(url: URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func webEmbedImplementation(embedUrl: String, url: String) -> WebEmbedImplementation {
|
enum WebEmbedType {
|
||||||
if let (videoId, timestamp) = extractYoutubeVideoIdAndTimestamp(url: url) {
|
case youtube(videoId: String, timestamp: Int)
|
||||||
return YoutubeEmbedImplementation(videoId: videoId, timestamp: timestamp)
|
case vimeo(videoId: String, timestamp: Int)
|
||||||
} else if let (videoId, timestamp) = extractVimeoVideoIdAndTimestamp(url: url) {
|
case iframe(url: String)
|
||||||
return VimeoEmbedImplementation(videoId: videoId, timestamp: timestamp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
final class WebEmbedPlayerNode: ASDisplayNode, WKNavigationDelegate {
|
||||||
private let statusValue = ValuePromise<MediaPlayerStatus>(MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused), ignoreRepeated: true)
|
private let statusValue = ValuePromise<MediaPlayerStatus>(MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused), ignoreRepeated: true)
|
||||||
@ -84,17 +102,11 @@ final class WebEmbedPlayerNode: ASDisplayNode, WKNavigationDelegate {
|
|||||||
self.view.addSubview(self.webView)
|
self.view.addSubview(self.webView)
|
||||||
|
|
||||||
self.impl.setup(self.webView, userContentController: userContentController, evaluateJavaScript: { [weak self] js in
|
self.impl.setup(self.webView, userContentController: userContentController, evaluateJavaScript: { [weak self] js in
|
||||||
if let strongSelf = self {
|
self?.evaluateJavaScript(js: js)
|
||||||
strongSelf.evaluateJavaScript(js: js)
|
|
||||||
}
|
|
||||||
}, updateStatus: { [weak self] status in
|
}, updateStatus: { [weak self] status in
|
||||||
if let strongSelf = self {
|
self?.statusValue.set(status)
|
||||||
strongSelf.statusValue.set(status)
|
|
||||||
}
|
|
||||||
}, onPlaybackStarted: { [weak self] in
|
}, onPlaybackStarted: { [weak self] in
|
||||||
if let strongSelf = self {
|
self?.readyValue.set(true)
|
||||||
strongSelf.readyValue.set(true)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -77,14 +77,9 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte
|
|||||||
|
|
||||||
self.imageNode = TransformImageNode()
|
self.imageNode = TransformImageNode()
|
||||||
|
|
||||||
let embedImpl: WebEmbedImplementation
|
let embedType = webEmbedType(content: webpageContent)
|
||||||
if let embedUrl = webpageContent.embedUrl {
|
let embedImpl = webEmbedImplementation(for: embedType)
|
||||||
embedImpl = webEmbedImplementation(embedUrl: embedUrl, url: webpageContent.url)
|
|
||||||
self.playerNode = WebEmbedPlayerNode(impl: embedImpl, intrinsicDimensions: self.intrinsicDimensions)
|
self.playerNode = WebEmbedPlayerNode(impl: embedImpl, intrinsicDimensions: self.intrinsicDimensions)
|
||||||
} else {
|
|
||||||
embedImpl = GenericEmbedImplementation(url: webpageContent.url)
|
|
||||||
self.playerNode = WebEmbedPlayerNode(impl: embedImpl, intrinsicDimensions: self.intrinsicDimensions)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.init()
|
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)
|
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 {
|
self.updateStatus?(self.status)
|
||||||
updateStatus(self.status)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ignorePosition = 2
|
self.ignorePosition = 2
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,8 +25,9 @@ typedef enum {
|
|||||||
Inch4 = 1,
|
Inch4 = 1,
|
||||||
Inch47 = 2,
|
Inch47 = 2,
|
||||||
Inch55 = 3,
|
Inch55 = 3,
|
||||||
iPad = 4,
|
Inch65 = 4,
|
||||||
iPadPro = 5
|
iPad = 5,
|
||||||
|
iPadPro = 6
|
||||||
} DeviceScreen;
|
} DeviceScreen;
|
||||||
|
|
||||||
static void TGDispatchOnMainThread(dispatch_block_t block) {
|
static void TGDispatchOnMainThread(dispatch_block_t block) {
|
||||||
@ -411,6 +412,9 @@ static void TGDispatchOnMainThread(dispatch_block_t block) {
|
|||||||
case 667:
|
case 667:
|
||||||
deviceScreen = Inch47;
|
deviceScreen = Inch47;
|
||||||
break;
|
break;
|
||||||
|
case 896:
|
||||||
|
deviceScreen = Inch65;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
deviceScreen = Inch55;
|
deviceScreen = Inch55;
|
||||||
break;
|
break;
|
||||||
@ -491,6 +495,13 @@ static void TGDispatchOnMainThread(dispatch_block_t block) {
|
|||||||
pageControlY = pageY + 160.0f;
|
pageControlY = pageY + 160.0f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Inch65:
|
||||||
|
glViewY = 62 + 85;
|
||||||
|
startButtonY = 75 + 30;
|
||||||
|
pageY = 245 + 125;
|
||||||
|
pageControlY = pageY + 160.0f;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user