Various improvements

This commit is contained in:
Ali 2023-01-10 21:52:58 +04:00
parent 7b5a45f326
commit a7fd29fe10
25 changed files with 363 additions and 118 deletions

View File

@ -791,6 +791,7 @@ public protocol SharedAccountContext: AnyObject {
func makeCreateGroupController(context: AccountContext, peerIds: [PeerId], initialTitle: String?, mode: CreateGroupMode, completion: ((PeerId, @escaping () -> Void) -> Void)?) -> ViewController func makeCreateGroupController(context: AccountContext, peerIds: [PeerId], initialTitle: String?, mode: CreateGroupMode, completion: ((PeerId, @escaping () -> Void) -> Void)?) -> ViewController
func makeChatRecentActionsController(context: AccountContext, peer: Peer, adminPeerId: PeerId?) -> ViewController func makeChatRecentActionsController(context: AccountContext, peer: Peer, adminPeerId: PeerId?) -> ViewController
func makePrivacyAndSecurityController(context: AccountContext) -> ViewController func makePrivacyAndSecurityController(context: AccountContext) -> ViewController
func makeSetupTwoFactorAuthController(context: AccountContext) -> ViewController
func makeStorageManagementController(context: AccountContext) -> ViewController func makeStorageManagementController(context: AccountContext) -> ViewController
func navigateToChatController(_ params: NavigateToChatControllerParams) func navigateToChatController(_ params: NavigateToChatControllerParams)
func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController) func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController)

View File

@ -185,7 +185,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}) }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {})
interaction.isInlineMode = isInlineMode interaction.isInlineMode = isInlineMode
let items = (0 ..< 2).map { _ -> ChatListItem in let items = (0 ..< 2).map { _ -> ChatListItem in

View File

@ -2005,6 +2005,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
self.listNode.clearHighlightAnimated(true) self.listNode.clearHighlightAnimated(true)
}) })
}, openStorageManagement: { }, openStorageManagement: {
}, openPasswordSetup: {
}) })
chatListInteraction.isSearchMode = true chatListInteraction.isSearchMode = true
@ -3206,7 +3207,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}) }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {})
var isInlineMode = false var isInlineMode = false
if case .topics = key { if case .topics = key {
isInlineMode = false isInlineMode = false

View File

@ -92,6 +92,7 @@ public final class ChatListNodeInteraction {
let present: (ViewController) -> Void let present: (ViewController) -> Void
let openForumThread: (EnginePeer.Id, Int64) -> Void let openForumThread: (EnginePeer.Id, Int64) -> Void
let openStorageManagement: () -> Void let openStorageManagement: () -> Void
let openPasswordSetup: () -> Void
public var searchTextHighightState: String? public var searchTextHighightState: String?
var highlightedChatLocation: ChatListHighlightedLocation? var highlightedChatLocation: ChatListHighlightedLocation?
@ -134,7 +135,8 @@ public final class ChatListNodeInteraction {
activateChatPreview: @escaping (ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, activateChatPreview: @escaping (ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void,
present: @escaping (ViewController) -> Void, present: @escaping (ViewController) -> Void,
openForumThread: @escaping (EnginePeer.Id, Int64) -> Void, openForumThread: @escaping (EnginePeer.Id, Int64) -> Void,
openStorageManagement: @escaping () -> Void openStorageManagement: @escaping () -> Void,
openPasswordSetup: @escaping () -> Void
) { ) {
self.activateSearch = activateSearch self.activateSearch = activateSearch
self.peerSelected = peerSelected self.peerSelected = peerSelected
@ -165,6 +167,7 @@ public final class ChatListNodeInteraction {
self.animationRenderer = animationRenderer self.animationRenderer = animationRenderer
self.openForumThread = openForumThread self.openForumThread = openForumThread
self.openStorageManagement = openStorageManagement self.openStorageManagement = openStorageManagement
self.openPasswordSetup = openPasswordSetup
} }
} }
@ -570,9 +573,14 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
), directionHint: entry.directionHint) ), directionHint: entry.directionHint)
case let .ArchiveIntro(presentationData): case let .ArchiveIntro(presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
case let .StorageInfo(presentationData, sizeFraction): case let .Notice(presentationData, notice):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, sizeFraction: sizeFraction, action: { [weak nodeInteraction] in return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, notice: notice, action: { [weak nodeInteraction] in
nodeInteraction?.openStorageManagement() switch notice {
case .clearStorage:
nodeInteraction?.openStorageManagement()
case .setupPassword:
nodeInteraction?.openPasswordSetup()
}
}), directionHint: entry.directionHint) }), directionHint: entry.directionHint)
} }
} }
@ -785,9 +793,14 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
), directionHint: entry.directionHint) ), directionHint: entry.directionHint)
case let .ArchiveIntro(presentationData): case let .ArchiveIntro(presentationData):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
case let .StorageInfo(presentationData, sizeFraction): case let .Notice(presentationData, notice):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, sizeFraction: sizeFraction, action: { [weak nodeInteraction] in return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, notice: notice, action: { [weak nodeInteraction] in
nodeInteraction?.openStorageManagement() switch notice {
case .clearStorage:
nodeInteraction?.openStorageManagement()
case .setupPassword:
nodeInteraction?.openPasswordSetup()
}
}), directionHint: entry.directionHint) }), directionHint: entry.directionHint)
case .HeaderEntry: case .HeaderEntry:
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyHeaderItem(), directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyHeaderItem(), directionHint: entry.directionHint)
@ -1277,6 +1290,20 @@ public final class ChatListNode: ListView {
} }
let controller = self.context.sharedContext.makeStorageManagementController(context: self.context) let controller = self.context.sharedContext.makeStorageManagementController(context: self.context)
self.push?(controller) self.push?(controller)
}, openPasswordSetup: { [weak self] in
guard let self else {
return
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.6, execute: { [weak self] in
guard let self else {
return
}
let _ = dismissServerProvidedSuggestion(account: self.context.account, suggestion: .setupPassword).start()
})
let controller = self.context.sharedContext.makeSetupTwoFactorAuthController(context: self.context)
self.push?(controller)
}) })
nodeInteraction.isInlineMode = isInlineMode nodeInteraction.isInlineMode = isInlineMode
@ -1342,6 +1369,32 @@ public final class ChatListNode: ListView {
displayArchiveIntro = .single(false) displayArchiveIntro = .single(false)
} }
let suggestPasswordSetup: Signal<Bool, NoError>
if case .chatList(groupId: .root) = location, chatListFilter == nil {
suggestPasswordSetup = combineLatest(
getServerProvidedSuggestions(account: context.account),
context.engine.auth.twoStepVerificationConfiguration()
)
|> map { suggestions, configuration -> Bool in
var notSet = false
switch configuration {
case let .notSet(pendingEmail):
if pendingEmail == nil {
notSet = true
}
case .set:
break
}
if !notSet {
return false
}
return suggestions.contains(.setupPassword)
}
|> distinctUntilChanged
} else {
suggestPasswordSetup = .single(false)
}
let storageInfo: Signal<Double?, NoError> let storageInfo: Signal<Double?, NoError>
if !"".isEmpty, case .chatList(groupId: .root) = location, chatListFilter == nil { if !"".isEmpty, case .chatList(groupId: .root) = location, chatListFilter == nil {
let totalSizeSignal = combineLatest(context.account.postbox.mediaBox.storageBox.totalSize(), context.account.postbox.mediaBox.cacheStorageBox.totalSize()) let totalSizeSignal = combineLatest(context.account.postbox.mediaBox.storageBox.totalSize(), context.account.postbox.mediaBox.cacheStorageBox.totalSize())
@ -1431,13 +1484,13 @@ public final class ChatListNode: ListView {
let currentPeerId: EnginePeer.Id = context.account.peerId let currentPeerId: EnginePeer.Id = context.account.peerId
let chatListNodeViewTransition = combineLatest(queue: viewProcessingQueue, hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, savedMessagesPeer, chatListViewUpdate, self.statePromise.get()) let chatListNodeViewTransition = combineLatest(queue: viewProcessingQueue, hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, suggestPasswordSetup, savedMessagesPeer, chatListViewUpdate, self.statePromise.get())
|> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, savedMessagesPeer, updateAndFilter, state) -> Signal<ChatListNodeListViewTransition, NoError> in |> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, suggestPasswordSetup, savedMessagesPeer, updateAndFilter, state) -> Signal<ChatListNodeListViewTransition, NoError> in
let (update, filter) = updateAndFilter let (update, filter) = updateAndFilter
let previousHideArchivedFolderByDefaultValue = previousHideArchivedFolderByDefault.swap(hideArchivedFolderByDefault) let previousHideArchivedFolderByDefaultValue = previousHideArchivedFolderByDefault.swap(hideArchivedFolderByDefault)
let (rawEntries, isLoading) = chatListNodeEntriesForView(update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, storageInfo: storageInfo, mode: mode, chatListLocation: location) let (rawEntries, isLoading) = chatListNodeEntriesForView(update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, storageInfo: storageInfo, suggestPasswordSetup: suggestPasswordSetup, mode: mode, chatListLocation: location)
let entries = rawEntries.filter { entry in let entries = rawEntries.filter { entry in
switch entry { switch entry {
case let .PeerEntry(peerEntry): case let .PeerEntry(peerEntry):
@ -2467,7 +2520,7 @@ public final class ChatListNode: ListView {
} else { } else {
break loop break loop
} }
case .ArchiveIntro, .StorageInfo, .HeaderEntry, .AdditionalCategory: case .ArchiveIntro, .Notice, .HeaderEntry, .AdditionalCategory:
break break
} }
} }

