mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Merge commit 'ca80657e161b28abde1b2eafa3528e04f5487ca6'
This commit is contained in:
commit
941b3a0477
@ -10886,6 +10886,7 @@ Sorry for the inconvenience.";
|
|||||||
"Call.StatusWeakSignal" = "Weak network signal";
|
"Call.StatusWeakSignal" = "Weak network signal";
|
||||||
|
|
||||||
"Conversation.ContactAddContact" = "ADD";
|
"Conversation.ContactAddContact" = "ADD";
|
||||||
|
"Conversation.ContactAddContactLong" = "ADD CONTACT";
|
||||||
"Conversation.ContactMessage" = "MESSAGE";
|
"Conversation.ContactMessage" = "MESSAGE";
|
||||||
|
|
||||||
"Chat.PlayOnceVideoMessageTooltip" = "This video message can only be played once.";
|
"Chat.PlayOnceVideoMessageTooltip" = "This video message can only be played once.";
|
||||||
|
@ -304,31 +304,35 @@ final class CameraOutput: NSObject {
|
|||||||
self.currentMode = mode
|
self.currentMode = mode
|
||||||
self.lastSampleTimestamp = nil
|
self.lastSampleTimestamp = nil
|
||||||
|
|
||||||
let codecType: AVVideoCodecType
|
|
||||||
if case .roundVideo = mode {
|
|
||||||
codecType = .h264
|
|
||||||
} else {
|
|
||||||
if hasHEVCHardwareEncoder {
|
|
||||||
codecType = .hevc
|
|
||||||
} else {
|
|
||||||
codecType = .h264
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard var videoSettings = self.videoOutput.recommendedVideoSettings(forVideoCodecType: codecType, assetWriterOutputFileType: .mp4) else {
|
|
||||||
return .complete()
|
|
||||||
}
|
|
||||||
|
|
||||||
var dimensions: CGSize = CGSize(width: 1080, height: 1920)
|
|
||||||
if orientation == .landscapeLeft || orientation == .landscapeRight {
|
|
||||||
dimensions = CGSize(width: 1920, height: 1080)
|
|
||||||
}
|
|
||||||
var orientation = orientation
|
var orientation = orientation
|
||||||
|
let dimensions: CGSize
|
||||||
|
let videoSettings: [String: Any]
|
||||||
if case .roundVideo = mode {
|
if case .roundVideo = mode {
|
||||||
videoSettings[AVVideoWidthKey] = 400
|
dimensions = videoMessageDimensions.cgSize
|
||||||
videoSettings[AVVideoHeightKey] = 400
|
|
||||||
dimensions = CGSize(width: 400, height: 400)
|
|
||||||
orientation = .landscapeRight
|
orientation = .landscapeRight
|
||||||
|
|
||||||
|
let compressionProperties: [String: Any] = [
|
||||||
|
AVVideoAverageBitRateKey: 1000 * 1000,
|
||||||
|
AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
|
||||||
|
AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC
|
||||||
|
]
|
||||||
|
videoSettings = [
|
||||||
|
AVVideoCodecKey: AVVideoCodecType.h264,
|
||||||
|
AVVideoCompressionPropertiesKey: compressionProperties,
|
||||||
|
AVVideoWidthKey: Int(dimensions.width),
|
||||||
|
AVVideoHeightKey: Int(dimensions.height)
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
let codecType: AVVideoCodecType = hasHEVCHardwareEncoder ? .hevc : .h264
|
||||||
|
if orientation == .landscapeLeft || orientation == .landscapeRight {
|
||||||
|
dimensions = CGSize(width: 1920, height: 1080)
|
||||||
|
} else {
|
||||||
|
dimensions = CGSize(width: 1080, height: 1920)
|
||||||
|
}
|
||||||
|
guard let settings = self.videoOutput.recommendedVideoSettings(forVideoCodecType: codecType, assetWriterOutputFileType: .mp4) else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
videoSettings = settings
|
||||||
}
|
}
|
||||||
|
|
||||||
let audioSettings = self.audioOutput.recommendedAudioSettingsForAssetWriter(writingTo: .mp4) ?? [:]
|
let audioSettings = self.audioOutput.recommendedAudioSettingsForAssetWriter(writingTo: .mp4) ?? [:]
|
||||||
@ -514,10 +518,10 @@ final class CameraOutput: NSObject {
|
|||||||
let extensions = CMFormatDescriptionGetExtensions(formatDescription) as! [String: Any]
|
let extensions = CMFormatDescriptionGetExtensions(formatDescription) as! [String: Any]
|
||||||
|
|
||||||
var updatedExtensions = extensions
|
var updatedExtensions = extensions
|
||||||
updatedExtensions["CVBytesPerRow"] = 400 * 4
|
updatedExtensions["CVBytesPerRow"] = videoMessageDimensions.width * 4
|
||||||
|
|
||||||
var newFormatDescription: CMFormatDescription?
|
var newFormatDescription: CMFormatDescription?
|
||||||
var status = CMVideoFormatDescriptionCreate(allocator: nil, codecType: mediaSubType, width: 400, height: 400, extensions: updatedExtensions as CFDictionary, formatDescriptionOut: &newFormatDescription)
|
var status = CMVideoFormatDescriptionCreate(allocator: nil, codecType: mediaSubType, width: videoMessageDimensions.width, height: videoMessageDimensions.height, extensions: updatedExtensions as CFDictionary, formatDescriptionOut: &newFormatDescription)
|
||||||
guard status == noErr, let newFormatDescription else {
|
guard status == noErr, let newFormatDescription else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,9 @@ import CoreMedia
|
|||||||
import CoreVideo
|
import CoreVideo
|
||||||
import Metal
|
import Metal
|
||||||
import Display
|
import Display
|
||||||
|
import TelegramCore
|
||||||
|
|
||||||
|
let videoMessageDimensions = PixelDimensions(width: 400, height: 400)
|
||||||
|
|
||||||
func allocateOutputBufferPool(with inputFormatDescription: CMFormatDescription, outputRetainedBufferCountHint: Int) -> (
|
func allocateOutputBufferPool(with inputFormatDescription: CMFormatDescription, outputRetainedBufferCountHint: Int) -> (
|
||||||
outputBufferPool: CVPixelBufferPool?,
|
outputBufferPool: CVPixelBufferPool?,
|
||||||
@ -114,8 +117,7 @@ class CameraRoundVideoFilter {
|
|||||||
}
|
}
|
||||||
self.inputFormatDescription = formatDescription
|
self.inputFormatDescription = formatDescription
|
||||||
|
|
||||||
let diameter: CGFloat = 400.0
|
let circleImage = generateImage(videoMessageDimensions.cgSize, opaque: false, scale: 1.0, rotatedContext: { size, context in
|
||||||
let circleImage = generateImage(CGSize(width: diameter, height: diameter), opaque: false, scale: 1.0, rotatedContext: { size, context in
|
|
||||||
let bounds = CGRect(origin: .zero, size: size)
|
let bounds = CGRect(origin: .zero, size: size)
|
||||||
context.clear(bounds)
|
context.clear(bounds)
|
||||||
context.setFillColor(UIColor.white.cgColor)
|
context.setFillColor(UIColor.white.cgColor)
|
||||||
@ -158,7 +160,7 @@ class CameraRoundVideoFilter {
|
|||||||
|
|
||||||
var sourceImage = CIImage(cvImageBuffer: pixelBuffer)
|
var sourceImage = CIImage(cvImageBuffer: pixelBuffer)
|
||||||
sourceImage = sourceImage.oriented(additional ? .leftMirrored : .right)
|
sourceImage = sourceImage.oriented(additional ? .leftMirrored : .right)
|
||||||
let scale = 400.0 / min(sourceImage.extent.width, sourceImage.extent.height)
|
let scale = CGFloat(videoMessageDimensions.width) / min(sourceImage.extent.width, sourceImage.extent.height)
|
||||||
|
|
||||||
resizeFilter.setValue(sourceImage, forKey: kCIInputImageKey)
|
resizeFilter.setValue(sourceImage, forKey: kCIInputImageKey)
|
||||||
resizeFilter.setValue(scale, forKey: kCIInputScaleKey)
|
resizeFilter.setValue(scale, forKey: kCIInputScaleKey)
|
||||||
@ -203,18 +205,14 @@ class CameraRoundVideoFilter {
|
|||||||
guard let finalImage else {
|
guard let finalImage else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if finalImage.extent.width != 400 {
|
|
||||||
print("wtf: \(finalImage)")
|
|
||||||
}
|
|
||||||
|
|
||||||
var pbuf: CVPixelBuffer?
|
var pbuf: CVPixelBuffer?
|
||||||
CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, outputPixelBufferPool!, &pbuf)
|
CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, outputPixelBufferPool!, &pbuf)
|
||||||
guard let outputPixelBuffer = pbuf else {
|
guard let outputPixelBuffer = pbuf else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ciContext.render(finalImage, to: outputPixelBuffer, bounds: CGRect(origin: .zero, size: CGSize(width: 400, height: 400)), colorSpace: outputColorSpace)
|
self.ciContext.render(finalImage, to: outputPixelBuffer, bounds: CGRect(origin: .zero, size: videoMessageDimensions.cgSize), colorSpace: outputColorSpace)
|
||||||
|
|
||||||
return outputPixelBuffer
|
return outputPixelBuffer
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ public class ItemListDisclosureItem: ListViewItem, ItemListItem {
|
|||||||
let title: String
|
let title: String
|
||||||
let titleColor: ItemListDisclosureItemTitleColor
|
let titleColor: ItemListDisclosureItemTitleColor
|
||||||
let titleFont: ItemListDisclosureItemTitleFont
|
let titleFont: ItemListDisclosureItemTitleFont
|
||||||
|
let titleIcon: UIImage?
|
||||||
let enabled: Bool
|
let enabled: Bool
|
||||||
let label: String
|
let label: String
|
||||||
let labelStyle: ItemListDisclosureLabelStyle
|
let labelStyle: ItemListDisclosureLabelStyle
|
||||||
@ -59,7 +60,7 @@ public class ItemListDisclosureItem: ListViewItem, ItemListItem {
|
|||||||
public let tag: ItemListItemTag?
|
public let tag: ItemListItemTag?
|
||||||
public let shimmeringIndex: Int?
|
public let shimmeringIndex: Int?
|
||||||
|
|
||||||
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, context: AccountContext? = nil, iconPeer: EnginePeer? = nil, title: String, enabled: Bool = true, titleColor: ItemListDisclosureItemTitleColor = .primary, titleFont: ItemListDisclosureItemTitleFont = .regular, label: String, labelStyle: ItemListDisclosureLabelStyle = .text, additionalDetailLabel: String? = nil, sectionId: ItemListSectionId, style: ItemListStyle, disclosureStyle: ItemListDisclosureStyle = .arrow, action: (() -> Void)?, clearHighlightAutomatically: Bool = true, tag: ItemListItemTag? = nil, shimmeringIndex: Int? = nil) {
|
public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, context: AccountContext? = nil, iconPeer: EnginePeer? = nil, title: String, enabled: Bool = true, titleColor: ItemListDisclosureItemTitleColor = .primary, titleFont: ItemListDisclosureItemTitleFont = .regular, titleIcon: UIImage? = nil, label: String, labelStyle: ItemListDisclosureLabelStyle = .text, additionalDetailLabel: String? = nil, sectionId: ItemListSectionId, style: ItemListStyle, disclosureStyle: ItemListDisclosureStyle = .arrow, action: (() -> Void)?, clearHighlightAutomatically: Bool = true, tag: ItemListItemTag? = nil, shimmeringIndex: Int? = nil) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -67,6 +68,7 @@ public class ItemListDisclosureItem: ListViewItem, ItemListItem {
|
|||||||
self.title = title
|
self.title = title
|
||||||
self.titleColor = titleColor
|
self.titleColor = titleColor
|
||||||
self.titleFont = titleFont
|
self.titleFont = titleFont
|
||||||
|
self.titleIcon = titleIcon
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
self.labelStyle = labelStyle
|
self.labelStyle = labelStyle
|
||||||
self.label = label
|
self.label = label
|
||||||
@ -138,6 +140,7 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
var avatarNode: AvatarNode?
|
var avatarNode: AvatarNode?
|
||||||
let iconNode: ASImageNode
|
let iconNode: ASImageNode
|
||||||
let titleNode: TextNode
|
let titleNode: TextNode
|
||||||
|
let titleIconNode: ASImageNode
|
||||||
public let labelNode: TextNode
|
public let labelNode: TextNode
|
||||||
var additionalDetailLabelNode: TextNode?
|
var additionalDetailLabelNode: TextNode?
|
||||||
let arrowNode: ASImageNode
|
let arrowNode: ASImageNode
|
||||||
@ -184,6 +187,10 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
self.titleNode = TextNode()
|
self.titleNode = TextNode()
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.titleIconNode = ASImageNode()
|
||||||
|
self.titleIconNode.displayWithoutProcessing = true
|
||||||
|
self.titleIconNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.labelNode = TextNode()
|
self.labelNode = TextNode()
|
||||||
self.labelNode.isUserInteractionEnabled = false
|
self.labelNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
@ -626,6 +633,19 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
strongSelf.additionalDetailLabelNode = nil
|
strongSelf.additionalDetailLabelNode = nil
|
||||||
additionalDetailLabelNode.removeFromSupernode()
|
additionalDetailLabelNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let titleIcon = item.titleIcon {
|
||||||
|
if strongSelf.titleIconNode.supernode == nil {
|
||||||
|
strongSelf.addSubnode(strongSelf.titleIconNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.titleIconNode.image = titleIcon
|
||||||
|
strongSelf.titleIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + 5.0, y: floor((layout.contentSize.height - titleIcon.size.height) / 2.0) - 1.0), size: titleIcon.size)
|
||||||
|
} else {
|
||||||
|
if strongSelf.titleIconNode.supernode != nil {
|
||||||
|
strongSelf.titleIconNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if case .textWithIcon = item.labelStyle {
|
if case .textWithIcon = item.labelStyle {
|
||||||
if let updatedLabelImage = updatedLabelImage {
|
if let updatedLabelImage = updatedLabelImage {
|
||||||
|
@ -97,7 +97,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
|||||||
case forwardPrivacy(PresentationTheme, String, String)
|
case forwardPrivacy(PresentationTheme, String, String)
|
||||||
case groupPrivacy(PresentationTheme, String, String)
|
case groupPrivacy(PresentationTheme, String, String)
|
||||||
case voiceMessagePrivacy(PresentationTheme, String, String, Bool)
|
case voiceMessagePrivacy(PresentationTheme, String, String, Bool)
|
||||||
case messagePrivacy(Bool)
|
case messagePrivacy(PresentationTheme, Bool, Bool)
|
||||||
case bioPrivacy(PresentationTheme, String, String)
|
case bioPrivacy(PresentationTheme, String, String)
|
||||||
case selectivePrivacyInfo(PresentationTheme, String)
|
case selectivePrivacyInfo(PresentationTheme, String)
|
||||||
case passcode(PresentationTheme, String, Bool, String)
|
case passcode(PresentationTheme, String, Bool, String)
|
||||||
@ -240,14 +240,14 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .voiceMessagePrivacy(lhsTheme, lhsText, lhsValue, lhsLocked):
|
case let .voiceMessagePrivacy(lhsTheme, lhsText, lhsValue, lhsHasPremium):
|
||||||
if case let .voiceMessagePrivacy(rhsTheme, rhsText, rhsValue, rhsLocked) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsLocked == rhsLocked {
|
if case let .voiceMessagePrivacy(rhsTheme, rhsText, rhsValue, rhsHasPremium) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsHasPremium == rhsHasPremium {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .messagePrivacy(value):
|
case let .messagePrivacy(lhsTheme, lhsValue, lhsHasPremium):
|
||||||
if case .messagePrivacy(value) = rhs {
|
if case let .messagePrivacy(rhsTheme, rhsValue, rhsHasPremium) = rhs, lhsTheme === rhsTheme, lhsValue == rhsValue, lhsHasPremium == rhsHasPremium {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -390,12 +390,12 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
|||||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openGroupsPrivacy()
|
arguments.openGroupsPrivacy()
|
||||||
})
|
})
|
||||||
case let .voiceMessagePrivacy(_, text, value, locked):
|
case let .voiceMessagePrivacy(theme, text, value, hasPremium):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, labelStyle: locked ? .textWithIcon(UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon")!.precomposed()) : .text, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, titleIcon: hasPremium ? PresentationResourcesItemList.premiumIcon(theme) : nil, label: value, labelStyle: !hasPremium ? .textWithIcon(UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon")!.precomposed()) : .text, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openVoiceMessagePrivacy()
|
arguments.openVoiceMessagePrivacy()
|
||||||
})
|
})
|
||||||
case let .messagePrivacy(value):
|
case let .messagePrivacy(theme, value, hasPremium):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, title: presentationData.strings.Settings_Privacy_Messages, label: !value ? presentationData.strings.Settings_Privacy_Messages_ValueEveryone : presentationData.strings.Settings_Privacy_Messages_ValueContactsAndPremium, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, title: presentationData.strings.Settings_Privacy_Messages, titleIcon: hasPremium ? PresentationResourcesItemList.premiumIcon(theme) : nil, label: !value ? presentationData.strings.Settings_Privacy_Messages_ValueEveryone : presentationData.strings.Settings_Privacy_Messages_ValueContactsAndPremium, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openMessagePrivacy()
|
arguments.openMessagePrivacy()
|
||||||
})
|
})
|
||||||
case let .bioPrivacy(_, text, value):
|
case let .bioPrivacy(_, text, value):
|
||||||
@ -591,8 +591,8 @@ private func privacyAndSecurityControllerEntries(
|
|||||||
entries.append(.voiceCallPrivacy(presentationData.theme, presentationData.strings.Privacy_Calls, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.voiceCalls)))
|
entries.append(.voiceCallPrivacy(presentationData.theme, presentationData.strings.Privacy_Calls, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.voiceCalls)))
|
||||||
entries.append(.groupPrivacy(presentationData.theme, presentationData.strings.Privacy_GroupsAndChannels, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.groupInvitations)))
|
entries.append(.groupPrivacy(presentationData.theme, presentationData.strings.Privacy_GroupsAndChannels, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.groupInvitations)))
|
||||||
if !isPremiumDisabled {
|
if !isPremiumDisabled {
|
||||||
entries.append(.voiceMessagePrivacy(presentationData.theme, presentationData.strings.Privacy_VoiceMessages, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.voiceMessages), !isPremium))
|
entries.append(.voiceMessagePrivacy(presentationData.theme, presentationData.strings.Privacy_VoiceMessages, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.voiceMessages), isPremium))
|
||||||
entries.append(.messagePrivacy(privacySettings.globalSettings.nonContactChatsRequirePremium))
|
entries.append(.messagePrivacy(presentationData.theme, privacySettings.globalSettings.nonContactChatsRequirePremium, isPremium))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
entries.append(.phoneNumberPrivacy(presentationData.theme, presentationData.strings.PrivacySettings_PhoneNumber, presentationData.strings.Channel_NotificationLoading))
|
entries.append(.phoneNumberPrivacy(presentationData.theme, presentationData.strings.PrivacySettings_PhoneNumber, presentationData.strings.Channel_NotificationLoading))
|
||||||
@ -603,7 +603,7 @@ private func privacyAndSecurityControllerEntries(
|
|||||||
entries.append(.voiceCallPrivacy(presentationData.theme, presentationData.strings.Privacy_Calls, presentationData.strings.Channel_NotificationLoading))
|
entries.append(.voiceCallPrivacy(presentationData.theme, presentationData.strings.Privacy_Calls, presentationData.strings.Channel_NotificationLoading))
|
||||||
entries.append(.groupPrivacy(presentationData.theme, presentationData.strings.Privacy_GroupsAndChannels, presentationData.strings.Channel_NotificationLoading))
|
entries.append(.groupPrivacy(presentationData.theme, presentationData.strings.Privacy_GroupsAndChannels, presentationData.strings.Channel_NotificationLoading))
|
||||||
if !isPremiumDisabled {
|
if !isPremiumDisabled {
|
||||||
entries.append(.voiceMessagePrivacy(presentationData.theme, presentationData.strings.Privacy_VoiceMessages, presentationData.strings.Channel_NotificationLoading, !isPremium))
|
entries.append(.voiceMessagePrivacy(presentationData.theme, presentationData.strings.Privacy_VoiceMessages, presentationData.strings.Channel_NotificationLoading, isPremium))
|
||||||
}
|
}
|
||||||
|
|
||||||
//entries.append(.selectivePrivacyInfo(presentationData.theme, presentationData.strings.PrivacyLastSeenSettings_GroupsAndChannelsHelp))
|
//entries.append(.selectivePrivacyInfo(presentationData.theme, presentationData.strings.PrivacyLastSeenSettings_GroupsAndChannelsHelp))
|
||||||
|
@ -72,6 +72,7 @@ public enum PresentationResourceKey: Int32 {
|
|||||||
case itemListCloudIcon
|
case itemListCloudIcon
|
||||||
case itemListTopicArrowIcon
|
case itemListTopicArrowIcon
|
||||||
case itemListAddBoostsIcon
|
case itemListAddBoostsIcon
|
||||||
|
case itemListPremiumIcon
|
||||||
|
|
||||||
case statsReactionsIcon
|
case statsReactionsIcon
|
||||||
case statsForwardsIcon
|
case statsForwardsIcon
|
||||||
|
@ -282,6 +282,30 @@ public struct PresentationResourcesItemList {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func premiumIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||||
|
return theme.image(PresentationResourceKey.itemListPremiumIcon.rawValue, { theme in
|
||||||
|
return generateImage(CGSize(width: 16.0, height: 16.0), contextGenerator: { size, context in
|
||||||
|
let bounds = CGRect(origin: .zero, size: size)
|
||||||
|
context.clear(bounds)
|
||||||
|
|
||||||
|
let image = UIImage(bundleImageName: "Item List/PremiumIcon")!
|
||||||
|
context.clip(to: bounds, mask: image.cgImage!)
|
||||||
|
|
||||||
|
let colorsArray: [CGColor] = [
|
||||||
|
UIColor(rgb: 0x6b93ff).cgColor,
|
||||||
|
UIColor(rgb: 0x6b93ff).cgColor,
|
||||||
|
UIColor(rgb: 0x8d77ff).cgColor,
|
||||||
|
UIColor(rgb: 0xb56eec).cgColor,
|
||||||
|
UIColor(rgb: 0xb56eec).cgColor
|
||||||
|
]
|
||||||
|
var locations: [CGFloat] = [0.0, 0.3, 0.5, 0.7, 1.0]
|
||||||
|
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
|
||||||
|
|
||||||
|
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public static func cornersImage(_ theme: PresentationTheme, top: Bool, bottom: Bool) -> UIImage? {
|
public static func cornersImage(_ theme: PresentationTheme, top: Bool, bottom: Bool) -> UIImage? {
|
||||||
if !top && !bottom {
|
if !top && !bottom {
|
||||||
return nil
|
return nil
|
||||||
|
@ -307,7 +307,18 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (messageButtonWidth, messageContinueLayout) = makeMessageButtonLayout(constrainedSize.width, nil, false, item.presentationData.strings.Conversation_ContactMessage.uppercased(), mainColor, false, false)
|
let (messageButtonWidth, messageContinueLayout) = makeMessageButtonLayout(constrainedSize.width, nil, false, item.presentationData.strings.Conversation_ContactMessage.uppercased(), mainColor, false, false)
|
||||||
let (addButtonWidth, addContinueLayout) = makeAddButtonLayout(constrainedSize.width, nil, false, !canMessage && !canAdd ? item.presentationData.strings.Conversation_ViewContactDetails.uppercased() : item.presentationData.strings.Conversation_ContactAddContact.uppercased(), mainColor, false, false)
|
|
||||||
|
let addTitle: String
|
||||||
|
if !canMessage && !canAdd {
|
||||||
|
addTitle = item.presentationData.strings.Conversation_ViewContactDetails
|
||||||
|
} else {
|
||||||
|
if canMessage {
|
||||||
|
addTitle = item.presentationData.strings.Conversation_ContactAddContact
|
||||||
|
} else {
|
||||||
|
addTitle = item.presentationData.strings.Conversation_ContactAddContactLong
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (addButtonWidth, addContinueLayout) = makeAddButtonLayout(constrainedSize.width, nil, false, addTitle.uppercased(), mainColor, false, false)
|
||||||
|
|
||||||
let maxButtonWidth = max(messageButtonWidth, addButtonWidth)
|
let maxButtonWidth = max(messageButtonWidth, addButtonWidth)
|
||||||
var maxContentWidth: CGFloat = avatarSize.width + 7.0
|
var maxContentWidth: CGFloat = avatarSize.width + 7.0
|
||||||
@ -327,7 +338,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
let lineWidth: CGFloat = 3.0
|
let lineWidth: CGFloat = 3.0
|
||||||
|
|
||||||
var buttonCount = 1
|
var buttonCount = 1
|
||||||
if canMessage {
|
if canMessage && canAdd {
|
||||||
buttonCount += 1
|
buttonCount += 1
|
||||||
}
|
}
|
||||||
var buttonWidth = floor((boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0 - lineWidth))
|
var buttonWidth = floor((boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0 - lineWidth))
|
||||||
@ -387,7 +398,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
strongSelf.messageButtonNode.isHidden = !canMessage
|
strongSelf.messageButtonNode.isHidden = !canMessage
|
||||||
|
|
||||||
let backgroundInsets = layoutConstants.text.bubbleInsets
|
let backgroundInsets = layoutConstants.text.bubbleInsets
|
||||||
let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top + 5.0), size: CGSize(width: contentWidth - layoutConstants.text.bubbleInsets.right * 2.0, height: layoutSize.height - 34.0))
|
let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top + 5.0), size: CGSize(width: boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0, height: layoutSize.height - 34.0))
|
||||||
|
|
||||||
if let statusSizeAndApply = statusSizeAndApply {
|
if let statusSizeAndApply = statusSizeAndApply {
|
||||||
strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.left, y: backgroundFrame.maxY + 3.0), size: statusSizeAndApply.0)
|
strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.left, y: backgroundFrame.maxY + 3.0), size: statusSizeAndApply.0)
|
||||||
|
@ -532,7 +532,7 @@ public class VideoMessageCameraScreen: ViewController {
|
|||||||
fileprivate var liveUploadInterface: LegacyLiveUploadInterface?
|
fileprivate var liveUploadInterface: LegacyLiveUploadInterface?
|
||||||
private var currentLiveUploadPath: String?
|
private var currentLiveUploadPath: String?
|
||||||
fileprivate var currentLiveUploadData: LegacyLiveUploadInterfaceResult?
|
fileprivate var currentLiveUploadData: LegacyLiveUploadInterfaceResult?
|
||||||
|
|
||||||
fileprivate let backgroundView: UIVisualEffectView
|
fileprivate let backgroundView: UIVisualEffectView
|
||||||
fileprivate let containerView: UIView
|
fileprivate let containerView: UIView
|
||||||
fileprivate let componentHost: ComponentView<ViewControllerComponentContainer.Environment>
|
fileprivate let componentHost: ComponentView<ViewControllerComponentContainer.Environment>
|
||||||
@ -689,16 +689,27 @@ public class VideoMessageCameraScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func withReadyCamera(isFirstTime: Bool = false, _ f: @escaping () -> Void) {
|
func withReadyCamera(isFirstTime: Bool = false, _ f: @escaping () -> Void) {
|
||||||
|
guard let controller = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
let _ = ((self.cameraState.isDualCameraEnabled ? self.additionalPreviewView.isPreviewing : self.mainPreviewView.isPreviewing)
|
let _ = (combineLatest(queue: Queue.mainQueue(),
|
||||||
|> filter { $0 }
|
self.cameraState.isDualCameraEnabled ? self.additionalPreviewView.isPreviewing : self.mainPreviewView.isPreviewing,
|
||||||
|> take(1)).startStandalone(next: { _ in
|
controller.audioSessionReady.get()
|
||||||
|
)
|
||||||
|
|> filter { $0 && $1 }
|
||||||
|
|> take(1)).startStandalone(next: { _, _ in
|
||||||
f()
|
f()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Queue.mainQueue().after(0.35) {
|
let _ = (combineLatest(queue: Queue.mainQueue(),
|
||||||
|
.single(true) |> delay(0.35, queue: Queue.mainQueue()),
|
||||||
|
controller.audioSessionReady.get()
|
||||||
|
)
|
||||||
|
|> filter { $0 && $1 }
|
||||||
|
|> take(1)).startStandalone(next: { _, _ in
|
||||||
f()
|
f()
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1241,6 +1252,7 @@ public class VideoMessageCameraScreen: ViewController {
|
|||||||
fileprivate let completion: (EnqueueMessage?, Bool?, Int32?) -> Void
|
fileprivate let completion: (EnqueueMessage?, Bool?, Int32?) -> Void
|
||||||
|
|
||||||
private var audioSessionDisposable: Disposable?
|
private var audioSessionDisposable: Disposable?
|
||||||
|
fileprivate let audioSessionReady = ValuePromise<Bool>(false)
|
||||||
|
|
||||||
private let hapticFeedback = HapticFeedback()
|
private let hapticFeedback = HapticFeedback()
|
||||||
|
|
||||||
@ -1484,11 +1496,13 @@ public class VideoMessageCameraScreen: ViewController {
|
|||||||
finalDuration = duration
|
finalDuration = duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dimensions = PixelDimensions(width: 400, height: 400)
|
||||||
|
|
||||||
var thumbnailImage = video.thumbnail
|
var thumbnailImage = video.thumbnail
|
||||||
if startTime > 0.0 {
|
if startTime > 0.0 {
|
||||||
let composition = composition(with: results)
|
let composition = composition(with: results)
|
||||||
let imageGenerator = AVAssetImageGenerator(asset: composition)
|
let imageGenerator = AVAssetImageGenerator(asset: composition)
|
||||||
imageGenerator.maximumSize = CGSize(width: 400, height: 400)
|
imageGenerator.maximumSize = dimensions.cgSize
|
||||||
imageGenerator.appliesPreferredTrackTransform = true
|
imageGenerator.appliesPreferredTrackTransform = true
|
||||||
|
|
||||||
if let cgImage = try? imageGenerator.copyCGImage(at: CMTime(seconds: startTime, preferredTimescale: composition.duration.timescale), actualTime: nil) {
|
if let cgImage = try? imageGenerator.copyCGImage(at: CMTime(seconds: startTime, preferredTimescale: composition.duration.timescale), actualTime: nil) {
|
||||||
@ -1496,7 +1510,7 @@ public class VideoMessageCameraScreen: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let values = MediaEditorValues(peerId: self.context.account.peerId, originalDimensions: PixelDimensions(width: 400, height: 400), cropOffset: .zero, cropRect: CGRect(origin: .zero, size: CGSize(width: 400.0, height: 400.0)), cropScale: 1.0, cropRotation: 0.0, cropMirroring: false, cropOrientation: nil, gradientColors: nil, videoTrimRange: self.node.previewState?.trimRange, videoIsMuted: false, videoIsFullHd: false, videoIsMirrored: false, videoVolume: nil, additionalVideoPath: nil, additionalVideoIsDual: false, additionalVideoPosition: nil, additionalVideoScale: nil, additionalVideoRotation: nil, additionalVideoPositionChanges: [], additionalVideoTrimRange: nil, additionalVideoOffset: nil, additionalVideoVolume: nil, nightTheme: false, drawing: nil, entities: [], toolValues: [:], audioTrack: nil, audioTrackTrimRange: nil, audioTrackOffset: nil, audioTrackVolume: nil, audioTrackSamples: nil, qualityPreset: .videoMessage)
|
let values = MediaEditorValues(peerId: self.context.account.peerId, originalDimensions: dimensions, cropOffset: .zero, cropRect: CGRect(origin: .zero, size: dimensions.cgSize), cropScale: 1.0, cropRotation: 0.0, cropMirroring: false, cropOrientation: nil, gradientColors: nil, videoTrimRange: self.node.previewState?.trimRange, videoIsMuted: false, videoIsFullHd: false, videoIsMirrored: false, videoVolume: nil, additionalVideoPath: nil, additionalVideoIsDual: false, additionalVideoPosition: nil, additionalVideoScale: nil, additionalVideoRotation: nil, additionalVideoPositionChanges: [], additionalVideoTrimRange: nil, additionalVideoOffset: nil, additionalVideoVolume: nil, nightTheme: false, drawing: nil, entities: [], toolValues: [:], audioTrack: nil, audioTrackTrimRange: nil, audioTrackOffset: nil, audioTrackVolume: nil, audioTrackSamples: nil, qualityPreset: .videoMessage)
|
||||||
|
|
||||||
var resourceAdjustments: VideoMediaResourceAdjustments? = nil
|
var resourceAdjustments: VideoMediaResourceAdjustments? = nil
|
||||||
if let valuesData = try? JSONEncoder().encode(values) {
|
if let valuesData = try? JSONEncoder().encode(values) {
|
||||||
@ -1614,10 +1628,13 @@ public class VideoMessageCameraScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func requestAudioSession() {
|
private func requestAudioSession() {
|
||||||
self.audioSessionDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: .recordWithOthers, activate: { _ in
|
self.audioSessionDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: .recordWithOthers, activate: { [weak self] _ in
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(true)
|
try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(true)
|
||||||
}
|
}
|
||||||
|
if let self {
|
||||||
|
self.audioSessionReady.set(true)
|
||||||
|
}
|
||||||
}, deactivate: { _ in
|
}, deactivate: { _ in
|
||||||
return .single(Void())
|
return .single(Void())
|
||||||
})
|
})
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Item List/PremiumIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Item List/PremiumIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "premiumstar_16.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
97
submodules/TelegramUI/Images.xcassets/Item List/PremiumIcon.imageset/premiumstar_16.pdf
vendored
Normal file
97
submodules/TelegramUI/Images.xcassets/Item List/PremiumIcon.imageset/premiumstar_16.pdf
vendored
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.494995 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
7.573102 2.542415 m
|
||||||
|
3.972436 0.336632 l
|
||||||
|
3.598035 0.107271 3.108589 0.224851 2.879229 0.599252 c
|
||||||
|
2.767209 0.782110 2.733811 1.002467 2.786615 1.210307 c
|
||||||
|
3.343997 3.404178 l
|
||||||
|
3.545203 4.196128 4.087134 4.858135 4.823744 5.211795 c
|
||||||
|
8.751891 7.097767 l
|
||||||
|
8.935022 7.185692 9.012203 7.405426 8.924278 7.588558 c
|
||||||
|
8.853073 7.736866 8.692046 7.819856 8.529942 7.791792 c
|
||||||
|
4.157411 7.034797 l
|
||||||
|
3.268577 6.880917 2.357086 7.126360 1.665707 7.705756 c
|
||||||
|
0.284388 8.863339 l
|
||||||
|
-0.052136 9.145357 -0.096324 9.646784 0.185694 9.983309 c
|
||||||
|
0.322857 10.146982 0.520100 10.248593 0.732998 10.265255 c
|
||||||
|
4.953338 10.595538 l
|
||||||
|
5.251494 10.618872 5.511330 10.807558 5.625789 11.083856 c
|
||||||
|
7.253917 15.014055 l
|
||||||
|
7.421958 15.419697 7.887019 15.612309 8.292661 15.444268 c
|
||||||
|
8.487435 15.363581 8.642185 15.208831 8.722873 15.014055 c
|
||||||
|
10.351001 11.083856 l
|
||||||
|
10.465460 10.807558 10.725295 10.618872 11.023451 10.595538 c
|
||||||
|
15.266980 10.263440 l
|
||||||
|
15.704712 10.229183 16.031794 9.846561 15.997537 9.408829 c
|
||||||
|
15.981057 9.198256 15.881458 9.002894 15.720723 8.865864 c
|
||||||
|
12.484364 6.106792 l
|
||||||
|
12.256535 5.912563 12.157140 5.606812 12.227205 5.315742 c
|
||||||
|
13.222160 1.182478 l
|
||||||
|
13.324918 0.755602 13.062167 0.326249 12.635291 0.223492 c
|
||||||
|
12.430174 0.174116 12.213841 0.208297 12.033939 0.318506 c
|
||||||
|
8.403688 2.542415 l
|
||||||
|
8.148836 2.698538 7.827954 2.698538 7.573102 2.542415 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
1440
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000001530 00000 n
|
||||||
|
0000001553 00000 n
|
||||||
|
0000001726 00000 n
|
||||||
|
0000001800 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
1859
|
||||||
|
%%EOF
|
@ -350,11 +350,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isFirstTime, !self.viewOnceButton.isHidden {
|
|
||||||
self.maybePresentViewOnceTooltip()
|
|
||||||
}
|
|
||||||
|
|
||||||
let panelHeight = defaultHeight(metrics: metrics)
|
let panelHeight = defaultHeight(metrics: metrics)
|
||||||
|
|
||||||
transition.updateFrame(node: self.deleteButton, frame: CGRect(origin: CGPoint(x: leftInset + 2.0 - UIScreenPixel, y: 1), size: CGSize(width: 40.0, height: 40)))
|
transition.updateFrame(node: self.deleteButton, frame: CGRect(origin: CGPoint(x: leftInset + 2.0 - UIScreenPixel, y: 1), size: CGSize(width: 40.0, height: 40)))
|
||||||
@ -488,6 +484,10 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isFirstTime, !self.viewOnceButton.isHidden {
|
||||||
|
self.maybePresentViewOnceTooltip()
|
||||||
|
}
|
||||||
|
|
||||||
return panelHeight
|
return panelHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +126,29 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
|||||||
}
|
}
|
||||||
if !pathComponents.isEmpty && !pathComponents[0].isEmpty {
|
if !pathComponents.isEmpty && !pathComponents[0].isEmpty {
|
||||||
let peerName: String = pathComponents[0]
|
let peerName: String = pathComponents[0]
|
||||||
|
|
||||||
|
if pathComponents[0].hasPrefix("+") || pathComponents[0].hasPrefix("%20") {
|
||||||
|
let component = pathComponents[0].replacingOccurrences(of: "%20", with: "+")
|
||||||
|
if component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789+").inverted) == nil {
|
||||||
|
var attach: String?
|
||||||
|
var startAttach: String?
|
||||||
|
if let queryItems = components.queryItems {
|
||||||
|
for queryItem in queryItems {
|
||||||
|
if let value = queryItem.value {
|
||||||
|
if queryItem.name == "attach" {
|
||||||
|
attach = value
|
||||||
|
} else if queryItem.name == "startattach" {
|
||||||
|
startAttach = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .phone(component.replacingOccurrences(of: "+", with: ""), attach, startAttach)
|
||||||
|
} else {
|
||||||
|
return .join(String(component.dropFirst()))
|
||||||
|
}
|
||||||
|
}
|
||||||
if pathComponents.count == 1 {
|
if pathComponents.count == 1 {
|
||||||
if let queryItems = components.queryItems {
|
if let queryItems = components.queryItems {
|
||||||
if peerName == "socks" || peerName == "proxy" {
|
if peerName == "socks" || peerName == "proxy" {
|
||||||
@ -288,27 +311,6 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
|||||||
}
|
}
|
||||||
} else if pathComponents[0].hasPrefix(phonebookUsernamePathPrefix), let idValue = Int64(String(pathComponents[0][pathComponents[0].index(pathComponents[0].startIndex, offsetBy: phonebookUsernamePathPrefix.count)...])) {
|
} else if pathComponents[0].hasPrefix(phonebookUsernamePathPrefix), let idValue = Int64(String(pathComponents[0][pathComponents[0].index(pathComponents[0].startIndex, offsetBy: phonebookUsernamePathPrefix.count)...])) {
|
||||||
return .peerId(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(idValue)))
|
return .peerId(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(idValue)))
|
||||||
} else if pathComponents[0].hasPrefix("+") || pathComponents[0].hasPrefix("%20") {
|
|
||||||
let component = pathComponents[0].replacingOccurrences(of: "%20", with: "+")
|
|
||||||
if component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789+").inverted) == nil {
|
|
||||||
var attach: String?
|
|
||||||
var startAttach: String?
|
|
||||||
if let queryItems = components.queryItems {
|
|
||||||
for queryItem in queryItems {
|
|
||||||
if let value = queryItem.value {
|
|
||||||
if queryItem.name == "attach" {
|
|
||||||
attach = value
|
|
||||||
} else if queryItem.name == "startattach" {
|
|
||||||
startAttach = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return .phone(component.replacingOccurrences(of: "+", with: ""), attach, startAttach)
|
|
||||||
} else {
|
|
||||||
return .join(String(component.dropFirst()))
|
|
||||||
}
|
|
||||||
} else if pathComponents[0].hasPrefix("$") || pathComponents[0].hasPrefix("%24") {
|
} else if pathComponents[0].hasPrefix("$") || pathComponents[0].hasPrefix("%24") {
|
||||||
var component = pathComponents[0].replacingOccurrences(of: "%24", with: "$")
|
var component = pathComponents[0].replacingOccurrences(of: "%24", with: "$")
|
||||||
if component.hasPrefix("$") {
|
if component.hasPrefix("$") {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user