ChatListNode: improved archive handling

ItemListEditableNode: improved swipe actions
This commit is contained in:
Peter 2019-04-21 23:53:32 +04:00
parent 61c1d8d350
commit 6b8adfc5b0
15 changed files with 236 additions and 136 deletions

View File

@ -316,15 +316,10 @@ public final class AvatarNode: ASDisplayNode {
let colorIndex: Int
if let parameters = parameters as? AvatarNodeParameters {
if case .archivedChatsIcon = parameters.icon {
let path = UIBezierPath(roundedRect: bounds, cornerRadius: floor(bounds.width / 3.8))
path.addClip()
} else {
context.beginPath()
context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height:
bounds.size.height))
context.clip()
}
context.beginPath()
context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height:
bounds.size.height))
context.clip()
if let explicitColorIndex = parameters.explicitColorIndex {
colorIndex = explicitColorIndex
@ -349,8 +344,8 @@ public final class AvatarNode: ASDisplayNode {
colorsArray = savedMessagesColors
} else if case .editAvatarIcon = parameters.icon, let theme = parameters.theme {
colorsArray = [theme.list.blocksBackgroundColor.cgColor, theme.list.blocksBackgroundColor.cgColor]
} else if case .archivedChatsIcon = parameters.icon {
let color = UIColor(rgb: 0x4ac058)
} else if case .archivedChatsIcon = parameters.icon, let theme = parameters.theme {
let color = theme.chatList.neutralAvatarColor
colorsArray = [color.cgColor, color.cgColor]
} else {
colorsArray = grayscaleColors

View File

@ -1981,6 +1981,15 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
return chatLocationInfoReady
})
if self.context.sharedContext.immediateExperimentalUISettings.crashOnLongQueries {
let _ = (self.ready.get()
|> filter({ $0 })
|> take(1)
|> timeout(0.8, queue: .concurrentDefaultQueue(), alternate: Signal { _ in
preconditionFailure()
})).start()
}
self.chatDisplayNode.historyNode.contentPositionChanged = { [weak self] offset in
if let strongSelf = self {
let offsetAlpha: CGFloat

View File

@ -13,10 +13,10 @@ private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBa
let scrollToItem: ListViewScrollToItem
let targetProgress: CGFloat
if searchNode.expansionProgress < 0.6 {
scrollToItem = ListViewScrollToItem(index: 1, position: .top(-navigationBarSearchContentHeight), animated: true, curve: .Default(duration: 0.3), directionHint: .Up)
scrollToItem = ListViewScrollToItem(index: 0, position: .top(-navigationBarSearchContentHeight), animated: true, curve: .Default(duration: 0.3), directionHint: .Up)
targetProgress = 0.0
} else {
scrollToItem = ListViewScrollToItem(index: 1, position: .top(0.0), animated: true, curve: .Default(duration: 0.3), directionHint: .Up)
scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: 0.3), directionHint: .Up)
targetProgress = 1.0
}
searchNode.updateExpansionProgress(targetProgress, animated: true)
@ -35,7 +35,7 @@ private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBa
}
})
if let sortItemNode = sortItemNode {
if false, let sortItemNode = sortItemNode {
let itemFrame = sortItemNode.apparentFrame
if itemFrame.contains(CGPoint(x: 0.0, y: listNode.insets.top)) {
var scrollToItem: ListViewScrollToItem?
@ -744,13 +744,13 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
}
}*/
/*self.chatListDisplayNode.chatListNode.contentScrollingEnded = { [weak self] listView in
self.chatListDisplayNode.chatListNode.contentScrollingEnded = { [weak self] listView in
if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode {
return fixListNodeScrolling(listView, searchNode: searchContentNode)
} else {
return false
}
}*/
}
self.chatListDisplayNode.toolbarActionSelected = { [weak self] left in
self?.toolbarActionSelected(left: left)

View File

@ -133,6 +133,47 @@ final class ChatListControllerNode: ASDisplayNode {
insets.left += layout.safeInsets.left
insets.right += layout.safeInsets.right
if let toolbar = self.toolbar {
var tabBarHeight: CGFloat
var options: ContainerViewLayoutInsetOptions = []
if layout.metrics.widthClass == .regular {
options.insert(.input)
}
let bottomInset: CGFloat = layout.insets(options: options).bottom
if !layout.safeInsets.left.isZero {
tabBarHeight = 34.0 + bottomInset
insets.bottom += 34.0
} else {
tabBarHeight = 49.0 + bottomInset
insets.bottom += 49.0
}
let tabBarFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - tabBarHeight), size: CGSize(width: layout.size.width, height: tabBarHeight))
if let toolbarNode = self.toolbarNode {
transition.updateFrame(node: toolbarNode, frame: tabBarFrame)
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: bottomInset, toolbar: toolbar, transition: transition)
} else {
let toolbarNode = ToolbarNode(theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme), displaySeparator: true, left: { [weak self] in
self?.toolbarActionSelected?(true)
}, right: { [weak self] in
self?.toolbarActionSelected?(false)
})
toolbarNode.frame = tabBarFrame
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: bottomInset, toolbar: toolbar, transition: .immediate)
self.addSubnode(toolbarNode)
self.toolbarNode = toolbarNode
if transition.isAnimated {
toolbarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
} else if let toolbarNode = self.toolbarNode {
self.toolbarNode = nil
transition.updateAlpha(node: toolbarNode, alpha: 0.0, completion: { [weak toolbarNode] _ in
toolbarNode?.removeFromSupernode()
})
}
self.chatListNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
self.chatListNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)
@ -177,45 +218,6 @@ final class ChatListControllerNode: ASDisplayNode {
if let searchDisplayController = self.searchDisplayController {
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
}
if let toolbar = self.toolbar {
var tabBarHeight: CGFloat
var options: ContainerViewLayoutInsetOptions = []
if layout.metrics.widthClass == .regular {
options.insert(.input)
}
let bottomInset: CGFloat = layout.insets(options: options).bottom
if !layout.safeInsets.left.isZero {
tabBarHeight = 34.0 + bottomInset
} else {
tabBarHeight = 49.0 + bottomInset
}
let tabBarFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - tabBarHeight), size: CGSize(width: layout.size.width, height: tabBarHeight))
if let toolbarNode = self.toolbarNode {
transition.updateFrame(node: toolbarNode, frame: tabBarFrame)
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: bottomInset, toolbar: toolbar, transition: transition)
} else {
let toolbarNode = ToolbarNode(theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme), displaySeparator: true, left: { [weak self] in
self?.toolbarActionSelected?(true)
}, right: { [weak self] in
self?.toolbarActionSelected?(false)
})
toolbarNode.frame = tabBarFrame
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: bottomInset, toolbar: toolbar, transition: .immediate)
self.addSubnode(toolbarNode)
self.toolbarNode = toolbarNode
if transition.isAnimated {
toolbarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
} else if let toolbarNode = self.toolbarNode {
self.toolbarNode = nil
transition.updateAlpha(node: toolbarNode, alpha: 0.0, completion: { [weak toolbarNode] _ in
toolbarNode?.removeFromSupernode()
})
}
}
func activateSearch(placeholderNode: SearchBarPlaceholderNode) {

View File

@ -35,6 +35,10 @@ class ChatListItem: ListViewItem {
let selectable: Bool = true
var approximateHeight: CGFloat {
return self.hiddenOffset ? 0.0 : 44.0
}
let header: ListViewItemHeader?
init(presentationData: ChatListPresentationData, account: Account, peerGroupId: PeerGroupId?, index: ChatListIndex, content: ChatListItemContent, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, header: ListViewItemHeader?, enableContextActions: Bool, hiddenOffset: Bool, interaction: ChatListNodeInteraction) {
@ -837,7 +841,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var layoutOffset: CGFloat = 0.0
if item.hiddenOffset {
layoutOffset = -itemHeight
//layoutOffset = -itemHeight
}
let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: layoutOffset + 8.0), size: CGSize(width: params.width - leftInset - params.rightInset - 10.0 - 1.0 - editingOffset, height: itemHeight - 12.0 - 9.0))
@ -918,11 +922,18 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
let insets = ChatListItemNode.insets(first: first, last: last, firstWithHeader: firstWithHeader)
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: max(0.0, itemHeight + layoutOffset)), insets: insets)
var heightOffset: CGFloat = 0.0
if item.hiddenOffset {
heightOffset = -itemHeight
}
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: max(0.0, itemHeight + heightOffset)), insets: insets)
return (layout, { [weak self] synchronousLoads, animated in
if let strongSelf = self {
strongSelf.layoutParams = (item, first, last, firstWithHeader, nextIsPinned, params)
if true || !animated {
strongSelf.layer.sublayerTransform = CATransform3DMakeTranslation(0.0, layout.contentSize.height - itemHeight, 0.0)
}
if let _ = updatedTheme {
strongSelf.separatorNode.backgroundColor = item.presentationData.theme.chatList.itemSeparatorColor
@ -1049,21 +1060,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let previousBadgeFrame = strongSelf.badgeNode.frame
let badgeFrame = CGRect(x: contentRect.maxX - badgeLayout.width, y: contentRect.maxY - badgeLayout.height - 2.0, width: badgeLayout.width, height: badgeLayout.height)
if !previousBadgeFrame.width.isZero && !badgeFrame.width.isZero && badgeFrame != previousBadgeFrame {
if animateContent {
strongSelf.badgeNode.frame = badgeFrame
strongSelf.badgeNode.layer.animateFrame(from: previousBadgeFrame, to: badgeFrame, duration: 0.15, timingFunction: kCAMediaTimingFunctionEaseInEaseOut)
} else {
transition.updateFrame(node: strongSelf.badgeNode, frame: badgeFrame)
}
} else {
strongSelf.badgeNode.frame = badgeFrame
}
transition.updateFrame(node: strongSelf.badgeNode, frame: badgeFrame)
}
if currentMentionBadgeImage != nil || currentBadgeBackgroundImage != nil {
let previousBadgeFrame = strongSelf.mentionBadgeNode.frame
let mentionBadgeOffset: CGFloat
if badgeLayout.width.isZero {
mentionBadgeOffset = contentRect.maxX - mentionBadgeLayout.width
@ -1072,13 +1072,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
let badgeFrame = CGRect(x: mentionBadgeOffset, y: contentRect.maxY - mentionBadgeLayout.height - 2.0, width: mentionBadgeLayout.width, height: mentionBadgeLayout.height)
strongSelf.mentionBadgeNode.position = badgeFrame.center
strongSelf.mentionBadgeNode.bounds = CGRect(origin: CGPoint(), size: badgeFrame.size)
if animateContent && !previousBadgeFrame.width.isZero && !badgeFrame.width.isZero && badgeFrame != previousBadgeFrame {
strongSelf.mentionBadgeNode.layer.animatePosition(from: previousBadgeFrame.center, to: badgeFrame.center, duration: 0.15, timingFunction: kCAMediaTimingFunctionEaseInEaseOut)
strongSelf.mentionBadgeNode.layer.animateBounds(from: CGRect(origin: CGPoint(), size: previousBadgeFrame.size), to: CGRect(origin: CGPoint(), size: badgeFrame.size), duration: 0.15, timingFunction: kCAMediaTimingFunctionEaseInEaseOut)
}
transition.updateFrame(node: strongSelf.mentionBadgeNode, frame: badgeFrame)
}
if let currentPinnedIconImage = currentPinnedIconImage {
@ -1219,7 +1214,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
let separatorInset: CGFloat
if (!nextIsPinned && item.index.pinningIndex != nil) || last || groupHiddenByDefault {
if (!nextIsPinned && item.index.pinningIndex != nil) || last {
separatorInset = 0.0
} else {
separatorInset = editingOffset + leftInset + rawContentRect.origin.x
@ -1227,7 +1222,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: layoutOffset + itemHeight - separatorHeight), size: CGSize(width: params.width - separatorInset, height: separatorHeight)))
transition.updateFrame(node: strongSelf.backgroundNode, frame: CGRect(origin: CGPoint(), size: layout.contentSize))
transition.updateFrame(node: strongSelf.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.contentSize.width, height: itemHeight)))
if item.selected {
strongSelf.backgroundNode.backgroundColor = theme.itemSelectedBackgroundColor
} else if item.index.pinningIndex != nil {
@ -1291,7 +1286,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var layoutOffset: CGFloat = 0.0
if item.hiddenOffset {
layoutOffset = -itemHeight
//layoutOffset = -itemHeight
}
if let reorderControlNode = self.reorderControlNode {
@ -1356,7 +1351,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
transition.updateFrame(node: self.badgeNode, frame: updatedBadgeFrame)
var mentionBadgeFrame = self.mentionBadgeNode.frame
mentionBadgeFrame.origin.x = updatedBadgeFrame.minX - 6.0 - mentionBadgeFrame.width
if updatedBadgeFrame.width.isZero {
mentionBadgeFrame.origin.x = updatedBadgeFrame.minX - mentionBadgeFrame.width
} else {
mentionBadgeFrame.origin.x = updatedBadgeFrame.minX - 6.0 - mentionBadgeFrame.width
}
transition.updateFrame(node: self.mentionBadgeNode, frame: mentionBadgeFrame)
let pinnedIconSize = self.pinnedIconNode.bounds.size
@ -1431,6 +1430,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
close = false
case RevealOptionKey.hide.rawValue, RevealOptionKey.unhide.rawValue:
item.interaction.toggleArchivedFolderHiddenByDefault()
close = false
default:
break
}
@ -1458,4 +1458,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
self?.updateIsHighlighted(transition: .immediate)
})
}
override func animateFrameTransition(_ progress: CGFloat, _ currentValue: CGFloat) {
super.animateFrameTransition(progress, currentValue)
self.layer.sublayerTransform = CATransform3DMakeTranslation(0.0, currentValue - itemHeight, 0.0)
}
}