View File

@ -13,7 +13,7 @@ enum ChatListNodeEntryId: Hashable {
case ThreadId(Int64) case ThreadId(Int64)
case GroupId(EngineChatList.Group) case GroupId(EngineChatList.Group)
case ArchiveIntro case ArchiveIntro
case StorageInfo case Notice
case additionalCategory(Int) case additionalCategory(Int)
} }
@ -46,6 +46,11 @@ public enum ChatListNodeEntryPromoInfo: Equatable {
case psa(type: String, message: String?) case psa(type: String, message: String?)
} }
enum ChatListNotice: Equatable {
case clearStorage(sizeFraction: Double)
case setupPassword
}
enum ChatListNodeEntry: Comparable, Identifiable { enum ChatListNodeEntry: Comparable, Identifiable {
struct PeerEntryData: Equatable { struct PeerEntryData: Equatable {
var index: EngineChatList.Item.Index var index: EngineChatList.Item.Index
@ -235,7 +240,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
case HoleEntry(EngineMessage.Index, theme: PresentationTheme) case HoleEntry(EngineMessage.Index, theme: PresentationTheme)
case GroupReferenceEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, editing: Bool, unreadCount: Int, revealed: Bool, hiddenByDefault: Bool) case GroupReferenceEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, editing: Bool, unreadCount: Int, revealed: Bool, hiddenByDefault: Bool)
case ArchiveIntro(presentationData: ChatListPresentationData) case ArchiveIntro(presentationData: ChatListPresentationData)
case StorageInfo(presentationData: ChatListPresentationData, sizeFraction: Double) case Notice(presentationData: ChatListPresentationData, notice: ChatListNotice)
case AdditionalCategory(index: Int, id: Int, title: String, image: UIImage?, appearance: ChatListNodeAdditionalCategory.Appearance, selected: Bool, presentationData: ChatListPresentationData) case AdditionalCategory(index: Int, id: Int, title: String, image: UIImage?, appearance: ChatListNodeAdditionalCategory.Appearance, selected: Bool, presentationData: ChatListPresentationData)
var sortIndex: ChatListNodeEntrySortIndex { var sortIndex: ChatListNodeEntrySortIndex {
@ -250,7 +255,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
return .index(index) return .index(index)
case .ArchiveIntro: case .ArchiveIntro:
return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor)) return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor))
case .StorageInfo: case .Notice:
return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor.successor)) return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor.successor))
case let .AdditionalCategory(index, _, _, _, _, _, _): case let .AdditionalCategory(index, _, _, _, _, _, _):
return .additionalCategory(index) return .additionalCategory(index)
@ -274,8 +279,8 @@ enum ChatListNodeEntry: Comparable, Identifiable {
return .GroupId(groupId) return .GroupId(groupId)
case .ArchiveIntro: case .ArchiveIntro:
return .ArchiveIntro return .ArchiveIntro
case .StorageInfo: case .Notice:
return .StorageInfo return .Notice
case let .AdditionalCategory(_, id, _, _, _, _, _): case let .AdditionalCategory(_, id, _, _, _, _, _):
return .additionalCategory(id) return .additionalCategory(id)
} }
@ -348,8 +353,8 @@ enum ChatListNodeEntry: Comparable, Identifiable {
} else { } else {
return false return false
} }
case let .StorageInfo(lhsPresentationData, lhsInfo): case let .Notice(lhsPresentationData, lhsInfo):
if case let .StorageInfo(rhsPresentationData, rhsInfo) = rhs { if case let .Notice(rhsPresentationData, rhsInfo) = rhs {
if lhsPresentationData !== rhsPresentationData { if lhsPresentationData !== rhsPresentationData {
return false return false
} }
@ -399,7 +404,7 @@ private func offsetPinnedIndex(_ index: EngineChatList.Item.Index, offset: UInt1
} }
} }
func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, storageInfo: Double?, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation) -> (entries: [ChatListNodeEntry], loading: Bool) { func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, storageInfo: Double?, suggestPasswordSetup: Bool, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation) -> (entries: [ChatListNodeEntry], loading: Bool) {
var result: [ChatListNodeEntry] = [] var result: [ChatListNodeEntry] = []
var pinnedIndexOffset: UInt16 = 0 var pinnedIndexOffset: UInt16 = 0
@ -661,8 +666,10 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
if displayArchiveIntro { if displayArchiveIntro {
result.append(.ArchiveIntro(presentationData: state.presentationData)) result.append(.ArchiveIntro(presentationData: state.presentationData))
} }
if let storageInfo { if suggestPasswordSetup {
result.append(.StorageInfo(presentationData: state.presentationData, sizeFraction: storageInfo)) result.append(.Notice(presentationData: state.presentationData, notice: .setupPassword))
} else if let storageInfo {
result.append(.Notice(presentationData: state.presentationData, notice: .clearStorage(sizeFraction: storageInfo)))
} }
result.append(.HeaderEntry) result.append(.HeaderEntry)

View File

@ -11,15 +11,15 @@ import AppBundle
class ChatListStorageInfoItem: ListViewItem { class ChatListStorageInfoItem: ListViewItem {
let theme: PresentationTheme let theme: PresentationTheme
let strings: PresentationStrings let strings: PresentationStrings
let sizeFraction: Double let notice: ChatListNotice
let action: () -> Void let action: () -> Void
let selectable: Bool = true let selectable: Bool = true
init(theme: PresentationTheme, strings: PresentationStrings, sizeFraction: Double, action: @escaping () -> Void) { init(theme: PresentationTheme, strings: PresentationStrings, notice: ChatListNotice, action: @escaping () -> Void) {
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
self.sizeFraction = sizeFraction self.notice = notice
self.action = action self.action = action
} }
@ -117,23 +117,37 @@ class ChatListStorageInfoItemNode: ListViewItemNode {
let _ = baseWidth let _ = baseWidth
let sideInset: CGFloat = params.leftInset + 16.0 let sideInset: CGFloat = params.leftInset + 16.0
let height: CGFloat = 54.0
let rightInset: CGFloat = sideInset + 24.0 let rightInset: CGFloat = sideInset + 24.0
let verticalInset: CGFloat = 8.0
let spacing: CGFloat = 0.0
let themeUpdated = item.theme !== previousItem?.theme let themeUpdated = item.theme !== previousItem?.theme
let sizeString = dataSizeString(Int64(item.sizeFraction), formatting: DataSizeStringFormatting(strings: item.strings, decimalSeparator: ".")) let titleString: NSAttributedString
let rawTitleString = item.strings.ChatList_StorageHintTitle(sizeString) let textString: NSAttributedString
let titleString = NSMutableAttributedString(attributedString: NSAttributedString(string: rawTitleString.string, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor))
if let range = rawTitleString.ranges.first { switch item.notice {
titleString.addAttribute(.foregroundColor, value: item.theme.rootController.navigationBar.accentTextColor, range: range.range) case let .clearStorage(sizeFraction):
let sizeString = dataSizeString(Int64(sizeFraction), formatting: DataSizeStringFormatting(strings: item.strings, decimalSeparator: "."))
let rawTitleString = item.strings.ChatList_StorageHintTitle(sizeString)
let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: rawTitleString.string, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor))
if let range = rawTitleString.ranges.first {
titleStringValue.addAttribute(.foregroundColor, value: item.theme.rootController.navigationBar.accentTextColor, range: range.range)
}
titleString = titleStringValue
textString = NSAttributedString(string: item.strings.ChatList_StorageHintText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
case .setupPassword:
//TODO:localize
titleString = NSAttributedString(string: "Protect Your Account", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)
textString = NSAttributedString(string: "Set a password that will be required each time you log in with this phone number.", font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
} }
let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0))) let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))
let textLayout = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.ChatList_StorageHintText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0))) let textLayout = makeTextLayout(TextNodeLayoutArguments(attributedString: textString, maximumNumberOfLines: 5, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: height), insets: UIEdgeInsets()) let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: verticalInset * 2.0 + titleLayout.0.size.height + textLayout.0.size.height), insets: UIEdgeInsets())
return (layout, { [weak self] in return (layout, { [weak self] in
if let strongSelf = self { if let strongSelf = self {
@ -148,10 +162,10 @@ class ChatListStorageInfoItemNode: ListViewItemNode {
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - UIScreenPixel), size: CGSize(width: layout.size.width, height: UIScreenPixel)) strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - UIScreenPixel), size: CGSize(width: layout.size.width, height: UIScreenPixel))
let _ = titleLayout.1() let _ = titleLayout.1()
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: sideInset, y: 9.0), size: titleLayout.0.size) strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: sideInset, y: verticalInset), size: titleLayout.0.size)
let _ = textLayout.1() let _ = textLayout.1()
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: sideInset, y: strongSelf.titleNode.frame.maxY - 0.0), size: textLayout.0.size) strongSelf.textNode.frame = CGRect(origin: CGPoint(x: sideInset, y: strongSelf.titleNode.frame.maxY + spacing), size: textLayout.0.size)
if let image = strongSelf.arrowNode.image { if let image = strongSelf.arrowNode.image {
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: layout.size.width - sideInset - image.size.width + 8.0, y: floor((layout.size.height - image.size.height) / 2.0)), size: image.size) strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: layout.size.width - sideInset - image.size.width + 8.0, y: floor((layout.size.height - image.size.height) / 2.0)), size: image.size)

