diff --git a/yobble/Views/Login/LoginView.swift b/yobble/Views/Login/LoginView.swift index 66ceb08..6f8c9fa 100644 --- a/yobble/Views/Login/LoginView.swift +++ b/yobble/Views/Login/LoginView.swift @@ -112,7 +112,7 @@ struct LoginView: View { .font(.caption) } - TermsAgreementView( + TermsAgreementCard( isAccepted: $viewModel.hasAcceptedTerms, openTerms: { viewModel.loadTermsIfNeeded() @@ -272,106 +272,6 @@ struct LoginView: View { } } - private struct TermsAgreementView: View { - @Binding var isAccepted: Bool - var openTerms: () -> Void - - var body: some View { - VStack(alignment: .leading, spacing: 12) { - HStack(alignment: .top, spacing: 12) { - Button { - isAccepted.toggle() - } label: { - Image(systemName: isAccepted ? "checkmark.square.fill" : "square") - .font(.system(size: 24, weight: .semibold)) - .foregroundColor(isAccepted ? .blue : .secondary) - } - .buttonStyle(.plain) - .accessibilityLabel(NSLocalizedString("Согласиться с правилами", comment: "")) - .accessibilityValue(isAccepted ? NSLocalizedString("Включено", comment: "") : NSLocalizedString("Выключено", comment: "")) - - VStack(alignment: .leading, spacing: 6) { - Text(NSLocalizedString("Я ознакомился и принимаю правила сервиса", comment: "")) - .font(.subheadline) - .foregroundColor(.primary) - - Button(action: openTerms) { - HStack(spacing: 4) { - Text(NSLocalizedString("Открыть правила", comment: "")) - Image(systemName: "arrow.up.right") - .font(.caption) - } - } - .font(.footnote) - .foregroundColor(.blue) - .buttonStyle(.plain) - } - } - } - .frame(maxWidth: .infinity, alignment: .leading) - .padding(16) - .background(Color(.secondarySystemBackground)) - .cornerRadius(14) - } - } - - private struct TermsFullScreenView: View { - @Binding var isPresented: Bool - var title: String - var content: String - var isLoading: Bool - var errorMessage: String? - var onRetry: () -> Void - - var body: some View { - NavigationView { - Group { - if isLoading { - ProgressView() - } else if let errorMessage { - VStack(spacing: 16) { - Text(errorMessage) - .multilineTextAlignment(.center) - .padding(.horizontal) - Button(action: onRetry) { - Text(NSLocalizedString("Повторить", comment: "")) - .padding(.horizontal, 24) - .padding(.vertical, 12) - .background(Color.blue) - .foregroundColor(.white) - .cornerRadius(12) - } - } - } else { - ScrollView { - VStack(alignment: .leading, spacing: 16) { - if let attributed = try? AttributedString(markdown: content) { - Text(attributed) - } else { - Text(content) - } - } - .frame(maxWidth: .infinity, alignment: .leading) - .padding() - } - } - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color(.systemBackground)) - .navigationTitle(title) - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .cancellationAction) { - Button(action: { isPresented = false }) { - Text(NSLocalizedString("Закрыть", comment: "")) - } - } - } - } - .navigationViewStyle(StackNavigationViewStyle()) - } - } - private var selectedThemeOption: ThemeOption { ThemeOption.option(for: themeManager.theme) } diff --git a/yobble/Views/Login/RegistrationView.swift b/yobble/Views/Login/RegistrationView.swift index 46ffc44..94e0e4f 100644 --- a/yobble/Views/Login/RegistrationView.swift +++ b/yobble/Views/Login/RegistrationView.swift @@ -20,6 +20,7 @@ struct RegistrationView: View { @State private var isLoading: Bool = false @State private var showError: Bool = false @State private var errorMessage: String = "" + @State private var isShowingTerms: Bool = false @FocusState private var focusedField: Field? @@ -44,7 +45,7 @@ struct RegistrationView: View { } private var isFormValid: Bool { - isUsernameValid && isPasswordValid && isConfirmPasswordValid + isUsernameValid && isPasswordValid && isConfirmPasswordValid && viewModel.hasAcceptedTerms } var body: some View { @@ -146,6 +147,14 @@ struct RegistrationView: View { .focused($focusedField, equals: .invite) } + TermsAgreementCard( + isAccepted: $viewModel.hasAcceptedTerms, + openTerms: { + viewModel.loadTermsIfNeeded() + isShowingTerms = true + } + ) + Button(action: registerUser) { if isLoading { ProgressView() @@ -184,6 +193,23 @@ struct RegistrationView: View { ) } } + .fullScreenCover(isPresented: $isShowingTerms) { + TermsFullScreenView( + isPresented: $isShowingTerms, + title: NSLocalizedString("Правила сервиса", comment: ""), + content: viewModel.termsContent, + isLoading: viewModel.isLoadingTerms, + errorMessage: viewModel.termsErrorMessage, + onRetry: { + viewModel.reloadTerms() + } + ) + .onAppear { + if viewModel.termsContent.isEmpty { + viewModel.loadTermsIfNeeded() + } + } + } } private func registerUser() { @@ -212,6 +238,7 @@ struct RegistrationView_Previews: PreviewProvider { static var previews: some View { let viewModel = LoginViewModel() viewModel.isLoading = false // чтобы убрать спиннер + viewModel.hasAcceptedTerms = true return RegistrationView(viewModel: viewModel, isPresented: .constant(true)) } } diff --git a/yobble/Views/Login/TermsViews.swift b/yobble/Views/Login/TermsViews.swift new file mode 100644 index 0000000..5305b9d --- /dev/null +++ b/yobble/Views/Login/TermsViews.swift @@ -0,0 +1,102 @@ +import SwiftUI + +struct TermsAgreementCard: View { + @Binding var isAccepted: Bool + var openTerms: () -> Void + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + HStack(alignment: .top, spacing: 12) { + Button { + isAccepted.toggle() + } label: { + Image(systemName: isAccepted ? "checkmark.square.fill" : "square") + .font(.system(size: 24, weight: .semibold)) + .foregroundColor(isAccepted ? .blue : .secondary) + } + .buttonStyle(.plain) + .accessibilityLabel(NSLocalizedString("Согласиться с правилами", comment: "")) + .accessibilityValue(isAccepted ? NSLocalizedString("Включено", comment: "") : NSLocalizedString("Выключено", comment: "")) + + VStack(alignment: .leading, spacing: 6) { + Text(NSLocalizedString("Я ознакомился и принимаю правила сервиса", comment: "")) + .font(.subheadline) + .foregroundColor(.primary) + + Button(action: openTerms) { + HStack(spacing: 4) { + Text(NSLocalizedString("Открыть правила", comment: "")) + Image(systemName: "arrow.up.right") + .font(.caption) + } + } + .buttonStyle(.plain) + .font(.footnote) + .foregroundColor(.blue) + } + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(16) + .background(Color(.secondarySystemBackground)) + .cornerRadius(14) + } +} + +struct TermsFullScreenView: View { + @Binding var isPresented: Bool + var title: String + var content: String + var isLoading: Bool + var errorMessage: String? + var onRetry: () -> Void + + var body: some View { + NavigationView { + Group { + if isLoading { + ProgressView() + } else if let errorMessage { + VStack(spacing: 16) { + Text(errorMessage) + .multilineTextAlignment(.center) + .padding(.horizontal) + Button(action: onRetry) { + Text(NSLocalizedString("Повторить", comment: "")) + .padding(.horizontal, 24) + .padding(.vertical, 12) + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(12) + } + } + } else { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + if let attributed = try? AttributedString(markdown: content) { + Text(attributed) + } else { + Text(content) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding() + } + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(.systemBackground)) + .navigationTitle(title) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button(action: { isPresented = false }) { + Text(NSLocalizedString("Закрыть", comment: "")) + } + } + } + } + .navigationViewStyle(StackNavigationViewStyle()) + } +} +