search add
This commit is contained in:
		
							parent
							
								
									ebb34b672a
								
							
						
					
					
						commit
						32dd59e635
					
				@ -1,8 +1,7 @@
 | 
				
			|||||||
//
 | 
					 | 
				
			||||||
//  User.swift
 | 
					 | 
				
			||||||
//  volnahub (iOS)
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//  Created by cheykrym on 09/06/2025.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import Foundation
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct User: Identifiable, Decodable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let username: String
 | 
				
			||||||
 | 
					    let email: String
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										41
									
								
								Shared/ViewModels/SearchViewModel.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								Shared/ViewModels/SearchViewModel.swift
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					import Combine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SearchViewModel: ObservableObject {
 | 
				
			||||||
 | 
					    @Published var searchText = ""
 | 
				
			||||||
 | 
					    @Published var users = [User]()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private var cancellables = Set<AnyCancellable>()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    init() {
 | 
				
			||||||
 | 
					        $searchText
 | 
				
			||||||
 | 
					            .debounce(for: .milliseconds(500), scheduler: RunLoop.main)
 | 
				
			||||||
 | 
					            .removeDuplicates()
 | 
				
			||||||
 | 
					            .flatMap { query -> AnyPublisher<[User], Error> in
 | 
				
			||||||
 | 
					                if query.isEmpty {
 | 
				
			||||||
 | 
					                    return Just([])
 | 
				
			||||||
 | 
					                        .setFailureType(to: Error.self)
 | 
				
			||||||
 | 
					                        .eraseToAnyPublisher()
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    return self.searchUsers(query: query)
 | 
				
			||||||
 | 
					                        .eraseToAnyPublisher()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            .catch { _ in Just([]) } // При ошибке возвращаем пустой массив
 | 
				
			||||||
 | 
					            .receive(on: RunLoop.main)
 | 
				
			||||||
 | 
					            .assign(to: \.users, on: self)
 | 
				
			||||||
 | 
					            .store(in: &cancellables)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func searchUsers(query: String) -> Future<[User], Error> {
 | 
				
			||||||
 | 
					        Future { promise in
 | 
				
			||||||
 | 
					            // Имитация сетевого запроса
 | 
				
			||||||
 | 
					            let mockUsers = [
 | 
				
			||||||
 | 
					                User(id: "1", username: "testuser1", email: "test1@test.com"),
 | 
				
			||||||
 | 
					                User(id: "2", username: "testuser2", email: "test2@test.com"),
 | 
				
			||||||
 | 
					                User(id: "3", username: query, email: "test3@test.com")
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					            promise(.success(mockUsers))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,16 +1,68 @@
 | 
				
			|||||||
import SwiftUI
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct SearchTab: View {
 | 
					struct SearchTab: View {
 | 
				
			||||||
 | 
					    @StateObject private var viewModel = SearchViewModel()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    var body: some View {
 | 
					    var body: some View {
 | 
				
			||||||
        NavigationView {
 | 
					        NavigationView {
 | 
				
			||||||
            VStack {
 | 
					            VStack {
 | 
				
			||||||
                Text("Поиск")
 | 
					                SearchBar(text: $viewModel.searchText)
 | 
				
			||||||
                    .font(.largeTitle)
 | 
					                    .padding(.top, 8)
 | 
				
			||||||
                    .bold()
 | 
					                
 | 
				
			||||||
                    .padding()
 | 
					                List(viewModel.users) { user in
 | 
				
			||||||
                Spacer()
 | 
					                    NavigationLink(destination: ProfileTab(viewModel: LoginViewModel())) { // Placeholder destination
 | 
				
			||||||
 | 
					                        HStack {
 | 
				
			||||||
 | 
					                            Image(systemName: "person.crop.circle")
 | 
				
			||||||
 | 
					                                .resizable()
 | 
				
			||||||
 | 
					                                .frame(width: 40, height: 40)
 | 
				
			||||||
 | 
					                                .clipShape(Circle())
 | 
				
			||||||
 | 
					                            Text(user.username)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                .listStyle(PlainListStyle())
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            .navigationTitle("Поиск")
 | 
					            .navigationTitle("Поиск")
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SearchBar: View {
 | 
				
			||||||
 | 
					    @Binding var text: String
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    var body: some View {
 | 
				
			||||||
 | 
					        HStack {
 | 
				
			||||||
 | 
					            TextField("Поиск...", text: $text)
 | 
				
			||||||
 | 
					                .padding(8)
 | 
				
			||||||
 | 
					                .padding(.horizontal, 25)
 | 
				
			||||||
 | 
					                .background(Color(.systemGray6))
 | 
				
			||||||
 | 
					                .cornerRadius(8)
 | 
				
			||||||
 | 
					                .overlay(
 | 
				
			||||||
 | 
					                    HStack {
 | 
				
			||||||
 | 
					                        Image(systemName: "magnifyingglass")
 | 
				
			||||||
 | 
					                            .foregroundColor(.gray)
 | 
				
			||||||
 | 
					                            .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
 | 
				
			||||||
 | 
					                            .padding(.leading, 8)
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        if !text.isEmpty {
 | 
				
			||||||
 | 
					                            Button(action: {
 | 
				
			||||||
 | 
					                                self.text = ""
 | 
				
			||||||
 | 
					                            }) {
 | 
				
			||||||
 | 
					                                Image(systemName: "multiply.circle.fill")
 | 
				
			||||||
 | 
					                                    .foregroundColor(.gray)
 | 
				
			||||||
 | 
					                                    .padding(.trailing, 8)
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .padding(.horizontal, 10)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SearchTab_Previews: PreviewProvider {
 | 
				
			||||||
 | 
					    static var previews: some View {
 | 
				
			||||||
 | 
					        SearchTab()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@
 | 
				
			|||||||
/* Begin PBXBuildFile section */
 | 
					/* Begin PBXBuildFile section */
 | 
				
			||||||
		1A0276032DF909F900D8BC53 /* refreshtokenex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0276022DF909F900D8BC53 /* refreshtokenex.swift */; };
 | 
							1A0276032DF909F900D8BC53 /* refreshtokenex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0276022DF909F900D8BC53 /* refreshtokenex.swift */; };
 | 
				
			||||||
		1A0276112DF9247000D8BC53 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0276102DF9247000D8BC53 /* CustomTextField.swift */; };
 | 
							1A0276112DF9247000D8BC53 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A0276102DF9247000D8BC53 /* CustomTextField.swift */; };
 | 
				
			||||||
 | 
							1A2302112E3050C60067BF4F /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2302102E3050C60067BF4F /* SearchViewModel.swift */; };
 | 
				
			||||||
		1A79408D2DF77BC3002569DA /* yobbleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79407A2DF77BC2002569DA /* yobbleApp.swift */; };
 | 
							1A79408D2DF77BC3002569DA /* yobbleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79407A2DF77BC2002569DA /* yobbleApp.swift */; };
 | 
				
			||||||
		1A79408F2DF77BC3002569DA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79407B2DF77BC2002569DA /* ContentView.swift */; };
 | 
							1A79408F2DF77BC3002569DA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79407B2DF77BC2002569DA /* ContentView.swift */; };
 | 
				
			||||||
		1A7940902DF77BC3002569DA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79407B2DF77BC2002569DA /* ContentView.swift */; };
 | 
							1A7940902DF77BC3002569DA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79407B2DF77BC2002569DA /* ContentView.swift */; };
 | 
				
			||||||
@ -49,6 +50,7 @@
 | 
				
			|||||||
/* Begin PBXFileReference section */
 | 
					/* Begin PBXFileReference section */
 | 
				
			||||||
		1A0276022DF909F900D8BC53 /* refreshtokenex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = refreshtokenex.swift; sourceTree = "<group>"; };
 | 
							1A0276022DF909F900D8BC53 /* refreshtokenex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = refreshtokenex.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		1A0276102DF9247000D8BC53 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; };
 | 
							1A0276102DF9247000D8BC53 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; };
 | 
				
			||||||
 | 
							1A2302102E3050C60067BF4F /* SearchViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchViewModel.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		1A79407A2DF77BC2002569DA /* yobbleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = yobbleApp.swift; sourceTree = "<group>"; };
 | 
							1A79407A2DF77BC2002569DA /* yobbleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = yobbleApp.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		1A79407B2DF77BC2002569DA /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
 | 
							1A79407B2DF77BC2002569DA /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		1A79407C2DF77BC3002569DA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 | 
							1A79407C2DF77BC3002569DA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 | 
				
			||||||
@ -177,6 +179,7 @@
 | 
				
			|||||||
		1A79409E2DF77DBD002569DA /* ViewModels */ = {
 | 
							1A79409E2DF77DBD002569DA /* ViewModels */ = {
 | 
				
			||||||
			isa = PBXGroup;
 | 
								isa = PBXGroup;
 | 
				
			||||||
			children = (
 | 
								children = (
 | 
				
			||||||
 | 
									1A2302102E3050C60067BF4F /* SearchViewModel.swift */,
 | 
				
			||||||
				1A7940A92DF77E05002569DA /* LoginViewModel.swift */,
 | 
									1A7940A92DF77E05002569DA /* LoginViewModel.swift */,
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			path = ViewModels;
 | 
								path = ViewModels;
 | 
				
			||||||
@ -387,6 +390,7 @@
 | 
				
			|||||||
				1ACE61212E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift in Sources */,
 | 
									1ACE61212E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift in Sources */,
 | 
				
			||||||
				1AB4F8F72E22ECAC002B6E40 /* FollowingView.swift in Sources */,
 | 
									1AB4F8F72E22ECAC002B6E40 /* FollowingView.swift in Sources */,
 | 
				
			||||||
				1A7940DE2DF7B0D7002569DA /* config.swift in Sources */,
 | 
									1A7940DE2DF7B0D7002569DA /* config.swift in Sources */,
 | 
				
			||||||
 | 
									1A2302112E3050C60067BF4F /* SearchViewModel.swift in Sources */,
 | 
				
			||||||
				1AE587252E23337000254F06 /* ProfileContentGrid.swift in Sources */,
 | 
									1AE587252E23337000254F06 /* ProfileContentGrid.swift in Sources */,
 | 
				
			||||||
				1A0276032DF909F900D8BC53 /* refreshtokenex.swift in Sources */,
 | 
									1A0276032DF909F900D8BC53 /* refreshtokenex.swift in Sources */,
 | 
				
			||||||
				1A7940B62DF77F21002569DA /* MainView.swift in Sources */,
 | 
									1A7940B62DF77F21002569DA /* MainView.swift in Sources */,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user