diff --git a/HappyAnding/HappyAnding.xcodeproj/project.pbxproj b/HappyAnding/HappyAnding.xcodeproj/project.pbxproj index 785c44b0..d0d284bb 100644 --- a/HappyAnding/HappyAnding.xcodeproj/project.pbxproj +++ b/HappyAnding/HappyAnding.xcodeproj/project.pbxproj @@ -179,6 +179,7 @@ F9DB8EBB293986E100516CE1 /* ShareExtensionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F976E85029395B350088BBA1 /* ShareExtensionViewModel.swift */; }; F9DB8EBD293987BD00516CE1 /* ShareExtensionCustomTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9DB8EBC293987BD00516CE1 /* ShareExtensionCustomTextEditor.swift */; }; F9DB8ED3293B4C3200516CE1 /* ShareExtensionTagTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9DB8ED2293B4C3200516CE1 /* ShareExtensionTagTextField.swift */; }; + F9E7073A2BC6933000319533 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E707392BC6933000319533 /* SearchViewModel.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -359,6 +360,7 @@ F9DB8EB82939853D00516CE1 /* ShareExtensionValidationTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareExtensionValidationTextField.swift; sourceTree = ""; }; F9DB8EBC293987BD00516CE1 /* ShareExtensionCustomTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareExtensionCustomTextEditor.swift; sourceTree = ""; }; F9DB8ED2293B4C3200516CE1 /* ShareExtensionTagTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareExtensionTagTextField.swift; sourceTree = ""; }; + F9E707392BC6933000319533 /* SearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModel.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -562,6 +564,7 @@ A34BF82929AFC308009BC946 /* FeatureViews */, 87E606AE291062D300C3DA13 /* SignInViews */, 8792479A291BDF820040D5C3 /* SearchView.swift */, + F9E707392BC6933000319533 /* SearchViewModel.swift */, 3D41EE07290A4C18008BE986 /* Launch Screen.storyboard */, F9136EB5293612310034AAB2 /* ShortcutsZipView.swift */, ); @@ -687,7 +690,6 @@ F91F09DC29AE012600E04FA0 /* ShortcutGrade.swift */, 872B5D3C2A2E0FF9008DCC57 /* CurationType.swift */, A323D3C92AEE870700DDA716 /* SuggestionForm.swift */, - F980171D2BBC2A24004F2EA7 /* ExploreShortcutSectionType.swift */, ); path = Model; sourceTree = ""; @@ -1039,6 +1041,7 @@ A323D3CA2AEE870700DDA716 /* SuggestionForm.swift in Sources */, 4DF15D752A4ECE1F0014F854 /* ListCategoryShortcutViewModel.swift in Sources */, 87E99CDB29042CCA009B691F /* Category.swift in Sources */, + F9E7073A2BC6933000319533 /* SearchViewModel.swift in Sources */, 876B4F6F299E3D91009672D9 /* NavigationRouter.swift in Sources */, F98017202BBC2A6F004F2EA7 /* ExploreCell.swift in Sources */, A04ACB062903D0B2004A85A6 /* MyShortcutCardView.swift in Sources */, @@ -1340,7 +1343,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.happyanding.HappyAnding; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ShortcutsZipProfile_Dev_20231113; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ShortcutsZipProfile_Dev_20240317; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -1383,7 +1386,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.happyanding.HappyAnding; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ShortcutsZipProfile_Dev_20231113; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ShortcutsZipProfile_Dev_20240317; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -1398,14 +1401,16 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 7JPPWR5997; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.happyanding.HappyAndingTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1418,14 +1423,16 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 7JPPWR5997; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.happyanding.HappyAndingTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1437,13 +1444,15 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 7JPPWR5997; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.happyanding.HappyAndingUITests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1455,13 +1464,15 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 7JPPWR5997; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.happyanding.HappyAndingUITests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1492,7 +1503,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.happyanding.HappyAnding.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ShortcutsZipShareExtProfile_Dev_20231113; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ShortcutsZipShareExtProfile_Dev_20240317; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -1524,7 +1535,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.happyanding.HappyAnding.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ShortcutsZipShareExtProfile_Dev_20231113; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = ShortcutsZipShareExtProfile_Dev_20240317; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/HappyAnding/HappyAnding/Extensions/SCZ+Color.swift b/HappyAnding/HappyAnding/Extensions/SCZ+Color.swift index 5ed1833b..7ba67af5 100644 --- a/HappyAnding/HappyAnding/Extensions/SCZ+Color.swift +++ b/HappyAnding/HappyAnding/Extensions/SCZ+Color.swift @@ -60,7 +60,6 @@ struct GradientType { } struct SCZColor { - static let defaultColor = Red().light.fillGradient() static let colors: [String: ColorProtocol] = [ "Red": Red(), "Coral": Coral(), @@ -71,6 +70,7 @@ struct SCZColor { "Teal": Teal(), "Cyan": Cyan(), "Blue": Blue(), + "Pink": Pink(), "Purple": Purple(), "LightPurple": Lavendar(), "Silver": Silver(), @@ -565,7 +565,7 @@ extension SCZColor { ) } struct CharcoalGray { - static let color: Color = Color(hexString: "404040") + static let color = Color(hexString: "404040") static let opacity88 = Color(hexString: "404040", opacity: 0.88) static let opacity64 = Color(hexString: "404040", opacity: 0.64) static let opacity48 = Color(hexString: "404040", opacity: 0.48) @@ -574,4 +574,12 @@ extension SCZColor { static let opacity08 = Color(hexString: "404040", opacity: 0.08) static let opacity04 = Color(hexString: "404040", opacity: 0.04) } + + struct SCZBlue { + static let strong = Color(hexString: "3366FF") + static let opacity88 = Color(hexString: "4B78FF") + static let opacity48 = Color(hexString: "9DB6FF") + static let opacity16 = Color(hexString: "DEE7FF") + static let opacity08 = Color(hexString: "EFF3FF") + } } diff --git a/HappyAnding/HappyAnding/Extensions/SCZColor.swift b/HappyAnding/HappyAnding/Extensions/SCZColor.swift new file mode 100644 index 00000000..e59149c9 --- /dev/null +++ b/HappyAnding/HappyAnding/Extensions/SCZColor.swift @@ -0,0 +1,8 @@ +// +// SCZColor.swift +// HappyAnding +// +// Created by JeonJimin on 4/10/24. +// + +import Foundation diff --git a/HappyAnding/HappyAnding/Extensions/String/String+Date.swift b/HappyAnding/HappyAnding/Extensions/String/String+Date.swift index 71a96588..598bdc6c 100644 --- a/HappyAnding/HappyAnding/Extensions/String/String+Date.swift +++ b/HappyAnding/HappyAnding/Extensions/String/String+Date.swift @@ -34,4 +34,20 @@ extension String { return date } + + func getPostDateFormat() -> String? { + let inputFormatter = DateFormatter() + inputFormatter.dateFormat = "yyyyMMddHHmmss" + + if let date = inputFormatter.date(from: self) { + let outputFormatter = DateFormatter() + outputFormatter.locale = Locale(identifier: "ko_KR") + outputFormatter.dateFormat = "M월 d일 a h시 m분" + + let output = outputFormatter.string(from: date) + return output + } else { + return nil + } + } } diff --git a/HappyAnding/HappyAnding/Extensions/View/View+Navigation.swift b/HappyAnding/HappyAnding/Extensions/View/View+Navigation.swift index 27f50f03..89b23fba 100644 --- a/HappyAnding/HappyAnding/Extensions/View/View+Navigation.swift +++ b/HappyAnding/HappyAnding/Extensions/View/View+Navigation.swift @@ -87,8 +87,6 @@ extension View { ListCurationView(viewModel: ListCurationViewModel(data: data as! CurationType)) case is User: ShowProfileView(viewModel: ShowProfileViewModel(data: data as! User)) - case is NavigationSearch: - SearchView() case is Category: ListCategoryShortcutView(viewModel: ListCategoryShortcutViewModel(data: data as! Category)) case is NavigationNicknameView: @@ -141,9 +139,6 @@ struct NavigationViewModifier: ViewModifier { .navigationDestination(for: NavigationWithdrawal.self) { _ in WithdrawalView() } - .navigationDestination(for: NavigationSearch.self) { _ in - SearchView() - } .navigationDestination(for: NavigationSettingView.self) { _ in SettingView() } diff --git a/HappyAnding/HappyAnding/Model/SectionType.swift b/HappyAnding/HappyAnding/Model/SectionType.swift index 34e040b9..46cdb649 100644 --- a/HappyAnding/HappyAnding/Model/SectionType.swift +++ b/HappyAnding/HappyAnding/Model/SectionType.swift @@ -83,8 +83,8 @@ enum SectionType { case .popular: return Image(systemName: self.icon) .foregroundStyle( - Color(hexString: "3366FF", opacity: 0.88), - Color(hexString: "3366FF", opacity: 0.88) + SCZColor.SCZBlue.opacity88, + SCZColor.SCZBlue.opacity88 ) case .myShortcut: return Image(systemName: "square.text.square.fill") @@ -101,8 +101,8 @@ enum SectionType { case .myLovingShortcut: return Image(systemName: "heart.fill") .foregroundStyle( - Color(hexString: "3366FF", opacity: 0.88), - Color(hexString: "3366FF", opacity: 0.88) + SCZColor.SCZBlue.opacity88, + SCZColor.SCZBlue.opacity88 ) } } diff --git a/HappyAnding/HappyAnding/TextLiteral.swift b/HappyAnding/HappyAnding/TextLiteral.swift index 487884f1..fc358608 100644 --- a/HappyAnding/HappyAnding/TextLiteral.swift +++ b/HappyAnding/HappyAnding/TextLiteral.swift @@ -246,6 +246,18 @@ enum TextLiteral { static let searchViewRecommendedKeyword: String = "추천 검색어" static let searchViewProposal: String = "단축어 제안하기" static let searchViewProposalURL: String = "https://docs.google.com/forms/d/e/1FAIpQLScQc3KeYjDGCE-C2YRU-Hwy2XNy5bt89KVX1OMUzRiySaMX1Q/viewform" + static let searchViewMoreResult: String = "더 많은 검색 결과 보기" + static let searchViewRelatedShortcut: String = "관련된 단축어" + static let searchVIewRelatedPost: String = "관련된 글" + static func searchViewEmptyResult(_ searchText: String) -> String { + return "😵 \'\(searchText)\'에 관련된 단축어나 글이 없어요." + } + static func searchTextRelatedShortcutShare(_ searchText: String) -> String { + return "\'\(searchText)\' 관련 단축어 공유하기" + } + static func searchTextRelatedPost(_ searchText: String) -> String { + return "\'\(searchText)\' 관련 질문하기" + } //MARK: - CustomShareViewController static let customShareViewControllerSignInAlertTitle: String = "로그인을 먼저 진행해주세요" diff --git a/HappyAnding/HappyAnding/Views/Components/ExploreCell.swift b/HappyAnding/HappyAnding/Views/Components/ExploreCell.swift index 4d00396e..98faa9c3 100644 --- a/HappyAnding/HappyAnding/Views/Components/ExploreCell.swift +++ b/HappyAnding/HappyAnding/Views/Components/ExploreCell.swift @@ -45,7 +45,7 @@ struct OrderedCell: View { } .padding(12) .frame(width: 108, height: 144, alignment: .top) - .background( SCZColor.colors[shortcut.color]?.color(for: colorScheme).fillGradient() ?? SCZColor.defaultColor) + .background( SCZColor.colors[shortcut.color]?.color(for: colorScheme).fillGradient() ?? Color.clear.toGradient()) .cornerRadius(16) .roundedBorder(cornerRadius: 16, color: Color.white, isNormalBlend: true, opacity: 0.12) } @@ -66,7 +66,7 @@ struct UnorderedCell: View{ HStack { RoundedRectangle(cornerRadius: 1) .frame(width: 2, height: 30) - .foregroundStyle(SCZColor.colors[shortcut.color]?.color(for: colorScheme).fillGradient() ?? SCZColor.defaultColor) + .foregroundStyle(SCZColor.colors[shortcut.color]?.color(for: colorScheme).fillGradient() ?? Color.clear.toGradient()) Image(systemName: shortcut.sfSymbol) .foregroundStyle(SCZColor.CharcoalGray.opacity88) } diff --git a/HappyAnding/HappyAnding/Views/Components/ShortcutIcon.swift b/HappyAnding/HappyAnding/Views/Components/ShortcutIcon.swift index 3fbee17d..800d259f 100644 --- a/HappyAnding/HappyAnding/Views/Components/ShortcutIcon.swift +++ b/HappyAnding/HappyAnding/Views/Components/ShortcutIcon.swift @@ -17,7 +17,7 @@ struct ShortcutIcon: View { var body: some View { ZStack { RoundedRectangle(cornerRadius: 13) - .foregroundStyle(SCZColor.colors[color]?.color(for: colorScheme).fillGradient() ?? SCZColor.defaultColor) + .foregroundStyle(SCZColor.colors[color]?.color(for: colorScheme).fillGradient() ?? Color.clear.toGradient()) .roundedBorder(cornerRadius: 13, color: .white, isNormalBlend: true, opacity: 0.24) .frame(width: size, height: size) Image(systemName: sfSymbol) diff --git a/HappyAnding/HappyAnding/Views/ExploreShortcutViews/ExploreShortcutView.swift b/HappyAnding/HappyAnding/Views/ExploreShortcutViews/ExploreShortcutView.swift index c3bce693..4dccb0e5 100644 --- a/HappyAnding/HappyAnding/Views/ExploreShortcutViews/ExploreShortcutView.swift +++ b/HappyAnding/HappyAnding/Views/ExploreShortcutViews/ExploreShortcutView.swift @@ -17,6 +17,7 @@ struct ExploreShortcutView: View { ScrollView(.vertical, showsIndicators: false) { VStack(spacing: 12) { PromoteSection(items: viewModel.fetchAdminCuration()) + .padding(.top, 12) ForEach (sectionType, id: \.self) { type in CardSection(type: type, shortcuts: viewModel.fetchShortcuts(by: type)) } @@ -32,7 +33,6 @@ struct ExploreShortcutView: View { withAnimation { isSearchActivated.toggle() } - print("검색창") } label: { Image(systemName: "sparkle.magnifyingglass") .symbolRenderingMode(.palette) @@ -52,7 +52,7 @@ struct ExploreShortcutView: View { Image(systemName: "bell.badge.fill") .symbolRenderingMode(.palette) .foregroundStyle( - Color(hexString: "3366FF"), + SCZColor.SCZBlue.strong, LinearGradient( colors: [SCZColor.CharcoalGray.opacity88, SCZColor.CharcoalGray.opacity48], startPoint: .top, diff --git a/HappyAnding/HappyAnding/Views/SearchView.swift b/HappyAnding/HappyAnding/Views/SearchView.swift index 58ed256a..5473a421 100644 --- a/HappyAnding/HappyAnding/Views/SearchView.swift +++ b/HappyAnding/HappyAnding/Views/SearchView.swift @@ -9,143 +9,406 @@ import MessageUI import SwiftUI struct SearchView: View { - @Environment(\.isSearching) private var isSearching: Bool - @Environment(\.dismissSearch) private var dismissSearch - @EnvironmentObject var shortcutsZipViewModel: ShortcutsZipViewModel - - @FocusState private var isFocused: Bool - - @State var keywords: Keyword = Keyword(keyword: [String]()) - @State var isSearched: Bool = false - @State var searchText: String = "" - @State var shortcutResults = Set() - - + @StateObject var viewModel: SearchViewModel + @FocusState var searchBarFocused: Bool + @Binding var isSearchAcivated: Bool + var body: some View { - VStack { - SearchBar() + VStack(spacing: 16) { + SearchBar(viewModel: self.viewModel, isSearchActivated: $isSearchAcivated, searchBarFocused: _searchBarFocused) + ScrollView { + if viewModel.searchText.isEmpty { + SearchSuggestionList(viewModel: self.viewModel, searchBarFocused: _searchBarFocused) + } else if viewModel.shortcutResults.isEmpty && viewModel.postResults.isEmpty { + EmptyResultView(searchText: $viewModel.searchText) + } else { + ResultSection(viewModel: self.viewModel) + } + } + .onTapGesture { + if searchBarFocused { + searchBarFocused = false + } else { + withAnimation { + isSearchAcivated = false + } + } + } } .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color.black.opacity(0.13).ignoresSafeArea()) + .background( SCZColor.CharcoalGray.opacity24.ignoresSafeArea() ) + .onChange(of: viewModel.searchText) { _ in + viewModel.didChangedSearchText() + if !viewModel.searchText.isEmpty { + viewModel.isSearched = true + } else { + viewModel.shortcutResults.removeAll() + viewModel.isSearched = false + } + } } - - private func runSearch() { - isSearched = true +} + +struct EmptyResultView: View { + @Binding var searchText: String + var body: some View { + VStack(alignment: .leading, spacing: 0) { + Text(TextLiteral.searchViewEmptyResult(searchText)) + .foregroundStyle(SCZColor.CharcoalGray.opacity48) + .padding(.horizontal, 16) + .padding(.vertical, 8) + Divider() + .padding(.vertical, 10) + Button{ + print("단축어 작성 페이지 연결") + } label: { + HStack { + Text(TextLiteral.searchTextRelatedShortcutShare(searchText)) + .foregroundStyle(SCZColor.CharcoalGray.opacity64) + Spacer() + Image(systemName: "chevron.right") + .foregroundStyle(SCZColor.CharcoalGray.opacity24) + } + .padding(.horizontal, 16) + .padding(.vertical, 8) + } + Divider() + .padding(.vertical, 10) + Button{ + print("post 작성 페이지 연결") + } label: { + HStack { + Text(TextLiteral.searchTextRelatedPost(searchText)) + .foregroundStyle(SCZColor.CharcoalGray.opacity64) + Spacer() + Image(systemName: "chevron.right") + .foregroundStyle(SCZColor.CharcoalGray.opacity24) + } + .padding(.horizontal, 16) + .padding(.vertical, 8) + } + } + .font(.system(size: 14, weight: .medium)) + .padding(.vertical, 8) + .background( + ZStack { + Color.white.opacity(0.64) + SCZColor.CharcoalGray.opacity08 + } + ) + .clipShape(RoundedRectangle(cornerRadius: 16)) + .roundedBorder(cornerRadius: 16, color: Color.white, isNormalBlend: true, opacity: 0.12) + .padding(.horizontal, 16) + .onTapGesture { } } +} + +struct ResultSection: View { + let maxNum = 2 - var searchTextfield: some View { - HStack(alignment: .center, spacing: 8) { - HStack { - Image(systemName: "magnifyingglass") - .foregroundStyle(Color.gray5) - TextField(TextLiteral.searchViewPrompt, text: $searchText) - .shortcutsZipBody1() - .accentColor(.gray5) - .disableAutocorrection(true) - .onChange(of: searchText) { _ in - // TODO: 수정 필요 - didChangedSearchText() + @StateObject var viewModel: SearchViewModel + + //TODO: Post 구조 추가 + var body: some View { + VStack(alignment: .leading, spacing: 16) { + if !viewModel.shortcutResults.isEmpty { + VStack (alignment: .leading, spacing: 8) { + if !viewModel.postResults.isEmpty { + Text(TextLiteral.searchViewRelatedShortcut) + .font(.system(size: 14, weight: .medium)) + .foregroundStyle(SCZColor.CharcoalGray.opacity48) + .padding(.horizontal, 12) } - .focused($isFocused) - .task { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - isFocused = true + VStack(alignment: .leading, spacing: 6) { + ForEach(Array(viewModel.shortcutResults.prefix(maxNum).enumerated()), id: \.offset) { index, shortcut in + ResultShortcutCell(shortcut: shortcut) + + if index != viewModel.shortcutResults.count-1 || viewModel.shortcutResults.count > maxNum { + Divider() + .padding(.vertical, 8) + .foregroundStyle(SCZColor.CharcoalGray.opacity08) + } + } + + if viewModel.shortcutResults.count > maxNum { + Button { + print("더많은 검색결과") + } label: { + HStack { + Text(TextLiteral.searchViewMoreResult) + .font(.system(size: 14, weight: .medium)) + .foregroundStyle(SCZColor.CharcoalGray.opacity48) + Spacer() + Image(systemName: "chevron.right") + .foregroundStyle(SCZColor.CharcoalGray.opacity24) + } + .padding(.horizontal, 16) + } } } - .shortcutsZipBody1() + .padding(.vertical, 16) + .background( + ZStack { + Color.white.opacity(0.64) + SCZColor.CharcoalGray.opacity08 + } + ) + .roundedBorder(cornerRadius: 16, color: Color.white, isNormalBlend: true, opacity: 0.12) + } } - .padding(11) - .background(Color.gray1) - .cornerRadius(12) - } - .padding(EdgeInsets(top: 12, leading: 16, bottom: 20, trailing: 16)) - } - - var recommendKeyword: some View { - VStack(alignment: .leading) { - Text(TextLiteral.searchViewRecommendedKeyword) - .padding(.leading, 16) - .shortcutsZipHeadline() - ScrollView(.horizontal) { - HStack { - ForEach(keywords.keyword, id: \.self) { keyword in - Text(keyword) - .shortcutsZipBody2() - .foregroundStyle(Color.gray4) + if !viewModel.postResults.isEmpty { + VStack (alignment: .leading, spacing: 8) { + if !viewModel.shortcutResults.isEmpty { + Text(TextLiteral.searchVIewRelatedPost) + .font(.system(size: 14, weight: .medium)) + .foregroundStyle(SCZColor.CharcoalGray.opacity48) .padding(.horizontal, 12) - .padding(.vertical, 8) - .overlay( - RoundedRectangle(cornerRadius: 12) - .strokeBorder(Color.gray4, lineWidth: 1) - ) - .onTapGesture { - searchText = keyword - runSearch() + } + VStack(alignment: .leading, spacing: 6) { + ForEach(Array(viewModel.postResults.prefix(maxNum).enumerated()), id: \.offset) { index, post in + ResultPostCell(post: post) + if index != viewModel.postResults.count-1 || viewModel.postResults.count>maxNum { + Divider() + .padding(.vertical, 8) + .foregroundStyle(SCZColor.CharcoalGray.opacity08) + } + } + + if viewModel.postResults.count > maxNum { + Button { + print("더많은 검색결과") + } label: { + HStack { + Text(TextLiteral.searchViewMoreResult) + .font(.system(size: 14, weight: .medium)) + .foregroundStyle(SCZColor.CharcoalGray.opacity48) + Spacer() + Image(systemName: "chevron.right") + .foregroundStyle(SCZColor.CharcoalGray.opacity24) + } + .padding(.horizontal, 16) } + } } + .padding(.vertical, 16) + .background( + ZStack { + Color.white.opacity(0.64) + SCZColor.CharcoalGray.opacity08 + } + ) + .roundedBorder(cornerRadius: 16, color: Color.white, isNormalBlend: true, opacity: 0.12) } - .padding(.leading, 16) - .padding(.top, 8) } } + .padding(.horizontal, 16) + .onTapGesture { } } +} + +struct ResultShortcutCell: View { + let shortcut: Shortcuts - var proposeView: some View { - VStack(alignment: .center) { - Text("\'\(searchText)\'의 결과가 없어요.\n원하는 단축어가 있다면 제안해보세요!").multilineTextAlignment(.center) - .shortcutsZipBody1() - .foregroundStyle(Color.gray4) - - Link(destination: URL(string: TextLiteral.searchViewProposalURL)!) { - Text(TextLiteral.searchViewProposal) - .padding(.horizontal, 28) - .padding(.vertical, 8) + var body: some View { + Button { + print("단축어 상세페이지 연결") + } label: { + HStack { + ShortcutIcon(sfSymbol: shortcut.sfSymbol, color: shortcut.color, size: 56) + VStack(alignment: .leading, spacing: 4) { + Text(shortcut.title) + //Pretendard bold 16 + .font(.system(size: 16, weight: .bold)) + .foregroundStyle(SCZColor.Basic) + .lineLimit(1) + Text(shortcut.subtitle.replacingOccurrences(of: "\\n", with: "\n")) + //Pretendard medieum 14 + .font(.system(size: 14, weight: .medium)) + .foregroundStyle(SCZColor.CharcoalGray.opacity48) + .multilineTextAlignment(.leading) + .lineLimit(2) + } + Spacer() + Image(systemName: "chevron.right") + .foregroundStyle(SCZColor.CharcoalGray.opacity24) } - .buttonStyle(.borderedProminent) - .padding(16) + .padding(.horizontal, 16) } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color.shortcutsZipBackground) } - private func didChangedSearchText() { - shortcutResults.removeAll() - - for data in shortcutsZipViewModel.allShortcuts { - if data.title.lowercased().contains(searchText.lowercased()) || - !data.requiredApp.filter({ $0.lowercased().contains(searchText.lowercased()) }).isEmpty || - data.subtitle.lowercased().contains(searchText.lowercased()) - { - shortcutResults.insert(data) +} + +struct ResultPostCell: View { + let post: Post + + var body: some View { + Button { + print("post 상세페이지 연결") + } label: { + HStack { + //Pretendard medieum 14 + VStack(alignment: .leading, spacing: 8) { + HStack(spacing: 8) { + Text(post.author) + .foregroundStyle(SCZColor.CharcoalGray.opacity64) + Text(post.createdAt.getPostDateFormat() ?? "") + .foregroundStyle(SCZColor.CharcoalGray.opacity48) + } + .lineLimit(1) + Text(post.content.replacingOccurrences(of: "\\n", with: "\n")) + .foregroundStyle(SCZColor.CharcoalGray.opacity64) + .multilineTextAlignment(.leading) + .lineLimit(2) + .frame(maxWidth: .infinity, alignment: .leading) + } + .font(.system(size: 14, weight: .medium)) + Spacer() + Image(systemName: "chevron.right") + .foregroundStyle(SCZColor.CharcoalGray.opacity24) } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 16) } } } +struct SearchSuggestionList: View { + @StateObject var viewModel: SearchViewModel + @FocusState var searchBarFocused: Bool + var body: some View { + VStack(alignment: .leading, spacing: 0) { + if viewModel.searchHistory.isEmpty { + //추천 검색어 + ForEach(0.. 5 { + searchHistory.removeLast() + } + UserDefaults.standard.set(searchHistory, forKey: "searchHistory") + } + + func removeSearchHistory(index: Int) { + searchHistory.remove(at: index) + UserDefaults.standard.set(searchHistory, forKey: "searchHistory") + } + + func didChangedSearchText() { + shortcutResults.removeAll() + + for data in shortcutsZipViewModel.allShortcuts { + if data.title.lowercased().contains(searchText.lowercased()) || + !data.requiredApp.filter({ $0.lowercased().contains(searchText.lowercased()) }).isEmpty || + data.subtitle.lowercased().contains(searchText.lowercased()) + { + shortcutResults.append(data) + } + } + } +} diff --git a/HappyAnding/HappyAnding/Views/TabView/ShortcutTabView.swift b/HappyAnding/HappyAnding/Views/TabView/ShortcutTabView.swift index 9f034133..182f49fb 100644 --- a/HappyAnding/HappyAnding/Views/TabView/ShortcutTabView.swift +++ b/HappyAnding/HappyAnding/Views/TabView/ShortcutTabView.swift @@ -97,6 +97,7 @@ struct ShortcutTabView: View { } .tag(3) } + .zIndex(1) .onChange(of: phase) { newPhase in switch newPhase { case .background: isShortcutDeeplink = false; isCurationDeeplink = false @@ -109,14 +110,9 @@ struct ShortcutTabView: View { } if isSearchActivated { - Color.black.opacity(0.33) - .ignoresSafeArea() - .onTapGesture { - withAnimation { - isSearchActivated.toggle() - } - - } + SearchView(viewModel: SearchViewModel(), isSearchAcivated: $isSearchActivated) + .background(.ultraThickMaterial) + .zIndex(2) } } }