mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
ca415e3145
@ -12132,3 +12132,5 @@ Sorry for the inconvenience.";
|
|||||||
"Channel.AdminLogFilter.Section.SettingsGroup" = "Group Settings";
|
"Channel.AdminLogFilter.Section.SettingsGroup" = "Group Settings";
|
||||||
"Channel.AdminLogFilter.Section.SettingsChannel" = "Channel Settings";
|
"Channel.AdminLogFilter.Section.SettingsChannel" = "Channel Settings";
|
||||||
"Channel.AdminLogFilter.Section.Messages" = "Messages";
|
"Channel.AdminLogFilter.Section.Messages" = "Messages";
|
||||||
|
|
||||||
|
"Premium.Gift.ContactSelection.AddBirthday" = "Add Your Birthday";
|
||||||
|
@ -93,7 +93,7 @@ public final class ContactMultiselectionControllerParams {
|
|||||||
public let context: AccountContext
|
public let context: AccountContext
|
||||||
public let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
public let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||||
public let mode: ContactMultiselectionControllerMode
|
public let mode: ContactMultiselectionControllerMode
|
||||||
public let options: [ContactListAdditionalOption]
|
public let options: Signal<[ContactListAdditionalOption], NoError>
|
||||||
public let filters: [ContactListFilter]
|
public let filters: [ContactListFilter]
|
||||||
public let onlyWriteable: Bool
|
public let onlyWriteable: Bool
|
||||||
public let isGroupInvitation: Bool
|
public let isGroupInvitation: Bool
|
||||||
@ -105,7 +105,7 @@ public final class ContactMultiselectionControllerParams {
|
|||||||
public let openProfile: ((EnginePeer) -> Void)?
|
public let openProfile: ((EnginePeer) -> Void)?
|
||||||
public let sendMessage: ((EnginePeer) -> Void)?
|
public let sendMessage: ((EnginePeer) -> Void)?
|
||||||
|
|
||||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: ContactMultiselectionControllerMode, options: [ContactListAdditionalOption], filters: [ContactListFilter] = [.excludeSelf], onlyWriteable: Bool = false, isGroupInvitation: Bool = false, isPeerEnabled: ((EnginePeer) -> Bool)? = nil, attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)? = nil, alwaysEnabled: Bool = false, limit: Int32? = nil, reachedLimit: ((Int32) -> Void)? = nil, openProfile: ((EnginePeer) -> Void)? = nil, sendMessage: ((EnginePeer) -> Void)? = nil) {
|
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: ContactMultiselectionControllerMode, options: Signal<[ContactListAdditionalOption], NoError> = .single([]), filters: [ContactListFilter] = [.excludeSelf], onlyWriteable: Bool = false, isGroupInvitation: Bool = false, isPeerEnabled: ((EnginePeer) -> Bool)? = nil, attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)? = nil, alwaysEnabled: Bool = false, limit: Int32? = nil, reachedLimit: ((Int32) -> Void)? = nil, openProfile: ((EnginePeer) -> Void)? = nil, sendMessage: ((EnginePeer) -> Void)? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.updatedPresentationData = updatedPresentationData
|
self.updatedPresentationData = updatedPresentationData
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
@ -760,7 +760,7 @@ private func internalChatListFilterAddChatsController(context: AccountContext, f
|
|||||||
selectedChats: Set(filterData.includePeers.peers),
|
selectedChats: Set(filterData.includePeers.peers),
|
||||||
additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories),
|
additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories),
|
||||||
chatListFilters: allFilters
|
chatListFilters: allFilters
|
||||||
)), options: [], filters: [], alwaysEnabled: true, limit: isPremium ? premiumLimit : limit, reachedLimit: { count in
|
)), filters: [], alwaysEnabled: true, limit: isPremium ? premiumLimit : limit, reachedLimit: { count in
|
||||||
if count >= premiumLimit {
|
if count >= premiumLimit {
|
||||||
let limitController = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: min(premiumLimit, count), action: {
|
let limitController = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: min(premiumLimit, count), action: {
|
||||||
return true
|
return true
|
||||||
@ -913,7 +913,7 @@ private func internalChatListFilterExcludeChatsController(context: AccountContex
|
|||||||
selectedChats: Set(filterData.excludePeers),
|
selectedChats: Set(filterData.excludePeers),
|
||||||
additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories),
|
additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories),
|
||||||
chatListFilters: allFilters
|
chatListFilters: allFilters
|
||||||
)), options: [], filters: [], alwaysEnabled: true, limit: 100))
|
)), filters: [], alwaysEnabled: true, limit: 100))
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
let _ = (controller.result
|
let _ = (controller.result
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|
@ -1770,25 +1770,32 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
if (authorizationStatus == .notDetermined || authorizationStatus == .denied) && peers.isEmpty {
|
if (authorizationStatus == .notDetermined || authorizationStatus == .denied) && peers.isEmpty {
|
||||||
isEmpty = true
|
isEmpty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let entries = contactListNodeEntries(accountPeer: view.1, peers: peers, presences: presences, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, peerRequiresPremiumForMessaging: view.2, authorizationStatus: authorizationStatus, warningSuppressed: warningSuppressed, displaySortOptions: displaySortOptions, displayCallIcons: displayCallIcons, storySubscriptions: storySubscriptions, topPeers: topPeers.map { $0.peer }, topPeersPresentation: displayTopPeers, interaction: interaction)
|
let entries = contactListNodeEntries(accountPeer: view.1, peers: peers, presences: presences, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, peerRequiresPremiumForMessaging: view.2, authorizationStatus: authorizationStatus, warningSuppressed: warningSuppressed, displaySortOptions: displaySortOptions, displayCallIcons: displayCallIcons, storySubscriptions: storySubscriptions, topPeers: topPeers.map { $0.peer }, topPeersPresentation: displayTopPeers, interaction: interaction)
|
||||||
let previous = previousEntries.swap(entries)
|
let previous = previousEntries.swap(entries)
|
||||||
let previousSelection = previousSelectionState.swap(selectionState)
|
let previousSelection = previousSelectionState.swap(selectionState)
|
||||||
let previousPendingRemovalPeerIds = previousPendingRemovalPeerIds.swap(pendingRemovalPeerIds)
|
let previousPendingRemovalPeerIds = previousPendingRemovalPeerIds.swap(pendingRemovalPeerIds)
|
||||||
|
|
||||||
var hadPermissionInfo = false
|
var hadPermissionInfo = false
|
||||||
|
var previousOptionsCount = 0
|
||||||
if let previous = previous {
|
if let previous = previous {
|
||||||
for entry in previous {
|
for entry in previous {
|
||||||
if case .permissionInfo = entry {
|
if case .permissionInfo = entry {
|
||||||
hadPermissionInfo = true
|
hadPermissionInfo = true
|
||||||
break
|
}
|
||||||
|
if case .option = entry {
|
||||||
|
previousOptionsCount += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var hasPermissionInfo = false
|
var hasPermissionInfo = false
|
||||||
|
var optionsCount = 0
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
if case .permissionInfo = entry {
|
if case .permissionInfo = entry {
|
||||||
hasPermissionInfo = true
|
hasPermissionInfo = true
|
||||||
break
|
}
|
||||||
|
if case .option = entry {
|
||||||
|
optionsCount += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1799,6 +1806,8 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
animation = .insertion
|
animation = .insertion
|
||||||
} else if hadPermissionInfo != hasPermissionInfo {
|
} else if hadPermissionInfo != hasPermissionInfo {
|
||||||
animation = .insertion
|
animation = .insertion
|
||||||
|
} else if optionsCount < previousOptionsCount {
|
||||||
|
animation = .insertion
|
||||||
} else {
|
} else {
|
||||||
animation = .none
|
animation = .none
|
||||||
}
|
}
|
||||||
|
@ -313,7 +313,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
|||||||
}
|
}
|
||||||
} else if let file = content.file {
|
} else if let file = content.file {
|
||||||
if content.type == "telegram_background" {
|
if content.type == "telegram_background" {
|
||||||
if let wallpaper = parseWallpaperUrl(content.url) {
|
if let wallpaper = parseWallpaperUrl(sharedContext: item.context.sharedContext, url: content.url) {
|
||||||
switch wallpaper {
|
switch wallpaper {
|
||||||
case let .slug(slug, _, colors, intensity, angle):
|
case let .slug(slug, _, colors, intensity, angle):
|
||||||
previewWallpaperFileReference = .message(message: MessageReference(message), media: file)
|
previewWallpaperFileReference = .message(message: MessageReference(message), media: file)
|
||||||
|
@ -38,6 +38,7 @@ private struct LocationViewTransaction {
|
|||||||
let updates: [ListViewUpdateItem]
|
let updates: [ListViewUpdateItem]
|
||||||
let gotTravelTimes: Bool
|
let gotTravelTimes: Bool
|
||||||
let count: Int
|
let count: Int
|
||||||
|
let animated: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum LocationViewEntryId: Hashable {
|
private enum LocationViewEntryId: Hashable {
|
||||||
@ -197,14 +198,14 @@ private enum LocationViewEntry: Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preparedTransition(from fromEntries: [LocationViewEntry], to toEntries: [LocationViewEntry], context: AccountContext, presentationData: PresentationData, interaction: LocationViewInteraction?, gotTravelTimes: Bool) -> LocationViewTransaction {
|
private func preparedTransition(from fromEntries: [LocationViewEntry], to toEntries: [LocationViewEntry], context: AccountContext, presentationData: PresentationData, interaction: LocationViewInteraction?, gotTravelTimes: Bool, animated: Bool) -> LocationViewTransaction {
|
||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
||||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
||||||
|
|
||||||
return LocationViewTransaction(deletions: deletions, insertions: insertions, updates: updates, gotTravelTimes: gotTravelTimes, count: toEntries.count)
|
return LocationViewTransaction(deletions: deletions, insertions: insertions, updates: updates, gotTravelTimes: gotTravelTimes, count: toEntries.count, animated: animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LocationViewLocation: Equatable {
|
enum LocationViewLocation: Equatable {
|
||||||
@ -573,7 +574,27 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
let previousState = previousState.swap(state)
|
let previousState = previousState.swap(state)
|
||||||
let previousHadTravelTimes = previousHadTravelTimes.swap(!travelTimes.isEmpty)
|
let previousHadTravelTimes = previousHadTravelTimes.swap(!travelTimes.isEmpty)
|
||||||
|
|
||||||
let transition = preparedTransition(from: previousEntries ?? [], to: entries, context: context, presentationData: presentationData, interaction: strongSelf.interaction, gotTravelTimes: !travelTimes.isEmpty && !previousHadTravelTimes)
|
var animated = false
|
||||||
|
var previousActionsCount = 0
|
||||||
|
var actionsCount = 0
|
||||||
|
if let previousEntries {
|
||||||
|
for entry in previousEntries {
|
||||||
|
if case .toggleLiveLocation = entry {
|
||||||
|
previousActionsCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for entry in entries {
|
||||||
|
if case .toggleLiveLocation = entry {
|
||||||
|
actionsCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if actionsCount < previousActionsCount {
|
||||||
|
animated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let transition = preparedTransition(from: previousEntries ?? [], to: entries, context: context, presentationData: presentationData, interaction: strongSelf.interaction, gotTravelTimes: !travelTimes.isEmpty && !previousHadTravelTimes, animated: animated)
|
||||||
strongSelf.enqueueTransition(transition)
|
strongSelf.enqueueTransition(transition)
|
||||||
|
|
||||||
strongSelf.headerNode.updateState(mapMode: state.mapMode, trackingMode: state.trackingMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, proximityNotification: proximityNotification, animated: false)
|
strongSelf.headerNode.updateState(mapMode: state.mapMode, trackingMode: state.trackingMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, proximityNotification: proximityNotification, animated: false)
|
||||||
@ -787,7 +808,10 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
scrollToItem = nil
|
scrollToItem = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let options = ListViewDeleteAndInsertOptions()
|
var options = ListViewDeleteAndInsertOptions()
|
||||||
|
if transition.animated {
|
||||||
|
options.insert(.AnimateInsertion)
|
||||||
|
}
|
||||||
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: scrollToItem, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { _ in
|
self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, scrollToItem: scrollToItem, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { _ in
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -518,7 +518,7 @@ public func channelMembersController(context: AccountContext, updatedPresentatio
|
|||||||
)
|
)
|
||||||
|> deliverOnMainQueue).start(next: { chatPeer, exportedInvitation, members in
|
|> deliverOnMainQueue).start(next: { chatPeer, exportedInvitation, members in
|
||||||
let disabledIds = members?.compactMap({$0.peer.id}) ?? []
|
let disabledIds = members?.compactMap({$0.peer.id}) ?? []
|
||||||
let contactsController = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, mode: .peerSelection(searchChatList: false, searchGroups: false, searchChannels: false), options: [], filters: [.excludeSelf, .disable(disabledIds)], onlyWriteable: true, isGroupInvitation: true))
|
let contactsController = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, mode: .peerSelection(searchChatList: false, searchGroups: false, searchChannels: false), filters: [.excludeSelf, .disable(disabledIds)], onlyWriteable: true, isGroupInvitation: true))
|
||||||
|
|
||||||
addMembersDisposable.set((
|
addMembersDisposable.set((
|
||||||
contactsController.result
|
contactsController.result
|
||||||
|
@ -2276,7 +2276,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
|||||||
nextImpl = { [weak controller] in
|
nextImpl = { [weak controller] in
|
||||||
if let controller = controller {
|
if let controller = controller {
|
||||||
if case .initialSetup = mode {
|
if case .initialSetup = mode {
|
||||||
let selectionController = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, mode: .channelCreation, options: [], onlyWriteable: true))
|
let selectionController = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, mode: .channelCreation, onlyWriteable: true))
|
||||||
(controller.navigationController as? NavigationController)?.replaceAllButRootController(selectionController, animated: true)
|
(controller.navigationController as? NavigationController)?.replaceAllButRootController(selectionController, animated: true)
|
||||||
let _ = (selectionController.result
|
let _ = (selectionController.result
|
||||||
|> deliverOnMainQueue).start(next: { [weak selectionController] result in
|
|> deliverOnMainQueue).start(next: { [weak selectionController] result in
|
||||||
|
@ -310,10 +310,10 @@ public enum ProxySettingsControllerMode {
|
|||||||
|
|
||||||
public func proxySettingsController(context: AccountContext, mode: ProxySettingsControllerMode = .default) -> ViewController {
|
public func proxySettingsController(context: AccountContext, mode: ProxySettingsControllerMode = .default) -> ViewController {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
return proxySettingsController(accountManager: context.sharedContext.accountManager, context: context, postbox: context.account.postbox, network: context.account.network, mode: mode, presentationData: presentationData, updatedPresentationData: context.sharedContext.presentationData)
|
return proxySettingsController(accountManager: context.sharedContext.accountManager, sharedContext: context.sharedContext, context: context, postbox: context.account.postbox, network: context.account.network, mode: mode, presentationData: presentationData, updatedPresentationData: context.sharedContext.presentationData)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func proxySettingsController(accountManager: AccountManager<TelegramAccountManagerTypes>, context: AccountContext? = nil, postbox: Postbox, network: Network, mode: ProxySettingsControllerMode, presentationData: PresentationData, updatedPresentationData: Signal<PresentationData, NoError>) -> ViewController {
|
public func proxySettingsController(accountManager: AccountManager<TelegramAccountManagerTypes>, sharedContext: SharedAccountContext, context: AccountContext? = nil, postbox: Postbox, network: Network, mode: ProxySettingsControllerMode, presentationData: PresentationData, updatedPresentationData: Signal<PresentationData, NoError>) -> ViewController {
|
||||||
var pushControllerImpl: ((ViewController) -> Void)?
|
var pushControllerImpl: ((ViewController) -> Void)?
|
||||||
var dismissImpl: (() -> Void)?
|
var dismissImpl: (() -> Void)?
|
||||||
let stateValue = Atomic(value: ProxySettingsControllerState())
|
let stateValue = Atomic(value: ProxySettingsControllerState())
|
||||||
@ -341,7 +341,7 @@ public func proxySettingsController(accountManager: AccountManager<TelegramAccou
|
|||||||
return current
|
return current
|
||||||
}).start()
|
}).start()
|
||||||
}, addNewServer: {
|
}, addNewServer: {
|
||||||
pushControllerImpl?(proxyServerSettingsController(presentationData: presentationData, updatedPresentationData: updatedPresentationData, accountManager: accountManager, network: network, currentSettings: nil))
|
pushControllerImpl?(proxyServerSettingsController(sharedContext: sharedContext, presentationData: presentationData, updatedPresentationData: updatedPresentationData, accountManager: accountManager, network: network, currentSettings: nil))
|
||||||
}, activateServer: { server in
|
}, activateServer: { server in
|
||||||
let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in
|
let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in
|
||||||
var current = current
|
var current = current
|
||||||
@ -354,7 +354,7 @@ public func proxySettingsController(accountManager: AccountManager<TelegramAccou
|
|||||||
return current
|
return current
|
||||||
}).start()
|
}).start()
|
||||||
}, editServer: { server in
|
}, editServer: { server in
|
||||||
pushControllerImpl?(proxyServerSettingsController(presentationData: presentationData, updatedPresentationData: updatedPresentationData, accountManager: accountManager, network: network, currentSettings: server))
|
pushControllerImpl?(proxyServerSettingsController(sharedContext: sharedContext, presentationData: presentationData, updatedPresentationData: updatedPresentationData, accountManager: accountManager, network: network, currentSettings: server))
|
||||||
}, removeServer: { server in
|
}, removeServer: { server in
|
||||||
let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in
|
let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in
|
||||||
var current = current
|
var current = current
|
||||||
|
@ -265,10 +265,10 @@ private func proxyServerSettings(with state: ProxyServerSettingsControllerState)
|
|||||||
|
|
||||||
public func proxyServerSettingsController(context: AccountContext, currentSettings: ProxyServerSettings? = nil) -> ViewController {
|
public func proxyServerSettingsController(context: AccountContext, currentSettings: ProxyServerSettings? = nil) -> ViewController {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
return proxyServerSettingsController(context: context, presentationData: presentationData, updatedPresentationData: context.sharedContext.presentationData, accountManager: context.sharedContext.accountManager, network: context.account.network, currentSettings: currentSettings)
|
return proxyServerSettingsController(sharedContext: context.sharedContext, context: context, presentationData: presentationData, updatedPresentationData: context.sharedContext.presentationData, accountManager: context.sharedContext.accountManager, network: context.account.network, currentSettings: currentSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
func proxyServerSettingsController(context: AccountContext? = nil, presentationData: PresentationData, updatedPresentationData: Signal<PresentationData, NoError>, accountManager: AccountManager<TelegramAccountManagerTypes>, network: Network, currentSettings: ProxyServerSettings?) -> ViewController {
|
func proxyServerSettingsController(sharedContext: SharedAccountContext, context: AccountContext? = nil, presentationData: PresentationData, updatedPresentationData: Signal<PresentationData, NoError>, accountManager: AccountManager<TelegramAccountManagerTypes>, network: Network, currentSettings: ProxyServerSettings?) -> ViewController {
|
||||||
var currentMode: ProxyServerSettingsControllerMode = .socks5
|
var currentMode: ProxyServerSettingsControllerMode = .socks5
|
||||||
var currentUsername: String?
|
var currentUsername: String?
|
||||||
var currentPassword: String?
|
var currentPassword: String?
|
||||||
@ -285,7 +285,7 @@ func proxyServerSettingsController(context: AccountContext? = nil, presentationD
|
|||||||
currentMode = .mtp
|
currentMode = .mtp
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let proxy = parseProxyUrl(UIPasteboard.general.string ?? "") {
|
if let proxy = parseProxyUrl(sharedContext: sharedContext, url: UIPasteboard.general.string ?? "") {
|
||||||
if let secret = proxy.secret, let parsedSecret = MTProxySecret.parseData(secret) {
|
if let secret = proxy.secret, let parsedSecret = MTProxySecret.parseData(secret) {
|
||||||
pasteboardSettings = ProxyServerSettings(host: proxy.host, port: proxy.port, connection: .mtp(secret: parsedSecret.serialize()))
|
pasteboardSettings = ProxyServerSettings(host: proxy.host, port: proxy.port, connection: .mtp(secret: parsedSecret.serialize()))
|
||||||
} else {
|
} else {
|
||||||
|
@ -315,7 +315,6 @@ public func globalAutoremoveScreen(context: AccountContext, initialValue: Int32,
|
|||||||
chatListFilters: nil,
|
chatListFilters: nil,
|
||||||
displayAutoremoveTimeout: true
|
displayAutoremoveTimeout: true
|
||||||
)),
|
)),
|
||||||
options: [],
|
|
||||||
filters: [.excludeSelf],
|
filters: [.excludeSelf],
|
||||||
isPeerEnabled: { peer in
|
isPeerEnabled: { peer in
|
||||||
var canManage = false
|
var canManage = false
|
||||||
|
@ -1141,7 +1141,7 @@ public func selectivePrivacySettingsController(
|
|||||||
onlyUsers: false,
|
onlyUsers: false,
|
||||||
disableChannels: true,
|
disableChannels: true,
|
||||||
disableBots: false
|
disableBots: false
|
||||||
)), options: [], filters: [.excludeSelf]))
|
)), filters: [.excludeSelf]))
|
||||||
addPeerDisposable.set((controller.result
|
addPeerDisposable.set((controller.result
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak controller] result in
|
|> deliverOnMainQueue).start(next: { [weak controller] result in
|
||||||
|
@ -355,7 +355,7 @@ public func selectivePrivacyPeersController(context: AccountContext, title: Stri
|
|||||||
onlyUsers: false,
|
onlyUsers: false,
|
||||||
disableChannels: true,
|
disableChannels: true,
|
||||||
disableBots: false
|
disableBots: false
|
||||||
)), options: [], alwaysEnabled: true))
|
)), alwaysEnabled: true))
|
||||||
addPeerDisposable.set((controller.result
|
addPeerDisposable.set((controller.result
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak controller] result in
|
|> deliverOnMainQueue).start(next: { [weak controller] result in
|
||||||
|
@ -93,8 +93,7 @@ final class AccountTaskManager {
|
|||||||
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .emoji).start())
|
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .emoji).start())
|
||||||
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .status).start())
|
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .status).start())
|
||||||
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .avatar).start())
|
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .avatar).start())
|
||||||
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .chatStickers).start())
|
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .combinedChatStickers).start())
|
||||||
tasks.add(managedSynchronizeEmojiSearchCategories(postbox: self.stateManager.postbox, network: self.stateManager.network, kind: .greetingStickers).start())
|
|
||||||
tasks.add(managedSynchronizeAttachMenuBots(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network, force: true).start())
|
tasks.add(managedSynchronizeAttachMenuBots(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network, force: true).start())
|
||||||
tasks.add(managedSynchronizeNotificationSoundList(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
tasks.add(managedSynchronizeNotificationSoundList(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||||
tasks.add(managedChatListFilters(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start())
|
tasks.add(managedChatListFilters(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start())
|
||||||
|
@ -8,8 +8,7 @@ public final class EmojiSearchCategories: Equatable, Codable {
|
|||||||
case emoji = 0
|
case emoji = 0
|
||||||
case status = 1
|
case status = 1
|
||||||
case avatar = 2
|
case avatar = 2
|
||||||
case chatStickers = 3
|
case combinedChatStickers = 3
|
||||||
case greetingStickers = 4
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Group: Codable, Equatable {
|
public struct Group: Codable, Equatable {
|
||||||
@ -17,16 +16,25 @@ public final class EmojiSearchCategories: Equatable, Codable {
|
|||||||
case id
|
case id
|
||||||
case title
|
case title
|
||||||
case identifiers
|
case identifiers
|
||||||
|
case kind
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Kind: Int32, Codable {
|
||||||
|
case generic
|
||||||
|
case greeting
|
||||||
|
case premium
|
||||||
}
|
}
|
||||||
|
|
||||||
public var id: Int64
|
public var id: Int64
|
||||||
public var title: String
|
public var title: String
|
||||||
public var identifiers: [String]
|
public var identifiers: [String]
|
||||||
|
public var kind: Kind
|
||||||
|
|
||||||
public init(id: Int64, title: String, identifiers: [String]) {
|
public init(id: Int64, title: String, identifiers: [String], kind: Kind) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.title = title
|
self.title = title
|
||||||
self.identifiers = identifiers
|
self.identifiers = identifiers
|
||||||
|
self.kind = kind
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@ -35,6 +43,16 @@ public final class EmojiSearchCategories: Equatable, Codable {
|
|||||||
self.id = try container.decode(Int64.self, forKey: .id)
|
self.id = try container.decode(Int64.self, forKey: .id)
|
||||||
self.title = try container.decode(String.self, forKey: .title)
|
self.title = try container.decode(String.self, forKey: .title)
|
||||||
self.identifiers = try container.decode([String].self, forKey: .identifiers)
|
self.identifiers = try container.decode([String].self, forKey: .identifiers)
|
||||||
|
self.kind = ((try container.decodeIfPresent(Int32.self, forKey: .kind)).flatMap(Kind.init(rawValue:))) ?? .generic
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: any Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
try container.encode(self.id, forKey: .id)
|
||||||
|
try container.encode(self.title, forKey: .title)
|
||||||
|
try container.encode(self.identifiers, forKey: .identifiers)
|
||||||
|
try container.encode(self.kind.rawValue, forKey: .kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,9 +148,8 @@ func managedSynchronizeEmojiSearchCategories(postbox: Postbox, network: Network,
|
|||||||
|> `catch` { _ -> Signal<Api.messages.EmojiGroups, NoError> in
|
|> `catch` { _ -> Signal<Api.messages.EmojiGroups, NoError> in
|
||||||
return .single(.emojiGroupsNotModified)
|
return .single(.emojiGroupsNotModified)
|
||||||
}
|
}
|
||||||
//TODO:localize
|
case .combinedChatStickers:
|
||||||
case .chatStickers, .greetingStickers:
|
signal = network.request(Api.functions.messages.getEmojiStickerGroups(hash: current?.hash ?? 0))
|
||||||
signal = network.request(Api.functions.messages.getEmojiGroups(hash: current?.hash ?? 0))
|
|
||||||
|> `catch` { _ -> Signal<Api.messages.EmojiGroups, NoError> in
|
|> `catch` { _ -> Signal<Api.messages.EmojiGroups, NoError> in
|
||||||
return .single(.emojiGroupsNotModified)
|
return .single(.emojiGroupsNotModified)
|
||||||
}
|
}
|
||||||
@ -150,10 +167,24 @@ func managedSynchronizeEmojiSearchCategories(postbox: Postbox, network: Network,
|
|||||||
case let .emojiGroup(title, iconEmojiId, emoticons):
|
case let .emojiGroup(title, iconEmojiId, emoticons):
|
||||||
return EmojiSearchCategories.Group(
|
return EmojiSearchCategories.Group(
|
||||||
id: iconEmojiId,
|
id: iconEmojiId,
|
||||||
title: title, identifiers: emoticons
|
title: title,
|
||||||
|
identifiers: emoticons,
|
||||||
|
kind: .generic
|
||||||
|
)
|
||||||
|
case let .emojiGroupGreeting(title, iconEmojiId, emoticons):
|
||||||
|
return EmojiSearchCategories.Group(
|
||||||
|
id: iconEmojiId,
|
||||||
|
title: title,
|
||||||
|
identifiers: emoticons,
|
||||||
|
kind: .greeting
|
||||||
|
)
|
||||||
|
case let .emojiGroupPremium(title, iconEmojiId):
|
||||||
|
return EmojiSearchCategories.Group(
|
||||||
|
id: iconEmojiId,
|
||||||
|
title: title,
|
||||||
|
identifiers: [],
|
||||||
|
kind: .premium
|
||||||
)
|
)
|
||||||
case .emojiGroupGreeting, .emojiGroupPremium:
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1193,6 +1193,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
}
|
}
|
||||||
recognizer.highlight = { [weak self] point in
|
recognizer.highlight = { [weak self] point in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
if strongSelf.selectionNode == nil {
|
||||||
if let replyInfoNode = strongSelf.replyInfoNode {
|
if let replyInfoNode = strongSelf.replyInfoNode {
|
||||||
var translatedPoint: CGPoint?
|
var translatedPoint: CGPoint?
|
||||||
let convertedNodeFrame = replyInfoNode.view.convert(replyInfoNode.bounds, to: strongSelf.view)
|
let convertedNodeFrame = replyInfoNode.view.convert(replyInfoNode.bounds, to: strongSelf.view)
|
||||||
@ -1209,6 +1210,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
}
|
}
|
||||||
forwardInfoNode.updateTouchesAtPoint(translatedPoint)
|
forwardInfoNode.updateTouchesAtPoint(translatedPoint)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for contentNode in strongSelf.contentNodes {
|
for contentNode in strongSelf.contentNodes {
|
||||||
var translatedPoint: CGPoint?
|
var translatedPoint: CGPoint?
|
||||||
let convertedNodeFrame = contentNode.view.convert(contentNode.bounds, to: strongSelf.view)
|
let convertedNodeFrame = contentNode.view.convert(contentNode.bounds, to: strongSelf.view)
|
||||||
|
@ -85,6 +85,8 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
|
|||||||
private var highlightColor: UIColor?
|
private var highlightColor: UIColor?
|
||||||
private var linkHighlightingNode: LinkHighlightingNode?
|
private var linkHighlightingNode: LinkHighlightingNode?
|
||||||
|
|
||||||
|
private var previousPeer: Peer?
|
||||||
|
|
||||||
public var openPsa: ((String, ASDisplayNode) -> Void)?
|
public var openPsa: ((String, ASDisplayNode) -> Void)?
|
||||||
|
|
||||||
override public init() {
|
override public init() {
|
||||||
@ -114,6 +116,32 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func getBoundingRects() -> [CGRect] {
|
||||||
|
var initialRects: [CGRect] = []
|
||||||
|
let addRects: (TextNode, CGPoint, CGFloat) -> Void = { textNode, offset, additionalWidth in
|
||||||
|
guard let cachedLayout = textNode.cachedLayout else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for rect in cachedLayout.linesRects() {
|
||||||
|
var rect = rect
|
||||||
|
rect.size.width += rect.origin.x + additionalWidth
|
||||||
|
rect.origin.x = 0.0
|
||||||
|
initialRects.append(rect.offsetBy(dx: offset.x, dy: offset.y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let offsetY: CGFloat = -12.0
|
||||||
|
if let titleNode = self.titleNode {
|
||||||
|
addRects(titleNode, CGPoint(x: titleNode.frame.minX, y: offsetY + titleNode.frame.minY), 0.0)
|
||||||
|
|
||||||
|
if let nameNode = self.nameNode {
|
||||||
|
addRects(nameNode, CGPoint(x: titleNode.frame.minX, y: offsetY + nameNode.frame.minY), nameNode.frame.minX - titleNode.frame.minX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return initialRects
|
||||||
|
}
|
||||||
|
|
||||||
public func updateTouchesAtPoint(_ point: CGPoint?) {
|
public func updateTouchesAtPoint(_ point: CGPoint?) {
|
||||||
var isHighlighted = false
|
var isHighlighted = false
|
||||||
if point != nil {
|
if point != nil {
|
||||||
@ -167,14 +195,19 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
|
|||||||
let titleNodeLayout = TextNode.asyncLayout(maybeNode?.titleNode)
|
let titleNodeLayout = TextNode.asyncLayout(maybeNode?.titleNode)
|
||||||
let nameNodeLayout = TextNode.asyncLayout(maybeNode?.nameNode)
|
let nameNodeLayout = TextNode.asyncLayout(maybeNode?.nameNode)
|
||||||
|
|
||||||
|
let previousPeer = maybeNode?.previousPeer
|
||||||
|
|
||||||
return { context, presentationData, strings, type, peer, authorName, psaType, storyData, constrainedSize in
|
return { context, presentationData, strings, type, peer, authorName, psaType, storyData, constrainedSize in
|
||||||
|
let originalPeer = peer
|
||||||
|
let peer = peer ?? previousPeer
|
||||||
|
|
||||||
let fontSize = floor(presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)
|
let fontSize = floor(presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)
|
||||||
let prefixFont = Font.regular(fontSize)
|
let prefixFont = Font.regular(fontSize)
|
||||||
let peerFont = Font.medium(fontSize)
|
let peerFont = Font.medium(fontSize)
|
||||||
|
|
||||||
let peerString: String
|
let peerString: String
|
||||||
if let peer = peer {
|
if let peer = peer {
|
||||||
if let authorName = authorName {
|
if let authorName = authorName, originalPeer === peer {
|
||||||
peerString = "\(EnginePeer(peer).displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)) (\(authorName))"
|
peerString = "\(EnginePeer(peer).displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)) (\(authorName))"
|
||||||
} else {
|
} else {
|
||||||
peerString = EnginePeer(peer).displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)
|
peerString = EnginePeer(peer).displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
@ -343,18 +376,16 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
|
|||||||
|
|
||||||
let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - credibilityIconWidth - infoWidth, height: constrainedSize.height), alignment: .natural, cutout: cutout, insets: UIEdgeInsets()))
|
let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - credibilityIconWidth - infoWidth, height: constrainedSize.height), alignment: .natural, cutout: cutout, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
var authorAvatarInset: CGFloat = 0.0
|
||||||
|
authorAvatarInset = 20.0
|
||||||
|
|
||||||
var nameLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
var nameLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
||||||
if let authorString {
|
if let authorString {
|
||||||
nameLayoutAndApply = nameNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: authorString, font: peerFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - credibilityIconWidth - infoWidth, height: constrainedSize.height), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
nameLayoutAndApply = nameNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: authorString, font: peer != nil ? peerFont : prefixFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - credibilityIconWidth - infoWidth - authorAvatarInset, height: constrainedSize.height), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
}
|
}
|
||||||
|
|
||||||
let titleAuthorSpacing: CGFloat = 0.0
|
let titleAuthorSpacing: CGFloat = 0.0
|
||||||
|
|
||||||
var authorAvatarInset: CGFloat = 0.0
|
|
||||||
if peer != nil {
|
|
||||||
authorAvatarInset = 20.0
|
|
||||||
}
|
|
||||||
|
|
||||||
let resultSize: CGSize
|
let resultSize: CGSize
|
||||||
if let nameLayoutAndApply {
|
if let nameLayoutAndApply {
|
||||||
resultSize = CGSize(
|
resultSize = CGSize(
|
||||||
@ -379,6 +410,8 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
|
|||||||
node.theme = presentationData.theme.theme
|
node.theme = presentationData.theme.theme
|
||||||
node.highlightColor = titleColor.withMultipliedAlpha(0.1)
|
node.highlightColor = titleColor.withMultipliedAlpha(0.1)
|
||||||
|
|
||||||
|
node.previousPeer = peer
|
||||||
|
|
||||||
let titleNode = titleApply()
|
let titleNode = titleApply()
|
||||||
titleNode.displaysAsynchronously = !presentationData.isPreview
|
titleNode.displaysAsynchronously = !presentationData.isPreview
|
||||||
|
|
||||||
@ -398,7 +431,7 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
nameNode.frame = CGRect(origin: CGPoint(x: leftOffset + authorAvatarInset, y: titleLayout.size.height + titleAuthorSpacing), size: nameLayout.size)
|
nameNode.frame = CGRect(origin: CGPoint(x: leftOffset + authorAvatarInset, y: titleLayout.size.height + titleAuthorSpacing), size: nameLayout.size)
|
||||||
|
|
||||||
if let peer, authorAvatarInset != 0.0 {
|
if authorAvatarInset != 0.0 {
|
||||||
let avatarNode: AvatarNode
|
let avatarNode: AvatarNode
|
||||||
if let current = node.avatarNode {
|
if let current = node.avatarNode {
|
||||||
avatarNode = current
|
avatarNode = current
|
||||||
@ -410,7 +443,13 @@ public class ChatMessageForwardInfoNode: ASDisplayNode {
|
|||||||
let avatarSize = CGSize(width: 16.0, height: 16.0)
|
let avatarSize = CGSize(width: 16.0, height: 16.0)
|
||||||
avatarNode.frame = CGRect(origin: CGPoint(x: leftOffset, y: titleLayout.size.height + titleAuthorSpacing), size: avatarSize)
|
avatarNode.frame = CGRect(origin: CGPoint(x: leftOffset, y: titleLayout.size.height + titleAuthorSpacing), size: avatarSize)
|
||||||
avatarNode.updateSize(size: avatarSize)
|
avatarNode.updateSize(size: avatarSize)
|
||||||
|
if let peer {
|
||||||
avatarNode.setPeer(context: context, theme: presentationData.theme.theme, peer: EnginePeer(peer), displayDimensions: avatarSize)
|
avatarNode.setPeer(context: context, theme: presentationData.theme.theme, peer: EnginePeer(peer), displayDimensions: avatarSize)
|
||||||
|
} else if let authorName, !authorName.isEmpty {
|
||||||
|
avatarNode.setCustomLetters([String(authorName[authorName.startIndex])])
|
||||||
|
} else {
|
||||||
|
avatarNode.setCustomLetters([" "])
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if let avatarNode = node.avatarNode {
|
if let avatarNode = node.avatarNode {
|
||||||
node.avatarNode = nil
|
node.avatarNode = nil
|
||||||
|
@ -58,6 +58,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
private var replyBackgroundContent: WallpaperBubbleBackgroundNode?
|
private var replyBackgroundContent: WallpaperBubbleBackgroundNode?
|
||||||
private var forwardInfoNode: ChatMessageForwardInfoNode?
|
private var forwardInfoNode: ChatMessageForwardInfoNode?
|
||||||
private var forwardBackgroundContent: WallpaperBubbleBackgroundNode?
|
private var forwardBackgroundContent: WallpaperBubbleBackgroundNode?
|
||||||
|
private var forwardBackgroundMaskNode: LinkHighlightingNode?
|
||||||
|
|
||||||
private var actionButtonsNode: ChatMessageActionButtonsNode?
|
private var actionButtonsNode: ChatMessageActionButtonsNode?
|
||||||
private var reactionButtonsNode: ChatMessageReactionButtonsNode?
|
private var reactionButtonsNode: ChatMessageReactionButtonsNode?
|
||||||
@ -1110,7 +1111,9 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
var forwardBackgroundFrame: CGRect?
|
var forwardBackgroundFrame: CGRect?
|
||||||
if let forwardAreaFrame {
|
if let forwardAreaFrame {
|
||||||
forwardBackgroundFrame = forwardAreaFrame.insetBy(dx: -6.0, dy: -3.0)
|
var forwardBackgroundFrameValue = forwardAreaFrame.insetBy(dx: -6.0, dy: -3.0)
|
||||||
|
forwardBackgroundFrameValue.size.height += 2.0
|
||||||
|
forwardBackgroundFrame = forwardBackgroundFrameValue
|
||||||
}
|
}
|
||||||
|
|
||||||
var replyBackgroundFrame: CGRect?
|
var replyBackgroundFrame: CGRect?
|
||||||
@ -1147,7 +1150,17 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let backgroundContent = strongSelf.forwardBackgroundContent, let forwardBackgroundFrame {
|
if let backgroundContent = strongSelf.forwardBackgroundContent, let forwardBackgroundFrame {
|
||||||
backgroundContent.cornerRadius = 4.0
|
let forwardBackgroundMaskNode: LinkHighlightingNode
|
||||||
|
if let current = strongSelf.forwardBackgroundMaskNode {
|
||||||
|
forwardBackgroundMaskNode = current
|
||||||
|
} else {
|
||||||
|
forwardBackgroundMaskNode = LinkHighlightingNode(color: .black)
|
||||||
|
forwardBackgroundMaskNode.inset = 4.0
|
||||||
|
forwardBackgroundMaskNode.outerRadius = 12.0
|
||||||
|
strongSelf.forwardBackgroundMaskNode = forwardBackgroundMaskNode
|
||||||
|
backgroundContent.view.mask = forwardBackgroundMaskNode.view
|
||||||
|
}
|
||||||
|
|
||||||
backgroundContent.frame = forwardBackgroundFrame
|
backgroundContent.frame = forwardBackgroundFrame
|
||||||
if let (rect, containerSize) = strongSelf.absoluteRect {
|
if let (rect, containerSize) = strongSelf.absoluteRect {
|
||||||
var backgroundFrame = backgroundContent.frame
|
var backgroundFrame = backgroundContent.frame
|
||||||
@ -1155,6 +1168,28 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
backgroundFrame.origin.y += rect.minY
|
backgroundFrame.origin.y += rect.minY
|
||||||
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let forwardInfoNode = strongSelf.forwardInfoNode {
|
||||||
|
forwardBackgroundMaskNode.frame = backgroundContent.bounds.offsetBy(dx: forwardInfoNode.frame.minX - backgroundContent.frame.minX, dy: forwardInfoNode.frame.minY - backgroundContent.frame.minY)
|
||||||
|
var backgroundRects = forwardInfoNode.getBoundingRects()
|
||||||
|
for i in 0 ..< backgroundRects.count {
|
||||||
|
backgroundRects[i].origin.x -= 2.0
|
||||||
|
backgroundRects[i].size.width += 4.0
|
||||||
|
}
|
||||||
|
for i in 0 ..< backgroundRects.count {
|
||||||
|
if i != 0 {
|
||||||
|
if abs(backgroundRects[i - 1].maxX - backgroundRects[i].maxX) < 16.0 {
|
||||||
|
let maxMaxX = max(backgroundRects[i - 1].maxX, backgroundRects[i].maxX)
|
||||||
|
backgroundRects[i - 1].size.width = max(0.0, maxMaxX - backgroundRects[i - 1].origin.x)
|
||||||
|
backgroundRects[i].size.width = max(0.0, maxMaxX - backgroundRects[i].origin.x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
forwardBackgroundMaskNode.updateRects(backgroundRects)
|
||||||
|
}
|
||||||
|
} else if let forwardBackgroundMaskNode = strongSelf.forwardBackgroundMaskNode {
|
||||||
|
strongSelf.forwardBackgroundMaskNode = nil
|
||||||
|
forwardBackgroundMaskNode.view.removeFromSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
let panelsAlpha: CGFloat = item.controllerInteraction.selectionState == nil ? 1.0 : 0.0
|
let panelsAlpha: CGFloat = item.controllerInteraction.selectionState == nil ? 1.0 : 0.0
|
||||||
|
@ -315,7 +315,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
|
|||||||
var colors: [UInt32] = []
|
var colors: [UInt32] = []
|
||||||
var rotation: Int32?
|
var rotation: Int32?
|
||||||
var intensity: Int32?
|
var intensity: Int32?
|
||||||
if let wallpaper = parseWallpaperUrl(webpage.url), case let .slug(_, _, colorsValue, intensityValue, rotationValue) = wallpaper {
|
if let wallpaper = parseWallpaperUrl(sharedContext: item.context.sharedContext, url: webpage.url), case let .slug(_, _, colorsValue, intensityValue, rotationValue) = wallpaper {
|
||||||
colors = colorsValue
|
colors = colorsValue
|
||||||
rotation = rotationValue
|
rotation = rotationValue
|
||||||
intensity = intensityValue
|
intensity = intensityValue
|
||||||
@ -353,7 +353,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
|
|||||||
if type == "telegram_background" {
|
if type == "telegram_background" {
|
||||||
var colors: [UInt32] = []
|
var colors: [UInt32] = []
|
||||||
var rotation: Int32?
|
var rotation: Int32?
|
||||||
if let wallpaper = parseWallpaperUrl(webpage.url) {
|
if let wallpaper = parseWallpaperUrl(sharedContext: item.context.sharedContext, url: webpage.url) {
|
||||||
if case let .color(color) = wallpaper {
|
if case let .color(color) = wallpaper {
|
||||||
colors = [color.rgb]
|
colors = [color.rgb]
|
||||||
} else if case let .gradient(colorsValue, rotationValue) = wallpaper {
|
} else if case let .gradient(colorsValue, rotationValue) = wallpaper {
|
||||||
|
@ -1663,10 +1663,36 @@ public extension EmojiPagerContentComponent {
|
|||||||
switch subject {
|
switch subject {
|
||||||
case .groupPhotoEmojiSelection, .profilePhotoEmojiSelection:
|
case .groupPhotoEmojiSelection, .profilePhotoEmojiSelection:
|
||||||
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar)
|
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar)
|
||||||
case .chatStickers:
|
case .chatStickers, .greetingStickers:
|
||||||
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .chatStickers)
|
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .combinedChatStickers)
|
||||||
case .greetingStickers:
|
|> map { result -> EmojiSearchCategories? in
|
||||||
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .greetingStickers)
|
guard let result else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var groups: [EmojiSearchCategories.Group] = []
|
||||||
|
groups = result.groups
|
||||||
|
if case .greetingStickers = subject {
|
||||||
|
if let index = groups.firstIndex(where: { group in
|
||||||
|
return group.kind == .greeting
|
||||||
|
}) {
|
||||||
|
let group = groups.remove(at: index)
|
||||||
|
groups.insert(group, at: 0)
|
||||||
|
}
|
||||||
|
} else if case .chatStickers = subject {
|
||||||
|
if let index = groups.firstIndex(where: { group in
|
||||||
|
return group.kind == .premium
|
||||||
|
}) {
|
||||||
|
let group = groups.remove(at: index)
|
||||||
|
groups.append(group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EmojiSearchCategories(
|
||||||
|
hash: result.hash,
|
||||||
|
groups: groups
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
|
@ -13097,7 +13097,7 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa
|
|||||||
}, clearHighlightAutomatically: true))
|
}, clearHighlightAutomatically: true))
|
||||||
}
|
}
|
||||||
|
|
||||||
let contactsController = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, mode: .peerSelection(searchChatList: false, searchGroups: false, searchChannels: false), options: options, filters: [.excludeSelf, .disable(recentIds)], onlyWriteable: true, isGroupInvitation: true))
|
let contactsController = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, mode: .peerSelection(searchChatList: false, searchGroups: false, searchChannels: false), options: .single(options), filters: [.excludeSelf, .disable(recentIds)], onlyWriteable: true, isGroupInvitation: true))
|
||||||
contactsController.navigationPresentation = .modal
|
contactsController.navigationPresentation = .modal
|
||||||
|
|
||||||
confirmationImpl = { [weak contactsController] peerId in
|
confirmationImpl = { [weak contactsController] peerId in
|
||||||
|
@ -384,7 +384,7 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component {
|
|||||||
additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories),
|
additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories),
|
||||||
chatListFilters: nil,
|
chatListFilters: nil,
|
||||||
onlyUsers: true
|
onlyUsers: true
|
||||||
)), options: [], filters: [], alwaysEnabled: true, limit: 100, reachedLimit: { _ in
|
)), filters: [], alwaysEnabled: true, limit: 100, reachedLimit: { _ in
|
||||||
}))
|
}))
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ final class BusinessRecipientListScreenComponent: Component {
|
|||||||
additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories),
|
additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories),
|
||||||
chatListFilters: nil,
|
chatListFilters: nil,
|
||||||
onlyUsers: true
|
onlyUsers: true
|
||||||
)), options: [], filters: [], alwaysEnabled: true, limit: 100, reachedLimit: { _ in
|
)), filters: [], alwaysEnabled: true, limit: 100, reachedLimit: { _ in
|
||||||
}))
|
}))
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
|
|
||||||
|
@ -2165,13 +2165,13 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
|||||||
return (sharedApplicationContext.sharedContext, context, authContext)
|
return (sharedApplicationContext.sharedContext, context, authContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { _, context, authContext in
|
|> deliverOnMainQueue).start(next: { sharedContext, context, authContext in
|
||||||
if let authContext = authContext, let confirmationCode = parseConfirmationCodeUrl(url) {
|
if let authContext = authContext, let confirmationCode = parseConfirmationCodeUrl(sharedContext: sharedContext, url: url) {
|
||||||
authContext.rootController.applyConfirmationCode(confirmationCode)
|
authContext.rootController.applyConfirmationCode(confirmationCode)
|
||||||
} else if let context = context {
|
} else if let context = context {
|
||||||
context.openUrl(url)
|
context.openUrl(url)
|
||||||
} else if let authContext = authContext {
|
} else if let authContext = authContext {
|
||||||
if let proxyData = parseProxyUrl(url) {
|
if let proxyData = parseProxyUrl(sharedContext: sharedContext, url: url) {
|
||||||
authContext.rootController.view.endEditing(true)
|
authContext.rootController.view.endEditing(true)
|
||||||
let presentationData = authContext.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = authContext.sharedContext.currentPresentationData.with { $0 }
|
||||||
let controller = ProxyServerActionSheetController(presentationData: presentationData, accountManager: authContext.sharedContext.accountManager, postbox: authContext.account.postbox, network: authContext.account.network, server: proxyData, updatedPresentationData: nil)
|
let controller = ProxyServerActionSheetController(presentationData: presentationData, accountManager: authContext.sharedContext.accountManager, postbox: authContext.account.postbox, network: authContext.account.network, server: proxyData, updatedPresentationData: nil)
|
||||||
|
@ -4156,7 +4156,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if case .user = peerType, maxQuantity > 1 {
|
if case .user = peerType, maxQuantity > 1 {
|
||||||
let presentationData = self.presentationData
|
let presentationData = self.presentationData
|
||||||
var reachedLimitImpl: ((Int32) -> Void)?
|
var reachedLimitImpl: ((Int32) -> Void)?
|
||||||
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .requestedUsersSelection, options: [], isPeerEnabled: { peer in
|
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .requestedUsersSelection, isPeerEnabled: { peer in
|
||||||
if case let .user(user) = peer, user.botInfo == nil {
|
if case let .user(user) = peer, user.botInfo == nil {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
|
@ -856,7 +856,7 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
}, recognizedQRCode: { [weak self] code in
|
}, recognizedQRCode: { [weak self] code in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let (host, port, username, password, secret) = parseProxyUrl(code) {
|
if let (host, port, username, password, secret) = parseProxyUrl(sharedContext: strongSelf.context.sharedContext, url: code) {
|
||||||
strongSelf.openResolved(result: ResolvedUrl.proxy(host: host, port: port, username: username, password: password, secret: secret), sourceMessageId: nil)
|
strongSelf.openResolved(result: ResolvedUrl.proxy(host: host, port: port, username: username, password: password, secret: secret), sourceMessageId: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1697,7 +1697,7 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
}, recognizedQRCode: { [weak self] code in
|
}, recognizedQRCode: { [weak self] code in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let (host, port, username, password, secret) = parseProxyUrl(code) {
|
if let (host, port, username, password, secret) = parseProxyUrl(sharedContext: strongSelf.context.sharedContext, url: code) {
|
||||||
strongSelf.openResolved(result: ResolvedUrl.proxy(host: host, port: port, username: username, password: password, secret: secret), sourceMessageId: nil)
|
strongSelf.openResolved(result: ResolvedUrl.proxy(host: host, port: port, username: username, password: password, secret: secret), sourceMessageId: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ public class ComposeControllerImpl: ViewController, ComposeController {
|
|||||||
|
|
||||||
self.contactsNode.openCreateNewGroup = { [weak self] in
|
self.contactsNode.openCreateNewGroup = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let controller = strongSelf.context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: strongSelf.context, mode: .groupCreation, options: [], onlyWriteable: true))
|
let controller = strongSelf.context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: strongSelf.context, mode: .groupCreation, onlyWriteable: true))
|
||||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller, completion: { [weak self] in
|
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller, completion: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
|
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
|
||||||
|
@ -81,7 +81,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
private var limitsConfiguration: LimitsConfiguration?
|
private var limitsConfiguration: LimitsConfiguration?
|
||||||
private var limitsConfigurationDisposable: Disposable?
|
private var limitsConfigurationDisposable: Disposable?
|
||||||
private var initialPeersDisposable: Disposable?
|
private var initialPeersDisposable: Disposable?
|
||||||
private let options: [ContactListAdditionalOption]
|
private let options: Signal<[ContactListAdditionalOption], NoError>
|
||||||
private let filters: [ContactListFilter]
|
private let filters: [ContactListFilter]
|
||||||
private let onlyWriteable: Bool
|
private let onlyWriteable: Bool
|
||||||
private let isGroupInvitation: Bool
|
private let isGroupInvitation: Bool
|
||||||
|
@ -82,7 +82,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
private let onlyWriteable: Bool
|
private let onlyWriteable: Bool
|
||||||
private let isGroupInvitation: Bool
|
private let isGroupInvitation: Bool
|
||||||
|
|
||||||
init(navigationBar: NavigationBar?, context: AccountContext, presentationData: PresentationData, mode: ContactMultiselectionControllerMode, isPeerEnabled: ((EnginePeer) -> Bool)?, attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?, options: [ContactListAdditionalOption], filters: [ContactListFilter], onlyWriteable: Bool, isGroupInvitation: Bool, limit: Int32?, reachedSelectionLimit: ((Int32) -> Void)?, present: @escaping (ViewController, Any?) -> Void) {
|
init(navigationBar: NavigationBar?, context: AccountContext, presentationData: PresentationData, mode: ContactMultiselectionControllerMode, isPeerEnabled: ((EnginePeer) -> Bool)?, attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?, options: Signal<[ContactListAdditionalOption], NoError>, filters: [ContactListFilter], onlyWriteable: Bool, isGroupInvitation: Bool, limit: Int32?, reachedSelectionLimit: ((Int32) -> Void)?, present: @escaping (ViewController, Any?) -> Void) {
|
||||||
self.navigationBar = navigationBar
|
self.navigationBar = navigationBar
|
||||||
|
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -235,7 +235,13 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
} else {
|
} else {
|
||||||
displayTopPeers = .none
|
displayTopPeers = .none
|
||||||
}
|
}
|
||||||
let contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: includeChatList, topPeers: displayTopPeers)), filters: filters, onlyWriteable: onlyWriteable, isGroupInvitation: isGroupInvitation, selectionState: ContactListNodeGroupSelectionState())
|
|
||||||
|
let presentation: Signal<ContactListPresentation, NoError> = options
|
||||||
|
|> map { options in
|
||||||
|
return .natural(options: options, includeChatList: includeChatList, topPeers: displayTopPeers)
|
||||||
|
}
|
||||||
|
|
||||||
|
let contactListNode = ContactListNode(context: context, presentation: presentation, filters: filters, onlyWriteable: onlyWriteable, isGroupInvitation: isGroupInvitation, selectionState: ContactListNodeGroupSelectionState())
|
||||||
self.contentNode = .contacts(contactListNode)
|
self.contentNode = .contacts(contactListNode)
|
||||||
|
|
||||||
if !selectedPeers.isEmpty {
|
if !selectedPeers.isEmpty {
|
||||||
|
@ -25,8 +25,8 @@ public struct ParsedSecureIdUrl {
|
|||||||
public let opaqueNonce: Data
|
public let opaqueNonce: Data
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseProxyUrl(_ url: URL) -> ProxyServerSettings? {
|
public func parseProxyUrl(sharedContext: SharedAccountContext, url: URL) -> ProxyServerSettings? {
|
||||||
guard let proxy = parseProxyUrl(url.absoluteString) else {
|
guard let proxy = parseProxyUrl(sharedContext: sharedContext, url: url.absoluteString) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if let secret = proxy.secret, let _ = MTProxySecret.parseData(secret) {
|
if let secret = proxy.secret, let _ = MTProxySecret.parseData(secret) {
|
||||||
@ -107,14 +107,14 @@ public func parseSecureIdUrl(_ url: URL) -> ParsedSecureIdUrl? {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseConfirmationCodeUrl(_ url: URL) -> Int? {
|
public func parseConfirmationCodeUrl(sharedContext: SharedAccountContext, url: URL) -> Int? {
|
||||||
if url.pathComponents.count == 3 && url.pathComponents[1].lowercased() == "login" {
|
if url.pathComponents.count == 3 && url.pathComponents[1].lowercased() == "login" {
|
||||||
if let code = Int(url.pathComponents[2]) {
|
if let code = Int(url.pathComponents[2]) {
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if url.scheme == "tg" {
|
if url.scheme == "tg" {
|
||||||
if let host = url.host, let query = url.query, let parsedUrl = parseInternalUrl(query: host + "?" + query) {
|
if let host = url.host, let query = url.query, let parsedUrl = parseInternalUrl(sharedContext: sharedContext, query: host + "?" + query) {
|
||||||
switch parsedUrl {
|
switch parsedUrl {
|
||||||
case let .confirmationCode(code):
|
case let .confirmationCode(code):
|
||||||
return code
|
return code
|
||||||
@ -168,7 +168,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let parsedUrl = parsedUrlValue else {
|
guard var parsedUrl = parsedUrlValue else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,6 +249,20 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
|> deliverOnMainQueue).startStandalone(next: handleResolvedUrl)
|
|> deliverOnMainQueue).startStandalone(next: handleResolvedUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if context.sharedContext.immediateExperimentalUISettings.browserExperiment {
|
||||||
|
if let scheme = parsedUrl.scheme, (scheme == "tg" || scheme == context.sharedContext.applicationBindings.appSpecificScheme) {
|
||||||
|
if parsedUrl.host == "ipfs" {
|
||||||
|
if let value = URL(string: "ipfs:/" + parsedUrl.path) {
|
||||||
|
parsedUrl = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let scheme = parsedUrl.scheme, scheme == "https", parsedUrl.host == "t.me", parsedUrl.path.hasPrefix("/ipfs/") {
|
||||||
|
if let value = URL(string: "ipfs://" + String(parsedUrl.path[parsedUrl.path.index(parsedUrl.path.startIndex, offsetBy: "/ipfs/".count)...])) {
|
||||||
|
parsedUrl = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let scheme = parsedUrl.scheme, (scheme == "tg" || scheme == context.sharedContext.applicationBindings.appSpecificScheme) {
|
if let scheme = parsedUrl.scheme, (scheme == "tg" || scheme == context.sharedContext.applicationBindings.appSpecificScheme) {
|
||||||
var convertedUrl: String?
|
var convertedUrl: String?
|
||||||
if let query = parsedUrl.query {
|
if let query = parsedUrl.query {
|
||||||
|
@ -2165,16 +2165,26 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
mode = .premiumGifting(birthdays: nil, selectToday: false)
|
mode = .premiumGifting(birthdays: nil, selectToday: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
var contactOptions: [ContactListAdditionalOption] = []
|
let contactOptions: Signal<[ContactListAdditionalOption], NoError>
|
||||||
if currentBirthdays != nil || "".isEmpty {
|
if currentBirthdays != nil || "".isEmpty {
|
||||||
contactOptions = [ContactListAdditionalOption(
|
contactOptions = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Birthday(id: context.account.peerId))
|
||||||
title: "Add Your Birthday",
|
|> map { birthday in
|
||||||
|
if birthday == nil {
|
||||||
|
return [ContactListAdditionalOption(
|
||||||
|
title: presentationData.strings.Premium_Gift_ContactSelection_AddBirthday,
|
||||||
icon: .generic(UIImage(bundleImageName: "Contact List/AddBirthdayIcon")!),
|
icon: .generic(UIImage(bundleImageName: "Contact List/AddBirthdayIcon")!),
|
||||||
action: {
|
action: {
|
||||||
presentBirthdayPickerImpl?()
|
presentBirthdayPickerImpl?()
|
||||||
},
|
},
|
||||||
clearHighlightAutomatically: true
|
clearHighlightAutomatically: true
|
||||||
)]
|
)]
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> deliverOnMainQueue
|
||||||
|
} else {
|
||||||
|
contactOptions = .single([])
|
||||||
}
|
}
|
||||||
|
|
||||||
var openProfileImpl: ((EnginePeer) -> Void)?
|
var openProfileImpl: ((EnginePeer) -> Void)?
|
||||||
@ -2544,7 +2554,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController {
|
public func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController {
|
||||||
return proxySettingsController(accountManager: sharedContext.accountManager, postbox: account.postbox, network: account.network, mode: .modal, presentationData: sharedContext.currentPresentationData.with { $0 }, updatedPresentationData: sharedContext.presentationData)
|
return proxySettingsController(accountManager: sharedContext.accountManager, sharedContext: sharedContext, postbox: account.postbox, network: account.network, mode: .modal, presentationData: sharedContext.currentPresentationData.with { $0 }, updatedPresentationData: sharedContext.presentationData)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController {
|
public func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController {
|
||||||
|
@ -103,6 +103,7 @@ public enum ParsedInternalUrl {
|
|||||||
case chatFolder(slug: String)
|
case chatFolder(slug: String)
|
||||||
case premiumGiftCode(slug: String)
|
case premiumGiftCode(slug: String)
|
||||||
case messageLink(slug: String)
|
case messageLink(slug: String)
|
||||||
|
case externalUrl(url: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ParsedUrl {
|
private enum ParsedUrl {
|
||||||
@ -110,7 +111,7 @@ private enum ParsedUrl {
|
|||||||
case internalUrl(ParsedInternalUrl)
|
case internalUrl(ParsedInternalUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
public func parseInternalUrl(sharedContext: SharedAccountContext, query: String) -> ParsedInternalUrl? {
|
||||||
var query = query
|
var query = query
|
||||||
if query.hasPrefix("s/") {
|
if query.hasPrefix("s/") {
|
||||||
query = String(query[query.index(query.startIndex, offsetBy: 2)...])
|
query = String(query[query.index(query.startIndex, offsetBy: 2)...])
|
||||||
@ -129,6 +130,12 @@ 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 sharedContext.immediateExperimentalUISettings.browserExperiment {
|
||||||
|
if query.hasPrefix("ipfs/") {
|
||||||
|
return .externalUrl(url: "ipfs://" + String(query[query.index(query.startIndex, offsetBy: "ipfs/".count)...]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if pathComponents[0].hasPrefix("+") || pathComponents[0].hasPrefix("%20") {
|
if pathComponents[0].hasPrefix("+") || pathComponents[0].hasPrefix("%20") {
|
||||||
let component = pathComponents[0].replacingOccurrences(of: "%20", with: "+")
|
let component = pathComponents[0].replacingOccurrences(of: "%20", with: "+")
|
||||||
if component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789+").inverted) == nil {
|
if component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789+").inverted) == nil {
|
||||||
@ -1016,7 +1023,8 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
|||||||
return .single(.result(.messageLink(link: result)))
|
return .single(.result(.messageLink(link: result)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
case let .externalUrl(url):
|
||||||
|
return .single(.result(.externalUrl(url)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1046,20 +1054,20 @@ public func isTelegraPhLink(_ url: String) -> Bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseProxyUrl(_ url: String) -> (host: String, port: Int32, username: String?, password: String?, secret: Data?)? {
|
public func parseProxyUrl(sharedContext: SharedAccountContext, url: String) -> (host: String, port: Int32, username: String?, password: String?, secret: Data?)? {
|
||||||
let schemes = ["http://", "https://", ""]
|
let schemes = ["http://", "https://", ""]
|
||||||
for basePath in baseTelegramMePaths {
|
for basePath in baseTelegramMePaths {
|
||||||
for scheme in schemes {
|
for scheme in schemes {
|
||||||
let basePrefix = scheme + basePath + "/"
|
let basePrefix = scheme + basePath + "/"
|
||||||
if url.lowercased().hasPrefix(basePrefix) {
|
if url.lowercased().hasPrefix(basePrefix) {
|
||||||
if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])), case let .proxy(host, port, username, password, secret) = internalUrl {
|
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: String(url[basePrefix.endIndex...])), case let .proxy(host, port, username, password, secret) = internalUrl {
|
||||||
return (host, port, username, password, secret)
|
return (host, port, username, password, secret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query {
|
if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query {
|
||||||
if let internalUrl = parseInternalUrl(query: host + "?" + query), case let .proxy(host, port, username, password, secret) = internalUrl {
|
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: host + "?" + query), case let .proxy(host, port, username, password, secret) = internalUrl {
|
||||||
return (host, port, username, password, secret)
|
return (host, port, username, password, secret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1067,20 +1075,20 @@ public func parseProxyUrl(_ url: String) -> (host: String, port: Int32, username
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseStickerPackUrl(_ url: String) -> String? {
|
public func parseStickerPackUrl(sharedContext: SharedAccountContext, url: String) -> String? {
|
||||||
let schemes = ["http://", "https://", ""]
|
let schemes = ["http://", "https://", ""]
|
||||||
for basePath in baseTelegramMePaths {
|
for basePath in baseTelegramMePaths {
|
||||||
for scheme in schemes {
|
for scheme in schemes {
|
||||||
let basePrefix = scheme + basePath + "/"
|
let basePrefix = scheme + basePath + "/"
|
||||||
if url.lowercased().hasPrefix(basePrefix) {
|
if url.lowercased().hasPrefix(basePrefix) {
|
||||||
if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])), case let .stickerPack(name, _) = internalUrl {
|
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: String(url[basePrefix.endIndex...])), case let .stickerPack(name, _) = internalUrl {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query {
|
if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query {
|
||||||
if let internalUrl = parseInternalUrl(query: host + "?" + query), case let .stickerPack(name, _) = internalUrl {
|
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: host + "?" + query), case let .stickerPack(name, _) = internalUrl {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1088,20 +1096,20 @@ public func parseStickerPackUrl(_ url: String) -> String? {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseWallpaperUrl(_ url: String) -> WallpaperUrlParameter? {
|
public func parseWallpaperUrl(sharedContext: SharedAccountContext, url: String) -> WallpaperUrlParameter? {
|
||||||
let schemes = ["http://", "https://", ""]
|
let schemes = ["http://", "https://", ""]
|
||||||
for basePath in baseTelegramMePaths {
|
for basePath in baseTelegramMePaths {
|
||||||
for scheme in schemes {
|
for scheme in schemes {
|
||||||
let basePrefix = scheme + basePath + "/"
|
let basePrefix = scheme + basePath + "/"
|
||||||
if url.lowercased().hasPrefix(basePrefix) {
|
if url.lowercased().hasPrefix(basePrefix) {
|
||||||
if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])), case let .wallpaper(wallpaper) = internalUrl {
|
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: String(url[basePrefix.endIndex...])), case let .wallpaper(wallpaper) = internalUrl {
|
||||||
return wallpaper
|
return wallpaper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query {
|
if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query {
|
||||||
if let internalUrl = parseInternalUrl(query: host + "?" + query), case let .wallpaper(wallpaper) = internalUrl {
|
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: host + "?" + query), case let .wallpaper(wallpaper) = internalUrl {
|
||||||
return wallpaper
|
return wallpaper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1178,7 +1186,7 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String
|
|||||||
url = basePrefix + String(url[scheme.endIndex...]).replacingOccurrences(of: ".\(basePath)/", with: "").replacingOccurrences(of: ".\(basePath)", with: "")
|
url = basePrefix + String(url[scheme.endIndex...]).replacingOccurrences(of: ".\(basePath)/", with: "").replacingOccurrences(of: ".\(basePath)", with: "")
|
||||||
}
|
}
|
||||||
if url.lowercased().hasPrefix(basePrefix) {
|
if url.lowercased().hasPrefix(basePrefix) {
|
||||||
if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])) {
|
if let internalUrl = parseInternalUrl(sharedContext: context.sharedContext, query: String(url[basePrefix.endIndex...])) {
|
||||||
return resolveInternalUrl(context: context, url: internalUrl)
|
return resolveInternalUrl(context: context, url: internalUrl)
|
||||||
|> map { result -> ResolveUrlResult in
|
|> map { result -> ResolveUrlResult in
|
||||||
switch result {
|
switch result {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user