mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-18 04:10:14 +00:00
ios: search names and the directory from New chat
The New chat sheet gets the chat list's online search: a "Search in Directory" row, the keyboard Search key for names and the directory, and Names and Directory sections after the contacts. Tapping a result connects and closes the sheet, as other New chat actions do. A contact the list already shows is not repeated as a name result, and the contacts list's empty state stays hidden while a search runs or shows results. Archived contacts, which share the search bar and the list, are unchanged. Run: 002-ios-directory-search-ui, slice 4
This commit is contained in:
@@ -43,6 +43,7 @@ struct NewChatSheet: View {
|
||||
@State private var searchChatFilteredBySimplexLink: String? = nil
|
||||
// when the search text is a SimpleX name, the string to connect to (with @/# preserved); nil otherwise
|
||||
@State private var connectNameCandidate: String? = nil
|
||||
@StateObject private var onlineSearch = OnlineSearchModel()
|
||||
@State private var alert: SomeAlert?
|
||||
|
||||
// Sheet height management
|
||||
@@ -52,6 +53,7 @@ struct NewChatSheet: View {
|
||||
@State private var allowSmallSheet = true
|
||||
|
||||
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true
|
||||
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
|
||||
|
||||
var body: some View {
|
||||
let showArchive = chatModel.chats.contains { $0.chatInfo.contact?.chatDeleted == true }
|
||||
@@ -90,10 +92,11 @@ struct NewChatSheet: View {
|
||||
searchText: $searchText,
|
||||
searchShowingSimplexLink: $searchShowingSimplexLink,
|
||||
searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink,
|
||||
connectNameCandidate: $connectNameCandidate
|
||||
connectNameCandidate: $connectNameCandidate,
|
||||
onSearch: runOnlineSearch
|
||||
)
|
||||
.frame(maxWidth: .infinity)
|
||||
if let candidate = connectNameCandidate {
|
||||
if let candidate = connectNameCandidate, onlineSearch.names.isEmpty {
|
||||
ConnectByNameRow(
|
||||
name: candidate,
|
||||
searchText: $searchText,
|
||||
@@ -102,6 +105,9 @@ struct NewChatSheet: View {
|
||||
dismiss: true
|
||||
)
|
||||
}
|
||||
if !searchTrimmed.isEmpty, !searchShowingSimplexLink {
|
||||
SearchInDirectoryRow(searchFocussed: $searchFocussed, onSearch: runDirectorySearch)
|
||||
}
|
||||
}
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowBackground(Color.clear)
|
||||
@@ -155,7 +161,16 @@ struct NewChatSheet: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let shownContacts = ContactsList.shownChats(
|
||||
chats: chatModel.chats,
|
||||
predicate: contactListChatPredicate,
|
||||
searchText: searchText,
|
||||
searchShowingSimplexLink: searchShowingSimplexLink,
|
||||
searchChatFilteredBySimplexLink: searchChatFilteredBySimplexLink,
|
||||
showUnreadAndFavorites: showUnreadAndFavorites
|
||||
).shown
|
||||
let rows = onlineSearch.rows(shownChatIds: Set(shownContacts.map { $0.id }))
|
||||
ContactsList(
|
||||
chatPredicate: contactListChatPredicate,
|
||||
searchMode: $searchMode,
|
||||
@@ -164,9 +179,71 @@ struct NewChatSheet: View {
|
||||
searchFocussed: $searchFocussed,
|
||||
searchShowingSimplexLink: $searchShowingSimplexLink,
|
||||
searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink,
|
||||
showDeletedChatIcon: true
|
||||
showDeletedChatIcon: true,
|
||||
showsEmptyState: !onlineSearch.searching && rows.isEmpty
|
||||
)
|
||||
onlineSearchSections(rows)
|
||||
}
|
||||
.onChange(of: searchText) { _ in
|
||||
onlineSearch.reset()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func onlineSearchSections(_ rows: OnlineSearchRows) -> some View {
|
||||
if !rows.names.isEmpty {
|
||||
Section {
|
||||
ForEach(rows.names) { result in
|
||||
NameSearchRow(result: result)
|
||||
.onTapGesture {
|
||||
searchFocussed = false
|
||||
planAndConnect(result.target, theme: theme, dismiss: true, cleanup: nil)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
onlineSearchHeader("Names")
|
||||
}
|
||||
}
|
||||
if !rows.directory.isEmpty || rows.footer != nil {
|
||||
Section {
|
||||
ForEach(rows.directory) { result in
|
||||
DirectorySearchRow(entry: result.entry)
|
||||
.onTapGesture {
|
||||
searchFocussed = false
|
||||
planAndConnect(result.entry.link, theme: theme, dismiss: true, cleanup: nil)
|
||||
}
|
||||
}
|
||||
switch rows.footer {
|
||||
case .retry: DirectoryRetryRow(onlineSearch: onlineSearch)
|
||||
case .showMore: DirectoryShowMoreRow(onlineSearch: onlineSearch)
|
||||
case nil: EmptyView()
|
||||
}
|
||||
} header: {
|
||||
onlineSearchHeader("Directory")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func onlineSearchHeader(_ title: LocalizedStringKey) -> some View {
|
||||
Text(title)
|
||||
.textCase(.uppercase)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
}
|
||||
|
||||
private var searchTrimmed: String {
|
||||
searchText.trimmingCharacters(in: .whitespaces)
|
||||
}
|
||||
|
||||
private func runDirectorySearch() {
|
||||
let text = searchTrimmed
|
||||
guard !text.isEmpty, !searchShowingSimplexLink else { return }
|
||||
confirmDirectorySearch { Task { await onlineSearch.searchDirectory(text) } }
|
||||
}
|
||||
|
||||
private func runOnlineSearch() {
|
||||
let text = searchTrimmed
|
||||
guard !text.isEmpty, !searchShowingSimplexLink else { return }
|
||||
let name = connectNameCandidate
|
||||
confirmDirectorySearch { Task { await onlineSearch.search(text, name: name) } }
|
||||
}
|
||||
|
||||
private func contactListChatPredicate(_ chat: Chat, _ withSearch: Bool) -> Bool {
|
||||
@@ -231,8 +308,9 @@ struct ContactsList: View {
|
||||
@Binding var searchShowingSimplexLink: Bool
|
||||
@Binding var searchChatFilteredBySimplexLink: String?
|
||||
var showDeletedChatIcon: Bool
|
||||
var showsEmptyState = true
|
||||
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
let (contactChats, filteredContactChats) = Self.shownChats(
|
||||
chats: chatModel.chats,
|
||||
@@ -259,10 +337,12 @@ struct ContactsList: View {
|
||||
}
|
||||
}
|
||||
|
||||
if filteredContactChats.isEmpty && !contactChats.isEmpty {
|
||||
noResultSection(text: "No filtered contacts")
|
||||
} else if contactChats.isEmpty {
|
||||
noResultSection(text: "No contacts")
|
||||
if showsEmptyState {
|
||||
if filteredContactChats.isEmpty && !contactChats.isEmpty {
|
||||
noResultSection(text: "No filtered contacts")
|
||||
} else if contactChats.isEmpty {
|
||||
noResultSection(text: "No contacts")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,6 +439,7 @@ struct ContactsListSearchBar: View {
|
||||
@Binding var searchShowingSimplexLink: Bool
|
||||
@Binding var searchChatFilteredBySimplexLink: String?
|
||||
@Binding var connectNameCandidate: String?
|
||||
var onSearch: (() -> Void)? = nil
|
||||
@State private var ignoreSearchTextChange = false
|
||||
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
|
||||
|
||||
@@ -376,6 +457,8 @@ struct ContactsListSearchBar: View {
|
||||
.disabled(searchShowingSimplexLink)
|
||||
.focused($searchFocussed)
|
||||
.frame(maxWidth: .infinity)
|
||||
.submitLabel(onSearch == nil ? .return : .search)
|
||||
.onSubmit { onSearch?() }
|
||||
if connectProgressManager.showConnectProgress != nil {
|
||||
ProgressView()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user