From 6583ce38bb2bbdb7babbc036103786ab0eae601b Mon Sep 17 00:00:00 2001 From: cheykrym Date: Tue, 7 Oct 2025 03:54:46 +0300 Subject: [PATCH 1/7] add search to top bar --- yobble/Components/TopBarView.swift | 35 ++++++++++++++++++++++++++++++ yobble/Views/Tab/ChatsTab.swift | 20 ++++++++--------- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/yobble/Components/TopBarView.swift b/yobble/Components/TopBarView.swift index 48da463..d5962f0 100644 --- a/yobble/Components/TopBarView.swift +++ b/yobble/Components/TopBarView.swift @@ -12,6 +12,8 @@ struct TopBarView: View { // Привязка для управления боковым меню @Binding var isSideMenuPresented: Bool + + @State private var searchText: String = "" var isHomeTab: Bool { return title == "Home" @@ -103,12 +105,45 @@ struct TopBarView: View { .padding() .frame(height: 50) // Стандартная высота для нав. бара + if isChatsTab { + searchBar + .padding(.horizontal) + .padding(.bottom, 8) + } + Divider() } .background(Color(UIColor.systemBackground)) } } +private extension TopBarView { + var searchBar: some View { + HStack(spacing: 8) { + Image(systemName: "magnifyingglass") + .foregroundColor(.secondary) + TextField(NSLocalizedString("Поиск", comment: ""), text: $searchText) + .textFieldStyle(.plain) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + if !searchText.isEmpty { + Button(action: { searchText = "" }) { + Image(systemName: "xmark.circle.fill") + .foregroundColor(.secondary) + } + .buttonStyle(.plain) + } + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .frame(minHeight: 36) + .background( + RoundedRectangle(cornerRadius: 12, style: .continuous) + .fill(Color(UIColor.secondarySystemBackground)) + ) + } +} + struct TopBarView_Previews: PreviewProvider { static var previews: some View { /*@START_MENU_TOKEN@*/Text("Hello, World!")/*@END_MENU_TOKEN@*/ diff --git a/yobble/Views/Tab/ChatsTab.swift b/yobble/Views/Tab/ChatsTab.swift index dfb0f2e..6c2ecba 100644 --- a/yobble/Views/Tab/ChatsTab.swift +++ b/yobble/Views/Tab/ChatsTab.swift @@ -115,16 +115,16 @@ struct ChatsTab: View { } } .listStyle(.plain) - .safeAreaInset(edge: .top) { - VStack(spacing: 0) { - searchBar - .padding(.horizontal, 16) - .padding(.top, 8) - .padding(.bottom, 8) - Divider() - } - .background(Color(UIColor.systemBackground)) - } +// .safeAreaInset(edge: .top) { +// VStack(spacing: 0) { +// searchBar +// .padding(.horizontal, 16) +// .padding(.top, 8) +// .padding(.bottom, 8) +// Divider() +// } +// .background(Color(UIColor.systemBackground)) +// } } private var searchBar: some View { -- 2.47.2 From 3d24e0afced5eca584d9e68ff71879f4eb5dc322 Mon Sep 17 00:00:00 2001 From: cheykrym Date: Tue, 7 Oct 2025 04:03:07 +0300 Subject: [PATCH 2/7] add swipe --- yobble/Components/TopBarView.swift | 22 +++++++++++++++++++++- yobble/Views/Tab/ChatsTab.swift | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/yobble/Components/TopBarView.swift b/yobble/Components/TopBarView.swift index d5962f0..5bda4c5 100644 --- a/yobble/Components/TopBarView.swift +++ b/yobble/Components/TopBarView.swift @@ -14,6 +14,7 @@ struct TopBarView: View { @Binding var isSideMenuPresented: Bool @State private var searchText: String = "" + @State private var isSearchBarVisible: Bool = false var isHomeTab: Bool { return title == "Home" @@ -105,15 +106,34 @@ struct TopBarView: View { .padding() .frame(height: 50) // Стандартная высота для нав. бара - if isChatsTab { + if isChatsTab && isSearchBarVisible { searchBar .padding(.horizontal) .padding(.bottom, 8) + .transition(.move(edge: .top).combined(with: .opacity)) } Divider() } .background(Color(UIColor.systemBackground)) + .animation(.spring(response: 0.35, dampingFraction: 0.85), value: isSearchBarVisible) + .onReceive(NotificationCenter.default.publisher(for: .chatsTabRevealSearchBar)) { _ in + guard isChatsTab else { return } + withAnimation { + isSearchBarVisible = true + } + } + .onReceive(NotificationCenter.default.publisher(for: .chatsTabHideSearchBar)) { _ in + guard isChatsTab else { return } + withAnimation { + isSearchBarVisible = false + } + } + .onChange(of: isChatsTab) { isChats in + if !isChats { + isSearchBarVisible = false + } + } } } diff --git a/yobble/Views/Tab/ChatsTab.swift b/yobble/Views/Tab/ChatsTab.swift index 6c2ecba..c6214d6 100644 --- a/yobble/Views/Tab/ChatsTab.swift +++ b/yobble/Views/Tab/ChatsTab.swift @@ -115,6 +115,7 @@ struct ChatsTab: View { } } .listStyle(.plain) + .simultaneousGesture(searchBarGesture) // .safeAreaInset(edge: .top) { // VStack(spacing: 0) { // searchBar @@ -152,6 +153,21 @@ struct ChatsTab: View { ) } + private var searchBarGesture: some Gesture { + DragGesture(minimumDistance: 24) + .onEnded { value in + let verticalTranslation = value.translation.height + let horizontalTranslation = value.translation.width + guard abs(verticalTranslation) > abs(horizontalTranslation) else { return } + + if verticalTranslation > 24 { + NotificationCenter.default.post(name: .chatsTabRevealSearchBar, object: nil) + } else if verticalTranslation < -24 { + NotificationCenter.default.post(name: .chatsTabHideSearchBar, object: nil) + } + } + } + private var isSearching: Bool { !searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } @@ -620,4 +636,6 @@ private struct ChatPlaceholderView: View { extension Notification.Name { static let debugRefreshChats = Notification.Name("debugRefreshChats") + static let chatsTabRevealSearchBar = Notification.Name("chatsTabRevealSearchBar") + static let chatsTabHideSearchBar = Notification.Name("chatsTabHideSearchBar") } -- 2.47.2 From e51a4ed6b20170020387c56f50983e97f86994b2 Mon Sep 17 00:00:00 2001 From: cheykrym Date: Tue, 7 Oct 2025 04:09:53 +0300 Subject: [PATCH 3/7] add animation --- yobble/Components/TopBarView.swift | 72 +++++++++++++++++++++--------- yobble/Views/Tab/ChatsTab.swift | 55 ++++++++++++++++++----- yobble/Views/Tab/MainView.swift | 10 ++++- 3 files changed, 101 insertions(+), 36 deletions(-) diff --git a/yobble/Components/TopBarView.swift b/yobble/Components/TopBarView.swift index 5bda4c5..ff87f01 100644 --- a/yobble/Components/TopBarView.swift +++ b/yobble/Components/TopBarView.swift @@ -12,9 +12,9 @@ struct TopBarView: View { // Привязка для управления боковым меню @Binding var isSideMenuPresented: Bool - + @Binding var chatSearchRevealProgress: CGFloat + @State private var searchText: String = "" - @State private var isSearchBarVisible: Bool = false var isHomeTab: Bool { return title == "Home" @@ -106,38 +106,47 @@ struct TopBarView: View { .padding() .frame(height: 50) // Стандартная высота для нав. бара - if isChatsTab && isSearchBarVisible { - searchBar - .padding(.horizontal) - .padding(.bottom, 8) - .transition(.move(edge: .top).combined(with: .opacity)) + if isChatsTab { + revealableSearchBar } Divider() } .background(Color(UIColor.systemBackground)) - .animation(.spring(response: 0.35, dampingFraction: 0.85), value: isSearchBarVisible) - .onReceive(NotificationCenter.default.publisher(for: .chatsTabRevealSearchBar)) { _ in - guard isChatsTab else { return } - withAnimation { - isSearchBarVisible = true - } - } - .onReceive(NotificationCenter.default.publisher(for: .chatsTabHideSearchBar)) { _ in - guard isChatsTab else { return } - withAnimation { - isSearchBarVisible = false - } - } .onChange(of: isChatsTab) { isChats in if !isChats { - isSearchBarVisible = false + withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) { + chatSearchRevealProgress = 0 + } } } } } private extension TopBarView { + private var normalizedRevealProgress: CGFloat { + guard isChatsTab else { return 0 } + return max(0, min(chatSearchRevealProgress, 1)) + } + + private var revealableSearchBar: some View { + let progress = normalizedRevealProgress + return VStack(spacing: 0) { + Spacer(minLength: 0) + searchBar + .padding(.horizontal) + .padding(.bottom, 8) + } + .frame(height: searchBarRevealHeight) + .clipped() + .scaleEffect(y: max(progress, 0.0001), anchor: .top) + .opacity(progress) + .allowsHitTesting(progress > 0.9) + .accessibilityHidden(progress < 0.9) + } + + private var searchBarRevealHeight: CGFloat { 56 } + var searchBar: some View { HStack(spacing: 8) { Image(systemName: "magnifyingglass") @@ -165,7 +174,26 @@ private extension TopBarView { } struct TopBarView_Previews: PreviewProvider { + struct Wrapper: View { + @State private var selectedAccount = "@user" + @State private var isSideMenuPresented = false + @State private var revealProgress: CGFloat = 1 + @StateObject private var viewModel = LoginViewModel() + + var body: some View { + TopBarView( + title: "Chats", + selectedAccount: $selectedAccount, + accounts: [selectedAccount], + viewModel: viewModel, + isSideMenuPresented: $isSideMenuPresented, + chatSearchRevealProgress: $revealProgress + ) + } + } + static var previews: some View { - /*@START_MENU_TOKEN@*/Text("Hello, World!")/*@END_MENU_TOKEN@*/ + Wrapper() + .environmentObject(ThemeManager()) } } diff --git a/yobble/Views/Tab/ChatsTab.swift b/yobble/Views/Tab/ChatsTab.swift index c6214d6..a36c6fe 100644 --- a/yobble/Views/Tab/ChatsTab.swift +++ b/yobble/Views/Tab/ChatsTab.swift @@ -8,10 +8,20 @@ import SwiftUI struct ChatsTab: View { - var currentUserId: String? = nil + var currentUserId: String? + @Binding var searchRevealProgress: CGFloat @StateObject private var viewModel = PrivateChatsViewModel() @State private var selectedChatId: String? @State private var searchText: String = "" + @State private var searchDragStartProgress: CGFloat = 0 + @State private var isSearchGestureActive: Bool = false + + private let searchRevealDistance: CGFloat = 90 + + init(currentUserId: String? = nil, searchRevealProgress: Binding) { + self.currentUserId = currentUserId + self._searchRevealProgress = searchRevealProgress + } var body: some View { content @@ -154,16 +164,31 @@ struct ChatsTab: View { } private var searchBarGesture: some Gesture { - DragGesture(minimumDistance: 24) - .onEnded { value in + DragGesture(minimumDistance: 10, coordinateSpace: .local) + .onChanged { value in let verticalTranslation = value.translation.height let horizontalTranslation = value.translation.width - guard abs(verticalTranslation) > abs(horizontalTranslation) else { return } - if verticalTranslation > 24 { - NotificationCenter.default.post(name: .chatsTabRevealSearchBar, object: nil) - } else if verticalTranslation < -24 { - NotificationCenter.default.post(name: .chatsTabHideSearchBar, object: nil) + if !isSearchGestureActive { + guard abs(verticalTranslation) > abs(horizontalTranslation) else { return } + if searchRevealProgress <= 0.001 && verticalTranslation < 0 { return } + isSearchGestureActive = true + searchDragStartProgress = searchRevealProgress + } + + guard isSearchGestureActive else { return } + + let delta = verticalTranslation / searchRevealDistance + let newProgress = searchDragStartProgress + delta + searchRevealProgress = max(0, min(1, newProgress)) + } + .onEnded { _ in + guard isSearchGestureActive else { return } + isSearchGestureActive = false + + let target: CGFloat = searchRevealProgress > 0.5 ? 1 : 0 + withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) { + searchRevealProgress = target } } } @@ -568,9 +593,17 @@ private struct ChatRowView: View { } struct ChatsTab_Previews: PreviewProvider { + struct Wrapper: View { + @State private var progress: CGFloat = 1 + + var body: some View { + ChatsTab(searchRevealProgress: $progress) + .environmentObject(ThemeManager()) + } + } + static var previews: some View { - ChatsTab() - .environmentObject(ThemeManager()) + Wrapper() } } @@ -636,6 +669,4 @@ private struct ChatPlaceholderView: View { extension Notification.Name { static let debugRefreshChats = Notification.Name("debugRefreshChats") - static let chatsTabRevealSearchBar = Notification.Name("chatsTabRevealSearchBar") - static let chatsTabHideSearchBar = Notification.Name("chatsTabHideSearchBar") } diff --git a/yobble/Views/Tab/MainView.swift b/yobble/Views/Tab/MainView.swift index 6db0581..8327632 100644 --- a/yobble/Views/Tab/MainView.swift +++ b/yobble/Views/Tab/MainView.swift @@ -13,6 +13,7 @@ struct MainView: View { // Состояния для бокового меню @State private var isSideMenuPresented = false @State private var menuOffset: CGFloat = 0 + @State private var chatSearchRevealProgress: CGFloat = 0 private var tabTitle: String { switch selectedTab { @@ -38,7 +39,8 @@ struct MainView: View { selectedAccount: $selectedAccount, accounts: accounts, viewModel: viewModel, - isSideMenuPresented: $isSideMenuPresented + isSideMenuPresented: $isSideMenuPresented, + chatSearchRevealProgress: $chatSearchRevealProgress ) ZStack { @@ -48,8 +50,12 @@ struct MainView: View { FeedbackTab() .opacity(selectedTab == 1 ? 1 : 0) - ChatsTab(currentUserId: viewModel.userId.isEmpty ? nil : viewModel.userId) + ChatsTab( + currentUserId: viewModel.userId.isEmpty ? nil : viewModel.userId, + searchRevealProgress: $chatSearchRevealProgress + ) .opacity(selectedTab == 2 ? 1 : 0) + .allowsHitTesting(selectedTab == 2) ProfileTab() .opacity(selectedTab == 3 ? 1 : 0) -- 2.47.2 From 24b718d515596e518a571e9f14d3440df7441561 Mon Sep 17 00:00:00 2001 From: cheykrym Date: Tue, 7 Oct 2025 04:12:21 +0300 Subject: [PATCH 4/7] fix top bar --- yobble/Components/TopBarView.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/yobble/Components/TopBarView.swift b/yobble/Components/TopBarView.swift index ff87f01..e064c47 100644 --- a/yobble/Components/TopBarView.swift +++ b/yobble/Components/TopBarView.swift @@ -132,15 +132,13 @@ private extension TopBarView { private var revealableSearchBar: some View { let progress = normalizedRevealProgress return VStack(spacing: 0) { - Spacer(minLength: 0) searchBar .padding(.horizontal) .padding(.bottom, 8) + .opacity(progress) } - .frame(height: searchBarRevealHeight) + .frame(height: searchBarRevealHeight * progress, alignment: .top) .clipped() - .scaleEffect(y: max(progress, 0.0001), anchor: .top) - .opacity(progress) .allowsHitTesting(progress > 0.9) .accessibilityHidden(progress < 0.9) } -- 2.47.2 From 359b51627228b1b84f4102c0c69da771bc4d2285 Mon Sep 17 00:00:00 2001 From: cheykrym Date: Tue, 7 Oct 2025 04:24:38 +0300 Subject: [PATCH 5/7] add fix to tab bar search --- yobble/Components/TopBarView.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/yobble/Components/TopBarView.swift b/yobble/Components/TopBarView.swift index e064c47..8d3e9c7 100644 --- a/yobble/Components/TopBarView.swift +++ b/yobble/Components/TopBarView.swift @@ -134,7 +134,7 @@ private extension TopBarView { return VStack(spacing: 0) { searchBar .padding(.horizontal) - .padding(.bottom, 8) + .padding(.bottom, searchBarBottomSpacing) .opacity(progress) } .frame(height: searchBarRevealHeight * progress, alignment: .top) @@ -143,7 +143,12 @@ private extension TopBarView { .accessibilityHidden(progress < 0.9) } - private var searchBarRevealHeight: CGFloat { 56 } + private var searchBarRevealHeight: CGFloat { searchBarBaseHeight + searchBarBottomSpacing } + + // 36 min height + 6 * 2 vertical padding inside searchBar + private var searchBarBaseHeight: CGFloat { 48 } + + private var searchBarBottomSpacing: CGFloat { 0 } var searchBar: some View { HStack(spacing: 8) { -- 2.47.2 From 9952ca6d2bd4857bc8aec4c44c5c3dad9ec584b9 Mon Sep 17 00:00:00 2001 From: cheykrym Date: Tue, 7 Oct 2025 04:36:21 +0300 Subject: [PATCH 6/7] keyboard fix --- yobble/Views/Tab/ChatsTab.swift | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/yobble/Views/Tab/ChatsTab.swift b/yobble/Views/Tab/ChatsTab.swift index a36c6fe..fd0f9ff 100644 --- a/yobble/Views/Tab/ChatsTab.swift +++ b/yobble/Views/Tab/ChatsTab.swift @@ -6,6 +6,9 @@ // import SwiftUI +#if canImport(UIKit) +import UIKit +#endif struct ChatsTab: View { var currentUserId: String? @@ -125,7 +128,9 @@ struct ChatsTab: View { } } .listStyle(.plain) + .modifier(ScrollDismissesKeyboardModifier()) .simultaneousGesture(searchBarGesture) + .simultaneousGesture(tapToDismissKeyboardGesture) // .safeAreaInset(edge: .top) { // VStack(spacing: 0) { // searchBar @@ -193,6 +198,12 @@ struct ChatsTab: View { } } + private var tapToDismissKeyboardGesture: some Gesture { + TapGesture().onEnded { + dismissKeyboard() + } + } + private var isSearching: Bool { !searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } @@ -286,6 +297,24 @@ struct ChatsTab: View { } } +private extension ChatsTab { + func dismissKeyboard() { +#if canImport(UIKit) + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) +#endif + } +} + +private struct ScrollDismissesKeyboardModifier: ViewModifier { + func body(content: Content) -> some View { + if #available(iOS 16.0, *) { + content.scrollDismissesKeyboard(.interactively) + } else { + content + } + } +} + private struct ChatRowView: View { let chat: PrivateChatListItem let currentUserId: String? -- 2.47.2 From 2f68345c569aa1eba31589c3ec98a2852e87dc62 Mon Sep 17 00:00:00 2001 From: cheykrym Date: Tue, 7 Oct 2025 04:47:03 +0300 Subject: [PATCH 7/7] add new tabs --- yobble/Resources/Localizable.xcstrings | 16 +++++++++ yobble/Views/Tab/ConceptTab.swift | 33 +++++++++++++++++++ yobble/Views/Tab/CustomTabBar.swift | 2 +- yobble/Views/Tab/MainView.swift | 4 +-- yobble/Views/Tab/Settings/FAQView.swift | 30 ++++++++++++----- .../FeedbackView.swift} | 8 ++--- 6 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 yobble/Views/Tab/ConceptTab.swift rename yobble/Views/Tab/{FeedbackTab.swift => Settings/FeedbackView.swift} (97%) diff --git a/yobble/Resources/Localizable.xcstrings b/yobble/Resources/Localizable.xcstrings index ef6c18e..9df8ea2 100644 --- a/yobble/Resources/Localizable.xcstrings +++ b/yobble/Resources/Localizable.xcstrings @@ -235,6 +235,9 @@ } } }, + "Если не нашли ответ, напишите нам своё предложение или проблему." : { + "comment" : "FAQ: contact developers footer" + }, "Заглушка: Push-уведомления" : { }, @@ -333,6 +336,7 @@ } }, "Идеи" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -396,6 +400,12 @@ } } }, + "Кликер в разработке" : { + "comment" : "Concept tab placeholder title" + }, + "Концепт" : { + "comment" : "Tab bar: concept clicker" + }, "Корзина" : { "comment" : "Cart", "localizations" : { @@ -1124,6 +1134,9 @@ } } }, + "Связаться с разработчиками" : { + "comment" : "FAQ: contact developers link" + }, "Сервер не отвечает. Попробуйте позже." : { "localizations" : { "en" : { @@ -1165,6 +1178,9 @@ } } }, + "Скоро появится мини-игра, где можно заработать очки для кастомизации профиля. Следите за обновлениями!" : { + "comment" : "Concept tab placeholder description" + }, "Слишком много запросов." : { "localizations" : { "en" : { diff --git a/yobble/Views/Tab/ConceptTab.swift b/yobble/Views/Tab/ConceptTab.swift new file mode 100644 index 0000000..8aeb614 --- /dev/null +++ b/yobble/Views/Tab/ConceptTab.swift @@ -0,0 +1,33 @@ +import SwiftUI + +struct ConceptTab: View { + var body: some View { + ScrollView { + VStack(spacing: 24) { + Image(systemName: "gamecontroller.fill") + .resizable() + .scaledToFit() + .frame(width: 96, height: 96) + .foregroundColor(.accentColor) + + Text(NSLocalizedString("Кликер в разработке", comment: "Concept tab placeholder title")) + .font(.title2) + .fontWeight(.semibold) + + Text(NSLocalizedString("Скоро появится мини-игра, где можно заработать очки для кастомизации профиля. Следите за обновлениями!", comment: "Concept tab placeholder description")) + .font(.body) + .multilineTextAlignment(.center) + .foregroundColor(.secondary) + .padding(.horizontal) + } + .padding(.vertical, 48) + .frame(maxWidth: .infinity) + } + .background(Color(UIColor.systemGroupedBackground)) + } +} + +#Preview { + ConceptTab() + .environmentObject(ThemeManager()) +} diff --git a/yobble/Views/Tab/CustomTabBar.swift b/yobble/Views/Tab/CustomTabBar.swift index 6a818ac..a4bab42 100644 --- a/yobble/Views/Tab/CustomTabBar.swift +++ b/yobble/Views/Tab/CustomTabBar.swift @@ -12,7 +12,7 @@ struct CustomTabBar: View { } // Tab 2: Search - TabBarButton(systemName: "lightbulb", text: NSLocalizedString("Идеи", comment: ""), isSelected: selectedTab == 1) { + TabBarButton(systemName: "gamecontroller.fill", text: NSLocalizedString("Концепт", comment: "Tab bar: concept clicker"), isSelected: selectedTab == 1) { selectedTab = 1 } diff --git a/yobble/Views/Tab/MainView.swift b/yobble/Views/Tab/MainView.swift index 8327632..db3bc2f 100644 --- a/yobble/Views/Tab/MainView.swift +++ b/yobble/Views/Tab/MainView.swift @@ -18,7 +18,7 @@ struct MainView: View { private var tabTitle: String { switch selectedTab { case 0: return "Home" - case 1: return "Ideas" + case 1: return "Concept" case 2: return "Chats" case 3: return "Profile" default: return "Home" @@ -47,7 +47,7 @@ struct MainView: View { NewHomeTab() .opacity(selectedTab == 0 ? 1 : 0) - FeedbackTab() + ConceptTab() .opacity(selectedTab == 1 ? 1 : 0) ChatsTab( diff --git a/yobble/Views/Tab/Settings/FAQView.swift b/yobble/Views/Tab/Settings/FAQView.swift index b9c07cf..c71eb2f 100644 --- a/yobble/Views/Tab/Settings/FAQView.swift +++ b/yobble/Views/Tab/Settings/FAQView.swift @@ -23,15 +23,29 @@ struct FAQView: View { ] var body: some View { - List(faqItems) { item in - VStack(alignment: .leading, spacing: 6) { - Text(item.question) - .font(.headline) - Text(item.answer) - .font(.subheadline) - .foregroundColor(.secondary) + List { + ForEach(faqItems) { item in + VStack(alignment: .leading, spacing: 6) { + Text(item.question) + .font(.headline) + Text(item.answer) + .font(.subheadline) + .foregroundColor(.secondary) + } + .padding(.vertical, 6) + } + + Section { + NavigationLink(destination: FeedbackView()) { + Text(NSLocalizedString("Связаться с разработчиками", comment: "FAQ: contact developers link")) + .font(.callout) + .fontWeight(.semibold) + .foregroundColor(.accentColor) + } + } footer: { + Text(NSLocalizedString("Если не нашли ответ, напишите нам своё предложение или проблему.", comment: "FAQ: contact developers footer")) + .font(.footnote) } - .padding(.vertical, 6) } .listStyle(.insetGrouped) .navigationTitle(NSLocalizedString("Частые вопросы", comment: "FAQ navigation title")) diff --git a/yobble/Views/Tab/FeedbackTab.swift b/yobble/Views/Tab/Settings/FeedbackView.swift similarity index 97% rename from yobble/Views/Tab/FeedbackTab.swift rename to yobble/Views/Tab/Settings/FeedbackView.swift index 09c9b6e..47e9069 100644 --- a/yobble/Views/Tab/FeedbackTab.swift +++ b/yobble/Views/Tab/Settings/FeedbackView.swift @@ -3,7 +3,7 @@ import SwiftUI import UIKit #endif -struct FeedbackTab: View { +struct FeedbackView: View { @State private var suggestion: String = "" @State private var submittedSuggestion: String? = nil @State private var isSubmitting: Bool = false @@ -122,7 +122,7 @@ struct FeedbackTab: View { } } -private extension FeedbackTab { +private extension FeedbackView { func dismissKeyboardIfNeeded() { guard isSuggestionFocused else { return } isSuggestionFocused = false @@ -133,9 +133,9 @@ private extension FeedbackTab { } } -struct FeedbackTab_Previews: PreviewProvider { +struct FeedbackView_Previews: PreviewProvider { static var previews: some View { - FeedbackTab() + FeedbackView() .environmentObject(ThemeManager()) } } -- 2.47.2