View File

@ -123,7 +123,7 @@ public final class MultilineTextComponent: Component {
attributedString = parseMarkdownIntoAttributedString(text, attributes: attributes) attributedString = parseMarkdownIntoAttributedString(text, attributes: attributes)
} }
let previousText = self.attributedText?.string //let previousText = self.attributedText?.string
self.attributedText = attributedString self.attributedText = attributedString
self.maximumNumberOfLines = component.maximumNumberOfLines self.maximumNumberOfLines = component.maximumNumberOfLines
@ -140,7 +140,7 @@ public final class MultilineTextComponent: Component {
self.tapAttributeAction = component.tapAction self.tapAttributeAction = component.tapAction
self.longTapAttributeAction = component.longTapAction self.longTapAttributeAction = component.longTapAction
if case let .curve(duration, _) = transition.animation, let previousText = previousText, previousText != attributedString.string { /*if case let .curve(duration, _) = transition.animation, let previousText = previousText, previousText != attributedString.string {
if let snapshotView = self.snapshotView(afterScreenUpdates: false) { if let snapshotView = self.snapshotView(afterScreenUpdates: false) {
snapshotView.center = self.center snapshotView.center = self.center
self.superview?.addSubview(snapshotView) self.superview?.addSubview(snapshotView)
@ -150,7 +150,7 @@ public final class MultilineTextComponent: Component {
}) })
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
} }
} }*/
let size = self.updateLayout(availableSize) let size = self.updateLayout(availableSize)

View File

@ -92,6 +92,7 @@ public final class HashtagSearchController: TelegramBaseController {
}, present: { _ in }, present: { _ in
}, openForumThread: { _, _ in }, openForumThread: { _, _ in
}, openStorageManagement: { }, openStorageManagement: {
}, openPasswordSetup: {
}) })
let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil) let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil)

View File

@ -660,11 +660,18 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
return storageUsageExceptionsScreen(context: context, category: category) return storageUsageExceptionsScreen(context: context, category: category)
})) }))
}, openNetworkUsage: { }, openNetworkUsage: {
//pushControllerImpl?(networkUsageStatsController(context: context))
let _ = (accountNetworkUsageStats(account: context.account, reset: []) let _ = (accountNetworkUsageStats(account: context.account, reset: [])
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { stats in |> deliverOnMainQueue).start(next: { stats in
var stats = stats
if stats.resetWifiTimestamp == 0 {
var value = stat()
if stat(context.account.basePath, &value) == 0 {
stats.resetWifiTimestamp = Int32(value.st_ctimespec.tv_sec)
}
}
pushControllerImpl?(DataUsageScreen(context: context, stats: stats)) pushControllerImpl?(DataUsageScreen(context: context, stats: stats))
}) })
}, openProxy: { }, openProxy: {

View File

@ -5,6 +5,7 @@ import Display
import Postbox import Postbox
import TelegramCore import TelegramCore
import AccountContext import AccountContext
import PasswordSetupUI
public protocol SettingsController: AnyObject { public protocol SettingsController: AnyObject {
func updateContext(context: AccountContext) func updateContext(context: AccountContext)
@ -13,3 +14,14 @@ public protocol SettingsController: AnyObject {
public func makePrivacyAndSecurityController(context: AccountContext) -> ViewController { public func makePrivacyAndSecurityController(context: AccountContext) -> ViewController {
return privacyAndSecurityController(context: context, focusOnItemTag: PrivacyAndSecurityEntryTag.autoArchive) return privacyAndSecurityController(context: context, focusOnItemTag: PrivacyAndSecurityEntryTag.autoArchive)
} }
public func makeSetupTwoFactorAuthController(context: AccountContext) -> ViewController {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = TwoFactorAuthSplashScreen(sharedContext: context.sharedContext, engine: .authorized(context.engine), mode: .intro(.init(
title: presentationData.strings.TwoFactorSetup_Intro_Title,
text: presentationData.strings.TwoFactorSetup_Intro_Text,
actionText: presentationData.strings.TwoFactorSetup_Intro_Action,
doneText: presentationData.strings.TwoFactorSetup_Done_Action
)))
return controller
}

View File

@ -222,7 +222,8 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in
}, activateChatPreview: { _, _, _, gesture, _ in }, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}) }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {
})
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)

