mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-28 23:36:03 +00:00
wip
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
//
|
||||
// ContactsView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by spaced4ndy on 06.05.2024.
|
||||
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct ContactsView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
|
||||
@State private var searchMode = false
|
||||
@FocusState private var searchFocussed
|
||||
@State private var searchText = ""
|
||||
|
||||
@AppStorage(DEFAULT_ONE_HAND_UI) private var oneHandUI = true
|
||||
|
||||
var body: some View {
|
||||
if #available(iOS 16.0, *) {
|
||||
viewBody.scrollDismissesKeyboard(.immediately)
|
||||
} else {
|
||||
viewBody
|
||||
}
|
||||
}
|
||||
|
||||
private var viewBody: some View {
|
||||
VStack {
|
||||
contactList
|
||||
}
|
||||
.listStyle(.plain)
|
||||
}
|
||||
|
||||
@ViewBuilder private var contactList: some View {
|
||||
let cs = filteredContacts()
|
||||
ZStack {
|
||||
VStack {
|
||||
List {
|
||||
if !chatModel.chats.isEmpty {
|
||||
ContactsSearchBar(
|
||||
searchMode: $searchMode,
|
||||
searchFocussed: $searchFocussed,
|
||||
searchText: $searchText
|
||||
)
|
||||
.listRowSeparator(.hidden)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
ForEach(cs, id: \.viewId) { chat in
|
||||
ChatListNavLink(chat: chat)
|
||||
.padding(.trailing, -16)
|
||||
.disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id))
|
||||
}
|
||||
.offset(x: -8)
|
||||
}
|
||||
}
|
||||
if cs.isEmpty && !chatModel.chats.isEmpty {
|
||||
Text("No filtered contacts")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func filteredContacts() -> [Chat] {
|
||||
let s = searchString()
|
||||
return chatModel.chats.filter { chat in
|
||||
let cInfo = chat.chatInfo
|
||||
switch cInfo {
|
||||
case let .direct(contact):
|
||||
return s == ""
|
||||
? true
|
||||
: (viewNameContains(cInfo, s) ||
|
||||
contact.profile.displayName.localizedLowercase.contains(s) ||
|
||||
contact.fullName.localizedLowercase.contains(s))
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func searchString() -> String {
|
||||
searchText.trimmingCharacters(in: .whitespaces).localizedLowercase
|
||||
}
|
||||
|
||||
func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool {
|
||||
cInfo.chatViewName.localizedLowercase.contains(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContactsSearchBar: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@Binding var searchMode: Bool
|
||||
@FocusState.Binding var searchFocussed: Bool
|
||||
@Binding var searchText: String
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 12) {
|
||||
HStack(spacing: 12) {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "magnifyingglass")
|
||||
TextField("Search", text: $searchText)
|
||||
.focused($searchFocussed)
|
||||
.frame(maxWidth: .infinity)
|
||||
if !searchText.isEmpty {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.onTapGesture {
|
||||
searchText = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(EdgeInsets(top: 7, leading: 7, bottom: 7, trailing: 7))
|
||||
.foregroundColor(.secondary)
|
||||
.background(Color(.tertiarySystemFill))
|
||||
.cornerRadius(10.0)
|
||||
|
||||
if searchFocussed {
|
||||
Text("Cancel")
|
||||
.foregroundColor(.accentColor)
|
||||
.onTapGesture {
|
||||
searchText = ""
|
||||
searchFocussed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: searchFocussed) { sf in
|
||||
withAnimation { searchMode = sf }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContactsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let chatModel = ChatModel()
|
||||
chatModel.chats = [
|
||||
Chat(
|
||||
chatInfo: ChatInfo.sampleData.direct,
|
||||
chatItems: []
|
||||
)
|
||||
]
|
||||
return Group {
|
||||
ContactsView()
|
||||
.environmentObject(chatModel)
|
||||
ContactsView()
|
||||
.environmentObject(ChatModel())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,18 +254,6 @@ struct ChatsSearchBar: View {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
func chatsStoppedIcon() -> some View {
|
||||
Button {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: "Chat is stopped",
|
||||
message: "You can start chat via app Settings / Database or by restarting the app"
|
||||
)
|
||||
} label: {
|
||||
Image(systemName: "exclamationmark.octagon.fill").foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let chatModel = ChatModel()
|
||||
|
||||
@@ -182,32 +182,56 @@ struct HomeView: View {
|
||||
.foregroundColor(homeTab == .chats ? .accentColor : .secondary)
|
||||
}
|
||||
|
||||
private func newChatButton() -> some View {
|
||||
Menu {
|
||||
Button {
|
||||
newChatMenuOption = .newGroup
|
||||
@ViewBuilder private func newChatButton() -> some View {
|
||||
if case .some(false) = chatModel.chatRunning {
|
||||
chatsStoppedButton()
|
||||
} else {
|
||||
Menu {
|
||||
Button {
|
||||
newChatMenuOption = .newGroup
|
||||
} label: {
|
||||
Text("Create group")
|
||||
}
|
||||
Button {
|
||||
newChatMenuOption = .scanPaste
|
||||
} label: {
|
||||
Text("Scan / Paste link")
|
||||
}
|
||||
Button {
|
||||
newChatMenuOption = .newContact
|
||||
} label: {
|
||||
Text("Add contact")
|
||||
}
|
||||
} label: {
|
||||
Text("Create group")
|
||||
iconLabel("square.and.pencil", "New chat")
|
||||
}
|
||||
Button {
|
||||
newChatMenuOption = .scanPaste
|
||||
} label: {
|
||||
Text("Scan / Paste link")
|
||||
.foregroundColor(.secondary)
|
||||
.sheet(item: $newChatMenuOption) { opt in
|
||||
switch opt {
|
||||
case .newContact: NewChatView(selection: .invite)
|
||||
case .scanPaste: NewChatView(selection: .connect, showQRCodeScanner: true)
|
||||
case .newGroup: AddGroupView()
|
||||
}
|
||||
}
|
||||
Button {
|
||||
newChatMenuOption = .newContact
|
||||
} label: {
|
||||
Text("Add contact")
|
||||
}
|
||||
} label: {
|
||||
iconLabel("square.and.pencil", "New chat")
|
||||
}
|
||||
.foregroundColor(.secondary)
|
||||
.sheet(item: $newChatMenuOption) { opt in
|
||||
switch opt {
|
||||
case .newContact: NewChatView(selection: .invite)
|
||||
case .scanPaste: NewChatView(selection: .connect, showQRCodeScanner: true)
|
||||
case .newGroup: AddGroupView()
|
||||
}
|
||||
|
||||
func chatsStoppedButton() -> some View {
|
||||
Button {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: "Chat is stopped",
|
||||
message: "You can start chat via app Settings / Database or by restarting the app"
|
||||
)
|
||||
} label: {
|
||||
VStack(spacing: 4) {
|
||||
Image(systemName: "exclamationmark.octagon.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.foregroundColor(.red)
|
||||
.frame(width: 24, height: 24)
|
||||
Text("Stopped")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,10 +248,7 @@ struct HomeView: View {
|
||||
}
|
||||
|
||||
private func contactsView() -> some View {
|
||||
// TODO
|
||||
VStack {
|
||||
Text("Contacts")
|
||||
}
|
||||
ContactsView()
|
||||
}
|
||||
|
||||
@ViewBuilder private func chatsView() -> some View {
|
||||
|
||||
@@ -174,6 +174,7 @@
|
||||
646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */; };
|
||||
647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */; };
|
||||
648010AB281ADD15009009B9 /* CIFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648010AA281ADD15009009B9 /* CIFileView.swift */; };
|
||||
6485F7C62BE8DB6500FAE413 /* ContactsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6485F7C52BE8DB6500FAE413 /* ContactsView.swift */; };
|
||||
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */; };
|
||||
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; };
|
||||
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; };
|
||||
@@ -471,6 +472,7 @@
|
||||
646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAuthenticationUtils.swift; sourceTree = "<group>"; };
|
||||
647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMemberInfoView.swift; sourceTree = "<group>"; };
|
||||
648010AA281ADD15009009B9 /* CIFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIFileView.swift; sourceTree = "<group>"; };
|
||||
6485F7C52BE8DB6500FAE413 /* ContactsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsView.swift; sourceTree = "<group>"; };
|
||||
648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemForwardingView.swift; sourceTree = "<group>"; };
|
||||
6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = "<group>"; };
|
||||
@@ -586,6 +588,7 @@
|
||||
5C5F4AC227A5E9AF00B51EF1 /* Chat */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6485F7C42BE8DB4F00FAE413 /* Contacts */,
|
||||
6440CA01288AEC770062C672 /* Group */,
|
||||
5CE4407427ADB657007B033A /* ChatItem */,
|
||||
5CEACCE527DE977C000BD591 /* ComposeMessage */,
|
||||
@@ -927,6 +930,14 @@
|
||||
path = Group;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6485F7C42BE8DB4F00FAE413 /* Contacts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6485F7C52BE8DB6500FAE413 /* ContactsView.swift */,
|
||||
);
|
||||
path = Contacts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8C7D94982B8894D300B7B9E1 /* Migration */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1182,6 +1193,7 @@
|
||||
5CB2084F28DA4B4800D024EC /* RTCServers.swift in Sources */,
|
||||
5CB634AF29E4BB7D0066AD6B /* SetAppPasscodeView.swift in Sources */,
|
||||
5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */,
|
||||
6485F7C62BE8DB6500FAE413 /* ContactsView.swift in Sources */,
|
||||
5CBE6C12294487F7002D9531 /* VerifyCodeView.swift in Sources */,
|
||||
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */,
|
||||
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */,
|
||||
|
||||
Reference in New Issue
Block a user