This commit is contained in:
Ilya Laktyushin 2020-02-11 19:41:59 +04:00
parent 97fd1d10f0
commit 801b26f157
11 changed files with 253 additions and 71 deletions

Binary file not shown.

View File

@ -7,7 +7,7 @@ private let leftFadeImage = generateImage(CGSize(width: 64.0, height: 1.0), opaq
let bounds = CGRect(origin: CGPoint(), size: size) let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds) context.clear(bounds)
let gradientColors = [UIColor.black.withAlphaComponent(0.5).cgColor, UIColor.black.withAlphaComponent(0.0).cgColor] as CFArray let gradientColors = [UIColor.black.withAlphaComponent(0.35).cgColor, UIColor.black.withAlphaComponent(0.0).cgColor] as CFArray
var locations: [CGFloat] = [0.0, 1.0] var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB() let colorSpace = CGColorSpaceCreateDeviceRGB()
@ -20,7 +20,7 @@ private let rightFadeImage = generateImage(CGSize(width: 64.0, height: 1.0), opa
let bounds = CGRect(origin: CGPoint(), size: size) let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds) context.clear(bounds)
let gradientColors = [UIColor.black.withAlphaComponent(0.0).cgColor, UIColor.black.withAlphaComponent(0.5).cgColor] as CFArray let gradientColors = [UIColor.black.withAlphaComponent(0.0).cgColor, UIColor.black.withAlphaComponent(0.35).cgColor] as CFArray
var locations: [CGFloat] = [0.0, 1.0] var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB() let colorSpace = CGColorSpaceCreateDeviceRGB()
@ -63,12 +63,12 @@ open class ZoomableContentGalleryItemNode: GalleryItemNode, UIScrollViewDelegate
self.leftFadeNode = ASImageNode() self.leftFadeNode = ASImageNode()
self.leftFadeNode.contentMode = .scaleToFill self.leftFadeNode.contentMode = .scaleToFill
self.leftFadeNode.image = leftFadeImage self.leftFadeNode.image = leftFadeImage
self.leftFadeNode.isHidden = true self.leftFadeNode.alpha = 0.0
self.rightFadeNode = ASImageNode() self.rightFadeNode = ASImageNode()
self.rightFadeNode.contentMode = .scaleToFill self.rightFadeNode.contentMode = .scaleToFill
self.rightFadeNode.image = rightFadeImage self.rightFadeNode.image = rightFadeImage
self.rightFadeNode.isHidden = true self.rightFadeNode.alpha = 0.0
super.init() super.init()
@ -90,22 +90,22 @@ open class ZoomableContentGalleryItemNode: GalleryItemNode, UIScrollViewDelegate
} }
tapRecognizer.highlight = { [weak self] location in tapRecognizer.highlight = { [weak self] location in
if let strongSelf = self { if let strongSelf = self {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.07, curve: .easeInOut)
if let location = location, location.x < 44.0 { if let location = location, location.x < 44.0 {
strongSelf.leftFadeNode.isHidden = false transition.updateAlpha(node: strongSelf.leftFadeNode, alpha: 1.0)
} else { } else {
strongSelf.leftFadeNode.isHidden = true transition.updateAlpha(node: strongSelf.leftFadeNode, alpha: 0.0)
} }
if let location = location, location.x > strongSelf.frame.width - 44.0 { if let location = location, location.x > strongSelf.frame.width - 44.0 {
strongSelf.rightFadeNode.isHidden = false transition.updateAlpha(node: strongSelf.rightFadeNode, alpha: 1.0)
} else { } else {
strongSelf.rightFadeNode.isHidden = true transition.updateAlpha(node: strongSelf.rightFadeNode, alpha: 0.0)
} }
} }
} }
self.scrollNode.view.addGestureRecognizer(tapRecognizer) self.scrollNode.view.addGestureRecognizer(tapRecognizer)
self.addSubnode(self.scrollNode) self.addSubnode(self.scrollNode)
self.addSubnode(self.leftFadeNode) self.addSubnode(self.leftFadeNode)
self.addSubnode(self.rightFadeNode) self.addSubnode(self.rightFadeNode)
@ -114,9 +114,10 @@ open class ZoomableContentGalleryItemNode: GalleryItemNode, UIScrollViewDelegate
@objc open func contentTap(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { @objc open func contentTap(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
if recognizer.state == .ended { if recognizer.state == .ended {
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation { if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
if location.x < 44.0 { let pointInNode = self.scrollNode.view.convert(location, to: self.view)
if pointInNode.x < 44.0 {
self.goToPreviousItem() self.goToPreviousItem()
} else if location.x > self.frame.width - 44.0 { } else if pointInNode.x > self.frame.width - 44.0 {
self.goToNextItem() self.goToNextItem()
} else { } else {
switch gesture { switch gesture {
@ -159,8 +160,9 @@ open class ZoomableContentGalleryItemNode: GalleryItemNode, UIScrollViewDelegate
} }
self.containerLayout = layout self.containerLayout = layout
self.leftFadeNode.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width * 0.2, height: layout.size.height) let fadeWidth = min(72.0, layout.size.width * 0.2)
self.rightFadeNode.frame = CGRect(x: layout.size.width - layout.size.width * 0.2, y: 0.0, width: layout.size.width * 0.2, height: layout.size.height) self.leftFadeNode.frame = CGRect(x: 0.0, y: 0.0, width: fadeWidth, height: layout.size.height)
self.rightFadeNode.frame = CGRect(x: layout.size.width - fadeWidth, y: 0.0, width: fadeWidth, height: layout.size.height)
if shouldResetContents { if shouldResetContents {
var previousFrame: CGRect? var previousFrame: CGRect?

View File

@ -24,6 +24,8 @@ import ContextUI
import TelegramNotices import TelegramNotices
import TelegramStringFormatting import TelegramStringFormatting
private let maxUsersDisplayedLimit: Int32 = 5
private struct PeerNearbyEntry { private struct PeerNearbyEntry {
let peer: (Peer, CachedPeerData?) let peer: (Peer, CachedPeerData?)
let expires: Int32 let expires: Int32
@ -57,14 +59,16 @@ private final class PeersNearbyControllerArguments {
let openChat: (Peer) -> Void let openChat: (Peer) -> Void
let openCreateGroup: (Double, Double, String?) -> Void let openCreateGroup: (Double, Double, String?) -> Void
let contextAction: (Peer, ASDisplayNode, ContextGesture?) -> Void let contextAction: (Peer, ASDisplayNode, ContextGesture?) -> Void
let expandUsers: () -> Void
init(context: AccountContext, toggleVisibility: @escaping (Bool) -> Void, openProfile: @escaping (Peer) -> Void, openChat: @escaping (Peer) -> Void, openCreateGroup: @escaping (Double, Double, String?) -> Void, contextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void) { init(context: AccountContext, toggleVisibility: @escaping (Bool) -> Void, openProfile: @escaping (Peer) -> Void, openChat: @escaping (Peer) -> Void, openCreateGroup: @escaping (Double, Double, String?) -> Void, contextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void, expandUsers: @escaping () -> Void) {
self.context = context self.context = context
self.toggleVisibility = toggleVisibility self.toggleVisibility = toggleVisibility
self.openProfile = openProfile self.openProfile = openProfile
self.openChat = openChat self.openChat = openChat
self.openCreateGroup = openCreateGroup self.openCreateGroup = openCreateGroup
self.contextAction = contextAction self.contextAction = contextAction
self.expandUsers = expandUsers
} }
} }
@ -82,6 +86,7 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
case empty(PresentationTheme, String) case empty(PresentationTheme, String)
case visibility(PresentationTheme, String, Bool) case visibility(PresentationTheme, String, Bool)
case user(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PeerNearbyEntry) case user(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PeerNearbyEntry)
case expand(PresentationTheme, String)
case groupsHeader(PresentationTheme, String, Bool) case groupsHeader(PresentationTheme, String, Bool)
case createGroup(PresentationTheme, String, Double?, Double?, String?) case createGroup(PresentationTheme, String, Double?, Double?, String?)
@ -94,7 +99,7 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
switch self { switch self {
case .header: case .header:
return PeersNearbySection.header.rawValue return PeersNearbySection.header.rawValue
case .usersHeader, .empty, .visibility, .user: case .usersHeader, .empty, .visibility, .user, .expand:
return PeersNearbySection.users.rawValue return PeersNearbySection.users.rawValue
case .groupsHeader, .createGroup, .group: case .groupsHeader, .createGroup, .group:
return PeersNearbySection.groups.rawValue return PeersNearbySection.groups.rawValue
@ -115,12 +120,14 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
return 3 return 3
case let .user(index, _, _, _, _, _): case let .user(index, _, _, _, _, _):
return 4 + index return 4 + index
case .groupsHeader: case .expand:
return 1000 return 1000
case .createGroup: case .groupsHeader:
return 1001 return 1001
case .createGroup:
return 1002
case let .group(index, _, _, _, _, _): case let .group(index, _, _, _, _, _):
return 1002 + index return 1003 + index
case .channelsHeader: case .channelsHeader:
return 2000 return 2000
case let .channel(index, _, _, _, _, _): case let .channel(index, _, _, _, _, _):
@ -161,6 +168,12 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .expand(lhsTheme, lhsText):
if case let .expand(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .groupsHeader(lhsTheme, lhsText, lhsLoading): case let .groupsHeader(lhsTheme, lhsText, lhsLoading):
if case let .groupsHeader(rhsTheme, rhsText, rhsLoading) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsLoading == rhsLoading { if case let .groupsHeader(rhsTheme, rhsText, rhsLoading) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsLoading == rhsLoading {
return true return true
@ -224,14 +237,19 @@ private enum PeersNearbyEntry: ItemListNodeEntry {
}) })
case let .user(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer): case let .user(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer):
var text = strings.Map_DistanceAway(stringForDistance(peer.distance)).0 var text = strings.Map_DistanceAway(stringForDistance(peer.distance)).0
if peer.peer.0.id == arguments.context.account.peerId { let isSelfPeer = peer.peer.0.id == arguments.context.account.peerId
if isSelfPeer {
text = strings.PeopleNearby_VisibleUntil(humanReadableStringForTimestamp(strings: strings, dateTimeFormat: dateTimeFormat, timestamp: peer.expires)).0 text = strings.PeopleNearby_VisibleUntil(humanReadableStringForTimestamp(strings: strings, dateTimeFormat: dateTimeFormat, timestamp: peer.expires)).0
} }
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: peer.peer.0, aliasHandling: .standard, nameColor: .primary, nameStyle: .distinctBold, presence: nil, text: .text(strings.Map_DistanceAway(stringForDistance(peer.distance)).0), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: { return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: peer.peer.0, aliasHandling: .standard, nameColor: .primary, nameStyle: .distinctBold, presence: nil, text: .text(text), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: !isSelfPeer, sectionId: self.section, action: {
if !isSelfPeer {
arguments.openProfile(peer.peer.0) arguments.openProfile(peer.peer.0)
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, contextAction: { node, gesture in }
arguments.contextAction(peer.peer.0, node, gesture) }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: nil, contextAction: nil, hasTopGroupInset: false, tag: nil)
}, hasTopGroupInset: false, tag: nil) case let .expand(theme, title):
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
arguments.expandUsers()
})
case let .groupsHeader(theme, text, loading): case let .groupsHeader(theme, text, loading):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, activityIndicator: loading ? .left : .none, sectionId: self.section) return ItemListSectionHeaderItem(presentationData: presentationData, text: text, activityIndicator: loading ? .left : .none, sectionId: self.section)
case let .createGroup(theme, title, latitude, longitude, address): case let .createGroup(theme, title, latitude, longitude, address):
@ -294,7 +312,7 @@ private struct PeersNearbyData: Equatable {
} }
} }
private func peersNearbyControllerEntries(data: PeersNearbyData?, presentationData: PresentationData, displayLoading: Bool) -> [PeersNearbyEntry] { private func peersNearbyControllerEntries(data: PeersNearbyData?, presentationData: PresentationData, displayLoading: Bool, expanded: Bool) -> [PeersNearbyEntry] {
var entries: [PeersNearbyEntry] = [] var entries: [PeersNearbyEntry] = []
entries.append(.header(presentationData.theme, presentationData.strings.PeopleNearby_DiscoverDescription)) entries.append(.header(presentationData.theme, presentationData.strings.PeopleNearby_DiscoverDescription))
@ -305,10 +323,22 @@ private func peersNearbyControllerEntries(data: PeersNearbyData?, presentationDa
if let data = data, !data.users.isEmpty { if let data = data, !data.users.isEmpty {
var i: Int32 = 0 var i: Int32 = 0
for user in data.users { var users = data.users
var effectiveExpanded = expanded
if users.count > maxUsersDisplayedLimit && !expanded {
users = Array(users.prefix(Int(maxUsersDisplayedLimit)))
} else {
effectiveExpanded = true
}
for user in users {
entries.append(.user(i, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, user)) entries.append(.user(i, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, user))
i += 1 i += 1
} }
if !effectiveExpanded {
entries.append(.expand(presentationData.theme, presentationData.strings.PeopleNearby_ShowMorePeople(Int32(data.users.count) - maxUsersDisplayedLimit)))
}
} }
entries.append(.groupsHeader(presentationData.theme, presentationData.strings.PeopleNearby_Groups.uppercased(), displayLoading && data == nil)) entries.append(.groupsHeader(presentationData.theme, presentationData.strings.PeopleNearby_Groups.uppercased(), displayLoading && data == nil))
@ -429,6 +459,9 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
let dataPromise = Promise<PeersNearbyData?>(nil) let dataPromise = Promise<PeersNearbyData?>(nil)
let addressPromise = Promise<String?>(nil) let addressPromise = Promise<String?>(nil)
let visibilityPromise = ValuePromise<Bool>(true)
let expandedPromise = ValuePromise<Bool>(false)
let coordinatePromise = Promise<CLLocationCoordinate2D?>(nil) let coordinatePromise = Promise<CLLocationCoordinate2D?>(nil)
coordinatePromise.set(.single(nil) |> then(currentLocationManagerCoordinate(manager: context.sharedContext.locationManager!, timeout: 5.0))) coordinatePromise.set(.single(nil) |> then(currentLocationManagerCoordinate(manager: context.sharedContext.locationManager!, timeout: 5.0)))
@ -440,12 +473,16 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
let _ = (coordinatePromise.get() let _ = (coordinatePromise.get()
|> deliverOnMainQueue).start(next: { coordinate in |> deliverOnMainQueue).start(next: { coordinate in
if let coordinate = coordinate { if let coordinate = coordinate {
let _ = peersNearbyUpdateVisibility(network: context.account.network, stateManager: context.account.stateManager, update: .visible(latitude: coordinate.latitude, longitude: coordinate.longitude), background: false).start() let _ = peersNearbyUpdateVisibility(account: context.account, update: .visible(latitude: coordinate.latitude, longitude: coordinate.longitude), background: false).start()
} }
}) })
})]), nil) })]), nil)
visibilityPromise.set(true)
} else { } else {
let _ = peersNearbyUpdateVisibility(network: context.account.network, stateManager: context.account.stateManager, update: .invisible, background: false).start() let _ = peersNearbyUpdateVisibility(account: context.account, update: .invisible, background: false).start()
visibilityPromise.set(false)
} }
}, openProfile: { peer in }, openProfile: { peer in
navigateToProfileImpl?(peer) navigateToProfileImpl?(peer)
@ -498,6 +535,8 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
presentControllerImpl?(c, nil) presentControllerImpl?(c, nil)
}), reactionItems: [], gesture: gesture) }), reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController) presentInGlobalOverlayImpl?(contextController)
}, expandUsers: {
expandedPromise.set(true)
}) })
let dataSignal: Signal<PeersNearbyData?, NoError> = coordinatePromise.get() let dataSignal: Signal<PeersNearbyData?, NoError> = coordinatePromise.get()
@ -569,9 +608,9 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
|> delay(1.0, queue: Queue.mainQueue()) |> delay(1.0, queue: Queue.mainQueue())
) )
let signal = combineLatest(context.sharedContext.presentationData, dataPromise.get(), displayLoading) let signal = combineLatest(context.sharedContext.presentationData, dataPromise.get(), displayLoading, visibilityPromise.get(), expandedPromise.get())
|> deliverOnMainQueue |> deliverOnMainQueue
|> map { presentationData, data, displayLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in |> map { presentationData, data, displayLoading, visibility, expanded -> (ItemListControllerState, (ItemListNodeState, Any)) in
let previous = previousData.swap(data) let previous = previousData.swap(data)
var crossfade = false var crossfade = false
@ -583,7 +622,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
} }
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.PeopleNearby_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.PeopleNearby_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: peersNearbyControllerEntries(data: data, presentationData: presentationData, displayLoading: displayLoading), style: .blocks, emptyStateItem: nil, crossfadeState: crossfade, animateChanges: !crossfade) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: peersNearbyControllerEntries(data: data, presentationData: presentationData, displayLoading: displayLoading, expanded: expanded), style: .blocks, emptyStateItem: nil, crossfadeState: crossfade, animateChanges: !crossfade)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} }
@ -596,7 +635,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController {
controller?.clearItemNodesHighlight(animated: true) controller?.clearItemNodesHighlight(animated: true)
} }
navigateToProfileImpl = { [weak controller] peer in navigateToProfileImpl = { [weak controller] peer in
if let navigationController = controller?.navigationController as? NavigationController, let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: true) { if let navigationController = controller?.navigationController as? NavigationController, let controller = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic, avatarInitiallyExpanded: false) {
(navigationController as? NavigationController)?.pushViewController(controller) (navigationController as? NavigationController)?.pushViewController(controller)
} }
} }