View File

@ -844,7 +844,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
gesture?.cancel() gesture?.cancel()
}, present: { _ in }, present: { _ in
}, openForumThread: { _, _ in }, }, openForumThread: { _, _ in },
openStorageManagement: {}) openStorageManagement: {}, openPasswordSetup: {
})
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
func makeChatListItem( func makeChatListItem(

View File

@ -367,7 +367,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
}, activateChatPreview: { _, _, _, gesture, _ in }, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel() gesture?.cancel()
}, present: { _ in }, present: { _ in
}, openForumThread: { _, _ in }, openStorageManagement: {}) }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {
})
func makeChatListItem( func makeChatListItem(
peer: EnginePeer, peer: EnginePeer,

View File

@ -234,21 +234,17 @@ public struct NetworkUsageStatsConnectionsEntry: Equatable {
} }
public struct NetworkUsageStats: Equatable { public struct NetworkUsageStats: Equatable {
public let generic: NetworkUsageStatsConnectionsEntry public var generic: NetworkUsageStatsConnectionsEntry
public let image: NetworkUsageStatsConnectionsEntry public var image: NetworkUsageStatsConnectionsEntry
public let video: NetworkUsageStatsConnectionsEntry public var video: NetworkUsageStatsConnectionsEntry
public let audio: NetworkUsageStatsConnectionsEntry public var audio: NetworkUsageStatsConnectionsEntry
public let file: NetworkUsageStatsConnectionsEntry public var file: NetworkUsageStatsConnectionsEntry
public let call: NetworkUsageStatsConnectionsEntry public var call: NetworkUsageStatsConnectionsEntry
public let sticker: NetworkUsageStatsConnectionsEntry public var sticker: NetworkUsageStatsConnectionsEntry
public let voiceMessage: NetworkUsageStatsConnectionsEntry public var voiceMessage: NetworkUsageStatsConnectionsEntry
public let resetWifiTimestamp: Int32 public var resetWifiTimestamp: Int32
public let resetCellularTimestamp: Int32 public var resetCellularTimestamp: Int32
public static func ==(lhs: NetworkUsageStats, rhs: NetworkUsageStats) -> Bool {
return lhs.generic == rhs.generic && lhs.image == rhs.image && lhs.video == rhs.video && lhs.audio == rhs.audio && lhs.file == rhs.file && lhs.call == rhs.call && lhs.resetWifiTimestamp == rhs.resetWifiTimestamp && lhs.resetCellularTimestamp == rhs.resetCellularTimestamp && lhs.sticker == rhs.sticker && lhs.voiceMessage == rhs.voiceMessage
}
} }
public struct ResetNetworkUsageStats: OptionSet { public struct ResetNetworkUsageStats: OptionSet {

View File

@ -8,6 +8,7 @@ public enum ServerProvidedSuggestion: String {
case newcomerTicks = "NEWCOMER_TICKS" case newcomerTicks = "NEWCOMER_TICKS"
case validatePhoneNumber = "VALIDATE_PHONE_NUMBER" case validatePhoneNumber = "VALIDATE_PHONE_NUMBER"
case validatePassword = "VALIDATE_PASSWORD" case validatePassword = "VALIDATE_PASSWORD"
case setupPassword = "SETUP_2FA"
} }
private var dismissedSuggestionsPromise = ValuePromise<[AccountRecordId: Set<ServerProvidedSuggestion>]>([:]) private var dismissedSuggestionsPromise = ValuePromise<[AccountRecordId: Set<ServerProvidedSuggestion>]>([:])
@ -28,9 +29,17 @@ public func getServerProvidedSuggestions(account: Account) -> Signal<[ServerProv
guard let appConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) else { guard let appConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) else {
return [] return []
} }
guard let data = appConfiguration.data, let list = data["pending_suggestions"] as? [String] else { guard let data = appConfiguration.data, let listItems = data["pending_suggestions"] as? [String] else {
return [] return []
} }
#if DEBUG
var list = listItems
list.append(ServerProvidedSuggestion.setupPassword.rawValue)
#else
let list = listItems
#endif
return list.compactMap { item -> ServerProvidedSuggestion? in return list.compactMap { item -> ServerProvidedSuggestion? in
return ServerProvidedSuggestion(rawValue: item) return ServerProvidedSuggestion(rawValue: item)
}.filter { !dismissedSuggestions.contains($0) } }.filter { !dismissedSuggestions.contains($0) }

View File

@ -175,6 +175,7 @@ final class DataCategoriesComponent: Component {
} }
self.backgroundColor = component.theme.list.itemBlocksBackgroundColor self.backgroundColor = component.theme.list.itemBlocksBackgroundColor
self.containerView.backgroundColor = component.theme.list.itemBlocksBackgroundColor
self.containerView.frame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: contentHeight)) self.containerView.frame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: contentHeight))

View File

