mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Improve recent sessions UI
This commit is contained in:
parent
b3285652c1
commit
76659a0603
@ -5143,7 +5143,7 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Settings.Devices" = "Devices";
|
||||
"Settings.AddDevice" = "Scan QR";
|
||||
"AuthSessions.DevicesTitle" = "Devices";
|
||||
"AuthSessions.AddDevice" = "Add Device";
|
||||
"AuthSessions.AddDevice" = "Scan QR";
|
||||
"AuthSessions.AddDevice.ScanInfo" = "Scan a QR code to log into\nthis account on another device.";
|
||||
"AuthSessions.AddDevice.ScanTitle" = "Scan QR Code";
|
||||
"AuthSessions.AddDevice.InvalidQRCode" = "Invalid QR Code";
|
||||
|
@ -392,6 +392,9 @@ public protocol AppLockContext: class {
|
||||
func failedUnlockAttempt()
|
||||
}
|
||||
|
||||
public protocol RecentSessionsController: class {
|
||||
}
|
||||
|
||||
public protocol SharedAccountContext: class {
|
||||
var basePath: String { get }
|
||||
var mainWindow: Window1? { get }
|
||||
@ -455,6 +458,8 @@ public protocol SharedAccountContext: class {
|
||||
func openWallet(context: AccountContext, walletContext: OpenWalletContext, present: @escaping (ViewController) -> Void)
|
||||
func openImagePicker(context: AccountContext, completion: @escaping (UIImage) -> Void, present: @escaping (ViewController) -> Void)
|
||||
|
||||
func makeRecentSessionsController(context: AccountContext, activeSessionsContext: ActiveSessionsContext) -> ViewController & RecentSessionsController
|
||||
|
||||
func navigateToCurrentCall()
|
||||
var hasOngoingCall: ValuePromise<Bool> { get }
|
||||
var immediateHasOngoingCall: Bool { get }
|
||||
|
@ -25,6 +25,7 @@ static_library(
|
||||
"//submodules/AnimationUI:AnimationUI",
|
||||
"//submodules/PresentationDataUtils:PresentationDataUtils",
|
||||
"//submodules/DeviceAccess:DeviceAccess",
|
||||
"//submodules/UndoUI:UndoUI",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -10,6 +10,7 @@ import CoreImage
|
||||
import AlertUI
|
||||
import TelegramPresentationData
|
||||
import TelegramCore
|
||||
import UndoUI
|
||||
|
||||
private func parseAuthTransferUrl(_ url: URL) -> Data? {
|
||||
var tokenString: String?
|
||||
@ -72,7 +73,7 @@ private func generateFrameImage() -> UIImage? {
|
||||
|
||||
public final class AuthTransferScanScreen: ViewController {
|
||||
private let context: AccountContext
|
||||
private let activeSessionsContext: ActiveSessionsContext?
|
||||
private let activeSessionsContext: ActiveSessionsContext
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private var codeDisposable: Disposable?
|
||||
@ -83,7 +84,7 @@ public final class AuthTransferScanScreen: ViewController {
|
||||
return self.displayNode as! AuthTransferScanScreenNode
|
||||
}
|
||||
|
||||
public init(context: AccountContext, activeSessionsContext: ActiveSessionsContext?) {
|
||||
public init(context: AccountContext, activeSessionsContext: ActiveSessionsContext) {
|
||||
self.context = context
|
||||
self.activeSessionsContext = activeSessionsContext
|
||||
|
||||
@ -99,7 +100,7 @@ public final class AuthTransferScanScreen: ViewController {
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
self.navigationBar?.intrinsicCanTransitionInline = false
|
||||
|
||||
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Wallet_Navigation_Back, style: .plain, target: nil, action: nil)
|
||||
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
|
||||
|
||||
self.inForegroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground
|
||||
|> deliverOnMainQueue).start(next: { [weak self] inForeground in
|
||||
@ -108,6 +109,10 @@ public final class AuthTransferScanScreen: ViewController {
|
||||
}
|
||||
(strongSelf.displayNode as! AuthTransferScanScreenNode).updateInForeground(inForeground)
|
||||
})
|
||||
|
||||
#if DEBUG
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Test", style: .plain, target: self, action: #selector(self.testPressed))
|
||||
#endif
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
@ -120,8 +125,34 @@ public final class AuthTransferScanScreen: ViewController {
|
||||
self.approveDisposable.dispose()
|
||||
}
|
||||
|
||||
@objc private func backPressed() {
|
||||
self.dismiss()
|
||||
@objc private func testPressed() {
|
||||
self.dismissWithSuccess(session: nil)
|
||||
}
|
||||
|
||||
private func dismissWithSuccess(session: RecentAccountSession?) {
|
||||
if let navigationController = navigationController as? NavigationController {
|
||||
let activeSessionsContext = self.activeSessionsContext
|
||||
self.present(UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: "Loggin Successful", text: "Telegram for macOS", cancel: "Terminate"), elevatedLayout: false, animateInAsReplacement: false, action: { value in
|
||||
if !value, let session = session {
|
||||
let _ = activeSessionsContext.remove(hash: session.hash).start()
|
||||
}
|
||||
}), in: .window(.root))
|
||||
|
||||
var viewControllers = navigationController.viewControllers
|
||||
viewControllers = viewControllers.filter { controller in
|
||||
if controller is RecentSessionsController {
|
||||
return false
|
||||
}
|
||||
if controller === self {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
viewControllers.append(self.context.sharedContext.makeRecentSessionsController(context: self.context, activeSessionsContext: activeSessionsContext))
|
||||
navigationController.setViewControllers(viewControllers, animated: true)
|
||||
} else {
|
||||
self.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
@ -145,36 +176,24 @@ public final class AuthTransferScanScreen: ViewController {
|
||||
return
|
||||
}
|
||||
if let url = URL(string: code), let parsedToken = parseAuthTransferUrl(url) {
|
||||
let _ = (getAuthTransferTokenInfo(network: strongSelf.context.account.network, token: parsedToken)
|
||||
|> deliverOnMainQueue).start(next: { tokenInfo in
|
||||
strongSelf.approveDisposable.set((approveAuthTransferToken(account: strongSelf.context.account, token: parsedToken, activeSessionsContext: strongSelf.activeSessionsContext)
|
||||
|> deliverOnMainQueue).start(next: { session in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.approveDisposable.set((approveAuthTransferToken(account: strongSelf.context.account, token: parsedToken)
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.controllerNode.codeWithError = code
|
||||
strongSelf.controllerNode.updateFocusedRect(nil)
|
||||
}, completed: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.controllerNode.codeWithError = nil
|
||||
let activeSessionsContext = strongSelf.activeSessionsContext
|
||||
Queue.mainQueue().after(1.5, {
|
||||
activeSessionsContext?.loadMore()
|
||||
})
|
||||
strongSelf.dismiss()
|
||||
}))
|
||||
strongSelf.controllerNode.codeWithError = nil
|
||||
let activeSessionsContext = strongSelf.activeSessionsContext
|
||||
Queue.mainQueue().after(1.5, {
|
||||
activeSessionsContext.loadMore()
|
||||
})
|
||||
strongSelf.dismissWithSuccess(session: session)
|
||||
}, error: { _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.controllerNode.codeWithError = code
|
||||
strongSelf.controllerNode.updateFocusedRect(nil)
|
||||
})
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -447,7 +447,10 @@ private func recentSessionsControllerEntries(presentationData: PresentationData,
|
||||
return entries
|
||||
}
|
||||
|
||||
public func recentSessionsController(context: AccountContext, activeSessionsContext: ActiveSessionsContext, webSessionsContext: WebSessionsContext, websitesOnly: Bool) -> ViewController {
|
||||
private final class RecentSessionsControllerImpl: ItemListController, RecentSessionsController {
|
||||
}
|
||||
|
||||
public func recentSessionsController(context: AccountContext, activeSessionsContext: ActiveSessionsContext, webSessionsContext: WebSessionsContext, websitesOnly: Bool) -> ViewController & RecentSessionsController {
|
||||
let statePromise = ValuePromise(RecentSessionsControllerState(), ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: RecentSessionsControllerState())
|
||||
let updateState: ((RecentSessionsControllerState) -> RecentSessionsControllerState) -> Void = { f in
|
||||
@ -685,7 +688,7 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont
|
||||
actionsDisposable.dispose()
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
let controller = RecentSessionsControllerImpl(context: context, state: signal)
|
||||
controller.titleControlValueChanged = { [weak mode] index in
|
||||
mode?.set(index == 0 ? .sessions : .websites)
|
||||
}
|
||||
|
@ -4167,29 +4167,15 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func acceptLoginToken(token: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
public static func acceptLoginToken(token: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Authorization>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1122447801)
|
||||
buffer.appendInt32(-392909491)
|
||||
serializeBytes(token, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "auth.acceptLoginToken", parameters: [("token", token)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
return (FunctionDescription(name: "auth.acceptLoginToken", parameters: [("token", token)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Authorization? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
var result: Api.Authorization?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func checkLoginToken(token: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.LoginTokenInfo>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(2102383792)
|
||||
serializeBytes(token, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "auth.checkLoginToken", parameters: [("token", token)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.LoginTokenInfo? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.LoginTokenInfo?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.LoginTokenInfo
|
||||
result = Api.parse(reader, signature: signature) as? Api.Authorization
|
||||
}
|
||||
return result
|
||||
})
|
||||
|
@ -126,6 +126,8 @@ struct AccountMutableState {
|
||||
|
||||
var externallyUpdatedPeerId = Set<PeerId>()
|
||||
|
||||
var authorizationListUpdated: Bool = false
|
||||
|
||||
init(initialState: AccountInitialState, initialPeers: [PeerId: Peer], initialReferencedMessageIds: Set<MessageId>, initialStoredMessages: Set<MessageId>, initialReadInboxMaxIds: [PeerId: MessageId], storedMessagesByPeerIdAndTimestamp: [PeerId: Set<MessageIndex>]) {
|
||||
self.initialState = initialState
|
||||
self.state = initialState.state
|
||||
@ -534,12 +536,13 @@ struct AccountFinalStateEvents {
|
||||
let updatedMaxMessageId: Int32?
|
||||
let updatedQts: Int32?
|
||||
let externallyUpdatedPeerId: Set<PeerId>
|
||||
let authorizationListUpdated: Bool
|
||||
|
||||
var isEmpty: Bool {
|
||||
return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty
|
||||
return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated
|
||||
}
|
||||
|
||||
init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set()) {
|
||||
init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], updatedTypingActivities: [PeerId: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false) {
|
||||
self.addedIncomingMessageIds = addedIncomingMessageIds
|
||||
self.wasScheduledMessageIds = wasScheduledMessageIds
|
||||
self.updatedTypingActivities = updatedTypingActivities
|
||||
@ -552,6 +555,7 @@ struct AccountFinalStateEvents {
|
||||
self.updatedMaxMessageId = updatedMaxMessageId
|
||||
self.updatedQts = updatedQts
|
||||
self.externallyUpdatedPeerId = externallyUpdatedPeerId
|
||||
self.authorizationListUpdated = authorizationListUpdated
|
||||
}
|
||||
|
||||
init(state: AccountReplayedFinalState) {
|
||||
@ -567,6 +571,7 @@ struct AccountFinalStateEvents {
|
||||
self.updatedMaxMessageId = state.state.state.updatedMaxMessageId
|
||||
self.updatedQts = state.state.state.updatedQts
|
||||
self.externallyUpdatedPeerId = state.state.state.externallyUpdatedPeerId
|
||||
self.authorizationListUpdated = state.state.state.authorizationListUpdated
|
||||
}
|
||||
|
||||
func union(with other: AccountFinalStateEvents) -> AccountFinalStateEvents {
|
||||
@ -590,7 +595,8 @@ struct AccountFinalStateEvents {
|
||||
}
|
||||
|
||||
let externallyUpdatedPeerId = self.externallyUpdatedPeerId.union(other.externallyUpdatedPeerId)
|
||||
let authorizationListUpdated = self.authorizationListUpdated || other.authorizationListUpdated
|
||||
|
||||
return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId)
|
||||
return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated)
|
||||
}
|
||||
}
|
||||
|
@ -962,6 +962,10 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: expirationTimer, countdownBeginTime: nil))
|
||||
}
|
||||
|
||||
if type.hasPrefix("auth") {
|
||||
updatedState.authorizationListUpdated = true
|
||||
}
|
||||
|
||||
let message = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: messageText, attributes: attributes, media: medias)
|
||||
updatedState.addMessages([message], location: .UpperHistoryBlock)
|
||||
}
|
||||
|
@ -130,6 +130,11 @@ public final class AccountStateManager {
|
||||
return self.significantStateUpdateCompletedPipe.signal()
|
||||
}
|
||||
|
||||
private let authorizationListUpdatesPipe = ValuePipe<Void>()
|
||||
var authorizationListUpdates: Signal<Void, NoError> {
|
||||
return self.authorizationListUpdatesPipe.signal()
|
||||
}
|
||||
|
||||
private var updatedWebpageContexts: [MediaId: UpdatedWebpageSubscriberContext] = [:]
|
||||
private var updatedPeersNearbyContext = UpdatedPeersNearbySubscriberContext()
|
||||
|
||||
@ -702,6 +707,10 @@ public final class AccountStateManager {
|
||||
if !events.externallyUpdatedPeerId.isEmpty {
|
||||
self.externallyUpdatedPeerIdsPipe.putNext(Array(events.externallyUpdatedPeerId))
|
||||
}
|
||||
|
||||
if events.authorizationListUpdated {
|
||||
self.authorizationListUpdatesPipe.putNext(Void())
|
||||
}
|
||||
case let .pollCompletion(pollId, preMessageIds, preSubscribers):
|
||||
if self.operations.count > 1 {
|
||||
self.operations.removeFirst()
|
||||
|
@ -8,7 +8,7 @@ public struct ActiveSessionsContextState: Equatable {
|
||||
public var sessions: [RecentAccountSession]
|
||||
}
|
||||
|
||||
public final class ActiveSessionsContext {
|
||||
private final class ActiveSessionsContextImpl {
|
||||
private let account: Account
|
||||
private var _state: ActiveSessionsContextState {
|
||||
didSet {
|
||||
@ -18,13 +18,14 @@ public final class ActiveSessionsContext {
|
||||
}
|
||||
}
|
||||
private let _statePromise = Promise<ActiveSessionsContextState>()
|
||||
public var state: Signal<ActiveSessionsContextState, NoError> {
|
||||
var state: Signal<ActiveSessionsContextState, NoError> {
|
||||
return self._statePromise.get()
|
||||
}
|
||||
|
||||
private let disposable = MetaDisposable()
|
||||
private var authorizationListUpdatesDisposable: Disposable?
|
||||
|
||||
public init(account: Account) {
|
||||
init(account: Account) {
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
|
||||
self.account = account
|
||||
@ -32,14 +33,20 @@ public final class ActiveSessionsContext {
|
||||
self._statePromise.set(.single(self._state))
|
||||
|
||||
self.loadMore()
|
||||
|
||||
self.authorizationListUpdatesDisposable = (account.stateManager.authorizationListUpdates
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
self?.loadMore()
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
self.disposable.dispose()
|
||||
self.authorizationListUpdatesDisposable?.dispose()
|
||||
}
|
||||
|
||||
public func loadMore() {
|
||||
func loadMore() {
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
|
||||
if self._state.isLoadingMore {
|
||||
@ -59,7 +66,23 @@ public final class ActiveSessionsContext {
|
||||
}))
|
||||
}
|
||||
|
||||
public func remove(hash: Int64) -> Signal<Never, TerminateSessionError> {
|
||||
func addSession(_ session: RecentAccountSession) {
|
||||
var mergedSessions = self._state.sessions
|
||||
var found = false
|
||||
for i in 0 ..< mergedSessions.count {
|
||||
if mergedSessions[i].hash == session.hash {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
mergedSessions.insert(session, at: 0)
|
||||
}
|
||||
|
||||
self._state = ActiveSessionsContextState(isLoadingMore: self._state.isLoadingMore, sessions: mergedSessions)
|
||||
}
|
||||
|
||||
func remove(hash: Int64) -> Signal<Never, TerminateSessionError> {
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
|
||||
return terminateAccountSession(account: self.account, hash: hash)
|
||||
@ -82,7 +105,7 @@ public final class ActiveSessionsContext {
|
||||
}
|
||||
}
|
||||
|
||||
public func removeOther() -> Signal<Never, TerminateSessionError> {
|
||||
func removeOther() -> Signal<Never, TerminateSessionError> {
|
||||
return terminateOtherAccountSessions(account: self.account)
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { [weak self] _ -> Signal<Never, TerminateSessionError> in
|
||||
@ -98,6 +121,68 @@ public final class ActiveSessionsContext {
|
||||
}
|
||||
}
|
||||
|
||||
public final class ActiveSessionsContext {
|
||||
private let impl: QueueLocalObject<ActiveSessionsContextImpl>
|
||||
|
||||
public var state: Signal<ActiveSessionsContextState, NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.state.start(next: { value in
|
||||
subscriber.putNext(value)
|
||||
}))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
public init(account: Account) {
|
||||
self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: {
|
||||
return ActiveSessionsContextImpl(account: account)
|
||||
})
|
||||
}
|
||||
|
||||
public func loadMore() {
|
||||
self.impl.with { impl in
|
||||
impl.loadMore()
|
||||
}
|
||||
}
|
||||
|
||||
func addSession(_ session: RecentAccountSession) {
|
||||
self.impl.with { impl in
|
||||
impl.addSession(session)
|
||||
}
|
||||
}
|
||||
|
||||
public func remove(hash: Int64) -> Signal<Never, TerminateSessionError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.remove(hash: hash).start(error: { error in
|
||||
subscriber.putError(error)
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
}))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
public func removeOther() -> Signal<Never, TerminateSessionError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.removeOther().start(error: { error in
|
||||
subscriber.putError(error)
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
}))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct WebSessionsContextState: Equatable {
|
||||
public var isLoadingMore: Bool
|
||||
public var sessions: [WebAuthorization]
|
||||
|
@ -9,19 +9,6 @@ public struct AuthTransferExportedToken {
|
||||
public let validUntil: Int32
|
||||
}
|
||||
|
||||
public struct AuthTransferTokenInfo {
|
||||
public let datacenterId: Int32
|
||||
public let authKeyId: Int64
|
||||
public let deviceModel: String
|
||||
public let platform: String
|
||||
public let systemVersion: String
|
||||
public let apiId: Int32
|
||||
public let appName: String
|
||||
public let appVersion: String
|
||||
public let ip: String
|
||||
public let region: String
|
||||
}
|
||||
|
||||
public enum ExportAuthTransferTokenError {
|
||||
case generic
|
||||
case limitExceeded
|
||||
@ -152,16 +139,16 @@ public func exportAuthTransferToken(accountManager: AccountManager, account: Una
|
||||
}
|
||||
}
|
||||
|
||||
public enum GetAuthTransferTokenInfoError {
|
||||
public enum ApproveAuthTransferTokenError {
|
||||
case generic
|
||||
case invalid
|
||||
case expired
|
||||
case alreadyAccepted
|
||||
}
|
||||
|
||||
public func getAuthTransferTokenInfo(network: Network, token: Data) -> Signal<AuthTransferTokenInfo, GetAuthTransferTokenInfoError> {
|
||||
return network.request(Api.functions.auth.checkLoginToken(token: Buffer(data: token)))
|
||||
|> mapError { error -> GetAuthTransferTokenInfoError in
|
||||
public func approveAuthTransferToken(account: Account, token: Data, activeSessionsContext: ActiveSessionsContext) -> Signal<RecentAccountSession, ApproveAuthTransferTokenError> {
|
||||
return account.network.request(Api.functions.auth.acceptLoginToken(token: Buffer(data: token)))
|
||||
|> mapError { error -> ApproveAuthTransferTokenError in
|
||||
switch error.errorDescription {
|
||||
case "AUTH_TOKEN_INVALID":
|
||||
return .invalid
|
||||
@ -173,25 +160,9 @@ public func getAuthTransferTokenInfo(network: Network, token: Data) -> Signal<Au
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
|> map { result -> AuthTransferTokenInfo in
|
||||
switch result {
|
||||
case let .loginTokenInfo(dcId, authKeyId, deviceModel, platform, systemVersion, apiId, appName, appVersion, ip, region):
|
||||
return AuthTransferTokenInfo(datacenterId: dcId, authKeyId: authKeyId, deviceModel: deviceModel, platform: platform, systemVersion: systemVersion, apiId: apiId, appName: appName, appVersion: appVersion, ip: ip, region: region)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum ApproveAuthTransferTokenError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func approveAuthTransferToken(account: Account, token: Data) -> Signal<Never, ApproveAuthTransferTokenError> {
|
||||
return account.network.request(Api.functions.auth.acceptLoginToken(token: Buffer(data: token)))
|
||||
|> mapError { _ -> ApproveAuthTransferTokenError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { updates -> Signal<Never, ApproveAuthTransferTokenError> in
|
||||
account.stateManager.addUpdates(updates)
|
||||
return .complete()
|
||||
|> mapToSignal { authorization -> Signal<RecentAccountSession, ApproveAuthTransferTokenError> in
|
||||
let session = RecentAccountSession(apiAuthorization: authorization)
|
||||
activeSessionsContext.addSession(session)
|
||||
return .single(session)
|
||||
}
|
||||
}
|
||||
|
@ -1150,6 +1150,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
present(legacyController)
|
||||
})
|
||||
}
|
||||
|
||||
public func makeRecentSessionsController(context: AccountContext, activeSessionsContext: ActiveSessionsContext) -> ViewController & RecentSessionsController {
|
||||
return recentSessionsController(context: context, activeSessionsContext: activeSessionsContext, webSessionsContext: WebSessionsContext(account: context.account), websitesOnly: false)
|
||||
}
|
||||
}
|
||||
|
||||
private let defaultChatControllerInteraction = ChatControllerInteraction.default
|
||||
|
@ -11,6 +11,7 @@ public enum UndoOverlayContent {
|
||||
case succeed(text: String)
|
||||
case emoji(path: String, text: String)
|
||||
case swipeToReply(title: String, text: String)
|
||||
case actionSucceeded(title: String, text: String, cancel: String)
|
||||
}
|
||||
|
||||
public final class UndoOverlayController: ViewController {
|
||||
|
@ -56,6 +56,8 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
|
||||
var displayUndo = true
|
||||
var undoText = presentationData.strings.Undo_Undo
|
||||
var undoTextColor = UIColor(rgb: 0x5ac8fa)
|
||||
|
||||
if presentationData.theme.overallDarkAppearance {
|
||||
self.animationBackgroundColor = presentationData.theme.rootController.tabBar.backgroundColor
|
||||
@ -122,6 +124,18 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.textNode.maximumNumberOfLines = 2
|
||||
displayUndo = false
|
||||
self.originalRemainingSeconds = 5
|
||||
case let .actionSucceeded(title, text, cancel):
|
||||
self.iconNode = nil
|
||||
self.iconCheckNode = nil
|
||||
self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
|
||||
self.animatedStickerNode = nil
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
|
||||
displayUndo = true
|
||||
undoText = cancel
|
||||
undoTextColor = UIColor(rgb: 0xff7b74)
|
||||
self.originalRemainingSeconds = 3
|
||||
case let .emoji(path, text):
|
||||
self.iconNode = nil
|
||||
self.iconCheckNode = nil
|
||||
@ -155,7 +169,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
self.buttonTextNode = ImmediateTextNode()
|
||||
self.buttonTextNode.displaysAsynchronously = false
|
||||
self.buttonTextNode.attributedText = NSAttributedString(string: presentationData.strings.Undo_Undo, font: Font.regular(17.0), textColor: UIColor(rgb: 0x5ac8fa))
|
||||
self.buttonTextNode.attributedText = NSAttributedString(string: undoText, font: Font.regular(17.0), textColor: undoTextColor)
|
||||
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
|
||||
@ -175,11 +189,11 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
super.init()
|
||||
|
||||
switch content {
|
||||
case .removedChat:
|
||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||
self.panelWrapperNode.addSubnode(self.statusNode)
|
||||
case .archivedChat, .hidArchive, .revealedArchive, .succeed, .emoji, .swipeToReply:
|
||||
break
|
||||
case .removedChat:
|
||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||
self.panelWrapperNode.addSubnode(self.statusNode)
|
||||
case .archivedChat, .hidArchive, .revealedArchive, .succeed, .emoji, .swipeToReply, .actionSucceeded:
|
||||
break
|
||||
}
|
||||
self.iconNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||
self.iconCheckNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||
|
Loading…
x
Reference in New Issue
Block a user