View File

@ -197,6 +197,7 @@ private enum PreferencesKeyValues: Int32 {
case walletCollection = 18 case walletCollection = 18
case contentSettings = 19 case contentSettings = 19
case chatListFilters = 20 case chatListFilters = 20
case peersNearby = 21
} }
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey { public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
@ -313,6 +314,12 @@ public struct PreferencesKeys {
key.setInt32(0, value: PreferencesKeyValues.chatListFilters.rawValue) key.setInt32(0, value: PreferencesKeyValues.chatListFilters.rawValue)
return key return key
}() }()
public static let peersNearby: ValueBoxKey = {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: PreferencesKeyValues.peersNearby.rawValue)
return key
}()
} }
private enum SharedDataKeyValues: Int32 { private enum SharedDataKeyValues: Int32 {

View File

@ -561,6 +561,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1739392570] = { return Api.DocumentAttribute.parse_documentAttributeAudio($0) } dict[-1739392570] = { return Api.DocumentAttribute.parse_documentAttributeAudio($0) }
dict[358154344] = { return Api.DocumentAttribute.parse_documentAttributeFilename($0) } dict[358154344] = { return Api.DocumentAttribute.parse_documentAttributeFilename($0) }
dict[-1744710921] = { return Api.DocumentAttribute.parse_documentAttributeHasStickers($0) } dict[-1744710921] = { return Api.DocumentAttribute.parse_documentAttributeHasStickers($0) }
dict[-177732982] = { return Api.BankCardOpenUrl.parse_bankCardOpenUrl($0) }
dict[307276766] = { return Api.account.Authorizations.parse_authorizations($0) } dict[307276766] = { return Api.account.Authorizations.parse_authorizations($0) }
dict[935395612] = { return Api.ChatPhoto.parse_chatPhotoEmpty($0) } dict[935395612] = { return Api.ChatPhoto.parse_chatPhotoEmpty($0) }
dict[1197267925] = { return Api.ChatPhoto.parse_chatPhoto($0) } dict[1197267925] = { return Api.ChatPhoto.parse_chatPhoto($0) }
@ -818,7 +819,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1707344487] = { return Api.messages.HighScores.parse_highScores($0) } dict[-1707344487] = { return Api.messages.HighScores.parse_highScores($0) }
dict[-892779534] = { return Api.WebAuthorization.parse_webAuthorization($0) } dict[-892779534] = { return Api.WebAuthorization.parse_webAuthorization($0) }
dict[-805141448] = { return Api.ImportedContact.parse_importedContact($0) } dict[-805141448] = { return Api.ImportedContact.parse_importedContact($0) }
dict[-419239361] = { return Api.payments.BankCardData.parse_bankCardData($0) } dict[1042605427] = { return Api.payments.BankCardData.parse_bankCardData($0) }
return dict return dict
}() }()
@ -1238,6 +1239,8 @@ public struct Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.DocumentAttribute: case let _1 as Api.DocumentAttribute:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.BankCardOpenUrl:
_1.serialize(buffer, boxed)
case let _1 as Api.account.Authorizations: case let _1 as Api.account.Authorizations:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.ChatPhoto: case let _1 as Api.ChatPhoto:

View File

@ -15952,6 +15952,44 @@ public extension Api {
return Api.DocumentAttribute.documentAttributeHasStickers return Api.DocumentAttribute.documentAttributeHasStickers
} }
}
public enum BankCardOpenUrl: TypeConstructorDescription {
case bankCardOpenUrl(url: String, name: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .bankCardOpenUrl(let url, let name):
if boxed {
buffer.appendInt32(-177732982)
}
serializeString(url, buffer: buffer, boxed: false)
serializeString(name, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .bankCardOpenUrl(let url, let name):
return ("bankCardOpenUrl", [("url", url), ("name", name)])
}
}
public static func parse_bankCardOpenUrl(_ reader: BufferReader) -> BankCardOpenUrl? {
var _1: String?
_1 = parseString(reader)
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.BankCardOpenUrl.bankCardOpenUrl(url: _1!, name: _2!)
}
else {
return nil
}
}
} }
public enum ChatPhoto: TypeConstructorDescription { public enum ChatPhoto: TypeConstructorDescription {
case chatPhotoEmpty case chatPhotoEmpty

View File

@ -491,44 +491,42 @@ public struct payments {
} }
public enum BankCardData: TypeConstructorDescription { public enum BankCardData: TypeConstructorDescription {
case bankCardData(flags: Int32, title: String, url: String?, urlName: String?) case bankCardData(title: String, openUrls: [Api.BankCardOpenUrl])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .bankCardData(let flags, let title, let url, let urlName): case .bankCardData(let title, let openUrls):
if boxed { if boxed {
buffer.appendInt32(-419239361) buffer.appendInt32(1042605427)
} }
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false) serializeString(title, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeString(url!, buffer: buffer, boxed: false)} buffer.appendInt32(481674261)
if Int(flags) & Int(1 << 0) != 0 {serializeString(urlName!, buffer: buffer, boxed: false)} buffer.appendInt32(Int32(openUrls.count))
for item in openUrls {
item.serialize(buffer, true)
}
break break
} }
} }
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .bankCardData(let flags, let title, let url, let urlName): case .bankCardData(let title, let openUrls):
return ("bankCardData", [("flags", flags), ("title", title), ("url", url), ("urlName", urlName)]) return ("bankCardData", [("title", title), ("openUrls", openUrls)])
} }
} }
public static func parse_bankCardData(_ reader: BufferReader) -> BankCardData? { public static func parse_bankCardData(_ reader: BufferReader) -> BankCardData? {
var _1: Int32? var _1: String?
_1 = reader.readInt32() _1 = parseString(reader)
var _2: String? var _2: [Api.BankCardOpenUrl]?
_2 = parseString(reader) if let _ = reader.readInt32() {
var _3: String? _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BankCardOpenUrl.self)
if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) } }
var _4: String?
if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil if _c1 && _c2 {
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil return Api.payments.BankCardData.bankCardData(title: _1!, openUrls: _2!)
if _c1 && _c2 && _c3 && _c4 {
return Api.payments.BankCardData.bankCardData(flags: _1!, title: _2!, url: _3, urlName: _4)
} }
else { else {
return nil return nil

View File

@ -3263,6 +3263,33 @@ public extension Api {
}) })
} }
} }
public static func inputMediaDice() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.InputMedia>) {
let buffer = Buffer()
buffer.appendInt32(-1358977017)
return (FunctionDescription(name: "inputMediaDice", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.InputMedia? in
let reader = BufferReader(buffer)
var result: Api.InputMedia?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.InputMedia
}
return result
})
}
public static func messageMediaDice(value: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.MessageMedia>) {
let buffer = Buffer()
buffer.appendInt32(1670374507)
serializeInt32(value, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messageMediaDice", parameters: [("value", value)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in
let reader = BufferReader(buffer)
var result: Api.MessageMedia?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.MessageMedia
}
return result
})
}
public struct channels { public struct channels {
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer() let buffer = Buffer()

View File

@ -2,33 +2,58 @@ import Foundation
import Postbox import Postbox
import TelegramApi import TelegramApi
import SyncCore import SyncCore
import MtProtoKit
import SwiftSignalKit import SwiftSignalKit
public struct BankCardUrl {
public let title: String
public let url: String
}
public struct BankCardInfo { public struct BankCardInfo {
public let title: String public let title: String
public let url: String? public let urls: [BankCardUrl]
public let actionTitle: String?
} }
public func getBankCardInfo(account: Account, cardNumber: String) -> Signal<BankCardInfo?, NoError> { public func getBankCardInfo(account: Account, cardNumber: String) -> Signal<BankCardInfo?, NoError> {
return account.network.request(Api.functions.payments.getBankCardData(number: cardNumber)) return currentWebDocumentsHostDatacenterId(postbox: account.postbox, isTestingEnvironment: false)
|> mapToSignal { datacenterId in
let signal: Signal<Api.payments.BankCardData, MTRpcError>
if account.network.datacenterId != datacenterId {
signal = account.network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil)
|> castError(MTRpcError.self)
|> mapToSignal { worker in
return worker.request(Api.functions.payments.getBankCardData(number: cardNumber))
}
} else {
signal = account.network.request(Api.functions.payments.getBankCardData(number: cardNumber))
}
return signal
|> map { result -> BankCardInfo? in |> map { result -> BankCardInfo? in
return BankCardInfo(apiBankCardData: result) return BankCardInfo(apiBankCardData: result)
} }
|> `catch` { _ -> Signal<BankCardInfo?, NoError> in |> `catch` { _ -> Signal<BankCardInfo?, NoError> in
return .single(nil) return .single(nil)
} }
}
}
extension BankCardUrl {
init(apiBankCardOpenUrl: Api.BankCardOpenUrl) {
switch apiBankCardOpenUrl {
case let .bankCardOpenUrl(url, name):
self.title = name
self.url = url
}
}
} }
extension BankCardInfo { extension BankCardInfo {
init(apiBankCardData: Api.payments.BankCardData) { init(apiBankCardData: Api.payments.BankCardData) {
switch apiBankCardData { switch apiBankCardData {
case let .bankCardData(flags, title, url, urlName): case let .bankCardData(title, urls):
self.title = title self.title = title
self.url = url self.urls = urls.map { BankCardUrl(apiBankCardOpenUrl: $0) }
self.actionTitle = urlName
} }
} }
} }

View File

@ -25,7 +25,7 @@ public enum PeerNearbyVisibilityUpdate {
case invisible case invisible
} }
public func peersNearbyUpdateVisibility(network: Network, stateManager: AccountStateManager, update: PeerNearbyVisibilityUpdate, background: Bool) -> Signal<Void, NoError> { public func peersNearbyUpdateVisibility(account: Account, update: PeerNearbyVisibilityUpdate, background: Bool) -> Signal<Void, NoError> {
var flags: Int32 = 0 var flags: Int32 = 0
var geoPoint: Api.InputGeoPoint var geoPoint: Api.InputGeoPoint
var selfExpires: Int32? var selfExpires: Int32?
@ -43,18 +43,30 @@ public func peersNearbyUpdateVisibility(network: Network, stateManager: AccountS
selfExpires = 0 selfExpires = 0
} }
let _ = (account.postbox.transaction { transaction in
transaction.updatePreferencesEntry(key: PreferencesKeys.peersNearby, { entry in
var settings = entry as? PeersNearbyState ?? PeersNearbyState.default
if let expires = selfExpires {
settings.visibilityExpires = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) + expires
} else if case .invisible = update {
settings.visibilityExpires = nil
}
return settings
})
}).start()
if background { if background {
flags |= (1 << 1) flags |= (1 << 1)
} }
return network.request(Api.functions.contacts.getLocated(flags: flags, geoPoint: geoPoint, selfExpires: selfExpires)) return account.network.request(Api.functions.contacts.getLocated(flags: flags, geoPoint: geoPoint, selfExpires: selfExpires))
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in |> `catch` { _ -> Signal<Api.Updates?, NoError> in
return .single(nil) return .single(nil)
} }
|> mapToSignal { updates -> Signal<Void, NoError> in |> mapToSignal { updates -> Signal<Void, NoError> in
if let updates = updates { if let updates = updates {
stateManager.addUpdates(updates) account.stateManager.addUpdates(updates)
} }
return .complete() return .complete()
} }
@ -258,3 +270,33 @@ public func updateChannelGeoLocation(postbox: Postbox, network: Network, channel
} }
} }
} }
public struct PeersNearbyState: PreferencesEntry, Equatable {
public var visibilityExpires: Int32?
public static var `default` = PeersNearbyState(visibilityExpires: nil)
public init(visibilityExpires: Int32?) {
self.visibilityExpires = visibilityExpires
}
public init(decoder: PostboxDecoder) {
self.visibilityExpires = decoder.decodeOptionalInt32ForKey("expires")
}
public func encode(_ encoder: PostboxEncoder) {
if let expires = self.visibilityExpires {
encoder.encodeInt32(expires, forKey: "expires")
} else {
encoder.encodeNil(forKey: "expires")
}
}
public func isEqual(to: PreferencesEntry) -> Bool {
if let to = to as? PeersNearbyState, self == to {
return true
} else {
return false
}
}
}

View File

@ -1357,17 +1357,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let message = message else { guard let message = message else {
return return
} }
let _ = (getBankCardInfo(account: strongSelf.context.account, cardNumber: number) let _ = (getBankCardInfo(account: strongSelf.context.account, cardNumber: number)
|> deliverOnMainQueue).start(next: { [weak self] info in |> deliverOnMainQueue).start(next: { [weak self] info in
if let strongSelf = self, let info = info { if let strongSelf = self, let info = info {
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
var items: [ActionSheetItem] = [] var items: [ActionSheetItem] = []
items.append(ActionSheetTextItem(title: info.title)) items.append(ActionSheetTextItem(title: info.title))
if let url = info.url, let actionTitle = info.actionTitle { for url in info.urls {
items.append(ActionSheetButtonItem(title: actionTitle, color: .accent, action: { [weak actionSheet] in items.append(ActionSheetButtonItem(title: url.title, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated() actionSheet?.dismissAnimated()
if let strongSelf = self { if let strongSelf = self {
strongSelf.controllerInteraction?.openUrl(url, false, false, message) strongSelf.controllerInteraction?.openUrl(url.url, false, false, message)
} }
})) }))
} }