add terms
This commit is contained in:
		
							parent
							
								
									726a6983b2
								
							
						
					
					
						commit
						140e82e122
					
				@ -257,6 +257,9 @@
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Включено" : {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Включить автоудаление аккаунта" : {
 | 
					    "Включить автоудаление аккаунта" : {
 | 
				
			||||||
      "localizations" : {
 | 
					      "localizations" : {
 | 
				
			||||||
@ -331,6 +334,9 @@
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Выключено" : {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Где найти сохранённые черновики?" : {
 | 
					    "Где найти сохранённые черновики?" : {
 | 
				
			||||||
      "comment" : "FAQ question: drafts"
 | 
					      "comment" : "FAQ question: drafts"
 | 
				
			||||||
@ -913,6 +919,9 @@
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Не удалось загрузить текст правил." : {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Не удалось загрузить чаты." : {
 | 
					    "Не удалось загрузить чаты." : {
 | 
				
			||||||
      "localizations" : {
 | 
					      "localizations" : {
 | 
				
			||||||
@ -1243,6 +1252,9 @@
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Открыть правила" : {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Отображаемое имя" : {
 | 
					    "Отображаемое имя" : {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1623,6 +1635,9 @@
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Правила сервиса" : {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Предложите, что добавить" : {
 | 
					    "Предложите, что добавить" : {
 | 
				
			||||||
      "comment" : "feedback category subtitle: idea",
 | 
					      "comment" : "feedback category subtitle: idea",
 | 
				
			||||||
@ -2031,6 +2046,9 @@
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Согласиться с правилами" : {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Сообщение" : {
 | 
					    "Сообщение" : {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2298,6 +2316,9 @@
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    "Экспериментальная поддержка iOS 15" : {
 | 
					    "Экспериментальная поддержка iOS 15" : {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Я ознакомился и принимаю правила сервиса" : {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Язык" : {
 | 
					    "Язык" : {
 | 
				
			||||||
      "localizations" : {
 | 
					      "localizations" : {
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,14 @@ class LoginViewModel: ObservableObject {
 | 
				
			|||||||
    @Published var isLoggedIn: Bool = false
 | 
					    @Published var isLoggedIn: Bool = false
 | 
				
			||||||
    @Published var socketState: SocketService.ConnectionState
 | 
					    @Published var socketState: SocketService.ConnectionState
 | 
				
			||||||
    @Published var chatLoadingState: ChatLoadingState = .idle
 | 
					    @Published var chatLoadingState: ChatLoadingState = .idle
 | 
				
			||||||
 | 
					    @Published var hasAcceptedTerms: Bool {
 | 
				
			||||||
 | 
					        didSet {
 | 
				
			||||||
 | 
					            UserDefaults.standard.set(hasAcceptedTerms, forKey: DefaultsKeys.termsAccepted)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    @Published var isLoadingTerms: Bool = false
 | 
				
			||||||
 | 
					    @Published var termsContent: String = ""
 | 
				
			||||||
 | 
					    @Published var termsErrorMessage: String?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private let authService = AuthService()
 | 
					    private let authService = AuthService()
 | 
				
			||||||
    private let socketService = SocketService.shared
 | 
					    private let socketService = SocketService.shared
 | 
				
			||||||
@ -31,10 +39,12 @@ class LoginViewModel: ObservableObject {
 | 
				
			|||||||
    private enum DefaultsKeys {
 | 
					    private enum DefaultsKeys {
 | 
				
			||||||
        static let currentUser = "currentUser"
 | 
					        static let currentUser = "currentUser"
 | 
				
			||||||
        static let userId = "userId"
 | 
					        static let userId = "userId"
 | 
				
			||||||
 | 
					        static let termsAccepted = "termsAccepted"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    init() {
 | 
					    init() {
 | 
				
			||||||
        socketState = socketService.currentConnectionState
 | 
					        socketState = socketService.currentConnectionState
 | 
				
			||||||
 | 
					        hasAcceptedTerms = UserDefaults.standard.bool(forKey: DefaultsKeys.termsAccepted)
 | 
				
			||||||
        observeSocketState()
 | 
					        observeSocketState()
 | 
				
			||||||
        observeChatsReload()
 | 
					        observeChatsReload()
 | 
				
			||||||
//        loadStoredUser()
 | 
					//        loadStoredUser()
 | 
				
			||||||
@ -169,4 +179,53 @@ class LoginViewModel: ObservableObject {
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        if AppConfig.DEBUG{ print("username: \(username) | userId: \(userId)")}
 | 
					        if AppConfig.DEBUG{ print("username: \(username) | userId: \(userId)")}
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func loadTermsIfNeeded() {
 | 
				
			||||||
 | 
					        guard !isLoadingTerms else { return }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if !termsContent.isEmpty {
 | 
				
			||||||
 | 
					            termsErrorMessage = nil
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isLoadingTerms = true
 | 
				
			||||||
 | 
					        termsErrorMessage = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        NetworkClient.shared.request(
 | 
				
			||||||
 | 
					            path: "/legal/terms",
 | 
				
			||||||
 | 
					            headers: ["Accept": "text/plain"],
 | 
				
			||||||
 | 
					            requiresAuth: false,
 | 
				
			||||||
 | 
					            callbackQueue: .main
 | 
				
			||||||
 | 
					        ) { [weak self] result in
 | 
				
			||||||
 | 
					            guard let self else { return }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.isLoadingTerms = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            switch result {
 | 
				
			||||||
 | 
					            case .success(let response):
 | 
				
			||||||
 | 
					                if let content = String(data: response.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines),
 | 
				
			||||||
 | 
					                   !content.isEmpty {
 | 
				
			||||||
 | 
					                    self.termsContent = content
 | 
				
			||||||
 | 
					                    return
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let jsonObject = try? JSONSerialization.jsonObject(with: response.data, options: []),
 | 
				
			||||||
 | 
					                   let json = jsonObject as? [String: Any],
 | 
				
			||||||
 | 
					                   let content = (json["content"] as? String) ?? (json["text"] as? String),
 | 
				
			||||||
 | 
					                   !content.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
 | 
				
			||||||
 | 
					                    self.termsContent = content
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    self.termsErrorMessage = NSLocalizedString("Не удалось загрузить текст правил.", comment: "")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            case .failure:
 | 
				
			||||||
 | 
					                self.termsErrorMessage = NSLocalizedString("Не удалось загрузить текст правил.", comment: "")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func reloadTerms() {
 | 
				
			||||||
 | 
					        termsContent = ""
 | 
				
			||||||
 | 
					        termsErrorMessage = nil
 | 
				
			||||||
 | 
					        loadTermsIfNeeded()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@ struct LoginView: View {
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    @State private var isShowingRegistration = false
 | 
					    @State private var isShowingRegistration = false
 | 
				
			||||||
    @State private var showLegacySupportNotice = false
 | 
					    @State private var showLegacySupportNotice = false
 | 
				
			||||||
 | 
					    @State private var isShowingTerms = false
 | 
				
			||||||
    @FocusState private var focusedField: Field?
 | 
					    @FocusState private var focusedField: Field?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private enum Field: Hashable {
 | 
					    private enum Field: Hashable {
 | 
				
			||||||
@ -30,6 +31,10 @@ struct LoginView: View {
 | 
				
			|||||||
    private var isPasswordValid: Bool {
 | 
					    private var isPasswordValid: Bool {
 | 
				
			||||||
        return viewModel.password.count >= 8 && viewModel.password.count <= 128
 | 
					        return viewModel.password.count >= 8 && viewModel.password.count <= 128
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var isLoginButtonEnabled: Bool {
 | 
				
			||||||
 | 
					        !viewModel.isLoading && isUsernameValid && isPasswordValid && viewModel.hasAcceptedTerms
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    var body: some View {
 | 
					    var body: some View {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -99,7 +104,7 @@ struct LoginView: View {
 | 
				
			|||||||
                            viewModel.password = String(newValue.prefix(32))
 | 
					                            viewModel.password = String(newValue.prefix(32))
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
                // Показываем ошибку для пароля
 | 
					                // Показываем ошибку для пароля
 | 
				
			||||||
                if !isPasswordValid && !viewModel.password.isEmpty {
 | 
					                if !isPasswordValid && !viewModel.password.isEmpty {
 | 
				
			||||||
                    Text(NSLocalizedString("Неверный пароль", comment: "Неверный пароль"))
 | 
					                    Text(NSLocalizedString("Неверный пароль", comment: "Неверный пароль"))
 | 
				
			||||||
@ -107,10 +112,15 @@ struct LoginView: View {
 | 
				
			|||||||
                        .font(.caption)
 | 
					                        .font(.caption)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var isButtonEnabled: Bool {
 | 
					                TermsAgreementView(
 | 
				
			||||||
                    !viewModel.isLoading && isUsernameValid && isPasswordValid
 | 
					                    isAccepted: $viewModel.hasAcceptedTerms,
 | 
				
			||||||
                }
 | 
					                    openTerms: {
 | 
				
			||||||
                
 | 
					                        viewModel.loadTermsIfNeeded()
 | 
				
			||||||
 | 
					                        isShowingTerms = true
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .padding(.vertical, 12)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Button(action: {
 | 
					                Button(action: {
 | 
				
			||||||
                    viewModel.login()
 | 
					                    viewModel.login()
 | 
				
			||||||
                }) {
 | 
					                }) {
 | 
				
			||||||
@ -126,11 +136,11 @@ struct LoginView: View {
 | 
				
			|||||||
                            .foregroundColor(.white)
 | 
					                            .foregroundColor(.white)
 | 
				
			||||||
                            .padding()
 | 
					                            .padding()
 | 
				
			||||||
                            .frame(maxWidth: .infinity)
 | 
					                            .frame(maxWidth: .infinity)
 | 
				
			||||||
                            .background(isButtonEnabled ? Color.blue : Color.gray)
 | 
					                            .background(isLoginButtonEnabled ? Color.blue : Color.gray)
 | 
				
			||||||
                            .cornerRadius(8)
 | 
					                            .cornerRadius(8)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                .disabled(!isButtonEnabled)
 | 
					                .disabled(!isLoginButtonEnabled)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//                Spacer()
 | 
					//                Spacer()
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
@ -171,6 +181,23 @@ struct LoginView: View {
 | 
				
			|||||||
                    .zIndex(1)
 | 
					                    .zIndex(1)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        .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 var themeIconName: String {
 | 
					    private var themeIconName: String {
 | 
				
			||||||
        switch themeManager.theme {
 | 
					        switch themeManager.theme {
 | 
				
			||||||
@ -245,6 +272,106 @@ 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 {
 | 
					    private var selectedThemeOption: ThemeOption {
 | 
				
			||||||
        ThemeOption.option(for: themeManager.theme)
 | 
					        ThemeOption.option(for: themeManager.theme)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -277,6 +404,7 @@ struct LoginView_Previews: PreviewProvider {
 | 
				
			|||||||
    static var previews: some View {
 | 
					    static var previews: some View {
 | 
				
			||||||
        let viewModel = LoginViewModel()
 | 
					        let viewModel = LoginViewModel()
 | 
				
			||||||
        viewModel.isLoading = false // чтобы убрать спиннер
 | 
					        viewModel.isLoading = false // чтобы убрать спиннер
 | 
				
			||||||
 | 
					        viewModel.hasAcceptedTerms = true
 | 
				
			||||||
        return LoginView(viewModel: viewModel)
 | 
					        return LoginView(viewModel: viewModel)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user