View File

@ -367,6 +367,8 @@ final class ChatListNode: ListView {
let _ = self.currentRemovingPeerId.swap(peerId)
}
private var hapticFeedback: HapticFeedback?
init(context: AccountContext, groupId: PeerGroupId?, controlsHistoryPreload: Bool, mode: ChatListNodeMode, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
self.context = context
self.controlsHistoryPreload = controlsHistoryPreload
@ -380,6 +382,7 @@ final class ChatListNode: ListView {
super.init()
self.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
self.verticalScrollIndicatorFollowsOverscroll = true
let nodeInteraction = ChatListNodeInteraction(activateSearch: { [weak self] in
if let strongSelf = self, let activateSearch = strongSelf.activateSearch {
@ -468,12 +471,17 @@ final class ChatListNode: ListView {
})
return updatedValue
}
|> deliverOnMainQueue).start(next: { updatedValue in
|> deliverOnMainQueue).start(next: { value in
guard let strongSelf = self else {
return
}
if !updatedValue {
strongSelf.updateState { state in
var state = state
if !value {
state.archiveShouldBeTemporaryRevealed = false
}
state.peerIdWithRevealedOptions = nil
return state
}
})
})
@ -488,6 +496,7 @@ final class ChatListNode: ListView {
let previousState = Atomic<ChatListNodeState>(value: self.currentState)
let previousView = Atomic<ChatListNodeView?>(value: nil)
let previousHideArchivedFolderByDefault = Atomic<Bool?>(value: nil)
let currentRemovingPeerId = self.currentRemovingPeerId
let savedMessagesPeer: Signal<Peer?, NoError>
@ -509,6 +518,8 @@ final class ChatListNode: ListView {
let chatListNodeViewTransition = combineLatest(hideArchivedFolderByDefault, savedMessagesPeer, chatListViewUpdate, self.statePromise.get()) |> mapToQueue { (hideArchivedFolderByDefault, savedMessagesPeer, update, state) -> Signal<ChatListNodeListViewTransition, NoError> in
let previousHideArchivedFolderByDefaultValue = previousHideArchivedFolderByDefault.swap(hideArchivedFolderByDefault)
let (rawEntries, isLoading) = chatListNodeEntriesForView(update.view, state: state, savedMessagesPeer: savedMessagesPeer, hideArchivedFolderByDefault: hideArchivedFolderByDefault, mode: mode)
let entries = rawEntries.filter { entry in
switch entry {
@ -644,7 +655,7 @@ final class ChatListNode: ListView {
if doesIncludeRemovingPeerId != didIncludeRemovingPeerId {
disableAnimations = false
}
if previousState.archiveShouldBeTemporaryRevealed != state.archiveShouldBeTemporaryRevealed && doesIncludeArchive {
if hideArchivedFolderByDefault && previousState.archiveShouldBeTemporaryRevealed != state.archiveShouldBeTemporaryRevealed && doesIncludeArchive {
disableAnimations = false
}
if didIncludeHiddenByDefaultArchive != doesIncludeHiddenByDefaultArchive {
@ -652,6 +663,10 @@ final class ChatListNode: ListView {
}
}
if let _ = previousHideArchivedFolderByDefaultValue, previousHideArchivedFolderByDefaultValue != hideArchivedFolderByDefault {
disableAnimations = false
}
var searchMode = false
if case .peers = mode {
searchMode = true
@ -689,6 +704,7 @@ final class ChatListNode: ListView {
var rawUnreadCount: Int32 = 0
var filteredUnreadCount: Int32 = 0
var archiveVisible = false
if let range = range.visibleRange {
let entryCount = chatListView.filteredEntries.count
for i in range.firstIndex ..< range.lastIndex {
@ -705,6 +721,8 @@ final class ChatListNode: ListView {
filteredUnreadCount += count
}
}
case .GroupReferenceEntry:
archiveVisible = true
default:
break
}
@ -714,6 +732,13 @@ final class ChatListNode: ListView {
visibleUnreadCountsValue.raw = rawUnreadCount
visibleUnreadCountsValue.filtered = filteredUnreadCount
strongSelf.visibleUnreadCountsValue = visibleUnreadCountsValue
if !archiveVisible && strongSelf.currentState.archiveShouldBeTemporaryRevealed {
strongSelf.updateState { state in
var state = state
state.archiveShouldBeTemporaryRevealed = false
return state
}
}
}
}
@ -925,7 +950,7 @@ final class ChatListNode: ListView {
case .none, .unknown:
revealHiddenItems = false
case let .known(value):
revealHiddenItems = value <= 76.0
revealHiddenItems = value <= 54.0
}
if !revealHiddenItems && strongSelf.currentState.archiveShouldBeTemporaryRevealed {
strongSelf.updateState { state in
@ -974,16 +999,30 @@ final class ChatListNode: ListView {
case let .known(value):
atTop = value <= 0.0
if startedScrollingAtUpperBound && strongSelf.isTracking {
revealHiddenItems = value <= -32.0
revealHiddenItems = value <= -76.0
}
}
strongSelf.scrolledAtTopValue = atTop
strongSelf.contentOffsetChanged?(offset)
if revealHiddenItems && !strongSelf.currentState.archiveShouldBeTemporaryRevealed {
strongSelf.updateState { state in
var state = state
state.archiveShouldBeTemporaryRevealed = true
return state
var isArchiveVisible = false
strongSelf.forEachItemNode({ itemNode in
if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item {
if case .groupReference = item.content {
isArchiveVisible = true
}
}
})
if isArchiveVisible {
if strongSelf.hapticFeedback == nil {
strongSelf.hapticFeedback = HapticFeedback()
}
strongSelf.hapticFeedback?.impact(.medium)
strongSelf.updateState { state in
var state = state
state.archiveShouldBeTemporaryRevealed = true
return state
}
}
}
}

View File

@ -40,6 +40,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case enableRaiseToSpeak(PresentationTheme, Bool)
case keepChatNavigationStack(PresentationTheme, Bool)
case skipReadHistory(PresentationTheme, Bool)
case crashOnSlowQueries(PresentationTheme, Bool)
case clearTips(PresentationTheme)
case reimport(PresentationTheme)
case resetData(PresentationTheme)
@ -54,7 +55,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return DebugControllerSection.logs.rawValue
case .logToFile, .logToConsole, .redactSensitiveData:
return DebugControllerSection.logging.rawValue
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory:
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
return DebugControllerSection.experiments.rawValue
case .clearTips, .reimport, .resetData, .animatedStickers:
return DebugControllerSection.experiments.rawValue
@ -85,12 +86,14 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 8
case .skipReadHistory:
return 9
case .clearTips:
case .crashOnSlowQueries:
return 10
case .reimport:
case .clearTips:
return 11
case .resetData:
case .reimport:
return 12
case .resetData:
return 13
case .animatedStickers:
return 14
case .versionInfo:
@ -275,6 +278,14 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return settings
}).start()
})
case let .crashOnSlowQueries(theme, value):
return ItemListSwitchItem(theme: theme, title: "Crash when slow", value: value, sectionId: self.section, style: .blocks, updated: { value in
let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in
var settings = settings
settings.crashOnLongQueries = value
return settings
}).start()
})
case let .clearTips(theme):
return ItemListActionItem(theme: theme, title: "Clear Tips", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
let _ = (arguments.sharedContext.accountManager.transaction { transaction -> Void in
@ -346,6 +357,7 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS
#if DEBUG
entries.append(.skipReadHistory(presentationData.theme, experimentalSettings.skipReadHistory))
#endif
entries.append(.crashOnSlowQueries(presentationData.theme, experimentalSettings.crashOnLongQueries))
entries.append(.clearTips(presentationData.theme))
if hasLegacyAppData {
entries.append(.reimport(presentationData.theme))

View File

@ -141,7 +141,8 @@ private let chatList = PresentationThemeChatList(
searchBarKeyboardColor: .dark,
verifiedIconFillColor: accentColor,
verifiedIconForegroundColor: .white,
secretIconColor: secretColor
secretIconColor: secretColor,
neutralAvatarColor: UIColor(rgb: 0xDBF5FF, alpha: 0.4)
)
private let bubble = PresentationThemeChatBubble(

View File

@ -141,7 +141,8 @@ private let chatList = PresentationThemeChatList(
searchBarKeyboardColor: .dark,
verifiedIconFillColor: accentColor,
verifiedIconForegroundColor: .white,
secretIconColor: secretColor
secretIconColor: secretColor,
neutralAvatarColor: UIColor(rgb: 0x666666)
)
private let bubble = PresentationThemeChatBubble(

View File

@ -141,7 +141,8 @@ private func makeDefaultPresentationTheme(accentColor: UIColor, serviceBackgroun
searchBarKeyboardColor: .light,
verifiedIconFillColor: accentColor,
verifiedIconForegroundColor: .white,
secretIconColor: secretColor
secretIconColor: secretColor,
neutralAvatarColor: UIColor(rgb: 0xb6b6ba)
)
let chatListDay = PresentationThemeChatList(
@ -172,7 +173,8 @@ private func makeDefaultPresentationTheme(accentColor: UIColor, serviceBackgroun
searchBarKeyboardColor: .light,
verifiedIconFillColor: accentColor,
verifiedIconForegroundColor: .white,
secretIconColor: secretColor
secretIconColor: secretColor,
neutralAvatarColor: UIColor(rgb: 0xb6b6ba)
)
let bubble = PresentationThemeChatBubble(

View File

@ -5,24 +5,28 @@ import SwiftSignalKit
public struct ExperimentalUISettings: Equatable, PreferencesEntry {
public var keepChatNavigationStack: Bool
public var skipReadHistory: Bool
public var crashOnLongQueries: Bool
public static var defaultSettings: ExperimentalUISettings {
return ExperimentalUISettings(keepChatNavigationStack: false, skipReadHistory: false)
return ExperimentalUISettings(keepChatNavigationStack: false, skipReadHistory: false, crashOnLongQueries: false)
}
public init(keepChatNavigationStack: Bool, skipReadHistory: Bool) {
public init(keepChatNavigationStack: Bool, skipReadHistory: Bool, crashOnLongQueries: Bool) {
self.keepChatNavigationStack = keepChatNavigationStack
self.skipReadHistory = skipReadHistory
self.crashOnLongQueries = crashOnLongQueries
}
public init(decoder: PostboxDecoder) {
self.keepChatNavigationStack = decoder.decodeInt32ForKey("keepChatNavigationStack", orElse: 0) != 0
self.skipReadHistory = decoder.decodeInt32ForKey("skipReadHistory", orElse: 0) != 0
self.crashOnLongQueries = decoder.decodeInt32ForKey("crashOnLongQueries", orElse: 0) != 0
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.keepChatNavigationStack ? 1 : 0, forKey: "keepChatNavigationStack")
encoder.encodeInt32(self.skipReadHistory ? 1 : 0, forKey: "skipReadHistory")
encoder.encodeInt32(self.crashOnLongQueries ? 1 : 0, forKey: "crashOnLongQueries")
}
public func isEqual(to: PreferencesEntry) -> Bool {

View File

@ -269,9 +269,21 @@ class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelega
reveal = false
}
}
self.updateRevealOffsetInternal(offset: reveal ? -revealSize.width : 0.0, transition: .animated(duration: 0.3, curve: .spring))
if !reveal {
self.revealOptionsInteractivelyClosed()
var selectedOption: ItemListRevealOption?
if reveal && rightRevealNode.isDisplayingExtendedAction() {
reveal = false
selectedOption = self.revealOptions.right.last
} else {
self.updateRevealOffsetInternal(offset: reveal ? -revealSize.width : 0.0, transition: .animated(duration: 0.3, curve: .spring))
}
if let selectedOption = selectedOption {
self.revealOptionSelected(selectedOption, animated: true)
} else {
if !reveal {
self.revealOptionsInteractivelyClosed()
}
}
}
default:

View File

@ -128,6 +128,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
private let animationNode: ItemListRevealAnimationNode?
private var animationNodeOffset: CGFloat = 0.0
var alignment: ItemListRevealOptionAlignment?
var isExpanded: Bool = false
init(title: String, icon: ItemListRevealOptionIcon, color: UIColor, textColor: UIColor) {
self.backgroundNode = ASDisplayNode()
@ -178,11 +179,11 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
}
}
func updateLayout(isFirst: Bool, isLeft: Bool, baseSize: CGSize, alignment: ItemListRevealOptionAlignment, extendedWidth: CGFloat, sideInset: CGFloat, transition: ContainedViewLayoutTransition, revealFactor: CGFloat) {
func updateLayout(isFirst: Bool, isLeft: Bool, baseSize: CGSize, alignment: ItemListRevealOptionAlignment, isExpanded: Bool, extendedWidth: CGFloat, sideInset: CGFloat, transition: ContainedViewLayoutTransition, additive: Bool, revealFactor: CGFloat) {
self.highlightNode.frame = CGRect(origin: CGPoint(), size: baseSize)
var animateAdditive = false
if transition.isAnimated, self.alignment != alignment {
if additive && transition.isAnimated && self.isExpanded != isExpanded {
animateAdditive = true
}
@ -192,15 +193,23 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
} else {
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: extendedWidth, height: baseSize.height))
}
let deltaX: CGFloat
if animateAdditive {
let previousFrame = self.backgroundNode.frame
self.backgroundNode.frame = backgroundFrame
transition.animatePositionAdditive(node: self.backgroundNode, offset: CGPoint(x: previousFrame.width - backgroundFrame.width, y: 0.0))
if isLeft {
deltaX = previousFrame.width - backgroundFrame.width
} else {
deltaX = -(previousFrame.width - backgroundFrame.width)
}
transition.animatePositionAdditive(node: self.backgroundNode, offset: CGPoint(x: deltaX, y: 0.0))
} else {
deltaX = 0.0
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
}
self.alignment = alignment
self.isExpanded = isExpanded
let titleSize = self.titleNode.calculatedSize
var contentRect = CGRect(origin: CGPoint(), size: baseSize)
switch alignment {
@ -215,13 +224,13 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
let titleIconSpacing: CGFloat = 11.0
let iconFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - imageSize.width + sideInset) / 2.0), y: contentRect.midY - imageSize.height / 2.0 + iconOffset), size: imageSize)
if animateAdditive {
transition.animatePositionAdditive(node: animationNode, offset: CGPoint(x: animationNode.frame.minX - iconFrame.minX, y: 0.0))
transition.animatePositionAdditive(node: animationNode, offset: CGPoint(x: deltaX, y: 0.0))
}
animationNode.frame = iconFrame
let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - titleSize.width + sideInset) / 2.0), y: contentRect.midY + titleIconSpacing), size: titleSize)
if animateAdditive {
transition.animatePositionAdditive(node: self.titleNode, offset: CGPoint(x: self.titleNode.frame.minX - titleFrame.minX, y: 0.0))
transition.animatePositionAdditive(node: self.titleNode, offset: CGPoint(x: deltaX, y: 0.0))
}
self.titleNode.frame = titleFrame
@ -235,13 +244,13 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
let titleIconSpacing: CGFloat = 11.0
let iconFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - imageSize.width + sideInset) / 2.0), y: contentRect.midY - imageSize.height / 2.0 + iconOffset), size: imageSize)
if animateAdditive {
transition.animatePositionAdditive(node: iconNode, offset: CGPoint(x: iconNode.frame.minX - iconFrame.minX, y: 0.0))
transition.animatePositionAdditive(node: iconNode, offset: CGPoint(x: deltaX, y: 0.0))
}
iconNode.frame = iconFrame
let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - titleSize.width + sideInset) / 2.0), y: contentRect.midY + titleIconSpacing), size: titleSize)
if animateAdditive {
transition.animatePositionAdditive(node: self.titleNode, offset: CGPoint(x: self.titleNode.frame.minX - titleFrame.minX, y: 0.0))
transition.animatePositionAdditive(node: self.titleNode, offset: CGPoint(x: deltaX, y: 0.0))
}
self.titleNode.frame = titleFrame
} else {
@ -345,33 +354,30 @@ final class ItemListRevealOptionsNode: ASDisplayNode {
let lastNodeWidth = size.width - basicNodeWidth * CGFloat(self.optionNodes.count - 1)
let revealFactor = self.revealOffset / size.width
let boundaryRevealFactor: CGFloat = 1.0 + basicNodeWidth / size.width * 0.7
var leftOffset: CGFloat = 0.0
var leftOffset: CGFloat
if self.isLeft {
leftOffset = size.width + max(0.0, abs(revealFactor) - 1.0) * size.width
} else {
leftOffset = 0.0
}
for i in 0 ..< self.optionNodes.count {
var i = self.isLeft ? (self.optionNodes.count - 1) : 0
while i >= 0 && i < self.optionNodes.count {
let node = self.optionNodes[i]
let nodeWidth = i == (self.optionNodes.count - 1) ? lastNodeWidth : basicNodeWidth
var extendedWidth = nodeWidth
let defaultAlignment: ItemListRevealOptionAlignment = isLeft ? .left : .right
var alignment = defaultAlignment
let defaultAlignment: ItemListRevealOptionAlignment = isLeft ? .right : .left
var nodeTransition = transition
extendedWidth = nodeWidth * max(1.0, abs(revealFactor))
extendedWidth = floorToScreenPixels(nodeWidth * max(1.0, abs(revealFactor)))
var isExpanded = false
if (isLeft && i == 0) || (!isLeft && i == self.optionNodes.count - 1) {
if isLeft && abs(revealFactor) > boundaryRevealFactor {
if abs(revealFactor) > boundaryRevealFactor {
extendedWidth = size.width * max(1.0, abs(revealFactor))
isExpanded = true
if isLeft {
alignment = .right
} else {
alignment = .left
}
}
}
if let nodeAlignment = node.alignment, alignment != nodeAlignment {
if let _ = node.alignment, node.isExpanded != isExpanded {
nodeTransition = transition.isAnimated ? transition : .animated(duration: 0.2, curve: .spring)
if alignment != defaultAlignment || !transition.isAnimated {
if !transition.isAnimated {
self.tapticAction()
}
}
@ -382,13 +388,25 @@ final class ItemListRevealOptionsNode: ASDisplayNode {
}
var nodeLeftOffset = leftOffset
if self.isLeft {
nodeLeftOffset -= extendedWidth
} else {
nodeLeftOffset *= abs(revealFactor)
}
if isExpanded {
nodeLeftOffset = 0.0
}
transition.updateFrame(node: node, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(nodeLeftOffset * abs(revealFactor)), y: 0.0), size: CGSize(width: extendedWidth, height: size.height)))
node.updateLayout(isFirst: i == 0, isLeft: self.isLeft, baseSize: CGSize(width: nodeWidth, height: size.height), alignment: alignment, extendedWidth: extendedWidth, sideInset: sideInset, transition: nodeTransition, revealFactor: revealFactor)
leftOffset += nodeWidth
transition.updateFrame(node: node, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(nodeLeftOffset), y: 0.0), size: CGSize(width: extendedWidth, height: size.height)))
node.updateLayout(isFirst: (self.isLeft && i == 0) || (!self.isLeft && i == self.optionNodes.count - 1), isLeft: self.isLeft, baseSize: CGSize(width: nodeWidth, height: size.height), alignment: defaultAlignment, isExpanded: isExpanded, extendedWidth: extendedWidth, sideInset: sideInset, transition: nodeTransition, additive: !transition.isAnimated, revealFactor: revealFactor)
if self.isLeft {
leftOffset -= extendedWidth
i -= 1
} else {
leftOffset += nodeWidth
i += 1
}
}
}
@ -409,12 +427,6 @@ final class ItemListRevealOptionsNode: ASDisplayNode {
}
func isDisplayingExtendedAction() -> Bool {
let defaultAlignment: ItemListRevealOptionAlignment = self.isLeft ? .left : .right
for node in self.optionNodes {
if let alignment = node.alignment, alignment != defaultAlignment {
return true
}
}
return false
return self.optionNodes.contains(where: { $0.isExpanded })
}
}

