mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-08 05:30:47 +00:00
Merge commit '5e842163468fc91e3e22ab243415574b62b406de'
This commit is contained in:
commit
6fb5008c0f
@ -7326,3 +7326,5 @@ Sorry for the inconvenience.";
|
|||||||
"Attachment.DeselectedItems_any" = "%@ items deselected";
|
"Attachment.DeselectedItems_any" = "%@ items deselected";
|
||||||
"Attachment.DeselectedItems_many" = "%@ items deselected";
|
"Attachment.DeselectedItems_many" = "%@ items deselected";
|
||||||
"Attachment.DeselectedItems_0" = "%@ items deselected";
|
"Attachment.DeselectedItems_0" = "%@ items deselected";
|
||||||
|
|
||||||
|
"PrivacyPhoneNumberSettings.CustomPublicLink" = "Users who have your number saved in their contacts will also see it on Telegram.\n\nThis public link opens a chat with you:\n[https://t.me/%@]()";
|
||||||
|
@ -220,6 +220,7 @@ public class AttachmentController: ViewController {
|
|||||||
guard self.currentType != type else {
|
guard self.currentType != type else {
|
||||||
if let controller = self.currentController {
|
if let controller = self.currentController {
|
||||||
controller.scrollToTopWithTabBar?()
|
controller.scrollToTopWithTabBar?()
|
||||||
|
controller.requestAttachmentMenuExpansion()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -255,7 +256,7 @@ public class AttachmentController: ViewController {
|
|||||||
strongSelf.container.container.view.layer.animatePosition(from: CGPoint(x: ascending ? 70.0 : -70.0, y: 0.0), to: CGPoint(), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
strongSelf.container.container.view.layer.animatePosition(from: CGPoint(x: ascending ? 70.0 : -70.0, y: 0.0), to: CGPoint(), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||||
snapshotView?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
snapshotView?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||||
snapshotView?.removeFromSuperview()
|
snapshotView?.removeFromSuperview()
|
||||||
previousController?.prepareForReuse()
|
previousController?.resetForReuse()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,6 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
let legacySheetController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
|
let legacySheetController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
|
||||||
|
|
||||||
let controller = TGMediaPickerSendActionSheetController(context: legacyController.context, isDark: true, sendButtonFrame: model.interfaceView.doneButtonFrame, canSendSilently: hasSilentPosting, canSchedule: effectiveHasSchedule, reminder: reminder, hasTimer: hasTimer)
|
let controller = TGMediaPickerSendActionSheetController(context: legacyController.context, isDark: true, sendButtonFrame: model.interfaceView.doneButtonFrame, canSendSilently: hasSilentPosting, canSchedule: effectiveHasSchedule, reminder: reminder, hasTimer: hasTimer)
|
||||||
let dismissImpl = { [weak model] in
|
let dismissImpl = { [weak model] in
|
||||||
model?.dismiss(true, false)
|
model?.dismiss(true, false)
|
||||||
@ -241,8 +240,10 @@ func presentLegacyMediaPickerGallery(context: AccountContext, peer: EnginePeer?,
|
|||||||
legacySheetController?.dismiss()
|
legacySheetController?.dismiss()
|
||||||
}
|
}
|
||||||
legacySheetController.bind(controller: controller)
|
legacySheetController.bind(controller: controller)
|
||||||
|
|
||||||
present(legacySheetController, nil)
|
present(legacySheetController, nil)
|
||||||
|
|
||||||
|
let hapticFeedback = HapticFeedback()
|
||||||
|
hapticFeedback.impact()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
model.interfaceView.setThumbnailSignalForItem { item in
|
model.interfaceView.setThumbnailSignalForItem { item in
|
||||||
|
@ -66,7 +66,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
|
|||||||
icon = UIImage(bundleImageName: "Chat/Context Menu/ReportDrugs")
|
icon = UIImage(bundleImageName: "Chat/Context Menu/ReportDrugs")
|
||||||
case .personalDetails:
|
case .personalDetails:
|
||||||
title = presentationData.strings.ReportPeer_ReasonPersonalDetails
|
title = presentationData.strings.ReportPeer_ReasonPersonalDetails
|
||||||
icon = UIImage(bundleImageName: "Chat/Context Menu/User")
|
icon = UIImage(bundleImageName: "Chat/Context Menu/ReportPersonal")
|
||||||
case .other:
|
case .other:
|
||||||
title = presentationData.strings.ReportPeer_ReasonOther
|
title = presentationData.strings.ReportPeer_ReasonOther
|
||||||
icon = UIImage(bundleImageName: "Chat/Context Menu/Report")
|
icon = UIImage(bundleImageName: "Chat/Context Menu/Report")
|
||||||
|
@ -9,6 +9,7 @@ import TelegramUIPreferences
|
|||||||
import ItemListUI
|
import ItemListUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
import UndoUI
|
||||||
|
|
||||||
enum SelectivePrivacySettingsKind {
|
enum SelectivePrivacySettingsKind {
|
||||||
case presence
|
case presence
|
||||||
@ -50,8 +51,9 @@ private final class SelectivePrivacySettingsControllerArguments {
|
|||||||
let updateCallP2PMode: ((SelectivePrivacySettingType) -> Void)?
|
let updateCallP2PMode: ((SelectivePrivacySettingType) -> Void)?
|
||||||
let updateCallIntegrationEnabled: ((Bool) -> Void)?
|
let updateCallIntegrationEnabled: ((Bool) -> Void)?
|
||||||
let updatePhoneDiscovery: ((Bool) -> Void)?
|
let updatePhoneDiscovery: ((Bool) -> Void)?
|
||||||
|
let copyPhoneLink: ((String) -> Void)?
|
||||||
|
|
||||||
init(context: AccountContext, updateType: @escaping (SelectivePrivacySettingType) -> Void, openSelective: @escaping (SelectivePrivacySettingsPeerTarget, Bool) -> Void, updateCallP2PMode: ((SelectivePrivacySettingType) -> Void)?, updateCallIntegrationEnabled: ((Bool) -> Void)?, updatePhoneDiscovery: ((Bool) -> Void)?) {
|
init(context: AccountContext, updateType: @escaping (SelectivePrivacySettingType) -> Void, openSelective: @escaping (SelectivePrivacySettingsPeerTarget, Bool) -> Void, updateCallP2PMode: ((SelectivePrivacySettingType) -> Void)?, updateCallIntegrationEnabled: ((Bool) -> Void)?, updatePhoneDiscovery: ((Bool) -> Void)?, copyPhoneLink: ((String) -> Void)?) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.updateType = updateType
|
self.updateType = updateType
|
||||||
self.openSelective = openSelective
|
self.openSelective = openSelective
|
||||||
@ -59,6 +61,7 @@ private final class SelectivePrivacySettingsControllerArguments {
|
|||||||
self.updateCallP2PMode = updateCallP2PMode
|
self.updateCallP2PMode = updateCallP2PMode
|
||||||
self.updateCallIntegrationEnabled = updateCallIntegrationEnabled
|
self.updateCallIntegrationEnabled = updateCallIntegrationEnabled
|
||||||
self.updatePhoneDiscovery = updatePhoneDiscovery
|
self.updatePhoneDiscovery = updatePhoneDiscovery
|
||||||
|
self.copyPhoneLink = copyPhoneLink
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +94,7 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
|
|||||||
case everybody(PresentationTheme, String, Bool)
|
case everybody(PresentationTheme, String, Bool)
|
||||||
case contacts(PresentationTheme, String, Bool)
|
case contacts(PresentationTheme, String, Bool)
|
||||||
case nobody(PresentationTheme, String, Bool)
|
case nobody(PresentationTheme, String, Bool)
|
||||||
case settingInfo(PresentationTheme, String)
|
case settingInfo(PresentationTheme, String, String)
|
||||||
case exceptionsHeader(PresentationTheme, String)
|
case exceptionsHeader(PresentationTheme, String)
|
||||||
case disableFor(PresentationTheme, String, String)
|
case disableFor(PresentationTheme, String, String)
|
||||||
case enableFor(PresentationTheme, String, String)
|
case enableFor(PresentationTheme, String, String)
|
||||||
@ -109,7 +112,7 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
|
|||||||
case phoneDiscoveryHeader(PresentationTheme, String)
|
case phoneDiscoveryHeader(PresentationTheme, String)
|
||||||
case phoneDiscoveryEverybody(PresentationTheme, String, Bool)
|
case phoneDiscoveryEverybody(PresentationTheme, String, Bool)
|
||||||
case phoneDiscoveryMyContacts(PresentationTheme, String, Bool)
|
case phoneDiscoveryMyContacts(PresentationTheme, String, Bool)
|
||||||
case phoneDiscoveryInfo(PresentationTheme, String)
|
case phoneDiscoveryInfo(PresentationTheme, String, String)
|
||||||
|
|
||||||
var section: ItemListSectionId {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
@ -229,8 +232,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .settingInfo(lhsTheme, lhsText):
|
case let .settingInfo(lhsTheme, lhsText, lhsLink):
|
||||||
if case let .settingInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .settingInfo(rhsTheme, rhsText, rhsLink) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsLink == rhsLink {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -331,8 +334,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .phoneDiscoveryInfo(lhsTheme, lhsText):
|
case let .phoneDiscoveryInfo(lhsTheme, lhsText, lhsLink):
|
||||||
if case let .phoneDiscoveryInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .phoneDiscoveryInfo(rhsTheme, rhsText, rhsLink) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsLink == rhsLink {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -365,8 +368,10 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
|
|||||||
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||||
arguments.updateType(.nobody)
|
arguments.updateType(.nobody)
|
||||||
})
|
})
|
||||||
case let .settingInfo(_, text):
|
case let .settingInfo(_, text, link):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section, linkAction: { _ in
|
||||||
|
arguments.copyPhoneLink?(link)
|
||||||
|
})
|
||||||
case let .exceptionsHeader(_, text):
|
case let .exceptionsHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .disableFor(_, title, value):
|
case let .disableFor(_, title, value):
|
||||||
@ -421,8 +426,10 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
|
|||||||
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
|
||||||
arguments.updatePhoneDiscovery?(false)
|
arguments.updatePhoneDiscovery?(false)
|
||||||
})
|
})
|
||||||
case let .phoneDiscoveryInfo(_, text):
|
case let .phoneDiscoveryInfo(_, text, link):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section, linkAction: { _ in
|
||||||
|
arguments.copyPhoneLink?(link)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -531,7 +538,7 @@ private struct SelectivePrivacySettingsControllerState: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func selectivePrivacySettingsControllerEntries(presentationData: PresentationData, kind: SelectivePrivacySettingsKind, state: SelectivePrivacySettingsControllerState, peerName: String) -> [SelectivePrivacySettingsEntry] {
|
private func selectivePrivacySettingsControllerEntries(presentationData: PresentationData, kind: SelectivePrivacySettingsKind, state: SelectivePrivacySettingsControllerState, peerName: String, phoneNumber: String) -> [SelectivePrivacySettingsEntry] {
|
||||||
var entries: [SelectivePrivacySettingsEntry] = []
|
var entries: [SelectivePrivacySettingsEntry] = []
|
||||||
|
|
||||||
let settingTitle: String
|
let settingTitle: String
|
||||||
@ -569,7 +576,7 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
|
|||||||
if state.setting == .nobody {
|
if state.setting == .nobody {
|
||||||
settingInfoText = nil
|
settingInfoText = nil
|
||||||
} else {
|
} else {
|
||||||
settingInfoText = presentationData.strings.PrivacyPhoneNumberSettings_CustomHelp
|
settingInfoText = presentationData.strings.PrivacyPhoneNumberSettings_CustomPublicLink("+\(phoneNumber)").string
|
||||||
}
|
}
|
||||||
disableForText = presentationData.strings.PrivacyLastSeenSettings_NeverShareWith
|
disableForText = presentationData.strings.PrivacyLastSeenSettings_NeverShareWith
|
||||||
enableForText = presentationData.strings.PrivacyLastSeenSettings_AlwaysShareWith
|
enableForText = presentationData.strings.PrivacyLastSeenSettings_AlwaysShareWith
|
||||||
@ -603,15 +610,16 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
|
|||||||
case .groupInvitations, .profilePhoto:
|
case .groupInvitations, .profilePhoto:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
let phoneLink = "https://t.me/+\(phoneNumber)"
|
||||||
if let settingInfoText = settingInfoText {
|
if let settingInfoText = settingInfoText {
|
||||||
entries.append(.settingInfo(presentationData.theme, settingInfoText))
|
entries.append(.settingInfo(presentationData.theme, settingInfoText, phoneLink))
|
||||||
}
|
}
|
||||||
|
|
||||||
if case .phoneNumber = kind, state.setting == .nobody {
|
if case .phoneNumber = kind, state.setting == .nobody {
|
||||||
entries.append(.phoneDiscoveryHeader(presentationData.theme, presentationData.strings.PrivacyPhoneNumberSettings_DiscoveryHeader))
|
entries.append(.phoneDiscoveryHeader(presentationData.theme, presentationData.strings.PrivacyPhoneNumberSettings_DiscoveryHeader))
|
||||||
entries.append(.phoneDiscoveryEverybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.phoneDiscoveryEnabled != false))
|
entries.append(.phoneDiscoveryEverybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.phoneDiscoveryEnabled != false))
|
||||||
entries.append(.phoneDiscoveryMyContacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.phoneDiscoveryEnabled == false))
|
entries.append(.phoneDiscoveryMyContacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.phoneDiscoveryEnabled == false))
|
||||||
entries.append(.phoneDiscoveryInfo(presentationData.theme, state.phoneDiscoveryEnabled != false ? presentationData.strings.PrivacyPhoneNumberSettings_CustomHelp : presentationData.strings.PrivacyPhoneNumberSettings_CustomDisabledHelp))
|
entries.append(.phoneDiscoveryInfo(presentationData.theme, state.phoneDiscoveryEnabled != false ? presentationData.strings.PrivacyPhoneNumberSettings_CustomPublicLink("+\(phoneNumber)").string : presentationData.strings.PrivacyPhoneNumberSettings_CustomDisabledHelp, phoneLink))
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.append(.exceptionsHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_Exceptions))
|
entries.append(.exceptionsHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_Exceptions))
|
||||||
@ -930,6 +938,11 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
|
|||||||
updateState { state in
|
updateState { state in
|
||||||
return state.withUpdatedPhoneDiscoveryEnabled(value)
|
return state.withUpdatedPhoneDiscoveryEnabled(value)
|
||||||
}
|
}
|
||||||
|
}, copyPhoneLink: { link in
|
||||||
|
UIPasteboard.general.string = link
|
||||||
|
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
let peer = context.account.postbox.transaction { transaction -> Peer? in
|
let peer = context.account.postbox.transaction { transaction -> Peer? in
|
||||||
@ -939,6 +952,7 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
|
|||||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), peer) |> deliverOnMainQueue
|
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), peer) |> deliverOnMainQueue
|
||||||
|> map { presentationData, state, peer -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> map { presentationData, state, peer -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
let peerName = peer.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
let peerName = peer.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
|
let phoneNumber = (peer as? TelegramUser)?.phone ?? ""
|
||||||
|
|
||||||
let title: String
|
let title: String
|
||||||
switch kind {
|
switch kind {
|
||||||
@ -956,7 +970,7 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
|
|||||||
title = presentationData.strings.Privacy_PhoneNumber
|
title = presentationData.strings.Privacy_PhoneNumber
|
||||||
}
|
}
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: selectivePrivacySettingsControllerEntries(presentationData: presentationData, kind: kind, state: state, peerName: peerName ?? ""), style: .blocks, animateChanges: false)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: selectivePrivacySettingsControllerEntries(presentationData: presentationData, kind: kind, state: state, peerName: peerName ?? "", phoneNumber: phoneNumber), style: .blocks, animateChanges: false)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
} |> afterDisposed {
|
} |> afterDisposed {
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
import Foundation
|
||||||
|
import Postbox
|
||||||
|
|
||||||
|
public final class CachedResolvedByPhonePeer: Codable {
|
||||||
|
public let peerId: PeerId?
|
||||||
|
public let timestamp: Int32
|
||||||
|
|
||||||
|
public static func key(name: String) -> ValueBoxKey {
|
||||||
|
let key: ValueBoxKey
|
||||||
|
if let nameData = name.data(using: .utf8) {
|
||||||
|
key = ValueBoxKey(length: nameData.count)
|
||||||
|
nameData.withUnsafeBytes { rawBytes -> Void in
|
||||||
|
let bytes = rawBytes.baseAddress!.assumingMemoryBound(to: Int8.self)
|
||||||
|
|
||||||
|
memcpy(key.memory, bytes, nameData.count)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
key = ValueBoxKey(length: 0)
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(peerId: PeerId?, timestamp: Int32) {
|
||||||
|
self.peerId = peerId
|
||||||
|
self.timestamp = timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
||||||
|
|
||||||
|
self.peerId = (try container.decodeIfPresent(Int64.self, forKey: "p")).flatMap(PeerId.init)
|
||||||
|
self.timestamp = try container.decode(Int32.self, forKey: "t")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: StringCodingKey.self)
|
||||||
|
|
||||||
|
try container.encodeIfPresent(self.peerId?.toInt64(), forKey: "p")
|
||||||
|
try container.encode(self.timestamp, forKey: "t")
|
||||||
|
}
|
||||||
|
}
|
@ -82,6 +82,7 @@ public struct Namespaces {
|
|||||||
public static let cachedPeerExportedInvitations: Int8 = 17
|
public static let cachedPeerExportedInvitations: Int8 = 17
|
||||||
public static let cachedSendAsPeers: Int8 = 18
|
public static let cachedSendAsPeers: Int8 = 18
|
||||||
public static let availableReactions: Int8 = 19
|
public static let availableReactions: Int8 = 19
|
||||||
|
public static let resolvedByPhonePeers: Int8 = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct UnorderedItemList {
|
public struct UnorderedItemList {
|
||||||
|
@ -77,3 +77,66 @@ func _internal_resolvePeerByName(account: Account, name: String, ageLimit: Int32
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let resolvedByPhonePeersCollectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 150, highWaterItemCount: 200)
|
||||||
|
|
||||||
|
func _internal_resolvePeerByPhone(account: Account, phone: String, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal<PeerId?, NoError> {
|
||||||
|
var normalizedPhone = phone
|
||||||
|
if normalizedPhone.hasPrefix("+") {
|
||||||
|
normalizedPhone = String(normalizedPhone[normalizedPhone.index(after: normalizedPhone.startIndex)...])
|
||||||
|
}
|
||||||
|
|
||||||
|
return account.postbox.transaction { transaction -> CachedResolvedByPhonePeer? in
|
||||||
|
return transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.resolvedByPhonePeers, key: CachedResolvedByPhonePeer.key(name: normalizedPhone)))?.get(CachedResolvedByPhonePeer.self)
|
||||||
|
} |> mapToSignal { cachedEntry -> Signal<PeerId?, NoError> in
|
||||||
|
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||||
|
if let cachedEntry = cachedEntry, cachedEntry.timestamp <= timestamp && cachedEntry.timestamp >= timestamp - ageLimit {
|
||||||
|
return .single(cachedEntry.peerId)
|
||||||
|
} else {
|
||||||
|
return account.network.request(Api.functions.contacts.resolvePhone(phone: normalizedPhone))
|
||||||
|
|> mapError { _ -> Void in
|
||||||
|
return Void()
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<PeerId?, Void> in
|
||||||
|
return account.postbox.transaction { transaction -> PeerId? in
|
||||||
|
var peerId: PeerId? = nil
|
||||||
|
|
||||||
|
switch result {
|
||||||
|
case let .resolvedPeer(apiPeer, chats, users):
|
||||||
|
var peers: [PeerId: Peer] = [:]
|
||||||
|
|
||||||
|
for user in users {
|
||||||
|
if let user = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
||||||
|
peers[user.id] = user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for chat in chats {
|
||||||
|
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
|
||||||
|
peers[groupOrChannel.id] = groupOrChannel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let peer = peers[apiPeer.peerId] {
|
||||||
|
peerId = peer.id
|
||||||
|
|
||||||
|
updatePeers(transaction: transaction, peers: Array(peers.values), update: { _, updated -> Peer in
|
||||||
|
return updated
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||||
|
if let entry = CodableEntry(CachedResolvedByPhonePeer(peerId: peerId, timestamp: timestamp)) {
|
||||||
|
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.resolvedByPhonePeers, key: CachedResolvedByPhonePeer.key(name: normalizedPhone)), entry: entry, collectionSpec: resolvedByNamePeersCollectionSpec)
|
||||||
|
}
|
||||||
|
return peerId
|
||||||
|
}
|
||||||
|
|> castError(Void.self)
|
||||||
|
}
|
||||||
|
|> `catch` { _ -> Signal<PeerId?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -98,6 +98,18 @@ public extension TelegramEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func resolvePeerByPhone(phone: String, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal<EnginePeer?, NoError> {
|
||||||
|
return _internal_resolvePeerByPhone(account: self.account, phone: phone, ageLimit: ageLimit)
|
||||||
|
|> mapToSignal { peerId -> Signal<EnginePeer?, NoError> in
|
||||||
|
guard let peerId = peerId else {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
return self.account.postbox.transaction { transaction -> EnginePeer? in
|
||||||
|
return transaction.getPeer(peerId).flatMap(EnginePeer.init)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func updatedRemotePeer(peer: PeerReference) -> Signal<Peer, UpdatedRemotePeerError> {
|
public func updatedRemotePeer(peer: PeerReference) -> Signal<Peer, UpdatedRemotePeerError> {
|
||||||
return _internal_updatedRemotePeer(postbox: self.account.postbox, network: self.account.network, peer: peer)
|
return _internal_updatedRemotePeer(postbox: self.account.postbox, network: self.account.network, peer: peer)
|
||||||
}
|
}
|
||||||
|
20
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/ReportPersonal.imageset/Contents.json
vendored
Normal file
20
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/ReportPersonal.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
@ -605,6 +605,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
|
|
||||||
if parsedUrl.host == "resolve" {
|
if parsedUrl.host == "resolve" {
|
||||||
if let components = URLComponents(string: "/?" + query) {
|
if let components = URLComponents(string: "/?" + query) {
|
||||||
|
var phone: String?
|
||||||
var domain: String?
|
var domain: String?
|
||||||
var start: String?
|
var start: String?
|
||||||
var startGroup: String?
|
var startGroup: String?
|
||||||
@ -614,7 +615,9 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
if let queryItems = components.queryItems {
|
if let queryItems = components.queryItems {
|
||||||
for queryItem in queryItems {
|
for queryItem in queryItems {
|
||||||
if let value = queryItem.value {
|
if let value = queryItem.value {
|
||||||
if queryItem.name == "domain" {
|
if queryItem.name == "phone" {
|
||||||
|
phone = value
|
||||||
|
} else if queryItem.name == "domain" {
|
||||||
domain = value
|
domain = value
|
||||||
} else if queryItem.name == "start" {
|
} else if queryItem.name == "start" {
|
||||||
start = value
|
start = value
|
||||||
@ -633,7 +636,9 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let domain = domain {
|
if let phone = phone {
|
||||||
|
convertedUrl = "https://t.me/+\(phone)"
|
||||||
|
} else if let domain = domain {
|
||||||
var result = "https://t.me/\(domain)"
|
var result = "https://t.me/\(domain)"
|
||||||
if let post = post, let postValue = Int(post) {
|
if let post = post, let postValue = Int(post) {
|
||||||
result += "/\(postValue)"
|
result += "/\(postValue)"
|
||||||
|
@ -41,6 +41,7 @@ public enum ParsedInternalUrl {
|
|||||||
case share(url: String?, text: String?, to: String?)
|
case share(url: String?, text: String?, to: String?)
|
||||||
case wallpaper(WallpaperUrlParameter)
|
case wallpaper(WallpaperUrlParameter)
|
||||||
case theme(String)
|
case theme(String)
|
||||||
|
case phone(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ParsedUrl {
|
private enum ParsedUrl {
|
||||||
@ -154,7 +155,12 @@ 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") {
|
} else if pathComponents[0].hasPrefix("+") || pathComponents[0].hasPrefix("%20") {
|
||||||
return .join(String(pathComponents[0].dropFirst()))
|
let component = pathComponents[0].replacingOccurrences(of: "%20", with: "+")
|
||||||
|
if component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789+").inverted) == nil {
|
||||||
|
return .phone(component.replacingOccurrences(of: "+", with: ""))
|
||||||
|
} else {
|
||||||
|
return .join(String(component.dropFirst()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return .peerName(peerName, nil)
|
return .peerName(peerName, nil)
|
||||||
} else if pathComponents.count == 2 || pathComponents.count == 3 {
|
} else if pathComponents.count == 2 || pathComponents.count == 3 {
|
||||||
@ -350,6 +356,16 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
|||||||
|
|
||||||
private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) -> Signal<ResolvedUrl?, NoError> {
|
private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) -> Signal<ResolvedUrl?, NoError> {
|
||||||
switch url {
|
switch url {
|
||||||
|
case let .phone(phone):
|
||||||
|
return context.engine.peers.resolvePeerByPhone(phone: phone)
|
||||||
|
|> take(1)
|
||||||
|
|> map { peer -> ResolvedUrl? in
|
||||||
|
if let peer = peer?._asPeer() {
|
||||||
|
return .peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||||
|
} else {
|
||||||
|
return .peer(nil, .info)
|
||||||
|
}
|
||||||
|
}
|
||||||
case let .peerName(name, parameter):
|
case let .peerName(name, parameter):
|
||||||
return context.engine.peers.resolvePeerByName(name: name)
|
return context.engine.peers.resolvePeerByName(name: name)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
@ -383,11 +399,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
|||||||
return .single(.joinVoiceChat(peer.id, invite))
|
return .single(.joinVoiceChat(peer.id, invite))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let peer = peer as? TelegramUser, peer.botInfo == nil {
|
|
||||||
return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||||
} else {
|
|
||||||
return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return .single(.peer(nil, .info))
|
return .single(.peer(nil, .info))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user