From 8ed075f6db088befff3e8dcbaba4effbdac933f4 Mon Sep 17 00:00:00 2001 From: Diogo Cunha Date: Thu, 13 Jun 2024 22:32:07 +0100 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20implemented=20dissap?= =?UTF-8?q?earing=20toolbar=20on=20chat=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit toolbar dissapears when scrolling down and shows when scrolling back up in normal UI. Also implemented for one hand ui --- .../Shared/Views/ChatList/ChatListView.swift | 61 ++++++++++++++++--- apps/ios/Shared/Views/Home/HomeView.swift | 53 +++++++++++++--- 2 files changed, 94 insertions(+), 20 deletions(-) diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index da8d1723f6..aa5ea356f9 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -17,7 +17,7 @@ struct ChatListView: View { @State private var searchText = "" @State private var searchShowingSimplexLink = false @State private var searchChatFilteredBySimplexLink: String? = nil - + @State var topVisibleRowIndex: Int? = nil; @AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false @AppStorage(DEFAULT_ONE_HAND_UI) private var oneHandUI = false @@ -57,8 +57,8 @@ struct ChatListView: View { let cs = filteredChats() ZStack { VStack { - List { - if !chatModel.chats.isEmpty { + ScrollViewReader { scrollViewProxy in + if !chatModel.chats.isEmpty && oneHandUI{ ChatListSearchBar( searchMode: $searchMode, searchFocussed: $searchFocussed, @@ -66,17 +66,41 @@ struct ChatListView: View { searchShowingSimplexLink: $searchShowingSimplexLink, searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink ) - .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) + .scaleEffect(x: 1, y: -1, anchor: .center) .listRowSeparator(.hidden) .frame(maxWidth: .infinity) + .padding(10) } - ForEach(cs, id: \.viewId) { chat in - ChatListNavLink(chat: chat) - .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) - .padding(.trailing, -16) - .disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id)) + List { + if !chatModel.chats.isEmpty && !oneHandUI{ + ChatListSearchBar( + searchMode: $searchMode, + searchFocussed: $searchFocussed, + searchText: $searchText, + searchShowingSimplexLink: $searchShowingSimplexLink, + searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink + ) + .listRowSeparator(.hidden) + .frame(maxWidth: .infinity) + } + ForEach(cs.indices, id: \.self) { index in + ChatListNavLink(chat: cs[index]) + .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) + .padding(.trailing, -16) + .disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(cs[index].chatInfo.id)) + .background(GeometryReader { proxy in + Color.clear + .onAppear { + updateTopVisibleRowIndex(proxy: proxy, index: index) + } + .onChange(of: proxy.frame(in: .named("SCROLL")).minY) { _ in + updateTopVisibleRowIndex(proxy: proxy, index: index) + } + }) + } + .offset(x: -8) } - .offset(x: -8) + .coordinateSpace(name: "SCROLL") } } .onChange(of: chatModel.chatId) { _ in @@ -92,6 +116,23 @@ struct ChatListView: View { } } } + + private func updateTopVisibleRowIndex(proxy: GeometryProxy, index: Int) { + let frame = proxy.frame(in: .named("SCROLL")) + + if frame.minY >= 0 && frame.minY < frame.height / 2 { + if topVisibleRowIndex != index { + let shouldShowToolbar: Bool + if let topVisibleRowIndex = topVisibleRowIndex { + shouldShowToolbar = topVisibleRowIndex > index + } else { + shouldShowToolbar = true + } + topVisibleRowIndex = index + NotificationCenter.default.post(name: .toolbarVisibilityChanged, object: shouldShowToolbar) + } + } + } private func unreadBadge(_ text: Text? = Text(" "), size: CGFloat = 18) -> some View { Circle() diff --git a/apps/ios/Shared/Views/Home/HomeView.swift b/apps/ios/Shared/Views/Home/HomeView.swift index 90f9974ef2..ecd1101583 100644 --- a/apps/ios/Shared/Views/Home/HomeView.swift +++ b/apps/ios/Shared/Views/Home/HomeView.swift @@ -61,16 +61,15 @@ struct HomeView: View { case .chats: withToolbar("Chats", chatListView) } } - .toolbar { - ToolbarItemGroup(placement: .bottomBar) { - settingsButton() - Spacer() - contactsButton() - Spacer() - chatsButton() - Spacer() - newChatButton() - } + + ToolbarView { + settingsButton() + Spacer() + contactsButton() + Spacer() + chatsButton() + Spacer() + newChatButton() } if #unavailable(iOS 16) { @@ -224,6 +223,40 @@ struct HomeView: View { } } +extension Notification.Name { + static let toolbarVisibilityChanged = Notification.Name("toolbarVisibilityChanged") +} + +struct ToolbarView: View { + @State var isVisible: Bool = true; + let content: Content + + init(@ViewBuilder content: () -> Content) { + self.content = content() + } + + var body: some View { + VStack { + Spacer() + if isVisible { + VStack {} + .toolbar { + ToolbarItemGroup(placement: .bottomBar) { + content + } + } + } + } + .onReceive(NotificationCenter.default.publisher(for: .toolbarVisibilityChanged)) { notification in + if let visibility = notification.object as? Bool { + if isVisible != visibility { + isVisible.toggle() + } + } + } + } +} + #Preview { HomeView(showSettings: Binding.constant(false)) } From 0da98fbf6022345a174e95430bb0bbb6600fa068 Mon Sep 17 00:00:00 2001 From: Diogo Cunha Date: Thu, 20 Jun 2024 15:57:26 +0100 Subject: [PATCH 2/4] =?UTF-8?q?chore:=20=F0=9F=A4=96=20reverted=20home=20v?= =?UTF-8?q?iew=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE: 🧨 g --- apps/ios/Shared/Views/Home/HomeView.swift | 55 +++++------------------ 1 file changed, 11 insertions(+), 44 deletions(-) diff --git a/apps/ios/Shared/Views/Home/HomeView.swift b/apps/ios/Shared/Views/Home/HomeView.swift index ecd1101583..75837040e3 100644 --- a/apps/ios/Shared/Views/Home/HomeView.swift +++ b/apps/ios/Shared/Views/Home/HomeView.swift @@ -61,15 +61,16 @@ struct HomeView: View { case .chats: withToolbar("Chats", chatListView) } } - - ToolbarView { - settingsButton() - Spacer() - contactsButton() - Spacer() - chatsButton() - Spacer() - newChatButton() + .toolbar { + ToolbarItemGroup(placement: .bottomBar) { + settingsButton() + Spacer() + contactsButton() + Spacer() + chatsButton() + Spacer() + newChatButton() + } } if #unavailable(iOS 16) { @@ -223,40 +224,6 @@ struct HomeView: View { } } -extension Notification.Name { - static let toolbarVisibilityChanged = Notification.Name("toolbarVisibilityChanged") -} - -struct ToolbarView: View { - @State var isVisible: Bool = true; - let content: Content - - init(@ViewBuilder content: () -> Content) { - self.content = content() - } - - var body: some View { - VStack { - Spacer() - if isVisible { - VStack {} - .toolbar { - ToolbarItemGroup(placement: .bottomBar) { - content - } - } - } - } - .onReceive(NotificationCenter.default.publisher(for: .toolbarVisibilityChanged)) { notification in - if let visibility = notification.object as? Bool { - if isVisible != visibility { - isVisible.toggle() - } - } - } - } -} - #Preview { HomeView(showSettings: Binding.constant(false)) -} +} \ No newline at end of file From 32946d4628a5c685e80a35e4fbde38012bec1687 Mon Sep 17 00:00:00 2001 From: Diogo Cunha Date: Thu, 20 Jun 2024 17:34:29 +0100 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20made=20only=20search?= =?UTF-8?q?=20bar=20dissapear=20on=20scroll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Shared/Views/ChatList/ChatListView.swift | 79 +++++++++++-------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index aa5ea356f9..37204ff34d 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -18,6 +18,7 @@ struct ChatListView: View { @State private var searchShowingSimplexLink = false @State private var searchChatFilteredBySimplexLink: String? = nil @State var topVisibleRowIndex: Int? = nil; + @State private var searchVisible: Bool = true; @AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false @AppStorage(DEFAULT_ONE_HAND_UI) private var oneHandUI = false @@ -55,34 +56,12 @@ struct ChatListView: View { @ViewBuilder private var chatList: some View { let cs = filteredChats() - ZStack { + ZStack(alignment: .top) { VStack { ScrollViewReader { scrollViewProxy in - if !chatModel.chats.isEmpty && oneHandUI{ - ChatListSearchBar( - searchMode: $searchMode, - searchFocussed: $searchFocussed, - searchText: $searchText, - searchShowingSimplexLink: $searchShowingSimplexLink, - searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink - ) - .scaleEffect(x: 1, y: -1, anchor: .center) - .listRowSeparator(.hidden) - .frame(maxWidth: .infinity) - .padding(10) - } List { - if !chatModel.chats.isEmpty && !oneHandUI{ - ChatListSearchBar( - searchMode: $searchMode, - searchFocussed: $searchFocussed, - searchText: $searchText, - searchShowingSimplexLink: $searchShowingSimplexLink, - searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink - ) - .listRowSeparator(.hidden) - .frame(maxWidth: .infinity) - } + Color.clear + .frame(height: 30) ForEach(cs.indices, id: \.self) { index in ChatListNavLink(chat: cs[index]) .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) @@ -114,22 +93,54 @@ struct ChatListView: View { .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) .foregroundColor(.secondary) } + + if !chatModel.chats.isEmpty && searchVisible { + VStack { + ChatListSearchBar( + searchMode: $searchMode, + searchFocussed: $searchFocussed, + searchText: $searchText, + searchShowingSimplexLink: $searchShowingSimplexLink, + searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink + ) + .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) + .listRowSeparator(.hidden) + .frame(maxWidth: .infinity) + .padding(10) + } + .background(Color(.systemBackground)) + } } } private func updateTopVisibleRowIndex(proxy: GeometryProxy, index: Int) { let frame = proxy.frame(in: .named("SCROLL")) + + if (oneHandUI) { + let screenHeight = UIScreen.main.bounds.height - if frame.minY >= 0 && frame.minY < frame.height / 2 { - if topVisibleRowIndex != index { - let shouldShowToolbar: Bool - if let topVisibleRowIndex = topVisibleRowIndex { - shouldShowToolbar = topVisibleRowIndex > index - } else { - shouldShowToolbar = true + if frame.maxY <= screenHeight && frame.maxY > screenHeight - frame.height / 2 { + if topVisibleRowIndex != index { + if let topVisibleRowIndex = topVisibleRowIndex { + searchVisible = topVisibleRowIndex > index || index == 0 + } else { + searchVisible = true + } + + topVisibleRowIndex = index + } + } + } else { + if frame.minY >= 0 && frame.minY < frame.height / 2 { + if topVisibleRowIndex != index { + if let topVisibleRowIndex = topVisibleRowIndex { + searchVisible = topVisibleRowIndex > index + } else { + searchVisible = true + } + + topVisibleRowIndex = index } - topVisibleRowIndex = index - NotificationCenter.default.post(name: .toolbarVisibilityChanged, object: shouldShowToolbar) } } } From 9b6e0ad7fd173e6f3e641efaa026a4e140127bb6 Mon Sep 17 00:00:00 2001 From: Diogo Cunha Date: Wed, 26 Jun 2024 22:50:07 +0100 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20made=20custom=20tool?= =?UTF-8?q?bar=20that=20merges=20search=20and=20nav?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit similar to ios safari, only available on ios 16+ --- .../Shared/Views/ChatList/ChatListView.swift | 60 ++++++++++++------- apps/ios/Shared/Views/Home/HomeView.swift | 27 ++++++--- 2 files changed, 58 insertions(+), 29 deletions(-) diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index 37204ff34d..7f6cce6118 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -9,7 +9,7 @@ import SwiftUI import SimpleXChat -struct ChatListView: View { +struct ChatListView: View { @EnvironmentObject var chatModel: ChatModel @State private var searchMode = false @@ -21,6 +21,7 @@ struct ChatListView: View { @State private var searchVisible: Bool = true; @AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false @AppStorage(DEFAULT_ONE_HAND_UI) private var oneHandUI = false + let toolbarContent: ToolbarContent? var body: some View { if #available(iOS 16.0, *) { @@ -29,6 +30,10 @@ struct ChatListView: View { viewBody } } + + init(@ViewBuilder toolbarContent: () -> ToolbarContent?) { + self.toolbarContent = toolbarContent() + } private var viewBody: some View { VStack { @@ -61,7 +66,7 @@ struct ChatListView: View { ScrollViewReader { scrollViewProxy in List { Color.clear - .frame(height: 30) + .frame(height: oneHandUI ? 80 : 30) ForEach(cs.indices, id: \.self) { index in ChatListNavLink(chat: cs[index]) .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) @@ -94,25 +99,38 @@ struct ChatListView: View { .foregroundColor(.secondary) } - if !chatModel.chats.isEmpty && searchVisible { - VStack { - ChatListSearchBar( - searchMode: $searchMode, - searchFocussed: $searchFocussed, - searchText: $searchText, - searchShowingSimplexLink: $searchShowingSimplexLink, - searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink - ) - .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) - .listRowSeparator(.hidden) - .frame(maxWidth: .infinity) - .padding(10) + VStack { + if let tbcontent = toolbarContent, oneHandUI, #available(iOS 16.0, *), !searchFocussed { + tbcontent + .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) + .frame(maxWidth: .infinity) + .padding(.horizontal, 25) + .padding(.bottom, 5) + .toolbar(.hidden, for: .bottomBar) + } + + if !chatModel.chats.isEmpty && searchVisible { + VStack { + ChatListSearchBar( + searchMode: $searchMode, + searchFocussed: $searchFocussed, + searchText: $searchText, + searchShowingSimplexLink: $searchShowingSimplexLink, + searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink + ) + .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) + .listRowSeparator(.hidden) + .frame(maxWidth: .infinity) + .padding(10) + } } - .background(Color(.systemBackground)) } + .background(.bar) } } + + private func updateTopVisibleRowIndex(proxy: GeometryProxy, index: Int) { let frame = proxy.frame(in: .named("SCROLL")) @@ -327,10 +345,12 @@ struct ChatListView_Previews: PreviewProvider { ] return Group { - ChatListView() - .environmentObject(chatModel) - ChatListView() - .environmentObject(ChatModel()) + ChatListView { + EmptyView() + }.environmentObject(chatModel) + ChatListView { + EmptyView() + }.environmentObject(ChatModel()) } } } diff --git a/apps/ios/Shared/Views/Home/HomeView.swift b/apps/ios/Shared/Views/Home/HomeView.swift index 75837040e3..bcc41f975a 100644 --- a/apps/ios/Shared/Views/Home/HomeView.swift +++ b/apps/ios/Shared/Views/Home/HomeView.swift @@ -53,6 +53,16 @@ struct HomeView: View { ConnectDesktopView() } } + + @ViewBuilder private func toolbar() -> some View { + settingsButton() + Spacer() + contactsButton() + Spacer() + chatsButton() + Spacer() + newChatButton() + } @ViewBuilder private func homeView() -> some View { let v = VStack { @@ -61,15 +71,10 @@ struct HomeView: View { case .chats: withToolbar("Chats", chatListView) } } + .toolbar { ToolbarItemGroup(placement: .bottomBar) { - settingsButton() - Spacer() - contactsButton() - Spacer() - chatsButton() - Spacer() - newChatButton() + toolbar() } } @@ -212,7 +217,11 @@ struct HomeView: View { @ViewBuilder private func chatListView() -> some View { // TODO reverse scale effect for swipe actions - ChatListView() + ChatListView { + HStack { + toolbar() + } + } } @ViewBuilder private func chatView() -> some View { @@ -226,4 +235,4 @@ struct HomeView: View { #Preview { HomeView(showSettings: Binding.constant(false)) -} \ No newline at end of file +}