View File

@ -72,7 +72,7 @@ class NavigationBarSearchContentNode: NavigationBarContentNode {
}
func updateExpansionProgress(_ progress: CGFloat, animated: Bool = false) {
let newProgress = max(0.0, min(1.0, progress))
let newProgress = max(0.0, min(10.0, progress))
if abs(newProgress - self.expansionProgress) > 0.0001 {
self.expansionProgress = newProgress
@ -121,13 +121,16 @@ class NavigationBarSearchContentNode: NavigationBarContentNode {
let fieldHeight: CGFloat = 36.0
let fraction = fieldHeight / self.nominalHeight
let visibleProgress = max(0.0, self.expansionProgress - 1.0 + fraction) / fraction
let visibleProgress = max(0.0, min(1.0, self.expansionProgress) - 1.0 + fraction) / fraction
let overscrollProgress = max(0.0, max(0.0, self.expansionProgress - 1.0 + fraction) / fraction - visibleProgress)
let searchBarNodeLayout = self.placeholderNode.asyncLayout()
let (searchBarHeight, searchBarApply) = searchBarNodeLayout(NSAttributedString(string: self.placeholder, font: searchBarFont, textColor: self.theme?.rootController.activeNavigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93)), CGSize(width: baseWidth, height: fieldHeight), visibleProgress, self.theme?.rootController.activeNavigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93), self.theme?.rootController.activeNavigationSearchBar.inputFillColor ?? .clear, self.theme?.rootController.navigationBar.backgroundColor ?? .clear, transition)
searchBarApply()
let searchBarFrame = CGRect(origin: CGPoint(x: padding + leftInset, y: 8.0), size: CGSize(width: baseWidth, height: fieldHeight))
let searchBarFrame = CGRect(origin: CGPoint(x: padding + leftInset, y: 8.0 + overscrollProgress * fieldHeight), size: CGSize(width: baseWidth, height: fieldHeight))
transition.updateFrame(node: self.placeholderNode, frame: searchBarFrame)
self.placeholderHeight = searchBarHeight

View File

@ -375,8 +375,9 @@ public final class PresentationThemeChatList {
public let verifiedIconFillColor: UIColor
public let verifiedIconForegroundColor: UIColor
public let secretIconColor: UIColor
public let neutralAvatarColor: UIColor
init(backgroundColor: UIColor, itemSeparatorColor: UIColor, itemBackgroundColor: UIColor, pinnedItemBackgroundColor: UIColor, itemHighlightedBackgroundColor: UIColor, itemSelectedBackgroundColor: UIColor, titleColor: UIColor, secretTitleColor: UIColor, dateTextColor: UIColor, authorNameColor: UIColor, messageTextColor: UIColor, messageDraftTextColor: UIColor, checkmarkColor: UIColor, pendingIndicatorColor: UIColor, muteIconColor: UIColor, unreadBadgeActiveBackgroundColor: UIColor, unreadBadgeActiveTextColor: UIColor, unreadBadgeInactiveBackgroundColor: UIColor, unreadBadgeInactiveTextColor: UIColor, pinnedBadgeColor: UIColor, pinnedSearchBarColor: UIColor, regularSearchBarColor: UIColor, sectionHeaderFillColor: UIColor, sectionHeaderTextColor: UIColor, searchBarKeyboardColor: PresentationThemeKeyboardColor, verifiedIconFillColor: UIColor, verifiedIconForegroundColor: UIColor, secretIconColor: UIColor) {
init(backgroundColor: UIColor, itemSeparatorColor: UIColor, itemBackgroundColor: UIColor, pinnedItemBackgroundColor: UIColor, itemHighlightedBackgroundColor: UIColor, itemSelectedBackgroundColor: UIColor, titleColor: UIColor, secretTitleColor: UIColor, dateTextColor: UIColor, authorNameColor: UIColor, messageTextColor: UIColor, messageDraftTextColor: UIColor, checkmarkColor: UIColor, pendingIndicatorColor: UIColor, muteIconColor: UIColor, unreadBadgeActiveBackgroundColor: UIColor, unreadBadgeActiveTextColor: UIColor, unreadBadgeInactiveBackgroundColor: UIColor, unreadBadgeInactiveTextColor: UIColor, pinnedBadgeColor: UIColor, pinnedSearchBarColor: UIColor, regularSearchBarColor: UIColor, sectionHeaderFillColor: UIColor, sectionHeaderTextColor: UIColor, searchBarKeyboardColor: PresentationThemeKeyboardColor, verifiedIconFillColor: UIColor, verifiedIconForegroundColor: UIColor, secretIconColor: UIColor, neutralAvatarColor: UIColor) {
self.backgroundColor = backgroundColor
self.itemSeparatorColor = itemSeparatorColor
self.itemBackgroundColor = itemBackgroundColor
@ -405,6 +406,7 @@ public final class PresentationThemeChatList {
self.verifiedIconFillColor = verifiedIconFillColor
self.verifiedIconForegroundColor = verifiedIconForegroundColor
self.secretIconColor = secretIconColor
self.neutralAvatarColor = neutralAvatarColor
}
}