@ -168,7 +168,7 @@ final class DataUsageScreenComponent: Component {
init(stats: NetworkUsageStats) { init(stats: NetworkUsageStats) {
self.wifi = Stats(stats: stats, isWifi: true) self.wifi = Stats(stats: stats, isWifi: true)
self.cellular = Stats(stats: stats, isWifi: false) self.cellular = Stats(stats: stats, isWifi: false)
self.resetTimestamp = stats.resetWifiTimestamp self.resetTimestamp = max(stats.resetWifiTimestamp, stats.resetCellularTimestamp)
} }
} }
@ -293,7 +293,8 @@ final class DataUsageScreenComponent: Component {
private let headerOffsetContainer: UIView private let headerOffsetContainer: UIView
private let headerDescriptionView = ComponentView<Empty>() private let headerDescriptionView = ComponentView<Empty>()
private var doneStatusNode: RadialStatusNode? private var doneLabel: ComponentView<Empty>?
private var doneSupLabel: ComponentView<Empty>?
private let scrollContainerView: UIView private let scrollContainerView: UIView
@ -566,8 +567,17 @@ final class DataUsageScreenComponent: Component {
chartItems.append(PieChartComponent.ChartData.Item(id: AnyHashable(listCategory.key), displayValue: listCategory.sizeFraction, displaySize: listCategory.size, value: categoryChartFraction, color: listCategory.color, particle: nil, title: listCategory.key.title(strings: environment.strings), mergeable: false, mergeFactor: 1.0)) chartItems.append(PieChartComponent.ChartData.Item(id: AnyHashable(listCategory.key), displayValue: listCategory.sizeFraction, displaySize: listCategory.size, value: categoryChartFraction, color: listCategory.color, particle: nil, title: listCategory.key.title(strings: environment.strings), mergeable: false, mergeFactor: 1.0))
} }
var emptyValue: CGFloat = 0.0
if totalSize == 0 { if totalSize == 0 {
for i in 0 ..< chartItems.count {
chartItems[i].value = 0.0
}
emptyValue = 1.0
}
if let allStats = self.allStats, allStats.wifi.isEmpty && allStats.cellular.isEmpty {
chartItems.removeAll() chartItems.removeAll()
} else {
chartItems.append(PieChartComponent.ChartData.Item(id: "empty", displayValue: 0.0, displaySize: 0, value: emptyValue, color: UIColor(rgb: 0xC4C4C6), particle: nil, title: "", mergeable: false, mergeFactor: 1.0))
} }
let totalCategories: [DataCategoriesComponent.CategoryData] = [ let totalCategories: [DataCategoriesComponent.CategoryData] = [
@ -611,6 +621,7 @@ final class DataUsageScreenComponent: Component {
component: AnyComponent(PieChartComponent( component: AnyComponent(PieChartComponent(
theme: environment.theme, theme: environment.theme,
strings: environment.strings, strings: environment.strings,
emptyColor: environment.theme.list.itemAccentColor,
chartData: chartData chartData: chartData
)), )),
environment: {}, environment: {},
@ -625,42 +636,75 @@ final class DataUsageScreenComponent: Component {
pieChartTransition.setFrame(view: pieChartComponentView, frame: pieChartFrame) pieChartTransition.setFrame(view: pieChartComponentView, frame: pieChartFrame)
} }
if let allStats = self.allStats, allStats.wifi.isEmpty && allStats.cellular.isEmpty { if let allStats = self.allStats, allStats.wifi.isEmpty && allStats.cellular.isEmpty {
let checkColor = UIColor(rgb: 0x34C759) let checkColor = environment.theme.list.itemAccentColor
let doneStatusNode: RadialStatusNode var doneLabelTransition = transition
var animateIn = false let doneLabel: ComponentView<Empty>
if let current = self.doneStatusNode { if let current = self.doneLabel {
doneStatusNode = current doneLabel = current
} else { } else {
doneStatusNode = RadialStatusNode(backgroundNodeColor: .clear) doneLabelTransition = .immediate
self.doneStatusNode = doneStatusNode doneLabel = ComponentView()
self.scrollView.addSubnode(doneStatusNode) self.doneLabel = doneLabel
animateIn = true
}
let doneSize = CGSize(width: 100.0, height: 100.0)
doneStatusNode.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - doneSize.width) / 2.0), y: contentHeight), size: doneSize)
if animateIn {
Queue.mainQueue().after(0.18, {
doneStatusNode.transitionToState(.check(checkColor), animated: true)
})
} }
contentHeight += doneSize.height let doneSupLabel: ComponentView<Empty>
if let current = self.doneSupLabel {
doneSupLabel = current
} else {
doneSupLabel = ComponentView()
self.doneSupLabel = doneSupLabel
}
let doneLabelSize = doneLabel.update(transition: doneLabelTransition, component: AnyComponent(Text(text: "0", font: UIFont.systemFont(ofSize: 50.0, weight: UIFont.Weight(0.25)), color: checkColor)), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0))
let doneLabelFrame = CGRect(origin: CGPoint(x: pieChartFrame.minX + floor((pieChartFrame.width - doneLabelSize.width) * 0.5), y: pieChartFrame.minY + 16.0), size: doneLabelSize)
if let doneLabelView = doneLabel.view {
var animateIn = false
if doneLabelView.superview == nil {
self.scrollView.addSubview(doneLabelView)
animateIn = true
}
doneLabelTransition.setFrame(view: doneLabelView, frame: doneLabelFrame)
if animateIn {
doneLabelView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.2)
}
}
let doneSupLabelSize = doneSupLabel.update(transition: doneLabelTransition, component: AnyComponent(Text(text: "KB", font: avatarPlaceholderFont(size: 12.0), color: checkColor)), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0))
let doneSupLabelFrame = CGRect(origin: CGPoint(x: doneLabelFrame.maxX + 1.0, y: doneLabelFrame.minY + 10.0), size: doneSupLabelSize)
if let doneSupLabelView = doneSupLabel.view {
var animateIn = false
if doneSupLabelView.superview == nil {
self.scrollView.addSubview(doneSupLabelView)
animateIn = true
}
doneLabelTransition.setFrame(view: doneSupLabelView, frame: doneSupLabelFrame)
if animateIn {
doneSupLabelView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.2)
}
}
contentHeight += 100.0
} else { } else {
contentHeight += pieChartSize.height contentHeight += pieChartSize.height
if let doneStatusNode = self.doneStatusNode { if let doneLabel = self.doneLabel {
self.doneStatusNode = nil self.doneLabel = nil
doneStatusNode.removeFromSupernode() doneLabel.view?.removeFromSuperview()
}
if let doneSupLabel = self.doneSupLabel {
self.doneSupLabel = nil
doneSupLabel.view?.removeFromSuperview()
} }
} }
contentHeight += 23.0 contentHeight += 23.0
let headerText: String let headerText: String
if listCategories.isEmpty { if totalSize == 0 {
headerText = "Data Usage Reset" headerText = "No Data Used"
} else { } else {
headerText = "Data Usage" headerText = "Data Usage"
} }
@ -686,15 +730,19 @@ final class DataUsageScreenComponent: Component {
let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.freeTextColor) let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.freeTextColor)
//TODO:localize //TODO:localize
let timestampString: String let timestampString: String
if let allStats = self.allStats, allStats.resetTimestamp != 0 { if let allStats = self.allStats, allStats.resetTimestamp != 0 {
let formatter = DateFormatter() let dateStringPlain = stringForFullDate(timestamp: allStats.resetTimestamp, strings: environment.strings, dateTimeFormat: PresentationDateTimeFormat())
formatter.dateFormat = "E, d MMM yyyy HH:mm" switch self.selectedStats {
let dateStringPlain = formatter.string(from: Date(timeIntervalSince1970: Double(allStats.resetTimestamp))) case .all:
timestampString = "Your network usage since \(dateStringPlain)" timestampString = "Your data usage since \(dateStringPlain)"
case .mobile:
timestampString = "Your mobile data usage since \(dateStringPlain)"
case .wifi:
timestampString = "Your Wi-Fi data usage since \(dateStringPlain)"
}
} else { } else {
timestampString = "Your network usage" timestampString = ""
} }
let totalUsageText: String = timestampString let totalUsageText: String = timestampString
@ -743,8 +791,13 @@ final class DataUsageScreenComponent: Component {
animatedTextItems.append(AnimatedTextComponent.Item(id: "rest", isUnbreakable: true, content: .text(remainingSizeText))) animatedTextItems.append(AnimatedTextComponent.Item(id: "rest", isUnbreakable: true, content: .text(remainingSizeText)))
} }
var labelTransition = transition
if labelTransition.animation.isImmediate, let animationHint, animationHint.value == .modeChanged {
labelTransition = Transition(animation: .curve(duration: 0.3, curve: .easeInOut))
}
let chartTotalLabelSize = self.chartTotalLabel.update( let chartTotalLabelSize = self.chartTotalLabel.update(
transition: transition, transition: labelTransition,
component: AnyComponent(AnimatedTextComponent( component: AnyComponent(AnimatedTextComponent(
font: Font.with(size: 20.0, design: .round, weight: .bold), font: Font.with(size: 20.0, design: .round, weight: .bold),
color: environment.theme.list.itemPrimaryTextColor, color: environment.theme.list.itemPrimaryTextColor,
@ -758,8 +811,8 @@ final class DataUsageScreenComponent: Component {
self.scrollContainerView.addSubview(chartTotalLabelView) self.scrollContainerView.addSubview(chartTotalLabelView)
} }
let totalLabelFrame = CGRect(origin: CGPoint(x: pieChartFrame.minX + floor((pieChartFrame.width - chartTotalLabelSize.width) / 2.0), y: pieChartFrame.minY + floor((pieChartFrame.height - chartTotalLabelSize.height) / 2.0)), size: chartTotalLabelSize) let totalLabelFrame = CGRect(origin: CGPoint(x: pieChartFrame.minX + floor((pieChartFrame.width - chartTotalLabelSize.width) / 2.0), y: pieChartFrame.minY + floor((pieChartFrame.height - chartTotalLabelSize.height) / 2.0)), size: chartTotalLabelSize)
transition.setFrame(view: chartTotalLabelView, frame: totalLabelFrame) labelTransition.setFrame(view: chartTotalLabelView, frame: totalLabelFrame)
transition.setAlpha(view: chartTotalLabelView, alpha: listCategories.isEmpty ? 0.0 : 1.0) labelTransition.setAlpha(view: chartTotalLabelView, alpha: listCategories.isEmpty ? 0.0 : 1.0)
} }
} }
@ -848,9 +901,18 @@ final class DataUsageScreenComponent: Component {
contentHeight += 40.0 contentHeight += 40.0
//TODO:localize //TODO:localize
let totalTitle: String
switch self.selectedStats {
case .all:
totalTitle = "TOTAL NETWORK USAGE"
case .mobile:
totalTitle = "MOBILE NETWORK USAGE"
case .wifi:
totalTitle = "WI-FI NETWORK USAGE"
}
let totalCategoriesTitleSize = self.totalCategoriesTitleView.update( let totalCategoriesTitleSize = self.totalCategoriesTitleView.update(
transition: transition, transition: transition,
component: AnyComponent(MultilineTextComponent(text: .markdown(text: "TOTAL NETWORK USAGE", attributes: MarkdownAttributes( component: AnyComponent(MultilineTextComponent(text: .markdown(text: totalTitle, attributes: MarkdownAttributes(
body: body, body: body,
bold: bold, bold: bold,
link: body, link: body,

View File

@ -242,15 +242,18 @@ final class PieChartComponent: Component {
let theme: PresentationTheme let theme: PresentationTheme
let strings: PresentationStrings let strings: PresentationStrings
let emptyColor: UIColor
let chartData: ChartData let chartData: ChartData
init( init(
theme: PresentationTheme, theme: PresentationTheme,
strings: PresentationStrings, strings: PresentationStrings,
emptyColor: UIColor,
chartData: ChartData chartData: ChartData
) { ) {
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
self.emptyColor = emptyColor
self.chartData = chartData self.chartData = chartData
} }
@ -261,6 +264,9 @@ final class PieChartComponent: Component {
if lhs.strings !== rhs.strings { if lhs.strings !== rhs.strings {
return false return false
} }
if lhs.emptyColor != rhs.emptyColor {
return false
}
if lhs.chartData != rhs.chartData { if lhs.chartData != rhs.chartData {
return false return false
} }
@ -366,6 +372,8 @@ final class PieChartComponent: Component {
var label: CalculatedLabel? var label: CalculatedLabel?
if let leftLabel = left.label, let rightLabel = right.label { if let leftLabel = left.label, let rightLabel = right.label {
label = leftLabel.interpolateTo(rightLabel, amount: progress) label = leftLabel.interpolateTo(rightLabel, amount: progress)
} else {
label = right.label
} }
self.sections.append(CalculatedSection( self.sections.append(CalculatedSection(
@ -385,7 +393,7 @@ final class PieChartComponent: Component {
} }
} }
init(size: CGSize, items: [ChartData.Item], selectedKey: AnyHashable?, isEmpty: Bool) { init(size: CGSize, items: [ChartData.Item], selectedKey: AnyHashable?, isEmpty: Bool, emptyColor: UIColor) {
self.size = size self.size = size
self.sections = [] self.sections = []
self.isEmpty = isEmpty self.isEmpty = isEmpty
@ -447,7 +455,7 @@ final class PieChartComponent: Component {
var arcOuterEndAngle = startAngle + angleValue - angleSpacing * 0.5 * afterSpacingFraction var arcOuterEndAngle = startAngle + angleValue - angleSpacing * 0.5 * afterSpacingFraction
arcOuterEndAngle = max(arcOuterEndAngle, arcOuterStartAngle) arcOuterEndAngle = max(arcOuterEndAngle, arcOuterStartAngle)
let itemColor: UIColor = isEmpty ? UIColor(rgb: 0x34C759) : item.color let itemColor: UIColor = isEmpty ? emptyColor : item.color
self.sections.append(CalculatedSection( self.sections.append(CalculatedSection(
id: item.id, id: item.id,
@ -504,15 +512,17 @@ final class PieChartComponent: Component {
let fractionValue: Double = floor(displayValue * 100.0 * 10.0) / 10.0 let fractionValue: Double = floor(displayValue * 100.0 * 10.0) / 10.0
let fractionString: String let fractionString: String
if fractionValue < 0.1 { if fractionValue == 0.0 {
fractionString = "<0.1" fractionString = ""
} else if fractionValue < 0.1 {
fractionString = "<0.1%"
} else if abs(Double(Int(fractionValue)) - fractionValue) < 0.001 { } else if abs(Double(Int(fractionValue)) - fractionValue) < 0.001 {
fractionString = "\(Int(fractionValue))" fractionString = "\(Int(fractionValue))%"
} else { } else {
fractionString = "\(fractionValue)" fractionString = "\(fractionValue)%"
} }
let labelString = NSAttributedString(string: "\(fractionString)%", font: chartLabelFont, textColor: .white) let labelString = NSAttributedString(string: fractionString, font: chartLabelFont, textColor: .white)
let labelBounds = labelString.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil) let labelBounds = labelString.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil)
let labelSize = CGSize(width: ceil(labelBounds.width), height: ceil(labelBounds.height)) let labelSize = CGSize(width: ceil(labelBounds.width), height: ceil(labelBounds.height))
guard let labelImage = generateImage(labelSize, rotatedContext: { size, context in guard let labelImage = generateImage(labelSize, rotatedContext: { size, context in
@ -922,12 +932,15 @@ final class PieChartComponent: Component {
} }
private final class DoneLayer: SimpleLayer { private final class DoneLayer: SimpleLayer {
private let particleColor: UIColor
private let maskShapeLayer: CAShapeLayer private let maskShapeLayer: CAShapeLayer
private var particleImage: UIImage? private var particleImage: UIImage?
private var particleSet: ParticleSet? private var particleSet: ParticleSet?
private var particleLayers: [SimpleLayer] = [] private var particleLayers: [SimpleLayer] = []
override init() { init(particleColor: UIColor) {
self.particleColor = particleColor
self.maskShapeLayer = CAShapeLayer() self.maskShapeLayer = CAShapeLayer()
self.maskShapeLayer.fillColor = UIColor.black.cgColor self.maskShapeLayer.fillColor = UIColor.black.cgColor
self.maskShapeLayer.fillRule = .evenOdd self.maskShapeLayer.fillRule = .evenOdd
@ -948,6 +961,7 @@ final class PieChartComponent: Component {
} }
override init(layer: Any) { override init(layer: Any) {
self.particleColor = .white
self.maskShapeLayer = CAShapeLayer() self.maskShapeLayer = CAShapeLayer()
super.init(layer: layer) super.init(layer: layer)
@ -982,7 +996,7 @@ final class PieChartComponent: Component {
self.particleLayers.append(particleLayer) self.particleLayers.append(particleLayer)
self.addSublayer(particleLayer) self.addSublayer(particleLayer)
particleLayer.layerTintColor = UIColor(rgb: 0x34C759).cgColor particleLayer.layerTintColor = self.particleColor.cgColor
} }
particleLayer.position = particle.position particleLayer.position = particle.position
@ -1010,6 +1024,7 @@ final class PieChartComponent: Component {
private final class ChartDataView: UIView { private final class ChartDataView: UIView {
private(set) var theme: PresentationTheme? private(set) var theme: PresentationTheme?
private(set) var data: ChartData? private(set) var data: ChartData?
private var emptyColor: UIColor?
private(set) var selectedKey: AnyHashable? private(set) var selectedKey: AnyHashable?
private var currentAnimation: (start: CalculatedLayout, startTime: Double, duration: Double)? private var currentAnimation: (start: CalculatedLayout, startTime: Double, duration: Double)?
@ -1077,7 +1092,9 @@ final class PieChartComponent: Component {
return nil return nil
} }
func setItems(theme: PresentationTheme, data: ChartData, selectedKey: AnyHashable?, animated: Bool) { func setItems(theme: PresentationTheme, emptyColor: UIColor, data: ChartData, selectedKey: AnyHashable?, animated: Bool) {
self.emptyColor = emptyColor
let data = processChartData(data: data) let data = processChartData(data: data)
if self.theme !== theme || self.data != data || self.selectedKey != selectedKey { if self.theme !== theme || self.data != data || self.selectedKey != selectedKey {
@ -1099,14 +1116,16 @@ final class PieChartComponent: Component {
size: CGSize(width: 200.0, height: 200.0), size: CGSize(width: 200.0, height: 200.0),
items: previousData.items, items: previousData.items,
selectedKey: self.selectedKey, selectedKey: self.selectedKey,
isEmpty: true isEmpty: true,
emptyColor: emptyColor
) )
} else { } else {
targetLayout = CalculatedLayout( targetLayout = CalculatedLayout(
size: CGSize(width: 200.0, height: 200.0), size: CGSize(width: 200.0, height: 200.0),
items: data.items, items: data.items,
selectedKey: self.selectedKey, selectedKey: self.selectedKey,
isEmpty: false isEmpty: false,
emptyColor: emptyColor
) )
} }
@ -1118,14 +1137,16 @@ final class PieChartComponent: Component {
size: CGSize(width: 200.0, height: 200.0), size: CGSize(width: 200.0, height: 200.0),
items: [.init(id: AnyHashable(StorageUsageScreenComponent.Category.other), displayValue: 0.0, displaySize: 0, value: 1.0, color: .green, particle: "Settings/Storage/ParticleOther", title: "", mergeable: false, mergeFactor: 1.0)], items: [.init(id: AnyHashable(StorageUsageScreenComponent.Category.other), displayValue: 0.0, displaySize: 0, value: 1.0, color: .green, particle: "Settings/Storage/ParticleOther", title: "", mergeable: false, mergeFactor: 1.0)],
selectedKey: self.selectedKey, selectedKey: self.selectedKey,
isEmpty: true isEmpty: true,
emptyColor: emptyColor
) )
} else { } else {
self.currentLayout = CalculatedLayout( self.currentLayout = CalculatedLayout(
size: CGSize(width: 200.0, height: 200.0), size: CGSize(width: 200.0, height: 200.0),
items: data.items, items: data.items,
selectedKey: self.selectedKey, selectedKey: self.selectedKey,
isEmpty: data.items.isEmpty isEmpty: data.items.isEmpty,
emptyColor: emptyColor
) )
} }
} }
@ -1140,7 +1161,7 @@ final class PieChartComponent: Component {
self.particleSet.update(deltaTime: deltaTime) self.particleSet.update(deltaTime: deltaTime)
var validIds: [AnyHashable] = [] var validIds: [AnyHashable] = []
if let currentLayout = self.currentLayout { if let currentLayout = self.currentLayout, let emptyColor = self.emptyColor {
var effectiveLayout = currentLayout var effectiveLayout = currentLayout
var verticalOffset: CGFloat = 0.0 var verticalOffset: CGFloat = 0.0
var particleAlpha: CGFloat = 1.0 var particleAlpha: CGFloat = 1.0
@ -1195,7 +1216,7 @@ final class PieChartComponent: Component {
if let current = self.doneLayer { if let current = self.doneLayer {
doneLayer = current doneLayer = current
} else { } else {
doneLayer = DoneLayer() doneLayer = DoneLayer(particleColor: emptyColor)
self.doneLayer = doneLayer self.doneLayer = doneLayer
self.layer.insertSublayer(doneLayer, at: 0) self.layer.insertSublayer(doneLayer, at: 0)
} }
@ -1269,7 +1290,7 @@ final class PieChartComponent: Component {
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state { if case .ended = recognizer.state {
let point = recognizer.location(in: self.dataView) let point = recognizer.location(in: self.dataView)
if let key = self.dataView.sectionKey(at: point) { if let key = self.dataView.sectionKey(at: point), key != AnyHashable("empty") {
if self.selectedKey == key { if self.selectedKey == key {
self.selectedKey = nil self.selectedKey = nil
} else { } else {
@ -1293,7 +1314,7 @@ final class PieChartComponent: Component {
} }
transition.setFrame(view: self.dataView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - 200.0) / 2.0), y: 0.0), size: CGSize(width: 200.0, height: 200.0))) transition.setFrame(view: self.dataView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - 200.0) / 2.0), y: 0.0), size: CGSize(width: 200.0, height: 200.0)))
self.dataView.setItems(theme: component.theme, data: component.chartData, selectedKey: self.selectedKey, animated: !transition.animation.isImmediate) self.dataView.setItems(theme: component.theme, emptyColor: component.emptyColor, data: component.chartData, selectedKey: self.selectedKey, animated: !transition.animation.isImmediate)
if let selectedKey = self.selectedKey, let item = component.chartData.items.first(where: { $0.id == selectedKey }) { if let selectedKey = self.selectedKey, let item = component.chartData.items.first(where: { $0.id == selectedKey }) {
let tooltip: ComponentView<Empty> let tooltip: ComponentView<Empty>

View File

@ -1392,6 +1392,7 @@ final class StorageUsageScreenComponent: Component {
component: AnyComponent(PieChartComponent( component: AnyComponent(PieChartComponent(
theme: environment.theme, theme: environment.theme,
strings: environment.strings, strings: environment.strings,
emptyColor: UIColor(rgb: 0x34C759),
chartData: chartData chartData: chartData
)), )),
environment: {}, environment: {},
@ -1404,7 +1405,6 @@ final class StorageUsageScreenComponent: Component {
} }
pieChartTransition.setFrame(view: pieChartComponentView, frame: pieChartFrame) pieChartTransition.setFrame(view: pieChartComponentView, frame: pieChartFrame)
//transition.setAlpha(view: pieChartComponentView, alpha: listCategories.isEmpty ? 0.0 : 1.0)
} }
if let _ = self.aggregatedData, listCategories.isEmpty { if let _ = self.aggregatedData, listCategories.isEmpty {
let checkColor = UIColor(rgb: 0x34C759) let checkColor = UIColor(rgb: 0x34C759)

View File

@ -79,8 +79,15 @@ func inputContextPanelForChatPresentationIntefaceState(_ chatPresentationInterfa
} }
switch inputQueryResult { switch inputQueryResult {
case let .stickers(results): case let .stickers(unfilteredResults):
if !results.isEmpty { if !unfilteredResults.isEmpty {
var results: [FoundStickerItem] = []
for result in unfilteredResults {
if !results.contains(where: { $0.file.fileId == result.file.fileId }) {
results.append(result)
}
}
let query = chatPresentationInterfaceState.interfaceState.composeInputState.inputText.string let query = chatPresentationInterfaceState.interfaceState.composeInputState.inputText.string
if let currentPanel = currentPanel as? InlineReactionSearchPanel { if let currentPanel = currentPanel as? InlineReactionSearchPanel {

View File

@ -1122,7 +1122,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
if file.isAnimated { if file.isAnimated {
strongSelf.fetchDisposable.set(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(message.id.peerId), userContentType: MediaResourceUserContentType(file: file), reference: AnyMediaReference.message(message: MessageReference(message), media: file).resourceReference(file.resource), statsCategory: statsCategoryForFileWithAttributes(file.attributes)).start()) strongSelf.fetchDisposable.set(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(message.id.peerId), userContentType: MediaResourceUserContentType(file: file), reference: AnyMediaReference.message(message: MessageReference(message), media: file).resourceReference(file.resource), statsCategory: statsCategoryForFileWithAttributes(file.attributes)).start())
} else { } else {
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: context, message: message, file: file, userInitiated: manual).start()) strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: context, message: message, file: file, userInitiated: manual, storeToDownloadsPeerType: storeToDownloadsPeerType).start())
} }
} }
}, cancel: { }, cancel: {

View File

@ -263,6 +263,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
}, present: { _ in }, present: { _ in
}, openForumThread: { _, _ in }, openForumThread: { _, _ in
}, openStorageManagement: { }, openStorageManagement: {
}, openPasswordSetup: {
}) })
interaction.searchTextHighightState = searchQuery interaction.searchTextHighightState = searchQuery
self.interaction = interaction self.interaction = interaction

View File

@ -112,6 +112,7 @@ final class PeerInfoState {
final class TelegramGlobalSettings { final class TelegramGlobalSettings {
let suggestPhoneNumberConfirmation: Bool let suggestPhoneNumberConfirmation: Bool
let suggestPasswordConfirmation: Bool let suggestPasswordConfirmation: Bool
let suggestPasswordSetup: Bool
let accountsAndPeers: [(AccountContext, EnginePeer, Int32)] let accountsAndPeers: [(AccountContext, EnginePeer, Int32)]
let activeSessionsContext: ActiveSessionsContext? let activeSessionsContext: ActiveSessionsContext?
let webSessionsContext: WebSessionsContext? let webSessionsContext: WebSessionsContext?
@ -132,6 +133,7 @@ final class TelegramGlobalSettings {
init( init(
suggestPhoneNumberConfirmation: Bool, suggestPhoneNumberConfirmation: Bool,
suggestPasswordConfirmation: Bool, suggestPasswordConfirmation: Bool,
suggestPasswordSetup: Bool,
accountsAndPeers: [(AccountContext, EnginePeer, Int32)], accountsAndPeers: [(AccountContext, EnginePeer, Int32)],
activeSessionsContext: ActiveSessionsContext?, activeSessionsContext: ActiveSessionsContext?,
webSessionsContext: WebSessionsContext?, webSessionsContext: WebSessionsContext?,
@ -151,6 +153,7 @@ final class TelegramGlobalSettings {
) { ) {
self.suggestPhoneNumberConfirmation = suggestPhoneNumberConfirmation self.suggestPhoneNumberConfirmation = suggestPhoneNumberConfirmation
self.suggestPasswordConfirmation = suggestPasswordConfirmation self.suggestPasswordConfirmation = suggestPasswordConfirmation
self.suggestPasswordSetup = suggestPasswordSetup
self.accountsAndPeers = accountsAndPeers self.accountsAndPeers = accountsAndPeers
self.activeSessionsContext = activeSessionsContext self.activeSessionsContext = activeSessionsContext
self.webSessionsContext = webSessionsContext self.webSessionsContext = webSessionsContext
@ -409,6 +412,23 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
) )
} }
let hasPassword: Signal<Bool?, NoError> = .single(nil) |> then(
context.engine.auth.twoStepVerificationConfiguration()
|> map { configuration -> Bool? in
var notSet = false
switch configuration {
case let .notSet(pendingEmail):
if pendingEmail == nil {
notSet = true
}
case .set:
break
}
return !notSet
}
)
|> distinctUntilChanged
return combineLatest( return combineLatest(
context.account.viewTracker.peerView(peerId, updateData: true), context.account.viewTracker.peerView(peerId, updateData: true),
accountsAndPeers, accountsAndPeers,
@ -424,9 +444,10 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
context.engine.data.get( context.engine.data.get(
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false), TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true) TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
) ),
hasPassword
) )
|> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences, suggestions, limits -> PeerInfoScreenData in |> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences, suggestions, limits, hasPassword -> PeerInfoScreenData in
let (notificationExceptions, notificationsAuthorizationStatus, notificationsWarningSuppressed) = notifications let (notificationExceptions, notificationsAuthorizationStatus, notificationsWarningSuppressed) = notifications
let (featuredStickerPacks, archivedStickerPacks) = stickerPacks let (featuredStickerPacks, archivedStickerPacks) = stickerPacks
@ -443,10 +464,16 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
enableQRLogin = true enableQRLogin = true
} }
var suggestPasswordSetup = false
if suggestions.contains(.setupPassword), let hasPassword, !hasPassword {
suggestPasswordSetup = true
}
let peer = peerView.peers[peerId] let peer = peerView.peers[peerId]
let globalSettings = TelegramGlobalSettings( let globalSettings = TelegramGlobalSettings(
suggestPhoneNumberConfirmation: suggestions.contains(.validatePhoneNumber), suggestPhoneNumberConfirmation: suggestions.contains(.validatePhoneNumber),
suggestPasswordConfirmation: suggestions.contains(.validatePassword), suggestPasswordConfirmation: suggestions.contains(.validatePassword),
suggestPasswordSetup: suggestPasswordSetup,
accountsAndPeers: accountsAndPeers, accountsAndPeers: accountsAndPeers,
activeSessionsContext: accountSessions?.0, activeSessionsContext: accountSessions?.0,
webSessionsContext: accountSessions?.2, webSessionsContext: accountSessions?.2,

View File

@ -453,6 +453,7 @@ private enum PeerInfoSettingsSection {
case chatFolders case chatFolders
case notificationsAndSounds case notificationsAndSounds
case privacyAndSecurity case privacyAndSecurity
case passwordSetup
case dataAndStorage case dataAndStorage
case appearance case appearance
case language case language
@ -720,6 +721,13 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
items[.phone]!.append(PeerInfoScreenActionItem(id: 2, text: presentationData.strings.Settings_TryEnterPassword, action: { items[.phone]!.append(PeerInfoScreenActionItem(id: 2, text: presentationData.strings.Settings_TryEnterPassword, action: {
interaction.openSettings(.rememberPassword) interaction.openSettings(.rememberPassword)
})) }))
} else if settings.suggestPasswordSetup {
//TODO:localize
items[.phone]!.append(PeerInfoScreenInfoItem(id: 0, title: "Protect Your Account", text: .markdown("Set a password that will be required each time log in with this phone number."), linkAction: { _ in
}))
items[.phone]!.append(PeerInfoScreenActionItem(id: 2, text: "Set Additional Password", action: {
interaction.openSettings(.passwordSetup)
}))
} }
if !settings.accountsAndPeers.isEmpty { if !settings.accountsAndPeers.isEmpty {
@ -7455,6 +7463,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} }
}) })
} }
case .passwordSetup:
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.6, execute: { [weak self] in
guard let self else {
return
}
let _ = dismissServerProvidedSuggestion(account: self.context.account, suggestion: .setupPassword).start()
})
let controller = self.context.sharedContext.makeSetupTwoFactorAuthController(context: self.context)
push(controller)
case .dataAndStorage: case .dataAndStorage:
push(dataAndStorageController(context: self.context)) push(dataAndStorageController(context: self.context))
case .appearance: case .appearance:

View File

@ -1425,6 +1425,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return SettingsUI.makePrivacyAndSecurityController(context: context) return SettingsUI.makePrivacyAndSecurityController(context: context)
} }
public func makeSetupTwoFactorAuthController(context: AccountContext) -> ViewController {
return SettingsUI.makeSetupTwoFactorAuthController(context: context)
}
public func makeStorageManagementController(context: AccountContext) -> ViewController { public func makeStorageManagementController(context: AccountContext) -> ViewController {
return StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { [weak context] category in return StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { [weak context] category in
guard let context else { guard let context else {