Improve recent sessions UI

This commit is contained in:
Ali 2019-12-17 17:22:31 +04:00
parent b3285652c1
commit 76659a0603
14 changed files with 208 additions and 100 deletions

View File

@ -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";

View File

@ -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 }

View File

@ -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",

View File

@ -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)
})
}))
}
})
}

View File

@ -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)
}

View File

@ -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
})

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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()

View File

@ -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]

View File

@ -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)
}
}

View File

@ -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

View File

@ -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 {

View File

@ -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)