From 790278152c6c5123a0851a3ae0c6c8f9a0feb061 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Wed, 18 Mar 2026 01:42:58 +0300 Subject: [PATCH 01/65] setup goodroad client bootstrap --- LICENSE | 2 +- README.md | 40 +-- SETUP.md | 1 + app/build.gradle | 81 ++++++ app/proguard-rules.pro | 1 + app/src/main/AndroidManifest.xml | 25 ++ .../java/com/example/goodroad/MainActivity.kt | 19 ++ .../goodroad/data/network/ApiClient.kt | 31 +++ .../example/goodroad/data/network/AuthApi.kt | 13 + .../goodroad/data/network/AuthModels.kt | 32 +++ .../java/com/example/goodroad/ui/AuthApp.kt | 121 +++++++++ .../com/example/goodroad/ui/theme/Color.kt | 8 + .../com/example/goodroad/ui/theme/Theme.kt | 30 +++ .../com/example/goodroad/ui/theme/Type.kt | 5 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/values/themes.xml | 5 + app/src/main/res/xml/backup_rules.xml | 2 + .../main/res/xml/data_extraction_rules.xml | 5 + build.gradle | 4 + docker-compose.backend.yml | 43 +++ docker/.env | 6 + docker/.env.backend.example | 5 + gradle.properties | 4 + gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 251 ++++++++++++++++++ gradlew.bat | 94 +++++++ settings.gradle | 18 ++ 27 files changed, 835 insertions(+), 21 deletions(-) create mode 100644 SETUP.md create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/example/goodroad/MainActivity.kt create mode 100644 app/src/main/java/com/example/goodroad/data/network/ApiClient.kt create mode 100644 app/src/main/java/com/example/goodroad/data/network/AuthApi.kt create mode 100644 app/src/main/java/com/example/goodroad/data/network/AuthModels.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/AuthApp.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/theme/Color.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/theme/Theme.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/theme/Type.kt create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 build.gradle create mode 100644 docker-compose.backend.yml create mode 100644 docker/.env create mode 100644 docker/.env.backend.example create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/LICENSE b/LICENSE index 261eeb9..f49a4e1 100644 --- a/LICENSE +++ b/LICENSE @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md index 3855e65..cfc474f 100644 --- a/README.md +++ b/README.md @@ -13,35 +13,35 @@ ## Основная функциональность - **Профиль пользователя:** - - Выбор препятствий, которые нужно избегать при построении маршрута; - - Интерактивные задания, связанные с оценкой объектов и волонтерством; - - Рейтинг и система начисления баллов за отзывы и волонтерство; - - Лента заявок на помощь с фильтрами по местоположению и времени; - - Подача заявки на улучшение инфраструктуры. + - Выбор препятствий, которые нужно избегать при построении маршрута; + - Интерактивные задания, связанные с оценкой объектов и волонтерством; + - Рейтинг и система начисления баллов за отзывы и волонтерство; + - Лента заявок на помощь с фильтрами по местоположению и времени; + - Подача заявки на улучшение инфраструктуры. - **Профиль модератора:** - - Обработка заявок о помощи: система реагирования на критические ситуации; - - Модерация отзывов; + - Обработка заявок о помощи: система реагирования на критические ситуации; + - Модерация отзывов; - **Карта и маршруты:** - - Влияние отзывов на построение пути. - - Построение 3 вариантов маршрута: **доступный**, **быстрый**, **компромиссный**; - - Выделение проблемных участков и карточки объектов с деталями; - - История маршрутов и сохранение последнего построенного пути. + - Влияние отзывов на построение пути. + - Построение 3 вариантов маршрута: **доступный**, **быстрый**, **компромиссный**; + - Выделение проблемных участков и карточки объектов с деталями; + - История маршрутов и сохранение последнего построенного пути. ## MVP - **Регистрация / вход / восстановление доступа.** - **Маршрутизация:** - - Выбор избегаемых препятствий в профиле; - - Построение маршрута; - - Отображение маршрута и проблемных участков; - - Хранение препятствий (тип и тяжесть). + - Выбор избегаемых препятствий в профиле; + - Построение маршрута; + - Отображение маршрута и проблемных участков; + - Хранение препятствий (тип и тяжесть). - **Отзывы:** - - Чек-лист для оценивания; - - Прикрепление фотографии (опционально); - - Учет отзывов в маршрутизации. + - Чек-лист для оценивания; + - Прикрепление фотографии (опционально); + - Учет отзывов в маршрутизации. - **Модерация:** - - Проверка отзывов; - - Управление младшими модераторами (в случае главного). + - Проверка отзывов; + - Управление младшими модераторами (в случае главного). ## Наши продукты diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 0000000..3cdafda --- /dev/null +++ b/SETUP.md @@ -0,0 +1 @@ +## Тут что-то будет позже \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..543bf10 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,81 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.example.goodroad' + compileSdk 35 + + defaultConfig { + applicationId 'com.example.goodroad' + minSdk 28 + targetSdk 35 + versionCode 1 + versionName '1.0' + + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + buildConfigField 'String', 'BASE_URL', '"http://10.0.2.2:8080/"' + buildConfigField 'boolean', 'MOCK_AUTH', 'true' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + debug { + applicationIdSuffix '.debug' + debuggable true + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = '17' + } + + buildFeatures { + compose true + buildConfig true + } + + composeOptions { + kotlinCompilerExtensionVersion '1.5.14' + } + + packaging { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } +} + +dependencies { + def composeBom = platform('androidx.compose:compose-bom:2024.06.00') + + implementation 'androidx.core:core-ktx:1.15.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.7' + implementation 'androidx.activity:activity-compose:1.9.3' + implementation composeBom + androidTestImplementation composeBom + + implementation 'androidx.compose.ui:ui' + implementation 'androidx.compose.ui:ui-tooling-preview' + implementation 'androidx.compose.material3:material3' + implementation 'com.google.android.material:material:1.12.0' + implementation 'androidx.navigation:navigation-compose:2.8.5' + implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7' + + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0' + implementation 'com.squareup.retrofit2:retrofit:2.11.0' + implementation 'com.squareup.retrofit2:converter-gson:2.11.0' + implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0' + + debugImplementation 'androidx.compose.ui:ui-tooling' + debugImplementation 'androidx.compose.ui:ui-test-manifest' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..5cdf401 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1 @@ +# пока пусто diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6f8049c --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/example/goodroad/MainActivity.kt b/app/src/main/java/com/example/goodroad/MainActivity.kt new file mode 100644 index 0000000..c3a3b60 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/MainActivity.kt @@ -0,0 +1,19 @@ +package com.example.goodroad + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import com.example.goodroad.ui.AuthApp +import com.example.goodroad.ui.theme.GoodRoadTheme + +class MainActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + GoodRoadTheme { + AuthApp() + } + } + } +} diff --git a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt new file mode 100644 index 0000000..fc9615f --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt @@ -0,0 +1,31 @@ +package com.example.goodroad.data.network + +import com.example.goodroad.BuildConfig +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + +object ApiClient { + + private val logging = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + + private val client = OkHttpClient.Builder() + .addInterceptor(logging) + .connectTimeout(20, TimeUnit.SECONDS) + .readTimeout(20, TimeUnit.SECONDS) + .writeTimeout(20, TimeUnit.SECONDS) + .build() + + val authApi: AuthApi by lazy { + Retrofit.Builder() + .baseUrl(BuildConfig.BASE_URL) + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(AuthApi::class.java) + } +} diff --git a/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt b/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt new file mode 100644 index 0000000..85baf09 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt @@ -0,0 +1,13 @@ +package com.example.goodroad.data.network + +import retrofit2.http.Body +import retrofit2.http.POST + +interface AuthApi { + + @POST("/auth/login") + suspend fun login(@Body req: LoginReq): AuthResp + + @POST("/auth/register") + suspend fun register(@Body req: RegisterReq): AuthResp +} diff --git a/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt b/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt new file mode 100644 index 0000000..b6a2de1 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt @@ -0,0 +1,32 @@ +package com.example.goodroad.data.network + +data class LoginReq( + val phone: String, + val password: String +) + +data class RegisterReq( + val firstName: String, + val lastName: String, + val phone: String, + val password: String +) + +data class AuthResp( + val user: UserDto? = null, + val message: String? = null +) + +data class UserDto( + val id: Long? = null, + val firstName: String? = null, + val lastName: String? = null, + val role: String? = null, + val totalPoints: Int? = null +) + +data class ApiErrorDto( + val error: String? = null, + val message: String? = null, + val details: Map? = null +) diff --git a/app/src/main/java/com/example/goodroad/ui/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/AuthApp.kt new file mode 100644 index 0000000..cf728a4 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/AuthApp.kt @@ -0,0 +1,121 @@ +package com.example.goodroad.ui + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController + +private const val LOGIN_ROUTE = "login" +private const val REGISTER_ROUTE = "register" +private const val RECOVER_ROUTE = "recover" +private const val HOME_ROUTE = "home" + +@Composable +fun AuthApp(navController: NavHostController = rememberNavController()) { + NavHost(navController = navController, startDestination = LOGIN_ROUTE) { + composable(LOGIN_ROUTE) { + PlaceholderScreen( + title = "GoodRoad", + subtitle = "Урааа, все запустилось! Но пока тут заглушка для входа", + primaryText = if (com.example.goodroad.BuildConfig.MOCK_AUTH) "Войти локально" else "Продолжить", + onPrimary = { navController.navigate(HOME_ROUTE) }, + secondaryText = "Регистрация", + onSecondary = { navController.navigate(REGISTER_ROUTE) }, + tertiaryText = "Восстановление пароля", + onTertiary = { navController.navigate(RECOVER_ROUTE) } + ) + } + composable(REGISTER_ROUTE) { + PlaceholderScreen( + title = "Регистрация", + subtitle = "И снова заглушка", + primaryText = "Назад ко входу", + onPrimary = { navController.popBackStack() } + ) + } + composable(RECOVER_ROUTE) { + PlaceholderScreen( + title = "Восстановление пароля", + subtitle = "Экран пока не реализован, но вы держитесь", + primaryText = "Назад ко входу", + onPrimary = { navController.popBackStack() } + ) + } + composable(HOME_ROUTE) { + PlaceholderScreen( + title = "GoodRoad", + subtitle = "Проект успешно стартовал. Ну типа стартовал.", + primaryText = "Выйти", + onPrimary = { + navController.navigate(LOGIN_ROUTE) { + popUpTo(0) + } + } + ) + } + } +} + +@Composable +private fun PlaceholderScreen( + title: String, + subtitle: String, + primaryText: String, + onPrimary: () -> Unit, + secondaryText: String? = null, + onSecondary: (() -> Unit)? = null, + tertiaryText: String? = null, + onTertiary: (() -> Unit)? = null +) { + Scaffold { padding -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(padding) + .padding(24.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = title, + style = MaterialTheme.typography.headlineMedium + ) + Text( + text = subtitle, + modifier = Modifier.padding(top = 12.dp, bottom = 24.dp), + style = MaterialTheme.typography.bodyLarge + ) + Button(onClick = onPrimary) { + Text(primaryText) + } + if (secondaryText != null && onSecondary != null) { + Button( + onClick = onSecondary, + modifier = Modifier.padding(top = 12.dp) + ) { + Text(secondaryText) + } + } + if (tertiaryText != null && onTertiary != null) { + Button( + onClick = onTertiary, + modifier = Modifier.padding(top = 12.dp) + ) { + Text(tertiaryText) + } + } + } + } +} diff --git a/app/src/main/java/com/example/goodroad/ui/theme/Color.kt b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt new file mode 100644 index 0000000..2e222c0 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt @@ -0,0 +1,8 @@ +package com.example.goodroad.ui.theme + +import androidx.compose.ui.graphics.Color + +val SafeGreen = Color(0xFF4E9B6F) +val SafeGreenDark = Color(0xFF3D7A58) +val SurfaceWarm = Color(0xFFEEE7DD) +val TextPrimary = Color(0xFF1D1B20) diff --git a/app/src/main/java/com/example/goodroad/ui/theme/Theme.kt b/app/src/main/java/com/example/goodroad/ui/theme/Theme.kt new file mode 100644 index 0000000..9a27e50 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/theme/Theme.kt @@ -0,0 +1,30 @@ +package com.example.goodroad.ui.theme + +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable + +private val LightColors = lightColorScheme( + primary = SafeGreen, + secondary = SafeGreenDark, + surface = SurfaceWarm, + onSurface = TextPrimary +) + +private val DarkColors = darkColorScheme( + primary = SafeGreen, + secondary = SafeGreenDark +) + +@Composable +fun GoodRoadTheme( + darkTheme: Boolean = false, + content: @Composable () -> Unit +) { + MaterialTheme( + colorScheme = if (darkTheme) DarkColors else LightColors, + typography = Typography, + content = content + ) +} diff --git a/app/src/main/java/com/example/goodroad/ui/theme/Type.kt b/app/src/main/java/com/example/goodroad/ui/theme/Type.kt new file mode 100644 index 0000000..162c49d --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/theme/Type.kt @@ -0,0 +1,5 @@ +package com.example.goodroad.ui.theme + +import androidx.compose.material3.Typography + +val Typography = Typography() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..bdf83ac --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + GoodRoad + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..5114507 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..1b0854f --- /dev/null +++ b/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,2 @@ + + diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..da8aa42 --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..009b975 --- /dev/null +++ b/build.gradle @@ -0,0 +1,4 @@ +plugins { + id 'com.android.application' version '8.7.3' apply false + id 'org.jetbrains.kotlin.android' version '1.9.24' apply false +} diff --git a/docker-compose.backend.yml b/docker-compose.backend.yml new file mode 100644 index 0000000..0c6d5c4 --- /dev/null +++ b/docker-compose.backend.yml @@ -0,0 +1,43 @@ +services: + db: + image: postgis/postgis:16-3.4 + container_name: goodroad_db + ports: + - "5432:5432" + environment: + POSTGRES_DB: ${DB_NAME} + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASS} + volumes: + - goodroad_db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"] + interval: 5s + retries: 5 + timeout: 3s + + server: + build: + context: ../GoodRoad-Server + dockerfile: Dockerfile.server + container_name: goodroad_server + ports: + - "8080:8080" + environment: + DB_HOST: db + DB_NAME: ${DB_NAME} + DB_USER: ${DB_USER} + DB_PASS: ${DB_PASS} + ADMIN_PHONE: ${ADMIN_PHONE} + ADMIN_PASS: ${ADMIN_PASS} + depends_on: + db: + condition: service_healthy + logging: + driver: json-file + options: + max-size: 10m + max-file: '3' + +volumes: + goodroad_db: diff --git a/docker/.env b/docker/.env new file mode 100644 index 0000000..30a5c4f --- /dev/null +++ b/docker/.env @@ -0,0 +1,6 @@ +DB_NAME=goodroad +DB_USER=goodroad +DB_PASS=meduser + +ADMIN_PHONE=+71234567890 +ADMIN_PASS=medpass \ No newline at end of file diff --git a/docker/.env.backend.example b/docker/.env.backend.example new file mode 100644 index 0000000..d569181 --- /dev/null +++ b/docker/.env.backend.example @@ -0,0 +1,5 @@ +DB_NAME=goodroad +DB_USER=postgres +DB_PASS=postgres +ADMIN_PHONE=+71234567890 +ADMIN_PASS=admin diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..f0a2e55 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +android.useAndroidX=true +kotlin.code.style=official +android.nonTransitiveRClass=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..df97d72 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..ef07e01 --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..5eed7ee --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..5f2196b --- /dev/null +++ b/settings.gradle @@ -0,0 +1,18 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = 'GoodRoad-Client' +include ':app' From 90702f3d7c187dfef49d14f0dc63ef44b8ca1ac1 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Wed, 18 Mar 2026 15:21:06 +0300 Subject: [PATCH 02/65] feature: windows for auth and registration --- app/build.gradle | 9 +- .../goodroad/data/network/ApiClient.kt | 2 +- .../goodroad/data/network/AuthModels.kt | 2 +- .../java/com/example/goodroad/ui/AuthApp.kt | 865 ++++++++++++++++-- .../com/example/goodroad/ui/theme/Color.kt | 26 +- .../com/example/goodroad/ui/theme/Theme.kt | 87 +- .../com/example/goodroad/ui/theme/Type.kt | 37 +- docker/.env | 2 +- 8 files changed, 939 insertions(+), 91 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 543bf10..9b38061 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { versionName '1.0' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' - buildConfigField 'String', 'BASE_URL', '"http://10.0.2.2:8080/"' - buildConfigField 'boolean', 'MOCK_AUTH', 'true' + buildConfigField "boolean", "MOCK_AUTH", "false" + buildConfigField "String", "GOODROAD_SERVER_URL", "\"http://10.0.2.2:8080/\"" } buildTypes { @@ -67,9 +67,10 @@ dependencies { implementation 'androidx.compose.ui:ui' implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.compose.material3:material3' - implementation 'com.google.android.material:material:1.12.0' + implementation 'androidx.compose.material:material-icons-extended' implementation 'androidx.navigation:navigation-compose:2.8.5' implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7' + implementation 'com.google.android.material:material:1.12.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0' implementation 'com.squareup.retrofit2:retrofit:2.11.0' @@ -78,4 +79,4 @@ dependencies { debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt index fc9615f..f3a29e7 100644 --- a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt +++ b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt @@ -22,7 +22,7 @@ object ApiClient { val authApi: AuthApi by lazy { Retrofit.Builder() - .baseUrl(BuildConfig.BASE_URL) + .baseUrl(BuildConfig.GOODROAD_SERVER_URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build() diff --git a/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt b/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt index b6a2de1..071fe2e 100644 --- a/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt +++ b/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt @@ -18,7 +18,7 @@ data class AuthResp( ) data class UserDto( - val id: Long? = null, + val id: String? = null, val firstName: String? = null, val lastName: String? = null, val role: String? = null, diff --git a/app/src/main/java/com/example/goodroad/ui/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/AuthApp.kt index cf728a4..96ce0a5 100644 --- a/app/src/main/java/com/example/goodroad/ui/AuthApp.kt +++ b/app/src/main/java/com/example/goodroad/ui/AuthApp.kt @@ -1,121 +1,844 @@ package com.example.goodroad.ui +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.Phone +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import com.example.goodroad.BuildConfig +import com.example.goodroad.data.network.ApiClient +import com.example.goodroad.data.network.LoginReq +import com.example.goodroad.data.network.RegisterReq +import com.example.goodroad.ui.theme.AlertRed +import com.example.goodroad.ui.theme.BackgroundLight +import com.example.goodroad.ui.theme.BorderWarm +import com.example.goodroad.ui.theme.SafeGreen +import com.example.goodroad.ui.theme.SafeRoute +import com.example.goodroad.ui.theme.SurfaceWarm +import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.theme.TextSecondary +import com.example.goodroad.ui.theme.UrbanBrown +import kotlinx.coroutines.launch +import retrofit2.HttpException +import java.io.IOException private const val LOGIN_ROUTE = "login" private const val REGISTER_ROUTE = "register" private const val RECOVER_ROUTE = "recover" -private const val HOME_ROUTE = "home" +private const val USER_HOME_ROUTE = "user_home" +private const val MODERATOR_HOME_ROUTE = "moderator_home" + +private val cyrillicInputRegex = Regex("^[\\p{IsCyrillic} -]*$") +private val digitsInputRegex = Regex("^\\d*$") +private val cyrillicValueRegex = Regex("^[\\p{IsCyrillic} -]+$") +private val digitsValueRegex = Regex("^\\d+$") + +private fun homeRoute(role: String): String { + return if (role.startsWith("MODERATOR")) { + MODERATOR_HOME_ROUTE + } else { + USER_HOME_ROUTE + } +} + +private fun isAllowedCyrillicInput(value: String): Boolean { + return cyrillicInputRegex.matches(value) +} + +private fun isAllowedDigitsInput(value: String): Boolean { + return digitsInputRegex.matches(value) +} + +private fun normalizeRequiredCyrillic(value: String): String? { + val normalized = value.trim() + if (normalized.isEmpty()) { + return null + } + return if (cyrillicValueRegex.matches(normalized)) normalized else null +} + +private fun normalizeRequiredDigits(value: String): String? { + val normalized = value.trim() + if (normalized.isEmpty()) { + return null + } + return if (digitsValueRegex.matches(normalized)) normalized else null +} + +private fun formatPhoneForRequest(phoneDigits: String): String { + return "+$phoneDigits" +} @Composable fun AuthApp(navController: NavHostController = rememberNavController()) { - NavHost(navController = navController, startDestination = LOGIN_ROUTE) { - composable(LOGIN_ROUTE) { - PlaceholderScreen( - title = "GoodRoad", - subtitle = "Урааа, все запустилось! Но пока тут заглушка для входа", - primaryText = if (com.example.goodroad.BuildConfig.MOCK_AUTH) "Войти локально" else "Продолжить", - onPrimary = { navController.navigate(HOME_ROUTE) }, - secondaryText = "Регистрация", - onSecondary = { navController.navigate(REGISTER_ROUTE) }, - tertiaryText = "Восстановление пароля", - onTertiary = { navController.navigate(RECOVER_ROUTE) } - ) + Surface( + modifier = Modifier.fillMaxSize(), + color = BackgroundLight + ) { + NavHost( + navController = navController, + startDestination = LOGIN_ROUTE + ) { + composable(LOGIN_ROUTE) { + LoginScreen( + onLoginSuccess = { role -> + navController.navigate(homeRoute(role)) { + popUpTo(LOGIN_ROUTE) { + inclusive = true + } + launchSingleTop = true + } + }, + onSignUp = { + navController.navigate(REGISTER_ROUTE) + }, + onForgotPassword = { + navController.navigate(RECOVER_ROUTE) + } + ) + } + composable(REGISTER_ROUTE) { + RegisterScreen( + onRegisterSuccess = { role -> + navController.navigate(homeRoute(role)) { + popUpTo(LOGIN_ROUTE) { + inclusive = true + } + launchSingleTop = true + } + }, + onLogin = { + navController.popBackStack() + } + ) + } + composable(RECOVER_ROUTE) { + RecoverPasswordScreen( + onLogin = { + navController.popBackStack() + } + ) + } + composable(USER_HOME_ROUTE) { + RoleStubScreen( + title = "Главный экран пользователя", + onLogout = { + navController.navigate(LOGIN_ROUTE) { + popUpTo(USER_HOME_ROUTE) { + inclusive = true + } + launchSingleTop = true + } + } + ) + } + composable(MODERATOR_HOME_ROUTE) { + RoleStubScreen( + title = "Главный экран модератора", + onLogout = { + navController.navigate(LOGIN_ROUTE) { + popUpTo(MODERATOR_HOME_ROUTE) { + inclusive = true + } + launchSingleTop = true + } + } + ) + } } - composable(REGISTER_ROUTE) { - PlaceholderScreen( - title = "Регистрация", - subtitle = "И снова заглушка", - primaryText = "Назад ко входу", - onPrimary = { navController.popBackStack() } + } +} + +@Composable +private fun LoginScreen( + onLoginSuccess: (String) -> Unit, + onSignUp: () -> Unit, + onForgotPassword: () -> Unit +) { + var phone by rememberSaveable { mutableStateOf("") } + var password by rememberSaveable { mutableStateOf("") } + var errorText by rememberSaveable { mutableStateOf(null) } + var loading by rememberSaveable { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + AuthScreenFrame( + title = "Вход", + action = { + AuthButton( + text = if (loading) "Входим..." else "Войти", + enabled = !loading + ) { + val phoneDigits = normalizeRequiredDigits(phone) + if (phoneDigits == null || password.isBlank()) { + errorText = "Заполните телефон и пароль" + return@AuthButton + } + + if (BuildConfig.MOCK_AUTH) { + errorText = null + onLoginSuccess("USER") + return@AuthButton + } + + scope.launch { + loading = true + errorText = null + try { + val resp = ApiClient.authApi.login( + LoginReq( + phone = formatPhoneForRequest(phoneDigits), + password = password + ) + ) + val role = resp.user?.role + if (role.isNullOrBlank()) { + errorText = "Не удалось определить роль пользователя" + } else { + onLoginSuccess(role) + } + } catch (_: HttpException) { + errorText = "Неверный телефон или пароль" + } catch (_: IOException) { + errorText = "Нет соединения с сервером" + } catch (_: Exception) { + errorText = "Ошибка входа" + } finally { + loading = false + } + } + } + }, + footer = { + AuthFooter( + prefix = "Нет аккаунта?", + action = "Зарегистрироваться", + onClick = onSignUp ) } - composable(RECOVER_ROUTE) { - PlaceholderScreen( - title = "Восстановление пароля", - subtitle = "Экран пока не реализован, но вы держитесь", - primaryText = "Назад ко входу", - onPrimary = { navController.popBackStack() } - ) + ) { + PhoneField( + value = phone, + onValueChange = { value -> + if (isAllowedDigitsInput(value)) { + phone = value + } + }, + label = "Телефон" + ) + Spacer(Modifier.height(12.dp)) + PasswordField( + value = password, + onValueChange = { password = it }, + label = "Пароль" + ) + Spacer(Modifier.height(10.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically + ) { + TextButton( + onClick = onForgotPassword, + contentPadding = PaddingValues(0.dp) + ) { + Text( + text = "Забыли пароль?", + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown + ) + } } - composable(HOME_ROUTE) { - PlaceholderScreen( - title = "GoodRoad", - subtitle = "Проект успешно стартовал. Ну типа стартовал.", - primaryText = "Выйти", - onPrimary = { - navController.navigate(LOGIN_ROUTE) { - popUpTo(0) + AuthStatusText(text = errorText) + } +} + +@Composable +private fun RegisterScreen( + onRegisterSuccess: (String) -> Unit, + onLogin: () -> Unit +) { + var firstName by rememberSaveable { mutableStateOf("") } + var lastName by rememberSaveable { mutableStateOf("") } + var phone by rememberSaveable { mutableStateOf("") } + var password by rememberSaveable { mutableStateOf("") } + var confirmPassword by rememberSaveable { mutableStateOf("") } + var errorText by rememberSaveable { mutableStateOf(null) } + var loading by rememberSaveable { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + AuthScreenFrame( + title = "Создать аккаунт", + action = { + AuthButton( + text = if (loading) "Создаем..." else "Зарегистрироваться", + enabled = !loading + ) { + val firstNameNormalized = normalizeRequiredCyrillic(firstName) + if (firstNameNormalized == null) { + errorText = "Имя обязательно: только кириллица, пробел и -" + return@AuthButton + } + + val lastNameNormalized = normalizeRequiredCyrillic(lastName) + if (lastNameNormalized == null) { + errorText = "Фамилия обязательна: только кириллица, пробел и -" + return@AuthButton + } + + val phoneDigits = normalizeRequiredDigits(phone) + if (phoneDigits == null || password.isBlank()) { + errorText = "Телефон и пароль обязательны" + return@AuthButton + } + if (password != confirmPassword) { + errorText = "Пароли не совпадают" + return@AuthButton + } + + if (BuildConfig.MOCK_AUTH) { + errorText = null + onRegisterSuccess("USER") + return@AuthButton + } + + scope.launch { + loading = true + errorText = null + try { + val resp = ApiClient.authApi.register( + RegisterReq( + firstName = firstNameNormalized, + lastName = lastNameNormalized, + phone = formatPhoneForRequest(phoneDigits), + password = password + ) + ) + val role = resp.user?.role ?: "USER" + onRegisterSuccess(role) + } catch (_: HttpException) { + errorText = "Не удалось зарегистрироваться" + } catch (_: IOException) { + errorText = "Нет соединения с сервером" + } catch (_: Exception) { + errorText = "Ошибка регистрации" + } finally { + loading = false } } + } + }, + footer = { + AuthFooter( + prefix = "Уже есть аккаунт?", + action = "Войти", + onClick = onLogin ) } + ) { + PlainField( + value = firstName, + onValueChange = { value -> + if (isAllowedCyrillicInput(value)) { + firstName = value + } + }, + label = "Имя", + icon = { + Icon( + imageVector = Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + } + ) + Spacer(Modifier.height(12.dp)) + PlainField( + value = lastName, + onValueChange = { value -> + if (isAllowedCyrillicInput(value)) { + lastName = value + } + }, + label = "Фамилия", + icon = { + Icon( + imageVector = Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + } + ) + Spacer(Modifier.height(12.dp)) + PhoneField( + value = phone, + onValueChange = { value -> + if (isAllowedDigitsInput(value)) { + phone = value + } + }, + label = "Телефон" + ) + Spacer(Modifier.height(12.dp)) + PasswordField( + value = password, + onValueChange = { password = it }, + label = "Пароль" + ) + Spacer(Modifier.height(12.dp)) + PasswordField( + value = confirmPassword, + onValueChange = { confirmPassword = it }, + label = "Подтвердите пароль" + ) + AuthStatusText(text = errorText) } } @Composable -private fun PlaceholderScreen( +private fun RecoverPasswordScreen( + onLogin: () -> Unit +) { + var phone by rememberSaveable { mutableStateOf("") } + var newPassword by rememberSaveable { mutableStateOf("") } + var confirmPassword by rememberSaveable { mutableStateOf("") } + var infoText by rememberSaveable { mutableStateOf(null) } + + AuthScreenFrame( + title = "Смена пароля", + subtitle = "Для восстановления нужен отдельный endpoint на backend. Пока это только UI-заглушка.", + action = { + AuthButton(text = "Сменить пароль") { + infoText = "В текущем сервере доступна только смена пароля через старый пароль." + } + }, + footer = { + AuthFooter( + prefix = "Вспомнили пароль?", + action = "Вернуться ко входу", + onClick = onLogin + ) + } + ) { + PhoneField( + value = phone, + onValueChange = { value -> + if (isAllowedDigitsInput(value)) { + phone = value + } + }, + label = "Телефон" + ) + Spacer(Modifier.height(12.dp)) + PasswordField( + value = newPassword, + onValueChange = { newPassword = it }, + label = "Новый пароль" + ) + Spacer(Modifier.height(12.dp)) + PasswordField( + value = confirmPassword, + onValueChange = { confirmPassword = it }, + label = "Подтвердите пароль" + ) + AuthStatusText(text = infoText) + } +} + +@Composable +private fun AuthScreenFrame( title: String, - subtitle: String, - primaryText: String, - onPrimary: () -> Unit, - secondaryText: String? = null, - onSecondary: (() -> Unit)? = null, - tertiaryText: String? = null, - onTertiary: (() -> Unit)? = null + subtitle: String? = null, + action: @Composable () -> Unit, + footer: @Composable () -> Unit, + content: @Composable ColumnScope.() -> Unit ) { - Scaffold { padding -> + Column( + modifier = Modifier + .fillMaxSize() + .background(BackgroundLight) + .verticalScroll(rememberScrollState()) + ) { + AuthDecor() Column( modifier = Modifier - .fillMaxSize() - .padding(padding) - .padding(24.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally + .fillMaxWidth() + .padding(horizontal = 24.dp) + .padding(bottom = 24.dp) ) { Text( text = title, - style = MaterialTheme.typography.headlineMedium - ) - Text( - text = subtitle, - modifier = Modifier.padding(top = 12.dp, bottom = 24.dp), - style = MaterialTheme.typography.bodyLarge + style = MaterialTheme.typography.headlineLarge, + color = TextPrimary ) - Button(onClick = onPrimary) { - Text(primaryText) + if (subtitle != null) { + Text( + text = subtitle, + modifier = Modifier.padding(top = 8.dp), + style = MaterialTheme.typography.bodyMedium, + color = TextSecondary + ) } - if (secondaryText != null && onSecondary != null) { - Button( - onClick = onSecondary, - modifier = Modifier.padding(top = 12.dp) - ) { - Text(secondaryText) - } + Spacer(Modifier.height(28.dp)) + content() + Spacer(Modifier.height(28.dp)) + action() + Spacer(Modifier.height(16.dp)) + footer() + } + } +} + +@Composable +private fun AuthDecor() { + Box( + modifier = Modifier + .fillMaxWidth() + .height(230.dp) + .statusBarsPadding() + .padding(horizontal = 18.dp, vertical = 12.dp) + .clip(RoundedCornerShape(32.dp)) + .background(SurfaceWarm) + ) { + Canvas(modifier = Modifier.matchParentSize()) { + val lightPatch = Path().apply { + moveTo(0f, size.height * 0.76f) + cubicTo( + size.width * 0.1f, size.height * 0.7f, + size.width * 0.2f, size.height * 0.45f, + size.width * 0.34f, size.height * 0.42f + ) + cubicTo( + size.width * 0.49f, size.height * 0.38f, + size.width * 0.54f, size.height * 0.18f, + size.width * 0.72f, size.height * 0.12f + ) + lineTo(size.width, size.height * 0.12f) + lineTo(size.width, size.height) + lineTo(0f, size.height) + close() } - if (tertiaryText != null && onTertiary != null) { - Button( - onClick = onTertiary, - modifier = Modifier.padding(top = 12.dp) - ) { - Text(tertiaryText) - } + drawPath(path = lightPatch, color = BackgroundLight) + + val road = Path().apply { + moveTo(size.width * 0.84f, -12f) + cubicTo( + size.width * 0.72f, size.height * 0.1f, + size.width * 0.58f, size.height * 0.24f, + size.width * 0.56f, size.height * 0.42f + ) + cubicTo( + size.width * 0.54f, size.height * 0.55f, + size.width * 0.42f, size.height * 0.67f, + size.width * 0.22f, size.height * 0.71f + ) + cubicTo( + size.width * 0.08f, size.height * 0.75f, + size.width * 0.02f, size.height * 0.87f, + -16f, size.height + ) + } + drawPath( + path = road, + brush = SolidColor(UrbanBrown), + style = Stroke( + width = size.width * 0.12f, + cap = StrokeCap.Round + ) + ) + } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 18.dp, vertical = 16.dp), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.Top + ) { + Box( + modifier = Modifier + .size(42.dp) + .clip(CircleShape) + .background(BackgroundLight.copy(alpha = 0.92f)), + contentAlignment = Alignment.Center + ) { + Text( + text = "GR", + style = MaterialTheme.typography.titleMedium, + color = TextPrimary, + fontWeight = FontWeight.SemiBold + ) } } } } + +@Composable +private fun PhoneField( + value: String, + onValueChange: (String) -> Unit, + label: String +) { + PlainField( + value = value, + onValueChange = onValueChange, + label = label, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone), + icon = { + Icon( + imageVector = Icons.Default.Phone, + contentDescription = null, + tint = UrbanBrown + ) + }, + prefix = { + Text( + text = "+", + color = TextSecondary + ) + } + ) +} + +@Composable +private fun PasswordField( + value: String, + onValueChange: (String) -> Unit, + label: String +) { + var visible by remember { mutableStateOf(false) } + + PlainField( + value = value, + onValueChange = onValueChange, + label = label, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), + visualTransformation = if (visible) { + VisualTransformation.None + } else { + PasswordVisualTransformation() + }, + icon = { + Icon( + imageVector = Icons.Default.Lock, + contentDescription = null, + tint = UrbanBrown + ) + }, + trailing = { + Icon( + imageVector = if (visible) { + Icons.Default.VisibilityOff + } else { + Icons.Default.Visibility + }, + contentDescription = null, + tint = TextSecondary, + modifier = Modifier.clickable { visible = !visible } + ) + } + ) +} + +@Composable +private fun PlainField( + value: String, + onValueChange: (String) -> Unit, + label: String, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + visualTransformation: VisualTransformation = VisualTransformation.None, + icon: @Composable (() -> Unit)? = null, + trailing: @Composable (() -> Unit)? = null, + prefix: @Composable (() -> Unit)? = null +) { + TextField( + value = value, + onValueChange = onValueChange, + modifier = Modifier.fillMaxWidth(), + label = { + Text( + text = label, + color = UrbanBrown + ) + }, + textStyle = MaterialTheme.typography.bodyLarge.copy(color = TextPrimary), + singleLine = true, + keyboardOptions = keyboardOptions, + visualTransformation = visualTransformation, + leadingIcon = icon, + trailingIcon = trailing, + prefix = prefix, + colors = TextFieldDefaults.colors( + focusedContainerColor = BackgroundLight, + unfocusedContainerColor = BackgroundLight, + disabledContainerColor = BackgroundLight, + focusedIndicatorColor = SafeGreen, + unfocusedIndicatorColor = BorderWarm, + cursorColor = SafeGreen, + focusedLabelColor = UrbanBrown, + unfocusedLabelColor = UrbanBrown, + focusedLeadingIconColor = UrbanBrown, + unfocusedLeadingIconColor = UrbanBrown, + focusedTrailingIconColor = TextSecondary, + unfocusedTrailingIconColor = TextSecondary + ), + shape = RoundedCornerShape(18.dp) + ) +} + +@Composable +private fun AuthButton( + text: String, + enabled: Boolean = true, + onClick: () -> Unit +) { + Button( + onClick = onClick, + enabled = enabled, + modifier = Modifier + .fillMaxWidth() + .height(54.dp), + shape = RoundedCornerShape(16.dp), + colors = ButtonDefaults.buttonColors( + containerColor = SafeRoute, + contentColor = BackgroundLight, + disabledContainerColor = SafeRoute.copy(alpha = 0.6f), + disabledContentColor = BackgroundLight + ) + ) { + Text( + text = text, + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold + ) + } +} + +@Composable +private fun AuthFooter( + prefix: String, + action: String, + onClick: () -> Unit +) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = prefix, + style = MaterialTheme.typography.bodyMedium, + color = TextSecondary + ) + Spacer(Modifier.width(4.dp)) + TextButton( + onClick = onClick, + contentPadding = PaddingValues(0.dp) + ) { + Text( + text = action, + style = MaterialTheme.typography.bodyMedium, + color = UrbanBrown, + fontWeight = FontWeight.SemiBold + ) + } + } +} + +@Composable +private fun AuthStatusText( + text: String? +) { + if (text == null) { + return + } + Spacer(Modifier.height(12.dp)) + Text( + text = text, + style = MaterialTheme.typography.bodySmall, + color = AlertRed + ) +} + +@Composable +private fun RoleStubScreen( + title: String, + onLogout: () -> Unit +) { + Box( + modifier = Modifier + .fillMaxSize() + .background(BackgroundLight) + .padding(24.dp), + contentAlignment = Alignment.Center + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = title, + style = MaterialTheme.typography.headlineMedium, + color = TextPrimary + ) + Spacer(Modifier.height(20.dp)) + AuthButton( + text = "Выйти", + onClick = onLogout + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/theme/Color.kt b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt index 2e222c0..cfc6665 100644 --- a/app/src/main/java/com/example/goodroad/ui/theme/Color.kt +++ b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt @@ -2,7 +2,27 @@ package com.example.goodroad.ui.theme import androidx.compose.ui.graphics.Color -val SafeGreen = Color(0xFF4E9B6F) -val SafeGreenDark = Color(0xFF3D7A58) +val PrimaryBlue = Color(0xFF4F87C9) +val SafeGreen = Color(0xFF6FAE8A) +val InclusiveViolet = Color(0xFF8B7AC6) +val AlertRed = Color(0xFFD56B63) + +val UrbanBrown = Color(0xFFA28A72) + +val BackgroundLight = Color(0xFFF7F5F0) val SurfaceWarm = Color(0xFFEEE7DD) -val TextPrimary = Color(0xFF1D1B20) +val BorderWarm = Color(0xFFD8CEC0) + +val MapBackground = Color(0xFFF3EFE7) +val Buildings = Color(0xFFDCCFBE) +val Water = Color(0xFFBFDCF3) +val Parks = Color(0xFFCFE3C8) + +val SafeRoute = Color(0xFF4E9B6F) +val BalancedRoute = Color(0xFF4F87C9) +val FastRoute = Color(0xFF7C6BCB) +val Obstacle = Color(0xFFD56B63) + +val TextPrimary = Color(0xFF2F2B28) +val TextSecondary = Color(0xFF7A6F66) +val WhiteSoft = Color(0xFFFFFBF7) \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/theme/Theme.kt b/app/src/main/java/com/example/goodroad/ui/theme/Theme.kt index 9a27e50..c7a03cf 100644 --- a/app/src/main/java/com/example/goodroad/ui/theme/Theme.kt +++ b/app/src/main/java/com/example/goodroad/ui/theme/Theme.kt @@ -4,17 +4,80 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +private val GoodRoadLightColors = lightColorScheme( + primary = SafeRoute, + onPrimary = WhiteSoft, + + secondary = PrimaryBlue, + onSecondary = WhiteSoft, + + tertiary = InclusiveViolet, + onTertiary = WhiteSoft, + + error = AlertRed, + onError = WhiteSoft, + + background = BackgroundLight, + onBackground = TextPrimary, -private val LightColors = lightColorScheme( - primary = SafeGreen, - secondary = SafeGreenDark, surface = SurfaceWarm, - onSurface = TextPrimary + onSurface = TextPrimary, + + surfaceVariant = BorderWarm, + onSurfaceVariant = TextSecondary, + + outline = BorderWarm, + + primaryContainer = SafeGreen, + onPrimaryContainer = TextPrimary, + + secondaryContainer = Water, + onSecondaryContainer = TextPrimary, + + tertiaryContainer = FastRoute.copy(alpha = 0.18f), + onTertiaryContainer = TextPrimary, + + errorContainer = Obstacle.copy(alpha = 0.18f), + onErrorContainer = TextPrimary ) -private val DarkColors = darkColorScheme( - primary = SafeGreen, - secondary = SafeGreenDark +private val GoodRoadDarkColors = darkColorScheme( + primary = SafeRoute, + onPrimary = WhiteSoft, + + secondary = PrimaryBlue, + onSecondary = WhiteSoft, + + tertiary = InclusiveViolet, + onTertiary = WhiteSoft, + + error = AlertRed, + onError = WhiteSoft, + + background = TextPrimary, + onBackground = BackgroundLight, + + surface = Color(0xFF211D1A), + onSurface = BackgroundLight, + + surfaceVariant = Color(0xFF4B433D), + onSurfaceVariant = BorderWarm, + + outline = Color(0xFF6D635A), + + primaryContainer = Color(0xFF355F48), + onPrimaryContainer = BackgroundLight, + + secondaryContainer = Color(0xFF355D87), + onSecondaryContainer = BackgroundLight, + + tertiaryContainer = Color(0xFF5D5194), + onTertiaryContainer = BackgroundLight, + + errorContainer = Color(0xFF8D4A45), + onErrorContainer = BackgroundLight ) @Composable @@ -22,9 +85,15 @@ fun GoodRoadTheme( darkTheme: Boolean = false, content: @Composable () -> Unit ) { + val colorScheme = if (darkTheme) { + GoodRoadDarkColors + } else { + GoodRoadLightColors + } + MaterialTheme( - colorScheme = if (darkTheme) DarkColors else LightColors, + colorScheme = colorScheme, typography = Typography, content = content ) -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/theme/Type.kt b/app/src/main/java/com/example/goodroad/ui/theme/Type.kt index 162c49d..61b0e52 100644 --- a/app/src/main/java/com/example/goodroad/ui/theme/Type.kt +++ b/app/src/main/java/com/example/goodroad/ui/theme/Type.kt @@ -1,5 +1,40 @@ package com.example.goodroad.ui.theme import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp -val Typography = Typography() +val Typography = Typography( + headlineLarge = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Bold, + fontSize = 34.sp, + lineHeight = 40.sp + ), + titleMedium = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.SemiBold, + fontSize = 18.sp, + lineHeight = 24.sp + ), + bodyLarge = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 22.sp + ), + bodyMedium = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + lineHeight = 20.sp + ), + bodySmall = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 12.sp, + lineHeight = 16.sp + ) +) diff --git a/docker/.env b/docker/.env index 30a5c4f..f4731e0 100644 --- a/docker/.env +++ b/docker/.env @@ -1,6 +1,6 @@ +DB_URL=jdbc:postgresql://127.0.0.1:5432/goodroad DB_NAME=goodroad DB_USER=goodroad DB_PASS=meduser - ADMIN_PHONE=+71234567890 ADMIN_PASS=medpass \ No newline at end of file From 3c4fe08fdff1867d11f9d35c8c6f7df6dd060fd8 Mon Sep 17 00:00:00 2001 From: Tolstograeva Viktoria Date: Wed, 18 Mar 2026 15:23:23 +0300 Subject: [PATCH 03/65] Delete docker/.env --- docker/.env | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 docker/.env diff --git a/docker/.env b/docker/.env deleted file mode 100644 index f4731e0..0000000 --- a/docker/.env +++ /dev/null @@ -1,6 +0,0 @@ -DB_URL=jdbc:postgresql://127.0.0.1:5432/goodroad -DB_NAME=goodroad -DB_USER=goodroad -DB_PASS=meduser -ADMIN_PHONE=+71234567890 -ADMIN_PASS=medpass \ No newline at end of file From ccaa1fa6c0e9d625642e36013690f639222e8464 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Wed, 18 Mar 2026 15:45:56 +0300 Subject: [PATCH 04/65] feature: change password if user forgot it --- .../example/goodroad/data/network/AuthApi.kt | 5 +- .../goodroad/data/network/AuthModels.kt | 9 +- .../java/com/example/goodroad/ui/AuthApp.kt | 138 +++++++++++++++++- 3 files changed, 143 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt b/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt index 85baf09..95802dd 100644 --- a/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt +++ b/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt @@ -10,4 +10,7 @@ interface AuthApi { @POST("/auth/register") suspend fun register(@Body req: RegisterReq): AuthResp -} + + @POST("/auth/recover-password") + suspend fun recoverPassword(@Body req: RecoverPasswordReq) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt b/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt index 071fe2e..0bce938 100644 --- a/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt +++ b/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt @@ -12,6 +12,13 @@ data class RegisterReq( val password: String ) +data class RecoverPasswordReq( + val phone: String, + val firstName: String, + val lastName: String, + val newPassword: String +) + data class AuthResp( val user: UserDto? = null, val message: String? = null @@ -29,4 +36,4 @@ data class ApiErrorDto( val error: String? = null, val message: String? = null, val details: Map? = null -) +) \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/AuthApp.kt index 96ce0a5..6d7b62f 100644 --- a/app/src/main/java/com/example/goodroad/ui/AuthApp.kt +++ b/app/src/main/java/com/example/goodroad/ui/AuthApp.kt @@ -63,6 +63,7 @@ import androidx.navigation.compose.rememberNavController import com.example.goodroad.BuildConfig import com.example.goodroad.data.network.ApiClient import com.example.goodroad.data.network.LoginReq +import com.example.goodroad.data.network.RecoverPasswordReq import com.example.goodroad.data.network.RegisterReq import com.example.goodroad.ui.theme.AlertRed import com.example.goodroad.ui.theme.BackgroundLight @@ -83,9 +84,9 @@ private const val RECOVER_ROUTE = "recover" private const val USER_HOME_ROUTE = "user_home" private const val MODERATOR_HOME_ROUTE = "moderator_home" -private val cyrillicInputRegex = Regex("^[\\p{IsCyrillic} -]*$") +private val cyrillicInputRegex = Regex("^(?:(?=.*\\p{IsCyrillic})[\\p{IsCyrillic} -]*|[ -]*)$") private val digitsInputRegex = Regex("^\\d*$") -private val cyrillicValueRegex = Regex("^[\\p{IsCyrillic} -]+$") +private val cyrillicValueRegex = Regex("^(?=.*\\p{IsCyrillic})[\\p{IsCyrillic} -]+$") private val digitsValueRegex = Regex("^\\d+$") private fun homeRoute(role: String): String { @@ -453,17 +454,90 @@ private fun RegisterScreen( private fun RecoverPasswordScreen( onLogin: () -> Unit ) { + var firstName by rememberSaveable { mutableStateOf("") } + var lastName by rememberSaveable { mutableStateOf("") } var phone by rememberSaveable { mutableStateOf("") } var newPassword by rememberSaveable { mutableStateOf("") } var confirmPassword by rememberSaveable { mutableStateOf("") } - var infoText by rememberSaveable { mutableStateOf(null) } + var errorText by rememberSaveable { mutableStateOf(null) } + var successText by rememberSaveable { mutableStateOf(null) } + var loading by rememberSaveable { mutableStateOf(false) } + val scope = rememberCoroutineScope() AuthScreenFrame( title = "Смена пароля", - subtitle = "Для восстановления нужен отдельный endpoint на backend. Пока это только UI-заглушка.", + subtitle = "Для восстановления введите имя, фамилию, номер телефона и новый пароль.", action = { - AuthButton(text = "Сменить пароль") { - infoText = "В текущем сервере доступна только смена пароля через старый пароль." + AuthButton( + text = if (loading) "Сохраняем..." else "Сменить пароль", + enabled = !loading + ) { + val firstNameNormalized = normalizeRequiredCyrillic(firstName) + if (firstNameNormalized == null) { + errorText = "Имя обязательно: только кириллица, пробел и -" + successText = null + return@AuthButton + } + + val lastNameNormalized = normalizeRequiredCyrillic(lastName) + if (lastNameNormalized == null) { + errorText = "Фамилия обязательна: только кириллица, пробел и -" + successText = null + return@AuthButton + } + + val phoneDigits = normalizeRequiredDigits(phone) + if (phoneDigits == null || newPassword.isBlank() || confirmPassword.isBlank()) { + errorText = "Заполните все поля" + successText = null + return@AuthButton + } + if (newPassword != confirmPassword) { + errorText = "Пароли не совпадают" + successText = null + return@AuthButton + } + + if (BuildConfig.MOCK_AUTH) { + errorText = null + successText = "Пароль успешно изменен. Теперь можно войти." + firstName = "" + lastName = "" + phone = "" + newPassword = "" + confirmPassword = "" + return@AuthButton + } + + scope.launch { + loading = true + errorText = null + successText = null + try { + ApiClient.authApi.recoverPassword( + RecoverPasswordReq( + phone = formatPhoneForRequest(phoneDigits), + firstName = firstNameNormalized, + lastName = lastNameNormalized, + newPassword = newPassword + ) + ) + successText = "Пароль успешно изменен. Теперь можно войти." + firstName = "" + lastName = "" + phone = "" + newPassword = "" + confirmPassword = "" + } catch (_: HttpException) { + errorText = "Не удалось восстановить пароль" + } catch (_: IOException) { + errorText = "Нет соединения с сервером" + } catch (_: Exception) { + errorText = "Ошибка смены пароля" + } finally { + loading = false + } + } } }, footer = { @@ -474,6 +548,40 @@ private fun RecoverPasswordScreen( ) } ) { + PlainField( + value = firstName, + onValueChange = { value -> + if (isAllowedCyrillicInput(value)) { + firstName = value + } + }, + label = "Имя", + icon = { + Icon( + imageVector = Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + } + ) + Spacer(Modifier.height(12.dp)) + PlainField( + value = lastName, + onValueChange = { value -> + if (isAllowedCyrillicInput(value)) { + lastName = value + } + }, + label = "Фамилия", + icon = { + Icon( + imageVector = Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + } + ) + Spacer(Modifier.height(12.dp)) PhoneField( value = phone, onValueChange = { value -> @@ -495,7 +603,8 @@ private fun RecoverPasswordScreen( onValueChange = { confirmPassword = it }, label = "Подтвердите пароль" ) - AuthStatusText(text = infoText) + AuthStatusText(text = errorText) + AuthSuccessText(text = successText) } } @@ -814,6 +923,21 @@ private fun AuthStatusText( ) } +@Composable +private fun AuthSuccessText( + text: String? +) { + if (text == null) { + return + } + Spacer(Modifier.height(12.dp)) + Text( + text = text, + style = MaterialTheme.typography.bodySmall, + color = SafeGreen + ) +} + @Composable private fun RoleStubScreen( title: String, From 1c26373714513b04e4efeb459cdcb33c792b38b3 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Wed, 18 Mar 2026 18:03:49 +0300 Subject: [PATCH 05/65] feature: phone validation and fixing warnings for fist and last names --- .../java/com/example/goodroad/ui/AuthApp.kt | 192 ++++++++++++++---- 1 file changed, 156 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/AuthApp.kt index 6d7b62f..aaa58ba 100644 --- a/app/src/main/java/com/example/goodroad/ui/AuthApp.kt +++ b/app/src/main/java/com/example/goodroad/ui/AuthApp.kt @@ -84,10 +84,14 @@ private const val RECOVER_ROUTE = "recover" private const val USER_HOME_ROUTE = "user_home" private const val MODERATOR_HOME_ROUTE = "moderator_home" -private val cyrillicInputRegex = Regex("^(?:(?=.*\\p{IsCyrillic})[\\p{IsCyrillic} -]*|[ -]*)$") +private const val CYRILLIC_WARNING = "Допустимы только кириллица, пробел и -" +private const val PHONE_CHARS_WARNING = "Допустимы только цифры. Знак + добавляется автоматически" +private const val PHONE_FORMAT_WARNING = "Введите российский номер: 11 цифр, первая — 7 или 8" + +private val cyrillicInputRegex = Regex("^[\\p{IsCyrillic} -]*$") private val digitsInputRegex = Regex("^\\d*$") private val cyrillicValueRegex = Regex("^(?=.*\\p{IsCyrillic})[\\p{IsCyrillic} -]+$") -private val digitsValueRegex = Regex("^\\d+$") +private val russianPhoneDigitsRegex = Regex("^[78]\\d{10}$") private fun homeRoute(role: String): String { return if (role.startsWith("MODERATOR")) { @@ -105,6 +109,10 @@ private fun isAllowedDigitsInput(value: String): Boolean { return digitsInputRegex.matches(value) } +private fun isValidRussianPhoneDigits(value: String): Boolean { + return russianPhoneDigitsRegex.matches(value) +} + private fun normalizeRequiredCyrillic(value: String): String? { val normalized = value.trim() if (normalized.isEmpty()) { @@ -113,12 +121,12 @@ private fun normalizeRequiredCyrillic(value: String): String? { return if (cyrillicValueRegex.matches(normalized)) normalized else null } -private fun normalizeRequiredDigits(value: String): String? { +private fun normalizeRequiredRussianPhone(value: String): String? { val normalized = value.trim() if (normalized.isEmpty()) { return null } - return if (digitsValueRegex.matches(normalized)) normalized else null + return if (isValidRussianPhoneDigits(normalized)) normalized else null } private fun formatPhoneForRequest(phoneDigits: String): String { @@ -213,6 +221,7 @@ private fun LoginScreen( ) { var phone by rememberSaveable { mutableStateOf("") } var password by rememberSaveable { mutableStateOf("") } + var phoneWarning by rememberSaveable { mutableStateOf(null) } var errorText by rememberSaveable { mutableStateOf(null) } var loading by rememberSaveable { mutableStateOf(false) } val scope = rememberCoroutineScope() @@ -224,8 +233,13 @@ private fun LoginScreen( text = if (loading) "Входим..." else "Войти", enabled = !loading ) { - val phoneDigits = normalizeRequiredDigits(phone) + val phoneDigits = normalizeRequiredRussianPhone(phone) if (phoneDigits == null || password.isBlank()) { + phoneWarning = if (phone.isNotBlank() && !isValidRussianPhoneDigits(phone.trim())) { + PHONE_FORMAT_WARNING + } else { + phoneWarning + } errorText = "Заполните телефон и пароль" return@AuthButton } @@ -275,11 +289,26 @@ private fun LoginScreen( PhoneField( value = phone, onValueChange = { value -> - if (isAllowedDigitsInput(value)) { - phone = value + when { + !isAllowedDigitsInput(value) -> { + phoneWarning = PHONE_CHARS_WARNING + } + value.length > 11 -> { + phoneWarning = PHONE_FORMAT_WARNING + } + value.isNotEmpty() && value.first() !in listOf('7', '8') -> { + phoneWarning = PHONE_FORMAT_WARNING + } + else -> { + if (value != phone) { + phone = value + phoneWarning = null + } + } } }, - label = "Телефон" + label = "Телефон", + warning = phoneWarning ) Spacer(Modifier.height(12.dp)) PasswordField( @@ -318,6 +347,9 @@ private fun RegisterScreen( var phone by rememberSaveable { mutableStateOf("") } var password by rememberSaveable { mutableStateOf("") } var confirmPassword by rememberSaveable { mutableStateOf("") } + var firstNameWarning by rememberSaveable { mutableStateOf(null) } + var lastNameWarning by rememberSaveable { mutableStateOf(null) } + var phoneWarning by rememberSaveable { mutableStateOf(null) } var errorText by rememberSaveable { mutableStateOf(null) } var loading by rememberSaveable { mutableStateOf(false) } val scope = rememberCoroutineScope() @@ -331,21 +363,25 @@ private fun RegisterScreen( ) { val firstNameNormalized = normalizeRequiredCyrillic(firstName) if (firstNameNormalized == null) { - errorText = "Имя обязательно: только кириллица, пробел и -" + firstNameWarning = CYRILLIC_WARNING + errorText = "Имя обязательно и должно содержать только кириллицу, пробел и -" return@AuthButton } val lastNameNormalized = normalizeRequiredCyrillic(lastName) if (lastNameNormalized == null) { - errorText = "Фамилия обязательна: только кириллица, пробел и -" + lastNameWarning = CYRILLIC_WARNING + errorText = "Фамилия обязательна и должна содержать только кириллицу, пробел и -" return@AuthButton } - val phoneDigits = normalizeRequiredDigits(phone) + val phoneDigits = normalizeRequiredRussianPhone(phone) if (phoneDigits == null || password.isBlank()) { + phoneWarning = PHONE_FORMAT_WARNING errorText = "Телефон и пароль обязательны" return@AuthButton } + if (password != confirmPassword) { errorText = "Пароли не совпадают" return@AuthButton @@ -394,8 +430,14 @@ private fun RegisterScreen( PlainField( value = firstName, onValueChange = { value -> - if (isAllowedCyrillicInput(value)) { - firstName = value + when { + !isAllowedCyrillicInput(value) -> { + firstNameWarning = CYRILLIC_WARNING + } + value != firstName -> { + firstName = value + firstNameWarning = null + } } }, label = "Имя", @@ -405,14 +447,21 @@ private fun RegisterScreen( contentDescription = null, tint = UrbanBrown ) - } + }, + warning = firstNameWarning ) Spacer(Modifier.height(12.dp)) PlainField( value = lastName, onValueChange = { value -> - if (isAllowedCyrillicInput(value)) { - lastName = value + when { + !isAllowedCyrillicInput(value) -> { + lastNameWarning = CYRILLIC_WARNING + } + value != lastName -> { + lastName = value + lastNameWarning = null + } } }, label = "Фамилия", @@ -422,17 +471,33 @@ private fun RegisterScreen( contentDescription = null, tint = UrbanBrown ) - } + }, + warning = lastNameWarning ) Spacer(Modifier.height(12.dp)) PhoneField( value = phone, onValueChange = { value -> - if (isAllowedDigitsInput(value)) { - phone = value + when { + !isAllowedDigitsInput(value) -> { + phoneWarning = PHONE_CHARS_WARNING + } + value.length > 11 -> { + phoneWarning = PHONE_FORMAT_WARNING + } + value.isNotEmpty() && value.first() !in listOf('7', '8') -> { + phoneWarning = PHONE_FORMAT_WARNING + } + else -> { + if (value != phone) { + phone = value + phoneWarning = null + } + } } }, - label = "Телефон" + label = "Телефон", + warning = phoneWarning ) Spacer(Modifier.height(12.dp)) PasswordField( @@ -459,6 +524,9 @@ private fun RecoverPasswordScreen( var phone by rememberSaveable { mutableStateOf("") } var newPassword by rememberSaveable { mutableStateOf("") } var confirmPassword by rememberSaveable { mutableStateOf("") } + var firstNameWarning by rememberSaveable { mutableStateOf(null) } + var lastNameWarning by rememberSaveable { mutableStateOf(null) } + var phoneWarning by rememberSaveable { mutableStateOf(null) } var errorText by rememberSaveable { mutableStateOf(null) } var successText by rememberSaveable { mutableStateOf(null) } var loading by rememberSaveable { mutableStateOf(false) } @@ -474,24 +542,28 @@ private fun RecoverPasswordScreen( ) { val firstNameNormalized = normalizeRequiredCyrillic(firstName) if (firstNameNormalized == null) { - errorText = "Имя обязательно: только кириллица, пробел и -" + firstNameWarning = CYRILLIC_WARNING + errorText = "Имя обязательно и должно содержать только кириллицу, пробел и -" successText = null return@AuthButton } val lastNameNormalized = normalizeRequiredCyrillic(lastName) if (lastNameNormalized == null) { - errorText = "Фамилия обязательна: только кириллица, пробел и -" + lastNameWarning = CYRILLIC_WARNING + errorText = "Фамилия обязательна и должна содержать только кириллицу, пробел и -" successText = null return@AuthButton } - val phoneDigits = normalizeRequiredDigits(phone) + val phoneDigits = normalizeRequiredRussianPhone(phone) if (phoneDigits == null || newPassword.isBlank() || confirmPassword.isBlank()) { + phoneWarning = PHONE_FORMAT_WARNING errorText = "Заполните все поля" successText = null return@AuthButton } + if (newPassword != confirmPassword) { errorText = "Пароли не совпадают" successText = null @@ -506,6 +578,9 @@ private fun RecoverPasswordScreen( phone = "" newPassword = "" confirmPassword = "" + firstNameWarning = null + lastNameWarning = null + phoneWarning = null return@AuthButton } @@ -528,6 +603,9 @@ private fun RecoverPasswordScreen( phone = "" newPassword = "" confirmPassword = "" + firstNameWarning = null + lastNameWarning = null + phoneWarning = null } catch (_: HttpException) { errorText = "Не удалось восстановить пароль" } catch (_: IOException) { @@ -551,8 +629,14 @@ private fun RecoverPasswordScreen( PlainField( value = firstName, onValueChange = { value -> - if (isAllowedCyrillicInput(value)) { - firstName = value + when { + !isAllowedCyrillicInput(value) -> { + firstNameWarning = CYRILLIC_WARNING + } + value != firstName -> { + firstName = value + firstNameWarning = null + } } }, label = "Имя", @@ -562,14 +646,21 @@ private fun RecoverPasswordScreen( contentDescription = null, tint = UrbanBrown ) - } + }, + warning = firstNameWarning ) Spacer(Modifier.height(12.dp)) PlainField( value = lastName, onValueChange = { value -> - if (isAllowedCyrillicInput(value)) { - lastName = value + when { + !isAllowedCyrillicInput(value) -> { + lastNameWarning = CYRILLIC_WARNING + } + value != lastName -> { + lastName = value + lastNameWarning = null + } } }, label = "Фамилия", @@ -579,17 +670,33 @@ private fun RecoverPasswordScreen( contentDescription = null, tint = UrbanBrown ) - } + }, + warning = lastNameWarning ) Spacer(Modifier.height(12.dp)) PhoneField( value = phone, onValueChange = { value -> - if (isAllowedDigitsInput(value)) { - phone = value + when { + !isAllowedDigitsInput(value) -> { + phoneWarning = PHONE_CHARS_WARNING + } + value.length > 11 -> { + phoneWarning = PHONE_FORMAT_WARNING + } + value.isNotEmpty() && value.first() !in listOf('7', '8') -> { + phoneWarning = PHONE_FORMAT_WARNING + } + else -> { + if (value != phone) { + phone = value + phoneWarning = null + } + } } }, - label = "Телефон" + label = "Телефон", + warning = phoneWarning ) Spacer(Modifier.height(12.dp)) PasswordField( @@ -740,7 +847,8 @@ private fun AuthDecor() { private fun PhoneField( value: String, onValueChange: (String) -> Unit, - label: String + label: String, + warning: String? = null ) { PlainField( value = value, @@ -754,6 +862,7 @@ private fun PhoneField( tint = UrbanBrown ) }, + warning = warning, prefix = { Text( text = "+", @@ -812,7 +921,8 @@ private fun PlainField( visualTransformation: VisualTransformation = VisualTransformation.None, icon: @Composable (() -> Unit)? = null, trailing: @Composable (() -> Unit)? = null, - prefix: @Composable (() -> Unit)? = null + prefix: @Composable (() -> Unit)? = null, + warning: String? = null ) { TextField( value = value, @@ -831,12 +941,22 @@ private fun PlainField( leadingIcon = icon, trailingIcon = trailing, prefix = prefix, + isError = warning != null, + supportingText = { + if (warning != null) { + Text( + text = warning, + style = MaterialTheme.typography.bodySmall, + color = AlertRed + ) + } + }, colors = TextFieldDefaults.colors( focusedContainerColor = BackgroundLight, unfocusedContainerColor = BackgroundLight, disabledContainerColor = BackgroundLight, - focusedIndicatorColor = SafeGreen, - unfocusedIndicatorColor = BorderWarm, + focusedIndicatorColor = if (warning != null) AlertRed else SafeGreen, + unfocusedIndicatorColor = if (warning != null) AlertRed else BorderWarm, cursorColor = SafeGreen, focusedLabelColor = UrbanBrown, unfocusedLabelColor = UrbanBrown, From 345423817740caa52e746025c05121559640a322 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Thu, 19 Mar 2026 11:44:48 +0300 Subject: [PATCH 06/65] refactor: now drawPath in AuthDecor looks like a road --- .../java/com/example/goodroad/ui/AuthApp.kt | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/AuthApp.kt index aaa58ba..275495d 100644 --- a/app/src/main/java/com/example/goodroad/ui/AuthApp.kt +++ b/app/src/main/java/com/example/goodroad/ui/AuthApp.kt @@ -20,6 +20,8 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.ui.graphics.PathEffect +import androidx.compose.ui.graphics.StrokeJoin import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons @@ -791,29 +793,44 @@ private fun AuthDecor() { drawPath(path = lightPatch, color = BackgroundLight) val road = Path().apply { - moveTo(size.width * 0.84f, -12f) + moveTo(size.width * 0.88f, -8f) cubicTo( - size.width * 0.72f, size.height * 0.1f, - size.width * 0.58f, size.height * 0.24f, - size.width * 0.56f, size.height * 0.42f + size.width * 0.8f, size.height * 0.06f, + size.width * 0.7f, size.height * 0.14f, + size.width * 0.62f, size.height * 0.28f ) cubicTo( - size.width * 0.54f, size.height * 0.55f, - size.width * 0.42f, size.height * 0.67f, - size.width * 0.22f, size.height * 0.71f + size.width * 0.55f, size.height * 0.4f, + size.width * 0.42f, size.height * 0.52f, + size.width * 0.26f, size.height * 0.58f ) cubicTo( - size.width * 0.08f, size.height * 0.75f, - size.width * 0.02f, size.height * 0.87f, - -16f, size.height + size.width * 0.15f, size.height * 0.62f, + size.width * 0.07f, size.height * 0.68f, + -8f, size.height * 0.8f ) } + drawPath( path = road, brush = SolidColor(UrbanBrown), style = Stroke( - width = size.width * 0.12f, - cap = StrokeCap.Round + width = size.width * 0.09f, + cap = StrokeCap.Square, + join = StrokeJoin.Round + ) + ) + + drawPath( + path = road, + color = BackgroundLight.copy(alpha = 0.95f), + style = Stroke( + width = size.width * 0.014f, + cap = StrokeCap.Butt, + join = StrokeJoin.Round, + pathEffect = PathEffect.dashPathEffect( + floatArrayOf(size.width * 0.06f, size.width * 0.04f) + ) ) ) } From 01df1c31ef721745d172f668a2e7fbdc69d08a43 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Sun, 22 Mar 2026 14:10:30 +0300 Subject: [PATCH 07/65] fix: structure of files for auth screens --- .../example/goodroad/ui/AddReviewScreen.kt | 0 .../com/example/goodroad/ui/auth/AuthApp.kt | 97 ++++++++ .../goodroad/ui/auth/AuthComponents.kt | 112 +++++++++ .../com/example/goodroad/ui/auth/AuthDecor.kt | 186 ++++++++++++++ .../example/goodroad/ui/auth/AuthFields.kt | 159 ++++++++++++ .../goodroad/ui/auth/AuthValidators.kt | 42 ++++ .../example/goodroad/ui/auth/LoginScreen.kt | 154 ++++++++++++ .../goodroad/ui/auth/RecoverPasswordScreen.kt | 229 ++++++++++++++++++ .../goodroad/ui/auth/RegisterScreen.kt | 207 ++++++++++++++++ .../goodroad/ui/auth/RoleStubScreen.kt | 46 ++++ .../com/example/goodroad/ui/auth/Routes.kt | 15 ++ 11 files changed, 1247 insertions(+) create mode 100644 app/src/main/java/com/example/goodroad/ui/AddReviewScreen.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/auth/AuthValidators.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/auth/Routes.kt diff --git a/app/src/main/java/com/example/goodroad/ui/AddReviewScreen.kt b/app/src/main/java/com/example/goodroad/ui/AddReviewScreen.kt new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt new file mode 100644 index 0000000..794b2bb --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt @@ -0,0 +1,97 @@ +package com.example.goodroad.ui.auth + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import com.example.goodroad.ui.theme.BackgroundLight + +@Composable +fun AuthApp( + navController: NavHostController = rememberNavController() +) { + Surface( + modifier = Modifier.fillMaxSize(), + color = BackgroundLight + ) { + NavHost( + navController = navController, + startDestination = LOGIN_ROUTE + ) { + composable(LOGIN_ROUTE) { + LoginScreen( + onLoginSuccess = { role -> + navController.navigate(homeRoute(role)) { + popUpTo(LOGIN_ROUTE) { + inclusive = true + } + launchSingleTop = true + } + }, + onSignUp = { + navController.navigate(REGISTER_ROUTE) + }, + onForgotPassword = { + navController.navigate(RECOVER_ROUTE) + } + ) + } + + composable(REGISTER_ROUTE) { + RegisterScreen( + onRegisterSuccess = { role -> + navController.navigate(homeRoute(role)) { + popUpTo(LOGIN_ROUTE) { + inclusive = true + } + launchSingleTop = true + } + }, + onLogin = { + navController.popBackStack() + } + ) + } + + composable(RECOVER_ROUTE) { + RecoverPasswordScreen( + onLogin = { + navController.popBackStack() + } + ) + } + + composable(USER_HOME_ROUTE) { + RoleStubScreen( + title = "Главный экран пользователя", + onLogout = { + navController.navigate(LOGIN_ROUTE) { + popUpTo(USER_HOME_ROUTE) { + inclusive = true + } + launchSingleTop = true + } + } + ) + } + + composable(MODERATOR_HOME_ROUTE) { + RoleStubScreen( + title = "Главный экран модератора", + onLogout = { + navController.navigate(LOGIN_ROUTE) { + popUpTo(MODERATOR_HOME_ROUTE) { + inclusive = true + } + launchSingleTop = true + } + } + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt new file mode 100644 index 0000000..6e1b93b --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt @@ -0,0 +1,112 @@ +package com.example.goodroad.ui.auth + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.theme.AlertRed +import com.example.goodroad.ui.theme.BackgroundLight +import com.example.goodroad.ui.theme.SafeGreen +import com.example.goodroad.ui.theme.SafeRoute +import com.example.goodroad.ui.theme.TextSecondary +import com.example.goodroad.ui.theme.UrbanBrown + +@Composable +fun AuthButton( + text: String, + enabled: Boolean = true, + onClick: () -> Unit +) { + Button( + onClick = onClick, + enabled = enabled, + modifier = Modifier + .fillMaxWidth() + .height(54.dp), + shape = RoundedCornerShape(16.dp), + colors = ButtonDefaults.buttonColors( + containerColor = SafeRoute, + contentColor = BackgroundLight, + disabledContainerColor = SafeRoute.copy(alpha = 0.6f), + disabledContentColor = BackgroundLight + ) + ) { + Text( + text = text, + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold + ) + } +} + +@Composable +fun AuthFooter( + prefix: String, + action: String, + onClick: () -> Unit +) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = androidx.compose.ui.Alignment.CenterVertically + ) { + Text( + text = prefix, + style = MaterialTheme.typography.bodyMedium, + color = TextSecondary + ) + Spacer(Modifier.width(4.dp)) + TextButton( + onClick = onClick, + contentPadding = PaddingValues(0.dp) + ) { + Text( + text = action, + style = MaterialTheme.typography.bodyMedium, + color = UrbanBrown, + fontWeight = FontWeight.SemiBold + ) + } + } +} + +@Composable +fun AuthStatusText( + text: String? +) { + if (text == null) return + + Spacer(Modifier.height(12.dp)) + Text( + text = text, + style = MaterialTheme.typography.bodySmall, + color = AlertRed + ) +} + +@Composable +fun AuthSuccessText( + text: String? +) { + if (text == null) return + + Spacer(Modifier.height(12.dp)) + Text( + text = text, + style = MaterialTheme.typography.bodySmall, + color = SafeGreen + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt new file mode 100644 index 0000000..610d262 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt @@ -0,0 +1,186 @@ +package com.example.goodroad.ui.auth + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.verticalScroll +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.PathEffect +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.StrokeJoin +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.theme.BackgroundLight +import com.example.goodroad.ui.theme.SurfaceWarm +import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.theme.TextSecondary +import com.example.goodroad.ui.theme.UrbanBrown +import androidx.compose.foundation.layout.size + +@Composable +fun AuthScreenFrame( + title: String, + subtitle: String? = null, + action: @Composable () -> Unit, + footer: @Composable () -> Unit, + content: @Composable ColumnScope.() -> Unit +) { + Column( + modifier = Modifier + .fillMaxSize() + .background(BackgroundLight) + .verticalScroll(rememberScrollState()) + ) { + AuthDecor() + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + .padding(bottom = 24.dp) + ) { + Text( + text = title, + style = MaterialTheme.typography.headlineLarge, + color = TextPrimary + ) + if (subtitle != null) { + Text( + text = subtitle, + modifier = Modifier.padding(top = 8.dp), + style = MaterialTheme.typography.bodyMedium, + color = TextSecondary + ) + } + Spacer(Modifier.height(28.dp)) + content() + Spacer(Modifier.height(28.dp)) + action() + Spacer(Modifier.height(16.dp)) + footer() + } + } +} + +@Composable +fun AuthDecor() { + Box( + modifier = Modifier + .fillMaxWidth() + .height(230.dp) + .statusBarsPadding() + .padding(horizontal = 18.dp, vertical = 12.dp) + .clip(RoundedCornerShape(32.dp)) + .background(SurfaceWarm) + ) { + Canvas(modifier = Modifier.matchParentSize()) { + val lightPatch = Path().apply { + moveTo(0f, size.height * 0.76f) + cubicTo( + size.width * 0.1f, size.height * 0.7f, + size.width * 0.2f, size.height * 0.45f, + size.width * 0.34f, size.height * 0.42f + ) + cubicTo( + size.width * 0.49f, size.height * 0.38f, + size.width * 0.54f, size.height * 0.18f, + size.width * 0.72f, size.height * 0.12f + ) + lineTo(size.width, size.height * 0.12f) + lineTo(size.width, size.height) + lineTo(0f, size.height) + close() + } + + drawPath( + path = lightPatch, + color = BackgroundLight + ) + + val road = Path().apply { + moveTo(size.width * 0.88f, -8f) + cubicTo( + size.width * 0.8f, size.height * 0.06f, + size.width * 0.7f, size.height * 0.14f, + size.width * 0.62f, size.height * 0.28f + ) + cubicTo( + size.width * 0.55f, size.height * 0.4f, + size.width * 0.42f, size.height * 0.52f, + size.width * 0.26f, size.height * 0.58f + ) + cubicTo( + size.width * 0.15f, size.height * 0.62f, + size.width * 0.07f, size.height * 0.68f, + -8f, size.height * 0.8f + ) + } + + drawPath( + path = road, + brush = SolidColor(UrbanBrown), + style = Stroke( + width = size.width * 0.09f, + cap = StrokeCap.Square, + join = StrokeJoin.Round + ) + ) + + drawPath( + path = road, + color = BackgroundLight.copy(alpha = 0.95f), + style = Stroke( + width = size.width * 0.014f, + cap = StrokeCap.Butt, + join = StrokeJoin.Round, + pathEffect = PathEffect.dashPathEffect( + floatArrayOf(size.width * 0.06f, size.width * 0.04f) + ) + ) + ) + } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 18.dp, vertical = 16.dp), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.Top + ) { + Box( + modifier = Modifier + .size(42.dp) + .clip(CircleShape) + .background(BackgroundLight.copy(alpha = 0.92f)), + contentAlignment = Alignment.Center + ) { + Text( + text = "GR", + style = MaterialTheme.typography.titleMedium, + color = TextPrimary, + fontWeight = FontWeight.SemiBold + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt new file mode 100644 index 0000000..c17aecc --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt @@ -0,0 +1,159 @@ +package com.example.goodroad.ui.auth + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Phone +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.theme.AlertRed +import com.example.goodroad.ui.theme.BackgroundLight +import com.example.goodroad.ui.theme.BorderWarm +import com.example.goodroad.ui.theme.SafeGreen +import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.theme.TextSecondary +import com.example.goodroad.ui.theme.UrbanBrown + +@Composable +fun PhoneField( + value: String, + onValueChange: (String) -> Unit, + label: String, + warning: String? = null +) { + PlainField( + value = value, + onValueChange = onValueChange, + label = label, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone), + icon = { + Icon( + imageVector = Icons.Default.Phone, + contentDescription = null, + tint = UrbanBrown + ) + }, + warning = warning, + prefix = { + Text( + text = "+", + color = TextSecondary + ) + } + ) +} + +@Composable +fun PasswordField( + value: String, + onValueChange: (String) -> Unit, + label: String +) { + var visible by remember { mutableStateOf(false) } + + PlainField( + value = value, + onValueChange = onValueChange, + label = label, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), + visualTransformation = if (visible) { + VisualTransformation.None + } else { + PasswordVisualTransformation() + }, + icon = { + Icon( + imageVector = Icons.Default.Lock, + contentDescription = null, + tint = UrbanBrown + ) + }, + trailing = { + Icon( + imageVector = if (visible) { + Icons.Default.VisibilityOff + } else { + Icons.Default.Visibility + }, + contentDescription = null, + tint = TextSecondary, + modifier = Modifier.clickable { visible = !visible } + ) + } + ) +} + +@Composable +fun PlainField( + value: String, + onValueChange: (String) -> Unit, + label: String, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + visualTransformation: VisualTransformation = VisualTransformation.None, + icon: @Composable (() -> Unit)? = null, + trailing: @Composable (() -> Unit)? = null, + prefix: @Composable (() -> Unit)? = null, + warning: String? = null +) { + TextField( + value = value, + onValueChange = onValueChange, + modifier = Modifier.fillMaxWidth(), + label = { + Text( + text = label, + color = UrbanBrown + ) + }, + textStyle = MaterialTheme.typography.bodyLarge.copy(color = TextPrimary), + singleLine = true, + keyboardOptions = keyboardOptions, + visualTransformation = visualTransformation, + leadingIcon = icon, + trailingIcon = trailing, + prefix = prefix, + isError = warning != null, + supportingText = { + if (warning != null) { + Text( + text = warning, + style = MaterialTheme.typography.bodySmall, + color = AlertRed + ) + } + }, + colors = TextFieldDefaults.colors( + focusedContainerColor = BackgroundLight, + unfocusedContainerColor = BackgroundLight, + disabledContainerColor = BackgroundLight, + focusedIndicatorColor = if (warning != null) AlertRed else SafeGreen, + unfocusedIndicatorColor = if (warning != null) AlertRed else BorderWarm, + cursorColor = SafeGreen, + focusedLabelColor = UrbanBrown, + unfocusedLabelColor = UrbanBrown, + focusedLeadingIconColor = UrbanBrown, + unfocusedLeadingIconColor = UrbanBrown, + focusedTrailingIconColor = TextSecondary, + unfocusedTrailingIconColor = TextSecondary + ), + shape = RoundedCornerShape(18.dp) + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthValidators.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthValidators.kt new file mode 100644 index 0000000..5df307a --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthValidators.kt @@ -0,0 +1,42 @@ +package com.example.goodroad.ui.auth + +private val cyrillicInputRegex = Regex("^[\\p{IsCyrillic} -]*$") +private val digitsInputRegex = Regex("^\\d*$") +private val cyrillicValueRegex = Regex("^(?=.*\\p{IsCyrillic})[\\p{IsCyrillic} -]+$") +private val russianPhoneDigitsRegex = Regex("^[78]\\d{10}$") + +const val CYRILLIC_WARNING = "Допустимы только кириллица, пробел и -" +const val PHONE_CHARS_WARNING = "Допустимы только цифры. Знак + добавляется автоматически" +const val PHONE_FORMAT_WARNING = "Введите российский номер: 11 цифр, первая — 7 или 8" + +fun isAllowedCyrillicInput(value: String): Boolean { + return cyrillicInputRegex.matches(value) +} + +fun isAllowedDigitsInput(value: String): Boolean { + return digitsInputRegex.matches(value) +} + +fun isValidRussianPhoneDigits(value: String): Boolean { + return russianPhoneDigitsRegex.matches(value) +} + +fun normalizeRequiredCyrillic(value: String): String? { + val normalized = value.trim() + if (normalized.isEmpty()) { + return null + } + return if (cyrillicValueRegex.matches(normalized)) normalized else null +} + +fun normalizeRequiredRussianPhone(value: String): String? { + val normalized = value.trim() + if (normalized.isEmpty()) { + return null + } + return if (isValidRussianPhoneDigits(normalized)) normalized else null +} + +fun formatPhoneForRequest(phoneDigits: String): String { + return "+$phoneDigits" +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt new file mode 100644 index 0000000..d3c12d1 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt @@ -0,0 +1,154 @@ +package com.example.goodroad.ui.auth +import com.example.goodroad.ui.theme.* + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.example.goodroad.BuildConfig +import com.example.goodroad.data.network.ApiClient +import com.example.goodroad.data.network.LoginReq +import kotlinx.coroutines.launch +import retrofit2.HttpException +import java.io.IOException + +@Composable +fun LoginScreen( + onLoginSuccess: (String) -> Unit, + onSignUp: () -> Unit, + onForgotPassword: () -> Unit +) { + var phone by rememberSaveable { mutableStateOf("") } + var password by rememberSaveable { mutableStateOf("") } + var phoneWarning by rememberSaveable { mutableStateOf(null) } + var errorText by rememberSaveable { mutableStateOf(null) } + var loading by rememberSaveable { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + AuthScreenFrame( + title = "Вход", + action = { + AuthButton( + text = if (loading) "Входим..." else "Войти", + enabled = !loading + ) { + val phoneDigits = normalizeRequiredRussianPhone(phone) + if (phoneDigits == null || password.isBlank()) { + phoneWarning = if (phone.isNotBlank() && !isValidRussianPhoneDigits(phone.trim())) { + PHONE_FORMAT_WARNING + } else { + phoneWarning + } + errorText = "Заполните телефон и пароль" + return@AuthButton + } + + if (BuildConfig.MOCK_AUTH) { + errorText = null + onLoginSuccess("USER") + return@AuthButton + } + + scope.launch { + loading = true + errorText = null + try { + val resp = ApiClient.authApi.login( + LoginReq( + phone = formatPhoneForRequest(phoneDigits), + password = password + ) + ) + val role = resp.user?.role + if (role.isNullOrBlank()) { + errorText = "Не удалось определить роль пользователя" + } else { + onLoginSuccess(role) + } + } catch (_: HttpException) { + errorText = "Неверный телефон или пароль" + } catch (_: IOException) { + errorText = "Нет соединения с сервером" + } catch (_: Exception) { + errorText = "Ошибка входа" + } finally { + loading = false + } + } + } + }, + footer = { + AuthFooter( + prefix = "Нет аккаунта?", + action = "Зарегистрироваться", + onClick = onSignUp + ) + } + ) { + PhoneField( + value = phone, + onValueChange = { value -> + when { + !isAllowedDigitsInput(value) -> { + phoneWarning = PHONE_CHARS_WARNING + } + value.length > 11 -> { + phoneWarning = PHONE_FORMAT_WARNING + } + value.isNotEmpty() && value.first() !in listOf('7', '8') -> { + phoneWarning = PHONE_FORMAT_WARNING + } + else -> { + if (value != phone) { + phone = value + phoneWarning = null + } + } + } + }, + label = "Телефон", + warning = phoneWarning + ) + + Spacer(Modifier.height(12.dp)) + + PasswordField( + value = password, + onValueChange = { password = it }, + label = "Пароль" + ) + + Spacer(Modifier.height(10.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically + ) { + TextButton( + onClick = onForgotPassword, + contentPadding = PaddingValues(0.dp) + ) { + Text( + text = "Забыли пароль?", + color = UrbanBrown + ) + } + } + + AuthStatusText(text = errorText) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt new file mode 100644 index 0000000..f4c41ce --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt @@ -0,0 +1,229 @@ +package com.example.goodroad.ui.auth +import com.example.goodroad.ui.theme.* + +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.material.icons.filled.Person +import com.example.goodroad.BuildConfig +import com.example.goodroad.data.network.ApiClient +import com.example.goodroad.data.network.RecoverPasswordReq +import kotlinx.coroutines.launch +import retrofit2.HttpException +import java.io.IOException + +@Composable +fun RecoverPasswordScreen( + onLogin: () -> Unit +) { + var firstName by rememberSaveable { mutableStateOf("") } + var lastName by rememberSaveable { mutableStateOf("") } + var phone by rememberSaveable { mutableStateOf("") } + var newPassword by rememberSaveable { mutableStateOf("") } + var confirmPassword by rememberSaveable { mutableStateOf("") } + var firstNameWarning by rememberSaveable { mutableStateOf(null) } + var lastNameWarning by rememberSaveable { mutableStateOf(null) } + var phoneWarning by rememberSaveable { mutableStateOf(null) } + var errorText by rememberSaveable { mutableStateOf(null) } + var successText by rememberSaveable { mutableStateOf(null) } + var loading by rememberSaveable { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + AuthScreenFrame( + title = "Смена пароля", + subtitle = "Для восстановления введите имя, фамилию, номер телефона и новый пароль.", + action = { + AuthButton( + text = if (loading) "Сохраняем..." else "Сменить пароль", + enabled = !loading + ) { + val firstNameNormalized = normalizeRequiredCyrillic(firstName) + if (firstNameNormalized == null) { + firstNameWarning = CYRILLIC_WARNING + errorText = "Имя обязательно и должно содержать только кириллицу, пробел и -" + successText = null + return@AuthButton + } + + val lastNameNormalized = normalizeRequiredCyrillic(lastName) + if (lastNameNormalized == null) { + lastNameWarning = CYRILLIC_WARNING + errorText = "Фамилия обязательна и должна содержать только кириллицу, пробел и -" + successText = null + return@AuthButton + } + + val phoneDigits = normalizeRequiredRussianPhone(phone) + if (phoneDigits == null || newPassword.isBlank() || confirmPassword.isBlank()) { + phoneWarning = PHONE_FORMAT_WARNING + errorText = "Заполните все поля" + successText = null + return@AuthButton + } + + if (newPassword != confirmPassword) { + errorText = "Пароли не совпадают" + successText = null + return@AuthButton + } + + if (BuildConfig.MOCK_AUTH) { + errorText = null + successText = "Пароль успешно изменен. Теперь можно войти." + firstName = "" + lastName = "" + phone = "" + newPassword = "" + confirmPassword = "" + firstNameWarning = null + lastNameWarning = null + phoneWarning = null + return@AuthButton + } + + scope.launch { + loading = true + errorText = null + successText = null + try { + ApiClient.authApi.recoverPassword( + RecoverPasswordReq( + phone = formatPhoneForRequest(phoneDigits), + firstName = firstNameNormalized, + lastName = lastNameNormalized, + newPassword = newPassword + ) + ) + successText = "Пароль успешно изменен. Теперь можно войти." + firstName = "" + lastName = "" + phone = "" + newPassword = "" + confirmPassword = "" + firstNameWarning = null + lastNameWarning = null + phoneWarning = null + } catch (_: HttpException) { + errorText = "Не удалось восстановить пароль" + } catch (_: IOException) { + errorText = "Нет соединения с сервером" + } catch (_: Exception) { + errorText = "Ошибка смены пароля" + } finally { + loading = false + } + } + } + }, + footer = { + AuthFooter( + prefix = "Вспомнили пароль?", + action = "Вернуться ко входу", + onClick = onLogin + ) + } + ) { + PlainField( + value = firstName, + onValueChange = { value -> + when { + !isAllowedCyrillicInput(value) -> { + firstNameWarning = CYRILLIC_WARNING + } + value != firstName -> { + firstName = value + firstNameWarning = null + } + } + }, + label = "Имя", + icon = { + androidx.compose.material3.Icon( + imageVector = androidx.compose.material.icons.Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + }, + warning = firstNameWarning + ) + + Spacer(Modifier.height(12.dp)) + + PlainField( + value = lastName, + onValueChange = { value -> + when { + !isAllowedCyrillicInput(value) -> { + lastNameWarning = CYRILLIC_WARNING + } + value != lastName -> { + lastName = value + lastNameWarning = null + } + } + }, + label = "Фамилия", + icon = { + androidx.compose.material3.Icon( + imageVector = androidx.compose.material.icons.Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + }, + warning = lastNameWarning + ) + + Spacer(Modifier.height(12.dp)) + + PhoneField( + value = phone, + onValueChange = { value -> + when { + !isAllowedDigitsInput(value) -> { + phoneWarning = PHONE_CHARS_WARNING + } + value.length > 11 -> { + phoneWarning = PHONE_FORMAT_WARNING + } + value.isNotEmpty() && value.first() !in listOf('7', '8') -> { + phoneWarning = PHONE_FORMAT_WARNING + } + else -> { + if (value != phone) { + phone = value + phoneWarning = null + } + } + } + }, + label = "Телефон", + warning = phoneWarning + ) + + Spacer(Modifier.height(12.dp)) + + PasswordField( + value = newPassword, + onValueChange = { newPassword = it }, + label = "Новый пароль" + ) + + Spacer(Modifier.height(12.dp)) + + PasswordField( + value = confirmPassword, + onValueChange = { confirmPassword = it }, + label = "Подтвердите пароль" + ) + + AuthStatusText(text = errorText) + AuthSuccessText(text = successText) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt new file mode 100644 index 0000000..d75c0f6 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt @@ -0,0 +1,207 @@ +package com.example.goodroad.ui.auth +import com.example.goodroad.ui.theme.* + +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.material.icons.filled.Person +import com.example.goodroad.BuildConfig +import com.example.goodroad.data.network.ApiClient +import com.example.goodroad.data.network.RegisterReq +import kotlinx.coroutines.launch +import retrofit2.HttpException +import java.io.IOException + +@Composable +fun RegisterScreen( + onRegisterSuccess: (String) -> Unit, + onLogin: () -> Unit +) { + var firstName by rememberSaveable { mutableStateOf("") } + var lastName by rememberSaveable { mutableStateOf("") } + var phone by rememberSaveable { mutableStateOf("") } + var password by rememberSaveable { mutableStateOf("") } + var confirmPassword by rememberSaveable { mutableStateOf("") } + var firstNameWarning by rememberSaveable { mutableStateOf(null) } + var lastNameWarning by rememberSaveable { mutableStateOf(null) } + var phoneWarning by rememberSaveable { mutableStateOf(null) } + var errorText by rememberSaveable { mutableStateOf(null) } + var loading by rememberSaveable { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + AuthScreenFrame( + title = "Создать аккаунт", + action = { + AuthButton( + text = if (loading) "Создаем..." else "Зарегистрироваться", + enabled = !loading + ) { + val firstNameNormalized = normalizeRequiredCyrillic(firstName) + if (firstNameNormalized == null) { + firstNameWarning = CYRILLIC_WARNING + errorText = "Имя обязательно и должно содержать только кириллицу, пробел и -" + return@AuthButton + } + + val lastNameNormalized = normalizeRequiredCyrillic(lastName) + if (lastNameNormalized == null) { + lastNameWarning = CYRILLIC_WARNING + errorText = "Фамилия обязательна и должна содержать только кириллицу, пробел и -" + return@AuthButton + } + + val phoneDigits = normalizeRequiredRussianPhone(phone) + if (phoneDigits == null || password.isBlank()) { + phoneWarning = PHONE_FORMAT_WARNING + errorText = "Телефон и пароль обязательны" + return@AuthButton + } + + if (password != confirmPassword) { + errorText = "Пароли не совпадают" + return@AuthButton + } + + if (BuildConfig.MOCK_AUTH) { + errorText = null + onRegisterSuccess("USER") + return@AuthButton + } + + scope.launch { + loading = true + errorText = null + try { + val resp = ApiClient.authApi.register( + RegisterReq( + firstName = firstNameNormalized, + lastName = lastNameNormalized, + phone = formatPhoneForRequest(phoneDigits), + password = password + ) + ) + val role = resp.user?.role ?: "USER" + onRegisterSuccess(role) + } catch (_: HttpException) { + errorText = "Не удалось зарегистрироваться" + } catch (_: IOException) { + errorText = "Нет соединения с сервером" + } catch (_: Exception) { + errorText = "Ошибка регистрации" + } finally { + loading = false + } + } + } + }, + footer = { + AuthFooter( + prefix = "Уже есть аккаунт?", + action = "Войти", + onClick = onLogin + ) + } + ) { + PlainField( + value = firstName, + onValueChange = { value -> + when { + !isAllowedCyrillicInput(value) -> { + firstNameWarning = CYRILLIC_WARNING + } + value != firstName -> { + firstName = value + firstNameWarning = null + } + } + }, + label = "Имя", + icon = { + androidx.compose.material3.Icon( + imageVector = androidx.compose.material.icons.Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + }, + warning = firstNameWarning + ) + + Spacer(Modifier.height(12.dp)) + + PlainField( + value = lastName, + onValueChange = { value -> + when { + !isAllowedCyrillicInput(value) -> { + lastNameWarning = CYRILLIC_WARNING + } + value != lastName -> { + lastName = value + lastNameWarning = null + } + } + }, + label = "Фамилия", + icon = { + androidx.compose.material3.Icon( + imageVector = androidx.compose.material.icons.Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + }, + warning = lastNameWarning + ) + + Spacer(Modifier.height(12.dp)) + + PhoneField( + value = phone, + onValueChange = { value -> + when { + !isAllowedDigitsInput(value) -> { + phoneWarning = PHONE_CHARS_WARNING + } + value.length > 11 -> { + phoneWarning = PHONE_FORMAT_WARNING + } + value.isNotEmpty() && value.first() !in listOf('7', '8') -> { + phoneWarning = PHONE_FORMAT_WARNING + } + else -> { + if (value != phone) { + phone = value + phoneWarning = null + } + } + } + }, + label = "Телефон", + warning = phoneWarning + ) + + Spacer(Modifier.height(12.dp)) + + PasswordField( + value = password, + onValueChange = { password = it }, + label = "Пароль" + ) + + Spacer(Modifier.height(12.dp)) + + PasswordField( + value = confirmPassword, + onValueChange = { confirmPassword = it }, + label = "Подтвердите пароль" + ) + + AuthStatusText(text = errorText) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt new file mode 100644 index 0000000..7f68eac --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt @@ -0,0 +1,46 @@ +package com.example.goodroad.ui.auth + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.theme.BackgroundLight +import com.example.goodroad.ui.theme.TextPrimary + +@Composable +fun RoleStubScreen( + title: String, + onLogout: () -> Unit +) { + Box( + modifier = Modifier + .fillMaxSize() + .background(BackgroundLight) + .padding(24.dp), + contentAlignment = Alignment.Center + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = title, + style = MaterialTheme.typography.headlineMedium, + color = TextPrimary + ) + Spacer(Modifier.height(20.dp)) + AuthButton( + text = "Выйти", + onClick = onLogout + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/Routes.kt b/app/src/main/java/com/example/goodroad/ui/auth/Routes.kt new file mode 100644 index 0000000..ef5dd53 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/auth/Routes.kt @@ -0,0 +1,15 @@ +package com.example.goodroad.ui.auth + +const val LOGIN_ROUTE = "login" +const val REGISTER_ROUTE = "register" +const val RECOVER_ROUTE = "recover" +const val USER_HOME_ROUTE = "user_home" +const val MODERATOR_HOME_ROUTE = "moderator_home" + +fun homeRoute(role: String): String { + return if (role.startsWith("MODERATOR")) { + MODERATOR_HOME_ROUTE + } else { + USER_HOME_ROUTE + } +} \ No newline at end of file From 0bfed02451e495614a9a0f922c2ccb73f989b081 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Sun, 22 Mar 2026 14:20:52 +0300 Subject: [PATCH 08/65] delete: app/src/main/java/com/example/goodroad/ui/AuthApp.kt --- .../java/com/example/goodroad/ui/AuthApp.kt | 1105 ----------------- 1 file changed, 1105 deletions(-) delete mode 100644 app/src/main/java/com/example/goodroad/ui/AuthApp.kt diff --git a/app/src/main/java/com/example/goodroad/ui/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/AuthApp.kt deleted file mode 100644 index 275495d..0000000 --- a/app/src/main/java/com/example/goodroad/ui/AuthApp.kt +++ /dev/null @@ -1,1105 +0,0 @@ -package com.example.goodroad.ui - -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.ui.graphics.PathEffect -import androidx.compose.ui.graphics.StrokeJoin -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Lock -import androidx.compose.material.icons.filled.Person -import androidx.compose.material.icons.filled.Phone -import androidx.compose.material.icons.filled.Visibility -import androidx.compose.material.icons.filled.VisibilityOff -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.TextField -import androidx.compose.material3.TextFieldDefaults -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Path -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.StrokeCap -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.unit.dp -import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.rememberNavController -import com.example.goodroad.BuildConfig -import com.example.goodroad.data.network.ApiClient -import com.example.goodroad.data.network.LoginReq -import com.example.goodroad.data.network.RecoverPasswordReq -import com.example.goodroad.data.network.RegisterReq -import com.example.goodroad.ui.theme.AlertRed -import com.example.goodroad.ui.theme.BackgroundLight -import com.example.goodroad.ui.theme.BorderWarm -import com.example.goodroad.ui.theme.SafeGreen -import com.example.goodroad.ui.theme.SafeRoute -import com.example.goodroad.ui.theme.SurfaceWarm -import com.example.goodroad.ui.theme.TextPrimary -import com.example.goodroad.ui.theme.TextSecondary -import com.example.goodroad.ui.theme.UrbanBrown -import kotlinx.coroutines.launch -import retrofit2.HttpException -import java.io.IOException - -private const val LOGIN_ROUTE = "login" -private const val REGISTER_ROUTE = "register" -private const val RECOVER_ROUTE = "recover" -private const val USER_HOME_ROUTE = "user_home" -private const val MODERATOR_HOME_ROUTE = "moderator_home" - -private const val CYRILLIC_WARNING = "Допустимы только кириллица, пробел и -" -private const val PHONE_CHARS_WARNING = "Допустимы только цифры. Знак + добавляется автоматически" -private const val PHONE_FORMAT_WARNING = "Введите российский номер: 11 цифр, первая — 7 или 8" - -private val cyrillicInputRegex = Regex("^[\\p{IsCyrillic} -]*$") -private val digitsInputRegex = Regex("^\\d*$") -private val cyrillicValueRegex = Regex("^(?=.*\\p{IsCyrillic})[\\p{IsCyrillic} -]+$") -private val russianPhoneDigitsRegex = Regex("^[78]\\d{10}$") - -private fun homeRoute(role: String): String { - return if (role.startsWith("MODERATOR")) { - MODERATOR_HOME_ROUTE - } else { - USER_HOME_ROUTE - } -} - -private fun isAllowedCyrillicInput(value: String): Boolean { - return cyrillicInputRegex.matches(value) -} - -private fun isAllowedDigitsInput(value: String): Boolean { - return digitsInputRegex.matches(value) -} - -private fun isValidRussianPhoneDigits(value: String): Boolean { - return russianPhoneDigitsRegex.matches(value) -} - -private fun normalizeRequiredCyrillic(value: String): String? { - val normalized = value.trim() - if (normalized.isEmpty()) { - return null - } - return if (cyrillicValueRegex.matches(normalized)) normalized else null -} - -private fun normalizeRequiredRussianPhone(value: String): String? { - val normalized = value.trim() - if (normalized.isEmpty()) { - return null - } - return if (isValidRussianPhoneDigits(normalized)) normalized else null -} - -private fun formatPhoneForRequest(phoneDigits: String): String { - return "+$phoneDigits" -} - -@Composable -fun AuthApp(navController: NavHostController = rememberNavController()) { - Surface( - modifier = Modifier.fillMaxSize(), - color = BackgroundLight - ) { - NavHost( - navController = navController, - startDestination = LOGIN_ROUTE - ) { - composable(LOGIN_ROUTE) { - LoginScreen( - onLoginSuccess = { role -> - navController.navigate(homeRoute(role)) { - popUpTo(LOGIN_ROUTE) { - inclusive = true - } - launchSingleTop = true - } - }, - onSignUp = { - navController.navigate(REGISTER_ROUTE) - }, - onForgotPassword = { - navController.navigate(RECOVER_ROUTE) - } - ) - } - composable(REGISTER_ROUTE) { - RegisterScreen( - onRegisterSuccess = { role -> - navController.navigate(homeRoute(role)) { - popUpTo(LOGIN_ROUTE) { - inclusive = true - } - launchSingleTop = true - } - }, - onLogin = { - navController.popBackStack() - } - ) - } - composable(RECOVER_ROUTE) { - RecoverPasswordScreen( - onLogin = { - navController.popBackStack() - } - ) - } - composable(USER_HOME_ROUTE) { - RoleStubScreen( - title = "Главный экран пользователя", - onLogout = { - navController.navigate(LOGIN_ROUTE) { - popUpTo(USER_HOME_ROUTE) { - inclusive = true - } - launchSingleTop = true - } - } - ) - } - composable(MODERATOR_HOME_ROUTE) { - RoleStubScreen( - title = "Главный экран модератора", - onLogout = { - navController.navigate(LOGIN_ROUTE) { - popUpTo(MODERATOR_HOME_ROUTE) { - inclusive = true - } - launchSingleTop = true - } - } - ) - } - } - } -} - -@Composable -private fun LoginScreen( - onLoginSuccess: (String) -> Unit, - onSignUp: () -> Unit, - onForgotPassword: () -> Unit -) { - var phone by rememberSaveable { mutableStateOf("") } - var password by rememberSaveable { mutableStateOf("") } - var phoneWarning by rememberSaveable { mutableStateOf(null) } - var errorText by rememberSaveable { mutableStateOf(null) } - var loading by rememberSaveable { mutableStateOf(false) } - val scope = rememberCoroutineScope() - - AuthScreenFrame( - title = "Вход", - action = { - AuthButton( - text = if (loading) "Входим..." else "Войти", - enabled = !loading - ) { - val phoneDigits = normalizeRequiredRussianPhone(phone) - if (phoneDigits == null || password.isBlank()) { - phoneWarning = if (phone.isNotBlank() && !isValidRussianPhoneDigits(phone.trim())) { - PHONE_FORMAT_WARNING - } else { - phoneWarning - } - errorText = "Заполните телефон и пароль" - return@AuthButton - } - - if (BuildConfig.MOCK_AUTH) { - errorText = null - onLoginSuccess("USER") - return@AuthButton - } - - scope.launch { - loading = true - errorText = null - try { - val resp = ApiClient.authApi.login( - LoginReq( - phone = formatPhoneForRequest(phoneDigits), - password = password - ) - ) - val role = resp.user?.role - if (role.isNullOrBlank()) { - errorText = "Не удалось определить роль пользователя" - } else { - onLoginSuccess(role) - } - } catch (_: HttpException) { - errorText = "Неверный телефон или пароль" - } catch (_: IOException) { - errorText = "Нет соединения с сервером" - } catch (_: Exception) { - errorText = "Ошибка входа" - } finally { - loading = false - } - } - } - }, - footer = { - AuthFooter( - prefix = "Нет аккаунта?", - action = "Зарегистрироваться", - onClick = onSignUp - ) - } - ) { - PhoneField( - value = phone, - onValueChange = { value -> - when { - !isAllowedDigitsInput(value) -> { - phoneWarning = PHONE_CHARS_WARNING - } - value.length > 11 -> { - phoneWarning = PHONE_FORMAT_WARNING - } - value.isNotEmpty() && value.first() !in listOf('7', '8') -> { - phoneWarning = PHONE_FORMAT_WARNING - } - else -> { - if (value != phone) { - phone = value - phoneWarning = null - } - } - } - }, - label = "Телефон", - warning = phoneWarning - ) - Spacer(Modifier.height(12.dp)) - PasswordField( - value = password, - onValueChange = { password = it }, - label = "Пароль" - ) - Spacer(Modifier.height(10.dp)) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.End, - verticalAlignment = Alignment.CenterVertically - ) { - TextButton( - onClick = onForgotPassword, - contentPadding = PaddingValues(0.dp) - ) { - Text( - text = "Забыли пароль?", - style = MaterialTheme.typography.bodySmall, - color = UrbanBrown - ) - } - } - AuthStatusText(text = errorText) - } -} - -@Composable -private fun RegisterScreen( - onRegisterSuccess: (String) -> Unit, - onLogin: () -> Unit -) { - var firstName by rememberSaveable { mutableStateOf("") } - var lastName by rememberSaveable { mutableStateOf("") } - var phone by rememberSaveable { mutableStateOf("") } - var password by rememberSaveable { mutableStateOf("") } - var confirmPassword by rememberSaveable { mutableStateOf("") } - var firstNameWarning by rememberSaveable { mutableStateOf(null) } - var lastNameWarning by rememberSaveable { mutableStateOf(null) } - var phoneWarning by rememberSaveable { mutableStateOf(null) } - var errorText by rememberSaveable { mutableStateOf(null) } - var loading by rememberSaveable { mutableStateOf(false) } - val scope = rememberCoroutineScope() - - AuthScreenFrame( - title = "Создать аккаунт", - action = { - AuthButton( - text = if (loading) "Создаем..." else "Зарегистрироваться", - enabled = !loading - ) { - val firstNameNormalized = normalizeRequiredCyrillic(firstName) - if (firstNameNormalized == null) { - firstNameWarning = CYRILLIC_WARNING - errorText = "Имя обязательно и должно содержать только кириллицу, пробел и -" - return@AuthButton - } - - val lastNameNormalized = normalizeRequiredCyrillic(lastName) - if (lastNameNormalized == null) { - lastNameWarning = CYRILLIC_WARNING - errorText = "Фамилия обязательна и должна содержать только кириллицу, пробел и -" - return@AuthButton - } - - val phoneDigits = normalizeRequiredRussianPhone(phone) - if (phoneDigits == null || password.isBlank()) { - phoneWarning = PHONE_FORMAT_WARNING - errorText = "Телефон и пароль обязательны" - return@AuthButton - } - - if (password != confirmPassword) { - errorText = "Пароли не совпадают" - return@AuthButton - } - - if (BuildConfig.MOCK_AUTH) { - errorText = null - onRegisterSuccess("USER") - return@AuthButton - } - - scope.launch { - loading = true - errorText = null - try { - val resp = ApiClient.authApi.register( - RegisterReq( - firstName = firstNameNormalized, - lastName = lastNameNormalized, - phone = formatPhoneForRequest(phoneDigits), - password = password - ) - ) - val role = resp.user?.role ?: "USER" - onRegisterSuccess(role) - } catch (_: HttpException) { - errorText = "Не удалось зарегистрироваться" - } catch (_: IOException) { - errorText = "Нет соединения с сервером" - } catch (_: Exception) { - errorText = "Ошибка регистрации" - } finally { - loading = false - } - } - } - }, - footer = { - AuthFooter( - prefix = "Уже есть аккаунт?", - action = "Войти", - onClick = onLogin - ) - } - ) { - PlainField( - value = firstName, - onValueChange = { value -> - when { - !isAllowedCyrillicInput(value) -> { - firstNameWarning = CYRILLIC_WARNING - } - value != firstName -> { - firstName = value - firstNameWarning = null - } - } - }, - label = "Имя", - icon = { - Icon( - imageVector = Icons.Default.Person, - contentDescription = null, - tint = UrbanBrown - ) - }, - warning = firstNameWarning - ) - Spacer(Modifier.height(12.dp)) - PlainField( - value = lastName, - onValueChange = { value -> - when { - !isAllowedCyrillicInput(value) -> { - lastNameWarning = CYRILLIC_WARNING - } - value != lastName -> { - lastName = value - lastNameWarning = null - } - } - }, - label = "Фамилия", - icon = { - Icon( - imageVector = Icons.Default.Person, - contentDescription = null, - tint = UrbanBrown - ) - }, - warning = lastNameWarning - ) - Spacer(Modifier.height(12.dp)) - PhoneField( - value = phone, - onValueChange = { value -> - when { - !isAllowedDigitsInput(value) -> { - phoneWarning = PHONE_CHARS_WARNING - } - value.length > 11 -> { - phoneWarning = PHONE_FORMAT_WARNING - } - value.isNotEmpty() && value.first() !in listOf('7', '8') -> { - phoneWarning = PHONE_FORMAT_WARNING - } - else -> { - if (value != phone) { - phone = value - phoneWarning = null - } - } - } - }, - label = "Телефон", - warning = phoneWarning - ) - Spacer(Modifier.height(12.dp)) - PasswordField( - value = password, - onValueChange = { password = it }, - label = "Пароль" - ) - Spacer(Modifier.height(12.dp)) - PasswordField( - value = confirmPassword, - onValueChange = { confirmPassword = it }, - label = "Подтвердите пароль" - ) - AuthStatusText(text = errorText) - } -} - -@Composable -private fun RecoverPasswordScreen( - onLogin: () -> Unit -) { - var firstName by rememberSaveable { mutableStateOf("") } - var lastName by rememberSaveable { mutableStateOf("") } - var phone by rememberSaveable { mutableStateOf("") } - var newPassword by rememberSaveable { mutableStateOf("") } - var confirmPassword by rememberSaveable { mutableStateOf("") } - var firstNameWarning by rememberSaveable { mutableStateOf(null) } - var lastNameWarning by rememberSaveable { mutableStateOf(null) } - var phoneWarning by rememberSaveable { mutableStateOf(null) } - var errorText by rememberSaveable { mutableStateOf(null) } - var successText by rememberSaveable { mutableStateOf(null) } - var loading by rememberSaveable { mutableStateOf(false) } - val scope = rememberCoroutineScope() - - AuthScreenFrame( - title = "Смена пароля", - subtitle = "Для восстановления введите имя, фамилию, номер телефона и новый пароль.", - action = { - AuthButton( - text = if (loading) "Сохраняем..." else "Сменить пароль", - enabled = !loading - ) { - val firstNameNormalized = normalizeRequiredCyrillic(firstName) - if (firstNameNormalized == null) { - firstNameWarning = CYRILLIC_WARNING - errorText = "Имя обязательно и должно содержать только кириллицу, пробел и -" - successText = null - return@AuthButton - } - - val lastNameNormalized = normalizeRequiredCyrillic(lastName) - if (lastNameNormalized == null) { - lastNameWarning = CYRILLIC_WARNING - errorText = "Фамилия обязательна и должна содержать только кириллицу, пробел и -" - successText = null - return@AuthButton - } - - val phoneDigits = normalizeRequiredRussianPhone(phone) - if (phoneDigits == null || newPassword.isBlank() || confirmPassword.isBlank()) { - phoneWarning = PHONE_FORMAT_WARNING - errorText = "Заполните все поля" - successText = null - return@AuthButton - } - - if (newPassword != confirmPassword) { - errorText = "Пароли не совпадают" - successText = null - return@AuthButton - } - - if (BuildConfig.MOCK_AUTH) { - errorText = null - successText = "Пароль успешно изменен. Теперь можно войти." - firstName = "" - lastName = "" - phone = "" - newPassword = "" - confirmPassword = "" - firstNameWarning = null - lastNameWarning = null - phoneWarning = null - return@AuthButton - } - - scope.launch { - loading = true - errorText = null - successText = null - try { - ApiClient.authApi.recoverPassword( - RecoverPasswordReq( - phone = formatPhoneForRequest(phoneDigits), - firstName = firstNameNormalized, - lastName = lastNameNormalized, - newPassword = newPassword - ) - ) - successText = "Пароль успешно изменен. Теперь можно войти." - firstName = "" - lastName = "" - phone = "" - newPassword = "" - confirmPassword = "" - firstNameWarning = null - lastNameWarning = null - phoneWarning = null - } catch (_: HttpException) { - errorText = "Не удалось восстановить пароль" - } catch (_: IOException) { - errorText = "Нет соединения с сервером" - } catch (_: Exception) { - errorText = "Ошибка смены пароля" - } finally { - loading = false - } - } - } - }, - footer = { - AuthFooter( - prefix = "Вспомнили пароль?", - action = "Вернуться ко входу", - onClick = onLogin - ) - } - ) { - PlainField( - value = firstName, - onValueChange = { value -> - when { - !isAllowedCyrillicInput(value) -> { - firstNameWarning = CYRILLIC_WARNING - } - value != firstName -> { - firstName = value - firstNameWarning = null - } - } - }, - label = "Имя", - icon = { - Icon( - imageVector = Icons.Default.Person, - contentDescription = null, - tint = UrbanBrown - ) - }, - warning = firstNameWarning - ) - Spacer(Modifier.height(12.dp)) - PlainField( - value = lastName, - onValueChange = { value -> - when { - !isAllowedCyrillicInput(value) -> { - lastNameWarning = CYRILLIC_WARNING - } - value != lastName -> { - lastName = value - lastNameWarning = null - } - } - }, - label = "Фамилия", - icon = { - Icon( - imageVector = Icons.Default.Person, - contentDescription = null, - tint = UrbanBrown - ) - }, - warning = lastNameWarning - ) - Spacer(Modifier.height(12.dp)) - PhoneField( - value = phone, - onValueChange = { value -> - when { - !isAllowedDigitsInput(value) -> { - phoneWarning = PHONE_CHARS_WARNING - } - value.length > 11 -> { - phoneWarning = PHONE_FORMAT_WARNING - } - value.isNotEmpty() && value.first() !in listOf('7', '8') -> { - phoneWarning = PHONE_FORMAT_WARNING - } - else -> { - if (value != phone) { - phone = value - phoneWarning = null - } - } - } - }, - label = "Телефон", - warning = phoneWarning - ) - Spacer(Modifier.height(12.dp)) - PasswordField( - value = newPassword, - onValueChange = { newPassword = it }, - label = "Новый пароль" - ) - Spacer(Modifier.height(12.dp)) - PasswordField( - value = confirmPassword, - onValueChange = { confirmPassword = it }, - label = "Подтвердите пароль" - ) - AuthStatusText(text = errorText) - AuthSuccessText(text = successText) - } -} - -@Composable -private fun AuthScreenFrame( - title: String, - subtitle: String? = null, - action: @Composable () -> Unit, - footer: @Composable () -> Unit, - content: @Composable ColumnScope.() -> Unit -) { - Column( - modifier = Modifier - .fillMaxSize() - .background(BackgroundLight) - .verticalScroll(rememberScrollState()) - ) { - AuthDecor() - Column( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 24.dp) - .padding(bottom = 24.dp) - ) { - Text( - text = title, - style = MaterialTheme.typography.headlineLarge, - color = TextPrimary - ) - if (subtitle != null) { - Text( - text = subtitle, - modifier = Modifier.padding(top = 8.dp), - style = MaterialTheme.typography.bodyMedium, - color = TextSecondary - ) - } - Spacer(Modifier.height(28.dp)) - content() - Spacer(Modifier.height(28.dp)) - action() - Spacer(Modifier.height(16.dp)) - footer() - } - } -} - -@Composable -private fun AuthDecor() { - Box( - modifier = Modifier - .fillMaxWidth() - .height(230.dp) - .statusBarsPadding() - .padding(horizontal = 18.dp, vertical = 12.dp) - .clip(RoundedCornerShape(32.dp)) - .background(SurfaceWarm) - ) { - Canvas(modifier = Modifier.matchParentSize()) { - val lightPatch = Path().apply { - moveTo(0f, size.height * 0.76f) - cubicTo( - size.width * 0.1f, size.height * 0.7f, - size.width * 0.2f, size.height * 0.45f, - size.width * 0.34f, size.height * 0.42f - ) - cubicTo( - size.width * 0.49f, size.height * 0.38f, - size.width * 0.54f, size.height * 0.18f, - size.width * 0.72f, size.height * 0.12f - ) - lineTo(size.width, size.height * 0.12f) - lineTo(size.width, size.height) - lineTo(0f, size.height) - close() - } - drawPath(path = lightPatch, color = BackgroundLight) - - val road = Path().apply { - moveTo(size.width * 0.88f, -8f) - cubicTo( - size.width * 0.8f, size.height * 0.06f, - size.width * 0.7f, size.height * 0.14f, - size.width * 0.62f, size.height * 0.28f - ) - cubicTo( - size.width * 0.55f, size.height * 0.4f, - size.width * 0.42f, size.height * 0.52f, - size.width * 0.26f, size.height * 0.58f - ) - cubicTo( - size.width * 0.15f, size.height * 0.62f, - size.width * 0.07f, size.height * 0.68f, - -8f, size.height * 0.8f - ) - } - - drawPath( - path = road, - brush = SolidColor(UrbanBrown), - style = Stroke( - width = size.width * 0.09f, - cap = StrokeCap.Square, - join = StrokeJoin.Round - ) - ) - - drawPath( - path = road, - color = BackgroundLight.copy(alpha = 0.95f), - style = Stroke( - width = size.width * 0.014f, - cap = StrokeCap.Butt, - join = StrokeJoin.Round, - pathEffect = PathEffect.dashPathEffect( - floatArrayOf(size.width * 0.06f, size.width * 0.04f) - ) - ) - ) - } - - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 18.dp, vertical = 16.dp), - horizontalArrangement = Arrangement.Start, - verticalAlignment = Alignment.Top - ) { - Box( - modifier = Modifier - .size(42.dp) - .clip(CircleShape) - .background(BackgroundLight.copy(alpha = 0.92f)), - contentAlignment = Alignment.Center - ) { - Text( - text = "GR", - style = MaterialTheme.typography.titleMedium, - color = TextPrimary, - fontWeight = FontWeight.SemiBold - ) - } - } - } -} - -@Composable -private fun PhoneField( - value: String, - onValueChange: (String) -> Unit, - label: String, - warning: String? = null -) { - PlainField( - value = value, - onValueChange = onValueChange, - label = label, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone), - icon = { - Icon( - imageVector = Icons.Default.Phone, - contentDescription = null, - tint = UrbanBrown - ) - }, - warning = warning, - prefix = { - Text( - text = "+", - color = TextSecondary - ) - } - ) -} - -@Composable -private fun PasswordField( - value: String, - onValueChange: (String) -> Unit, - label: String -) { - var visible by remember { mutableStateOf(false) } - - PlainField( - value = value, - onValueChange = onValueChange, - label = label, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), - visualTransformation = if (visible) { - VisualTransformation.None - } else { - PasswordVisualTransformation() - }, - icon = { - Icon( - imageVector = Icons.Default.Lock, - contentDescription = null, - tint = UrbanBrown - ) - }, - trailing = { - Icon( - imageVector = if (visible) { - Icons.Default.VisibilityOff - } else { - Icons.Default.Visibility - }, - contentDescription = null, - tint = TextSecondary, - modifier = Modifier.clickable { visible = !visible } - ) - } - ) -} - -@Composable -private fun PlainField( - value: String, - onValueChange: (String) -> Unit, - label: String, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - visualTransformation: VisualTransformation = VisualTransformation.None, - icon: @Composable (() -> Unit)? = null, - trailing: @Composable (() -> Unit)? = null, - prefix: @Composable (() -> Unit)? = null, - warning: String? = null -) { - TextField( - value = value, - onValueChange = onValueChange, - modifier = Modifier.fillMaxWidth(), - label = { - Text( - text = label, - color = UrbanBrown - ) - }, - textStyle = MaterialTheme.typography.bodyLarge.copy(color = TextPrimary), - singleLine = true, - keyboardOptions = keyboardOptions, - visualTransformation = visualTransformation, - leadingIcon = icon, - trailingIcon = trailing, - prefix = prefix, - isError = warning != null, - supportingText = { - if (warning != null) { - Text( - text = warning, - style = MaterialTheme.typography.bodySmall, - color = AlertRed - ) - } - }, - colors = TextFieldDefaults.colors( - focusedContainerColor = BackgroundLight, - unfocusedContainerColor = BackgroundLight, - disabledContainerColor = BackgroundLight, - focusedIndicatorColor = if (warning != null) AlertRed else SafeGreen, - unfocusedIndicatorColor = if (warning != null) AlertRed else BorderWarm, - cursorColor = SafeGreen, - focusedLabelColor = UrbanBrown, - unfocusedLabelColor = UrbanBrown, - focusedLeadingIconColor = UrbanBrown, - unfocusedLeadingIconColor = UrbanBrown, - focusedTrailingIconColor = TextSecondary, - unfocusedTrailingIconColor = TextSecondary - ), - shape = RoundedCornerShape(18.dp) - ) -} - -@Composable -private fun AuthButton( - text: String, - enabled: Boolean = true, - onClick: () -> Unit -) { - Button( - onClick = onClick, - enabled = enabled, - modifier = Modifier - .fillMaxWidth() - .height(54.dp), - shape = RoundedCornerShape(16.dp), - colors = ButtonDefaults.buttonColors( - containerColor = SafeRoute, - contentColor = BackgroundLight, - disabledContainerColor = SafeRoute.copy(alpha = 0.6f), - disabledContentColor = BackgroundLight - ) - ) { - Text( - text = text, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) - } -} - -@Composable -private fun AuthFooter( - prefix: String, - action: String, - onClick: () -> Unit -) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = prefix, - style = MaterialTheme.typography.bodyMedium, - color = TextSecondary - ) - Spacer(Modifier.width(4.dp)) - TextButton( - onClick = onClick, - contentPadding = PaddingValues(0.dp) - ) { - Text( - text = action, - style = MaterialTheme.typography.bodyMedium, - color = UrbanBrown, - fontWeight = FontWeight.SemiBold - ) - } - } -} - -@Composable -private fun AuthStatusText( - text: String? -) { - if (text == null) { - return - } - Spacer(Modifier.height(12.dp)) - Text( - text = text, - style = MaterialTheme.typography.bodySmall, - color = AlertRed - ) -} - -@Composable -private fun AuthSuccessText( - text: String? -) { - if (text == null) { - return - } - Spacer(Modifier.height(12.dp)) - Text( - text = text, - style = MaterialTheme.typography.bodySmall, - color = SafeGreen - ) -} - -@Composable -private fun RoleStubScreen( - title: String, - onLogout: () -> Unit -) { - Box( - modifier = Modifier - .fillMaxSize() - .background(BackgroundLight) - .padding(24.dp), - contentAlignment = Alignment.Center - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - text = title, - style = MaterialTheme.typography.headlineMedium, - color = TextPrimary - ) - Spacer(Modifier.height(20.dp)) - AuthButton( - text = "Выйти", - onClick = onLogout - ) - } - } -} \ No newline at end of file From 4791d33626200811c6688a87a212443da16852af Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Sun, 22 Mar 2026 20:26:44 +0300 Subject: [PATCH 09/65] add: UserPofileScreen, UserDeleteScreen, UserEditScreen --- app/build.gradle | 3 +- .../java/com/example/goodroad/MainActivity.kt | 2 +- .../com/example/goodroad/ui/auth/AuthApp.kt | 8 +- .../goodroad/ui/auth/AuthComponents.kt | 24 +--- .../com/example/goodroad/ui/auth/AuthDecor.kt | 31 +---- .../example/goodroad/ui/auth/AuthFields.kt | 38 ++---- .../example/goodroad/ui/auth/LoginScreen.kt | 14 +- .../goodroad/ui/auth/RecoverPasswordScreen.kt | 7 +- .../goodroad/ui/auth/RoleStubScreen.kt | 7 +- .../com/example/goodroad/ui/theme/Color.kt | 4 +- .../com/example/goodroad/ui/user/UserApi.kt | 26 ++++ .../goodroad/ui/user/UserComponents.kt | 27 ++++ .../com/example/goodroad/ui/user/UserDecor.kt | 128 ++++++++++++++++++ .../goodroad/ui/user/UserDeleteAccount.kt | 57 ++++++++ .../goodroad/ui/user/UserEditScreen.kt | 64 +++++++++ .../com/example/goodroad/ui/user/UserNav.kt | 36 +++++ .../goodroad/ui/user/UserProfileScreen.kt | 72 ++++++++++ .../example/goodroad/ui/user/UserTestApp.kt | 4 + .../example/goodroad/ui/user/UserViewModel.kt | 32 +++++ 19 files changed, 485 insertions(+), 99 deletions(-) create mode 100644 app/src/main/java/com/example/goodroad/ui/user/UserApi.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/user/UserNav.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/user/UserTestApp.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/user/UserViewModel.kt diff --git a/app/build.gradle b/app/build.gradle index 9b38061..dc179be 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,7 +15,7 @@ android { versionName '1.0' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' - buildConfigField "boolean", "MOCK_AUTH", "false" + buildConfigField "boolean", "MOCK_AUTH", "true" buildConfigField "String", "GOODROAD_SERVER_URL", "\"http://10.0.2.2:8080/\"" } @@ -56,6 +56,7 @@ android { } dependencies { + implementation 'androidx.compose.foundation:foundation:1.10.5' def composeBom = platform('androidx.compose:compose-bom:2024.06.00') implementation 'androidx.core:core-ktx:1.15.0' diff --git a/app/src/main/java/com/example/goodroad/MainActivity.kt b/app/src/main/java/com/example/goodroad/MainActivity.kt index c3a3b60..5231822 100644 --- a/app/src/main/java/com/example/goodroad/MainActivity.kt +++ b/app/src/main/java/com/example/goodroad/MainActivity.kt @@ -3,7 +3,7 @@ package com.example.goodroad import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import com.example.goodroad.ui.AuthApp +import com.example.goodroad.ui.auth.* import com.example.goodroad.ui.theme.GoodRoadTheme class MainActivity : ComponentActivity() { diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt index 794b2bb..70e278f 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt @@ -1,4 +1,5 @@ package com.example.goodroad.ui.auth +import com.example.goodroad.ui.user.UserNav import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Surface @@ -66,13 +67,10 @@ fun AuthApp( } composable(USER_HOME_ROUTE) { - RoleStubScreen( - title = "Главный экран пользователя", + UserNav( onLogout = { navController.navigate(LOGIN_ROUTE) { - popUpTo(USER_HOME_ROUTE) { - inclusive = true - } + popUpTo(USER_HOME_ROUTE) { inclusive = true } launchSingleTop = true } } diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt index 6e1b93b..3d1f29f 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt @@ -1,32 +1,20 @@ package com.example.goodroad.ui.auth -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import com.example.goodroad.ui.theme.* import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.theme.AlertRed -import com.example.goodroad.ui.theme.BackgroundLight -import com.example.goodroad.ui.theme.SafeGreen -import com.example.goodroad.ui.theme.SafeRoute -import com.example.goodroad.ui.theme.TextSecondary -import com.example.goodroad.ui.theme.UrbanBrown +import androidx.compose.ui.graphics.Color @Composable fun AuthButton( text: String, + backgroundColor: Color = SafeRoute, + contentColor: Color = BackgroundLight, enabled: Boolean = true, onClick: () -> Unit ) { diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt index 610d262..3f38ee9 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt @@ -1,43 +1,22 @@ package com.example.goodroad.ui.auth +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.* +import androidx.compose.ui.graphics.* +import com.example.goodroad.ui.theme.* import androidx.compose.foundation.Canvas import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.verticalScroll -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Path -import androidx.compose.ui.graphics.PathEffect -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.StrokeCap -import androidx.compose.ui.graphics.StrokeJoin import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.theme.BackgroundLight -import com.example.goodroad.ui.theme.SurfaceWarm -import com.example.goodroad.ui.theme.TextPrimary -import com.example.goodroad.ui.theme.TextSecondary -import com.example.goodroad.ui.theme.UrbanBrown -import androidx.compose.foundation.layout.size - @Composable fun AuthScreenFrame( title: String, diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt index c17aecc..0582f8b 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt @@ -1,36 +1,19 @@ package com.example.goodroad.ui.auth +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.text.input.* +import com.example.goodroad.ui.theme.* +import androidx.compose.material.icons.filled.* import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Lock -import androidx.compose.material.icons.filled.Phone -import androidx.compose.material.icons.filled.Visibility -import androidx.compose.material.icons.filled.VisibilityOff -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.material3.TextFieldDefaults -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.theme.AlertRed -import com.example.goodroad.ui.theme.BackgroundLight -import com.example.goodroad.ui.theme.BorderWarm -import com.example.goodroad.ui.theme.SafeGreen -import com.example.goodroad.ui.theme.TextPrimary -import com.example.goodroad.ui.theme.TextSecondary -import com.example.goodroad.ui.theme.UrbanBrown +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.interaction.MutableInteractionSource @Composable fun PhoneField( @@ -95,7 +78,12 @@ fun PasswordField( }, contentDescription = null, tint = TextSecondary, - modifier = Modifier.clickable { visible = !visible } + modifier = Modifier.clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = LocalIndication.current + ) { + visible = !visible + } ) } ) diff --git a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt index d3c12d1..bb6499f 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt @@ -1,20 +1,11 @@ package com.example.goodroad.ui.auth import com.example.goodroad.ui.theme.* -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.* import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -24,6 +15,7 @@ import com.example.goodroad.data.network.LoginReq import kotlinx.coroutines.launch import retrofit2.HttpException import java.io.IOException +import androidx.compose.runtime.saveable.rememberSaveable @Composable fun LoginScreen( diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt index f4c41ce..3f6242c 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt @@ -4,14 +4,11 @@ import com.example.goodroad.ui.theme.* import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.material.icons.filled.Person +import androidx.compose.runtime.saveable.rememberSaveable import com.example.goodroad.BuildConfig import com.example.goodroad.data.network.ApiClient import com.example.goodroad.data.network.RecoverPasswordReq diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt index 7f68eac..839a9bb 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt @@ -1,12 +1,7 @@ package com.example.goodroad.ui.auth +import androidx.compose.foundation.layout.* import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/example/goodroad/ui/theme/Color.kt b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt index cfc6665..24a2d96 100644 --- a/app/src/main/java/com/example/goodroad/ui/theme/Color.kt +++ b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt @@ -25,4 +25,6 @@ val Obstacle = Color(0xFFD56B63) val TextPrimary = Color(0xFF2F2B28) val TextSecondary = Color(0xFF7A6F66) -val WhiteSoft = Color(0xFFFFFBF7) \ No newline at end of file +val WhiteSoft = Color(0xFFFFFBF7) + +val GrayButton = Color(0xFF9E9E9E) \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt b/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt new file mode 100644 index 0000000..a463fcf --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt @@ -0,0 +1,26 @@ +package com.example.goodroad.ui.user + +data class UserDto( + val id: String, + val role: String, + val firstName: String?, + val lastName: String?, + val photoUrl: String?, + val active: Boolean +) + +data class UpdateUserReq( + val firstName: String?, + val lastName: String?, + val photoUrl: String?, + val phone: String? +) + +data class ChangePasswordReq( + val oldPassword: String, + val newPassword: String +) + +data class DeleteAccountReq( + val password: String +) \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt b/app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt new file mode 100644 index 0000000..d7d8330 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt @@ -0,0 +1,27 @@ +package com.example.goodroad.ui.user +import com.example.goodroad.ui.theme.TextPrimary + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.theme.UrbanBrown + +@Composable +fun UserInfoBlock(label: String, value: String?) { + Column { + Text( + text = label, + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown + ) + Text( + text = value ?: "-", + style = MaterialTheme.typography.bodyLarge, + color = TextPrimary + ) + Spacer(Modifier.height(8.dp)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt b/app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt new file mode 100644 index 0000000..18b6c34 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt @@ -0,0 +1,128 @@ +package com.example.goodroad.ui.user + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.PathEffect +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.StrokeJoin +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.theme.BackgroundLight +import com.example.goodroad.ui.theme.SurfaceWarm +import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.theme.UrbanBrown + +@Composable +fun UserDecor() { + Box( + modifier = Modifier + .fillMaxWidth() + .height(230.dp) + .statusBarsPadding() +// .padding(horizontal = 18.dp, vertical = 12.dp) + .clip(RoundedCornerShape(32.dp)) + .background(SurfaceWarm) + ) { + Canvas(modifier = Modifier.matchParentSize()) { + val lightPatch = Path().apply { + moveTo(0f, size.height * 0.76f) + cubicTo( + size.width * 0.1f, size.height * 0.7f, + size.width * 0.2f, size.height * 0.45f, + size.width * 0.34f, size.height * 0.42f + ) + cubicTo( + size.width * 0.49f, size.height * 0.38f, + size.width * 0.54f, size.height * 0.18f, + size.width * 0.72f, size.height * 0.12f + ) + lineTo(size.width, size.height * 0.12f) + lineTo(size.width, size.height) + lineTo(0f, size.height) + close() + } + + drawPath( + path = lightPatch, + color = BackgroundLight + ) + + val road = Path().apply { + moveTo(size.width * 0.88f, -8f) + cubicTo( + size.width * 0.8f, size.height * 0.06f, + size.width * 0.7f, size.height * 0.14f, + size.width * 0.62f, size.height * 0.28f + ) + cubicTo( + size.width * 0.55f, size.height * 0.4f, + size.width * 0.42f, size.height * 0.52f, + size.width * 0.26f, size.height * 0.58f + ) + cubicTo( + size.width * 0.15f, size.height * 0.62f, + size.width * 0.07f, size.height * 0.68f, + -8f, size.height * 0.8f + ) + } + + drawPath( + path = road, + brush = SolidColor(UrbanBrown), + style = Stroke( + width = size.width * 0.09f, + cap = StrokeCap.Square, + join = StrokeJoin.Round + ) + ) + + drawPath( + path = road, + color = BackgroundLight.copy(alpha = 0.95f), + style = Stroke( + width = size.width * 0.014f, + cap = StrokeCap.Butt, + join = StrokeJoin.Round, + pathEffect = PathEffect.dashPathEffect( + floatArrayOf(size.width * 0.06f, size.width * 0.04f) + ) + ) + ) + } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 18.dp, vertical = 16.dp), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.Top + ) { + Box( + modifier = Modifier + .size(42.dp) + .clip(CircleShape) + .background(BackgroundLight.copy(alpha = 0.92f)), + contentAlignment = Alignment.Center + ) { + Text( + text = "GR", + style = MaterialTheme.typography.titleMedium, + color = TextPrimary, + fontWeight = FontWeight.SemiBold + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt new file mode 100644 index 0000000..c8d80ce --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt @@ -0,0 +1,57 @@ +package com.example.goodroad.ui.user + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.auth.AuthButton +import com.example.goodroad.ui.auth.PlainField +import com.example.goodroad.ui.theme.GrayButton +import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.theme.WhiteSoft + + +@Composable +fun UserDeleteAccountScreen( + onDelete: (String) -> Unit, + onExit: () -> Unit +) { + var password by remember { mutableStateOf("") } + + Column(modifier = Modifier.padding(24.dp)) { + + UserDecor() + + Text( + "Удаление аккаунта", + style = MaterialTheme.typography.headlineLarge, + color = TextPrimary + ) + + PlainField( + value = password, + onValueChange = { password = it }, + label = "Пароль" + ) + + Spacer(Modifier.height(20.dp)) + + AuthButton( + text = "Удалить аккаунт" + ) { + onDelete(password) + } + + Spacer(Modifier.height(16.dp)) + + AuthButton( + text = "Выйти", + backgroundColor = GrayButton, + contentColor = WhiteSoft + ) { + onExit() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt new file mode 100644 index 0000000..3fb8aaf --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -0,0 +1,64 @@ +package com.example.goodroad.ui.user + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.auth.AuthButton +import com.example.goodroad.ui.auth.PlainField +import com.example.goodroad.ui.theme.TextPrimary + +@Composable +fun UserEditScreen( + vm: UserViewModel, + onBack: () -> Unit +) { + val user = vm.user.value ?: return + + var firstName by remember { mutableStateOf(user.firstName ?: "") } + var lastName by remember { mutableStateOf(user.lastName ?: "") } + var photoUrl by remember { mutableStateOf(user.photoUrl ?: "") } + + Column(modifier = Modifier.padding(24.dp)) { + UserDecor() + + Text( + "Редактирование профиля", + style = MaterialTheme.typography.headlineLarge, + color = TextPrimary + ) + + PlainField( + value = firstName, + onValueChange = { firstName = it }, + label = "Имя" + ) + + Spacer(Modifier.height(12.dp)) + + PlainField( + value = lastName, + onValueChange = { lastName = it }, + label = "Фамилия" + ) + + PlainField( + value = photoUrl, + onValueChange = { photoUrl = it }, + label = "URL фото, телефон и пароль(на сервере функцию пофиксить): TODO" + ) + + + + Spacer(Modifier.height(20.dp)) + + AuthButton( + text = "Сохранить" + ) { + vm.update(firstName, lastName) + onBack() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt new file mode 100644 index 0000000..0f68132 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt @@ -0,0 +1,36 @@ +package com.example.goodroad.ui.user + +import androidx.compose.runtime.* +import androidx.lifecycle.viewmodel.compose.viewModel + +@Composable +fun UserNav( + onLogout: () -> Unit +) { + val vm: UserViewModel = viewModel() + var screen by remember { mutableStateOf("profile") } + + when (screen) { + "profile" -> UserProfileScreen( + vm = vm, + onEdit = { screen = "edit" }, + onDelete = { screen = "delete" }, + onLogout = onLogout + ) + + "edit" -> UserEditScreen( + vm = vm, + onBack = { screen = "profile" } + ) + + "delete" -> UserDeleteAccountScreen( + onDelete = { + vm.logout() + onLogout() + }, + onExit = { + screen = "profile" + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt new file mode 100644 index 0000000..bec70cf --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt @@ -0,0 +1,72 @@ +package com.example.goodroad.ui.user + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.auth.AuthButton +import com.example.goodroad.ui.theme.BackgroundLight +import com.example.goodroad.ui.theme.TextPrimary + +@Composable +fun UserProfileScreen( + vm: UserViewModel, + onEdit: () -> Unit, + onDelete: () -> Unit, + onLogout: () -> Unit +) { + + val user = vm.user.value + + if (user == null) { + Text("Загрузка...", color = TextPrimary) + return + } + + Surface( + modifier = Modifier.fillMaxSize(), + color = BackgroundLight + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(24.dp) + ) { + UserDecor() + + Text( + "Профиль", + style = MaterialTheme.typography.headlineLarge, + color = TextPrimary + ) + + Spacer(Modifier.height(20.dp)) + + UserInfoBlock("Имя", user.firstName) + UserInfoBlock("Фамилия", user.lastName) + UserInfoBlock("Роль", user.role) + + Spacer(Modifier.height(20.dp)) + + AuthButton( + text = "Редактировать", + onClick = onEdit + ) + Spacer(Modifier.height(10.dp)) + + AuthButton( + text = "Удалить аккаунт", + onClick = onDelete + ) + Spacer(Modifier.height(10.dp)) + + AuthButton( + text = "Выйти", + onClick = onLogout + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserTestApp.kt b/app/src/main/java/com/example/goodroad/ui/user/UserTestApp.kt new file mode 100644 index 0000000..923030e --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/user/UserTestApp.kt @@ -0,0 +1,4 @@ +package com.example.goodroad.ui.user + +class UserTestApp +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/user/UserViewModel.kt new file mode 100644 index 0000000..2849a61 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/user/UserViewModel.kt @@ -0,0 +1,32 @@ +package com.example.goodroad.ui.user + +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel + +class UserViewModel : ViewModel() { + + var user = mutableStateOf(null) + private set + + init { + user.value = UserDto( + id = "1", + role = "USER", + firstName = "Мария-Антуанетта", + lastName = "Австрийская", + photoUrl = null, + active = true + ) + } + + fun update(firstName: String, lastName: String) { + user.value = user.value?.copy( + firstName = firstName, + lastName = lastName + ) + } + + fun logout() { + user.value = null + } +} \ No newline at end of file From 4dda8b74c3905c3eaecf87a4e50255a3dd7734e7 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Sun, 22 Mar 2026 20:28:43 +0300 Subject: [PATCH 10/65] delete: app/src/main/java/com/example/goodroad/ui/user/UserTestApp.kt --- app/src/main/java/com/example/goodroad/ui/user/UserTestApp.kt | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 app/src/main/java/com/example/goodroad/ui/user/UserTestApp.kt diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserTestApp.kt b/app/src/main/java/com/example/goodroad/ui/user/UserTestApp.kt deleted file mode 100644 index 923030e..0000000 --- a/app/src/main/java/com/example/goodroad/ui/user/UserTestApp.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.goodroad.ui.user - -class UserTestApp -} \ No newline at end of file From d269626e528e1bf990fce31154e7d67c308aa0b6 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Sun, 5 Apr 2026 23:49:27 +0300 Subject: [PATCH 11/65] feat(auth): integrate login and registration calls --- app/build.gradle | 3 +- .../goodroad/data/network/ApiClient.kt | 13 +- .../example/goodroad/data/network/AuthApi.kt | 3 +- .../example/goodroad/data/network/UserApi.kt | 24 +++ .../data/repository/AuthRepository.kt | 25 +++ .../data/repository/UserRepository.kt | 62 +++++++ .../example/goodroad/ui/AddReviewScreen.kt | 0 .../com/example/goodroad/ui/auth/AuthApp.kt | 8 +- .../example/goodroad/ui/auth/LoginScreen.kt | 73 +++------ .../goodroad/ui/auth/RecoverPasswordScreen.kt | 155 +++++++----------- .../goodroad/ui/auth/RegisterScreen.kt | 46 +++--- .../com/example/goodroad/ui/user/UserApi.kt | 13 +- .../goodroad/ui/user/UserEditScreen.kt | 9 +- .../com/example/goodroad/ui/user/UserNav.kt | 27 ++- .../goodroad/ui/user/UserProfileScreen.kt | 27 ++- .../example/goodroad/ui/user/UserViewModel.kt | 32 ---- .../goodroad/ui/viewmodel/AuthViewModel.kt | 59 +++++++ .../goodroad/ui/viewmodel/UserViewModel.kt | 66 ++++++++ 18 files changed, 402 insertions(+), 243 deletions(-) create mode 100644 app/src/main/java/com/example/goodroad/data/network/UserApi.kt create mode 100644 app/src/main/java/com/example/goodroad/data/repository/AuthRepository.kt create mode 100644 app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt delete mode 100644 app/src/main/java/com/example/goodroad/ui/AddReviewScreen.kt delete mode 100644 app/src/main/java/com/example/goodroad/ui/user/UserViewModel.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt diff --git a/app/build.gradle b/app/build.gradle index dc179be..17d6f8f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,7 +15,7 @@ android { versionName '1.0' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' - buildConfigField "boolean", "MOCK_AUTH", "true" + buildConfigField "boolean", "MOCK_AUTH", "false" buildConfigField "String", "GOODROAD_SERVER_URL", "\"http://10.0.2.2:8080/\"" } @@ -57,6 +57,7 @@ android { dependencies { implementation 'androidx.compose.foundation:foundation:1.10.5' + implementation 'androidx.compose.runtime:runtime-livedata:1.10.6' def composeBom = platform('androidx.compose:compose-bom:2024.06.00') implementation 'androidx.core:core-ktx:1.15.0' diff --git a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt index f3a29e7..4a3e55e 100644 --- a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt +++ b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt @@ -20,7 +20,7 @@ object ApiClient { .writeTimeout(20, TimeUnit.SECONDS) .build() - val authApi: AuthApi by lazy { + val authApi: AuthApi by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { Retrofit.Builder() .baseUrl(BuildConfig.GOODROAD_SERVER_URL) .client(client) @@ -28,4 +28,13 @@ object ApiClient { .build() .create(AuthApi::class.java) } -} + + val userApi: UserApi by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + Retrofit.Builder() + .baseUrl(BuildConfig.GOODROAD_SERVER_URL) + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(UserApi::class.java) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt b/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt index 95802dd..a1d4189 100644 --- a/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt +++ b/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt @@ -1,5 +1,6 @@ package com.example.goodroad.data.network +import retrofit2.Response import retrofit2.http.Body import retrofit2.http.POST @@ -12,5 +13,5 @@ interface AuthApi { suspend fun register(@Body req: RegisterReq): AuthResp @POST("/auth/recover-password") - suspend fun recoverPassword(@Body req: RecoverPasswordReq) + suspend fun recoverPassword(@Body req: RecoverPasswordReq) : Response } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/data/network/UserApi.kt b/app/src/main/java/com/example/goodroad/data/network/UserApi.kt new file mode 100644 index 0000000..cba27a9 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/network/UserApi.kt @@ -0,0 +1,24 @@ +package com.example.goodroad.data.network + +import com.example.goodroad.ui.user.* +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.Response +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.PUT + +interface UserApi { + + @GET("users") + suspend fun getCurrentUser(): Response + + @PUT("users") + suspend fun updateCurrentUser(@Body req: UpdateSettingsReq): Response + + @POST("users/change-password") + suspend fun changePassword(@Body req: ChangePasswordReq): Response + + @DELETE("users") + suspend fun deleteCurrentUser(@Body req : DeleteAccountReq): Response +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/data/repository/AuthRepository.kt b/app/src/main/java/com/example/goodroad/data/repository/AuthRepository.kt new file mode 100644 index 0000000..b1e2b29 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/repository/AuthRepository.kt @@ -0,0 +1,25 @@ +package com.example.goodroad.data.repository + +import com.example.goodroad.data.network.ApiClient +import com.example.goodroad.data.network.AuthResp +import com.example.goodroad.data.network.LoginReq +import com.example.goodroad.data.network.RecoverPasswordReq +import com.example.goodroad.data.network.RegisterReq +import retrofit2.Response + +class AuthRepository { + private val api = ApiClient.authApi + + suspend fun loginUser(phone: String, password: String): AuthResp = + api.login(LoginReq(phone, password)) + + suspend fun registerUser(firstName: String, lastName: String, phone: String, password: String): AuthResp = + api.register(RegisterReq(firstName, lastName, phone, password)) + + suspend fun recoverPassword(phone: String, firstName: String, lastName: String, newPassword: String): Boolean { + val req = RecoverPasswordReq(phone, firstName, lastName, newPassword) + val response = api.recoverPassword(req) + if (response.isSuccessful) return true + else throw Exception("Ошибка восстановления пароля: ${response.code()}") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt b/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt new file mode 100644 index 0000000..732e955 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt @@ -0,0 +1,62 @@ +package com.example.goodroad.data.repository + +import com.example.goodroad.data.network.UserApi +import com.example.goodroad.ui.user.ChangePasswordReq +import com.example.goodroad.ui.user.DeleteAccountReq +import com.example.goodroad.ui.user.SettingsView +import com.example.goodroad.ui.user.UpdateSettingsReq + +class UserRepository(private val api: UserApi) { + + suspend fun getCurrentUser(): SettingsView? { + return try { + val response = api.getCurrentUser() + if (response.isSuccessful) { + response.body() ?: throw Exception("Пользователь не найден") + } else { + throw Exception("Ошибка загрузки пользователя: ${response.code()}") + } + } catch (e: Exception) { + e.printStackTrace() + null + } + } + + suspend fun updateCurrentUser(req: UpdateSettingsReq): SettingsView? { + return try { + val response = api.updateCurrentUser(req) + if (response.isSuccessful) { + response.body() ?: throw Exception("Ошибка обновления пользователя") + } else { + throw Exception("Ошибка обновления пользователя: ${response.code()}") + } + } catch (e: Exception) { + e.printStackTrace() + null + } + } + + suspend fun changePassword(oldPassword: String, newPassword: String) { + try { + val response = api.changePassword(ChangePasswordReq(oldPassword, newPassword)) + if (!response.isSuccessful) { + throw Exception("Ошибка смены пароля: ${response.code()}") + } + } catch (e: Exception) { + e.printStackTrace() + throw e + } + } + + suspend fun deleteCurrentUser(req: DeleteAccountReq) { + try { + val response = api.deleteCurrentUser(req) + if (!response.isSuccessful) { + throw Exception("Ошибка удаления аккаунта: ${response.code()}") + } + } catch (e: Exception) { + e.printStackTrace() + throw e + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/AddReviewScreen.kt b/app/src/main/java/com/example/goodroad/ui/AddReviewScreen.kt deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt index 70e278f..9c0d404 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt @@ -25,11 +25,9 @@ fun AuthApp( ) { composable(LOGIN_ROUTE) { LoginScreen( - onLoginSuccess = { role -> - navController.navigate(homeRoute(role)) { - popUpTo(LOGIN_ROUTE) { - inclusive = true - } + onLoginSuccess = { + navController.navigate(USER_HOME_ROUTE) { + popUpTo(LOGIN_ROUTE) { inclusive = true } launchSingleTop = true } }, diff --git a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt index bb6499f..31c1889 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt @@ -1,25 +1,22 @@ package com.example.goodroad.ui.auth -import com.example.goodroad.ui.theme.* import androidx.compose.foundation.layout.* import androidx.compose.runtime.* import androidx.compose.material3.Text import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel import com.example.goodroad.BuildConfig -import com.example.goodroad.data.network.ApiClient -import com.example.goodroad.data.network.LoginReq -import kotlinx.coroutines.launch -import retrofit2.HttpException -import java.io.IOException +import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.viewmodel.AuthViewModel import androidx.compose.runtime.saveable.rememberSaveable @Composable fun LoginScreen( - onLoginSuccess: (String) -> Unit, + onLoginSuccess: () -> Unit, onSignUp: () -> Unit, onForgotPassword: () -> Unit ) { @@ -28,7 +25,16 @@ fun LoginScreen( var phoneWarning by rememberSaveable { mutableStateOf(null) } var errorText by rememberSaveable { mutableStateOf(null) } var loading by rememberSaveable { mutableStateOf(false) } - val scope = rememberCoroutineScope() + + val viewModel: AuthViewModel = viewModel() + val loginResult by viewModel.loginResult.observeAsState() + val error by viewModel.error.observeAsState() + + LaunchedEffect(loginResult) { + if (loginResult?.user != null) { + onLoginSuccess() + } + } AuthScreenFrame( title = "Вход", @@ -41,45 +47,20 @@ fun LoginScreen( if (phoneDigits == null || password.isBlank()) { phoneWarning = if (phone.isNotBlank() && !isValidRussianPhoneDigits(phone.trim())) { PHONE_FORMAT_WARNING - } else { - phoneWarning - } + } else null errorText = "Заполните телефон и пароль" return@AuthButton } if (BuildConfig.MOCK_AUTH) { errorText = null - onLoginSuccess("USER") + onLoginSuccess() return@AuthButton } - scope.launch { - loading = true - errorText = null - try { - val resp = ApiClient.authApi.login( - LoginReq( - phone = formatPhoneForRequest(phoneDigits), - password = password - ) - ) - val role = resp.user?.role - if (role.isNullOrBlank()) { - errorText = "Не удалось определить роль пользователя" - } else { - onLoginSuccess(role) - } - } catch (_: HttpException) { - errorText = "Неверный телефон или пароль" - } catch (_: IOException) { - errorText = "Нет соединения с сервером" - } catch (_: Exception) { - errorText = "Ошибка входа" - } finally { - loading = false - } - } + loading = true + viewModel.login(formatPhoneForRequest(phoneDigits), password) + loading = false } }, footer = { @@ -94,15 +75,9 @@ fun LoginScreen( value = phone, onValueChange = { value -> when { - !isAllowedDigitsInput(value) -> { - phoneWarning = PHONE_CHARS_WARNING - } - value.length > 11 -> { - phoneWarning = PHONE_FORMAT_WARNING - } - value.isNotEmpty() && value.first() !in listOf('7', '8') -> { - phoneWarning = PHONE_FORMAT_WARNING - } + !isAllowedDigitsInput(value) -> phoneWarning = PHONE_CHARS_WARNING + value.length > 11 -> phoneWarning = PHONE_FORMAT_WARNING + value.isNotEmpty() && value.first() !in listOf('7', '8') -> phoneWarning = PHONE_FORMAT_WARNING else -> { if (value != phone) { phone = value @@ -141,6 +116,6 @@ fun LoginScreen( } } - AuthStatusText(text = errorText) + AuthStatusText(text = error ?: errorText) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt index 3f6242c..2c2c8f7 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt @@ -1,20 +1,17 @@ package com.example.goodroad.ui.auth -import com.example.goodroad.ui.theme.* +import android.util.Log import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.runtime.Composable import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.material.icons.filled.Person -import androidx.compose.runtime.saveable.rememberSaveable -import com.example.goodroad.BuildConfig -import com.example.goodroad.data.network.ApiClient -import com.example.goodroad.data.network.RecoverPasswordReq -import kotlinx.coroutines.launch -import retrofit2.HttpException -import java.io.IOException +import androidx.compose.runtime.livedata.observeAsState +import androidx.lifecycle.viewmodel.compose.viewModel +import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.viewmodel.AuthViewModel @Composable fun RecoverPasswordScreen( @@ -29,23 +26,39 @@ fun RecoverPasswordScreen( var lastNameWarning by rememberSaveable { mutableStateOf(null) } var phoneWarning by rememberSaveable { mutableStateOf(null) } var errorText by rememberSaveable { mutableStateOf(null) } - var successText by rememberSaveable { mutableStateOf(null) } - var loading by rememberSaveable { mutableStateOf(false) } - val scope = rememberCoroutineScope() + + val viewModel: AuthViewModel = viewModel() + + val recoverResult by viewModel.recoverResult.observeAsState() + val error by viewModel.error.observeAsState() + + LaunchedEffect(recoverResult) { + if (recoverResult == true) { + errorText = null + firstName = "" + lastName = "" + phone = "" + newPassword = "" + confirmPassword = "" + firstNameWarning = null + lastNameWarning = null + phoneWarning = null + errorText = "Пароль успешно изменен. Теперь можно войти." + } + } AuthScreenFrame( title = "Смена пароля", subtitle = "Для восстановления введите имя, фамилию, номер телефона и новый пароль.", action = { AuthButton( - text = if (loading) "Сохраняем..." else "Сменить пароль", - enabled = !loading + text = "Сменить пароль", + enabled = recoverResult != true ) { val firstNameNormalized = normalizeRequiredCyrillic(firstName) if (firstNameNormalized == null) { firstNameWarning = CYRILLIC_WARNING errorText = "Имя обязательно и должно содержать только кириллицу, пробел и -" - successText = null return@AuthButton } @@ -53,7 +66,6 @@ fun RecoverPasswordScreen( if (lastNameNormalized == null) { lastNameWarning = CYRILLIC_WARNING errorText = "Фамилия обязательна и должна содержать только кириллицу, пробел и -" - successText = null return@AuthButton } @@ -61,62 +73,22 @@ fun RecoverPasswordScreen( if (phoneDigits == null || newPassword.isBlank() || confirmPassword.isBlank()) { phoneWarning = PHONE_FORMAT_WARNING errorText = "Заполните все поля" - successText = null return@AuthButton } if (newPassword != confirmPassword) { errorText = "Пароли не совпадают" - successText = null return@AuthButton } - if (BuildConfig.MOCK_AUTH) { - errorText = null - successText = "Пароль успешно изменен. Теперь можно войти." - firstName = "" - lastName = "" - phone = "" - newPassword = "" - confirmPassword = "" - firstNameWarning = null - lastNameWarning = null - phoneWarning = null - return@AuthButton - } + Log.d("RecoverPassword", "phone=$phoneDigits, first=$firstNameNormalized, last=$lastNameNormalized") - scope.launch { - loading = true - errorText = null - successText = null - try { - ApiClient.authApi.recoverPassword( - RecoverPasswordReq( - phone = formatPhoneForRequest(phoneDigits), - firstName = firstNameNormalized, - lastName = lastNameNormalized, - newPassword = newPassword - ) - ) - successText = "Пароль успешно изменен. Теперь можно войти." - firstName = "" - lastName = "" - phone = "" - newPassword = "" - confirmPassword = "" - firstNameWarning = null - lastNameWarning = null - phoneWarning = null - } catch (_: HttpException) { - errorText = "Не удалось восстановить пароль" - } catch (_: IOException) { - errorText = "Нет соединения с сервером" - } catch (_: Exception) { - errorText = "Ошибка смены пароля" - } finally { - loading = false - } - } + viewModel.recoverPassword( + phone = formatPhoneForRequest(phoneDigits), + firstName = firstNameNormalized, + lastName = lastNameNormalized, + newPassword = newPassword + ) } }, footer = { @@ -131,23 +103,19 @@ fun RecoverPasswordScreen( value = firstName, onValueChange = { value -> when { - !isAllowedCyrillicInput(value) -> { - firstNameWarning = CYRILLIC_WARNING - } - value != firstName -> { + !isAllowedCyrillicInput(value) -> firstNameWarning = CYRILLIC_WARNING + else -> { firstName = value firstNameWarning = null } } }, label = "Имя", - icon = { - androidx.compose.material3.Icon( - imageVector = androidx.compose.material.icons.Icons.Default.Person, - contentDescription = null, - tint = UrbanBrown - ) - }, + icon = { androidx.compose.material3.Icon( + imageVector = androidx.compose.material.icons.Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) }, warning = firstNameWarning ) @@ -157,23 +125,19 @@ fun RecoverPasswordScreen( value = lastName, onValueChange = { value -> when { - !isAllowedCyrillicInput(value) -> { - lastNameWarning = CYRILLIC_WARNING - } - value != lastName -> { + !isAllowedCyrillicInput(value) -> lastNameWarning = CYRILLIC_WARNING + else -> { lastName = value lastNameWarning = null } } }, label = "Фамилия", - icon = { - androidx.compose.material3.Icon( - imageVector = androidx.compose.material.icons.Icons.Default.Person, - contentDescription = null, - tint = UrbanBrown - ) - }, + icon = { androidx.compose.material3.Icon( + imageVector = androidx.compose.material.icons.Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) }, warning = lastNameWarning ) @@ -183,20 +147,12 @@ fun RecoverPasswordScreen( value = phone, onValueChange = { value -> when { - !isAllowedDigitsInput(value) -> { - phoneWarning = PHONE_CHARS_WARNING - } - value.length > 11 -> { - phoneWarning = PHONE_FORMAT_WARNING - } - value.isNotEmpty() && value.first() !in listOf('7', '8') -> { + !isAllowedDigitsInput(value) -> phoneWarning = PHONE_CHARS_WARNING + value.length > 11 || value.isNotEmpty() && value.first() !in listOf('7', '8') -> phoneWarning = PHONE_FORMAT_WARNING - } else -> { - if (value != phone) { - phone = value - phoneWarning = null - } + phone = value + phoneWarning = null } } }, @@ -220,7 +176,8 @@ fun RecoverPasswordScreen( label = "Подтвердите пароль" ) - AuthStatusText(text = errorText) - AuthSuccessText(text = successText) + Spacer(Modifier.height(12.dp)) + + AuthStatusText(text = error ?: errorText) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt index d75c0f6..be4197e 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt @@ -12,9 +12,13 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.material.icons.filled.Person +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.livedata.observeAsState +import androidx.lifecycle.viewmodel.compose.viewModel import com.example.goodroad.BuildConfig import com.example.goodroad.data.network.ApiClient import com.example.goodroad.data.network.RegisterReq +import com.example.goodroad.ui.viewmodel.AuthViewModel import kotlinx.coroutines.launch import retrofit2.HttpException import java.io.IOException @@ -34,7 +38,15 @@ fun RegisterScreen( var phoneWarning by rememberSaveable { mutableStateOf(null) } var errorText by rememberSaveable { mutableStateOf(null) } var loading by rememberSaveable { mutableStateOf(false) } - val scope = rememberCoroutineScope() + val viewModel: AuthViewModel = viewModel() + val registerResult by viewModel.loginResult.observeAsState() + val error by viewModel.error.observeAsState() + + LaunchedEffect(registerResult) { + registerResult?.user?.role?.let { role -> + onRegisterSuccess(role) + } + } AuthScreenFrame( title = "Создать аккаунт", @@ -75,30 +87,12 @@ fun RegisterScreen( return@AuthButton } - scope.launch { - loading = true - errorText = null - try { - val resp = ApiClient.authApi.register( - RegisterReq( - firstName = firstNameNormalized, - lastName = lastNameNormalized, - phone = formatPhoneForRequest(phoneDigits), - password = password - ) - ) - val role = resp.user?.role ?: "USER" - onRegisterSuccess(role) - } catch (_: HttpException) { - errorText = "Не удалось зарегистрироваться" - } catch (_: IOException) { - errorText = "Нет соединения с сервером" - } catch (_: Exception) { - errorText = "Ошибка регистрации" - } finally { - loading = false - } - } + viewModel.register( + firstNameNormalized, + lastNameNormalized, + formatPhoneForRequest(phoneDigits), + password + ) } }, footer = { @@ -202,6 +196,6 @@ fun RegisterScreen( label = "Подтвердите пароль" ) - AuthStatusText(text = errorText) + AuthStatusText(text = error ?: errorText) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt b/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt index a463fcf..35e2f7b 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt @@ -9,11 +9,20 @@ data class UserDto( val active: Boolean ) -data class UpdateUserReq( +data class UpdateSettingsReq( + val firstName: String? = null, + val lastName: String? = null, + val photoUrl: String? = null, + val phone: String? = null +) + +data class SettingsView( + val id: String, + val role: String, val firstName: String?, val lastName: String?, val photoUrl: String?, - val phone: String? + val active: Boolean ) data class ChangePasswordReq( diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt index 3fb8aaf..c787c41 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.ui.unit.dp import com.example.goodroad.ui.auth.AuthButton import com.example.goodroad.ui.auth.PlainField import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.viewmodel.UserViewModel @Composable fun UserEditScreen( @@ -50,14 +51,16 @@ fun UserEditScreen( label = "URL фото, телефон и пароль(на сервере функцию пофиксить): TODO" ) - - Spacer(Modifier.height(20.dp)) AuthButton( text = "Сохранить" ) { - vm.update(firstName, lastName) + vm.updateUser( + firstName = firstName, + lastName = lastName, + photoUrl = photoUrl.ifBlank { null } + ) onBack() } } diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt index 0f68132..a151bd7 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt @@ -2,12 +2,27 @@ package com.example.goodroad.ui.user import androidx.compose.runtime.* import androidx.lifecycle.viewmodel.compose.viewModel +import com.example.goodroad.data.network.ApiClient +import com.example.goodroad.data.repository.UserRepository +import com.example.goodroad.ui.viewmodel.UserViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.compose.runtime.Composable @Composable -fun UserNav( - onLogout: () -> Unit -) { - val vm: UserViewModel = viewModel() +fun UserNav(onLogout: () -> Unit) { + val api = ApiClient.userApi + val factory = object : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(UserViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return UserViewModel(UserRepository(api)) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } + } + + val vm: UserViewModel = viewModel(factory = factory) + var screen by remember { mutableStateOf("profile") } when (screen) { @@ -28,9 +43,7 @@ fun UserNav( vm.logout() onLogout() }, - onExit = { - screen = "profile" - } + onExit = { screen = "profile" } ) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt index bec70cf..98d4279 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt @@ -4,12 +4,13 @@ import androidx.compose.foundation.layout.* import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.example.goodroad.ui.auth.AuthButton import com.example.goodroad.ui.theme.BackgroundLight import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.viewmodel.UserViewModel @Composable fun UserProfileScreen( @@ -18,6 +19,9 @@ fun UserProfileScreen( onDelete: () -> Unit, onLogout: () -> Unit ) { + LaunchedEffect(Unit) { + vm.getCurrentUser() + } val user = vm.user.value @@ -45,28 +49,19 @@ fun UserProfileScreen( Spacer(Modifier.height(20.dp)) - UserInfoBlock("Имя", user.firstName) - UserInfoBlock("Фамилия", user.lastName) - UserInfoBlock("Роль", user.role) + UserInfoBlock("Имя", user.firstName ?: "") + UserInfoBlock("Фамилия", user.lastName ?: "") + UserInfoBlock("Роль", user.role ?: "") Spacer(Modifier.height(20.dp)) - AuthButton( - text = "Редактировать", - onClick = onEdit - ) + AuthButton(text = "Редактировать", onClick = onEdit) Spacer(Modifier.height(10.dp)) - AuthButton( - text = "Удалить аккаунт", - onClick = onDelete - ) + AuthButton(text = "Удалить аккаунт", onClick = onDelete) Spacer(Modifier.height(10.dp)) - AuthButton( - text = "Выйти", - onClick = onLogout - ) + AuthButton(text = "Выйти", onClick = onLogout) } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/user/UserViewModel.kt deleted file mode 100644 index 2849a61..0000000 --- a/app/src/main/java/com/example/goodroad/ui/user/UserViewModel.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.example.goodroad.ui.user - -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel - -class UserViewModel : ViewModel() { - - var user = mutableStateOf(null) - private set - - init { - user.value = UserDto( - id = "1", - role = "USER", - firstName = "Мария-Антуанетта", - lastName = "Австрийская", - photoUrl = null, - active = true - ) - } - - fun update(firstName: String, lastName: String) { - user.value = user.value?.copy( - firstName = firstName, - lastName = lastName - ) - } - - fun logout() { - user.value = null - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt new file mode 100644 index 0000000..238e624 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt @@ -0,0 +1,59 @@ +package com.example.goodroad.ui.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.example.goodroad.data.network.AuthResp +import com.example.goodroad.data.repository.AuthRepository +import kotlinx.coroutines.launch + +class AuthViewModel : ViewModel() { + + private val authRepository = AuthRepository() + + private val _loginResult = MutableLiveData() + val loginResult: LiveData = _loginResult + + private val _error = MutableLiveData() + val error: LiveData = _error + + fun login(phone: String, password: String) { + viewModelScope.launch { + try { + val response = authRepository.loginUser(phone, password) + _loginResult.value = response + _error.value = null + } catch (e: Exception) { + _error.value = e.message + } + } + } + + fun register(firstName: String, lastName: String, phone: String, password: String) { + viewModelScope.launch { + try { + val response = authRepository.registerUser(firstName, lastName, phone, password) + _loginResult.value = response + _error.value = null + } catch (e: Exception) { + _error.value = e.message + } + } + } + + private val _recoverResult = MutableLiveData() + val recoverResult: LiveData = _recoverResult + + fun recoverPassword(phone: String, firstName: String, lastName: String, newPassword: String) { + viewModelScope.launch { + try { + val success = authRepository.recoverPassword(phone, firstName, lastName, newPassword) + _recoverResult.value = success + _error.value = null + } catch (e: Exception) { + _error.value = e.message + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt new file mode 100644 index 0000000..de9beb0 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -0,0 +1,66 @@ +package com.example.goodroad.ui.viewmodel + +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.goodroad.data.network.UserApi +import com.example.goodroad.data.repository.UserRepository +import com.example.goodroad.ui.user.DeleteAccountReq +import com.example.goodroad.ui.user.SettingsView +import com.example.goodroad.ui.user.UpdateSettingsReq +import kotlinx.coroutines.launch + +class UserViewModel(private val repository: UserRepository) : ViewModel() { + + var user = mutableStateOf(null) + private set + + fun getCurrentUser() { + viewModelScope.launch { + try { + val realUser = repository.getCurrentUser() + user.value = realUser + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + fun updateUser(firstName: String, lastName: String, photoUrl: String? = null, phone: String? = null) { + viewModelScope.launch { + try { + val updatedUser = repository.updateCurrentUser( + UpdateSettingsReq(firstName, lastName, photoUrl, phone) + ) + user.value = updatedUser + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + fun changePassword(oldPassword: String, newPassword: String) { + viewModelScope.launch { + try { + repository.changePassword(oldPassword, newPassword) + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + fun deleteUser(password: String) { + viewModelScope.launch { + try { + repository.deleteCurrentUser(DeleteAccountReq(password)) + user.value = null + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + fun logout() { + user.value = null + } +} \ No newline at end of file From 1380dfcca6f29c1692a92e467641022c05dddf29 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Mon, 6 Apr 2026 22:31:10 +0300 Subject: [PATCH 12/65] feat: intergrate getUserProfile, updateCurrentUserSettings, deleteCurrentUser. feat: add basic auth --- app/build.gradle | 1 + .../goodroad/data/network/ApiClient.kt | 19 +++++ .../example/goodroad/data/network/UserApi.kt | 8 +- .../data/repository/UserRepository.kt | 55 +++--------- .../example/goodroad/ui/auth/LoginScreen.kt | 5 ++ .../goodroad/ui/user/UserEditScreen.kt | 6 +- .../com/example/goodroad/ui/user/UserNav.kt | 8 +- .../goodroad/ui/user/UserProfileScreen.kt | 83 +++++++++---------- .../goodroad/ui/viewmodel/UserViewModel.kt | 24 +++--- 9 files changed, 100 insertions(+), 109 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 17d6f8f..3d054e3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -58,6 +58,7 @@ android { dependencies { implementation 'androidx.compose.foundation:foundation:1.10.5' implementation 'androidx.compose.runtime:runtime-livedata:1.10.6' + implementation 'androidx.compose.runtime:runtime:1.10.6' def composeBom = platform('androidx.compose:compose-bom:2024.06.00') implementation 'androidx.core:core-ktx:1.15.0' diff --git a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt index 4a3e55e..8b12710 100644 --- a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt +++ b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt @@ -1,6 +1,7 @@ package com.example.goodroad.data.network import com.example.goodroad.BuildConfig +import okhttp3.Credentials import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit @@ -13,8 +14,26 @@ object ApiClient { level = HttpLoggingInterceptor.Level.BODY } + private var userPhone: String? = null + private var userPassword: String? = null + + fun setCredentials(phone: String, password: String) { + userPhone = phone + userPassword = password + } + private val client = OkHttpClient.Builder() .addInterceptor(logging) + .addInterceptor { chain -> + val requestBuilder = chain.request().newBuilder() + val phone = userPhone + val password = userPassword + if (!phone.isNullOrBlank() && !password.isNullOrBlank()) { + val credential = Credentials.basic(phone, password) + requestBuilder.addHeader("Authorization", credential) + } + chain.proceed(requestBuilder.build()) + } .connectTimeout(20, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) .writeTimeout(20, TimeUnit.SECONDS) diff --git a/app/src/main/java/com/example/goodroad/data/network/UserApi.kt b/app/src/main/java/com/example/goodroad/data/network/UserApi.kt index cba27a9..23d34e3 100644 --- a/app/src/main/java/com/example/goodroad/data/network/UserApi.kt +++ b/app/src/main/java/com/example/goodroad/data/network/UserApi.kt @@ -10,15 +10,15 @@ import retrofit2.http.PUT interface UserApi { - @GET("users") + @GET("/users") suspend fun getCurrentUser(): Response - @PUT("users") + @PUT("/users") suspend fun updateCurrentUser(@Body req: UpdateSettingsReq): Response - @POST("users/change-password") + @POST("/users") suspend fun changePassword(@Body req: ChangePasswordReq): Response - @DELETE("users") + @DELETE("/users") suspend fun deleteCurrentUser(@Body req : DeleteAccountReq): Response } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt b/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt index 732e955..f386f01 100644 --- a/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt +++ b/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt @@ -1,62 +1,29 @@ package com.example.goodroad.data.repository import com.example.goodroad.data.network.UserApi -import com.example.goodroad.ui.user.ChangePasswordReq -import com.example.goodroad.ui.user.DeleteAccountReq -import com.example.goodroad.ui.user.SettingsView -import com.example.goodroad.ui.user.UpdateSettingsReq +import com.example.goodroad.ui.user.* class UserRepository(private val api: UserApi) { suspend fun getCurrentUser(): SettingsView? { - return try { - val response = api.getCurrentUser() - if (response.isSuccessful) { - response.body() ?: throw Exception("Пользователь не найден") - } else { - throw Exception("Ошибка загрузки пользователя: ${response.code()}") - } - } catch (e: Exception) { - e.printStackTrace() - null - } + val response = api.getCurrentUser() + if (response.isSuccessful) return response.body() + throw Exception("Ошибка загрузки пользователя: ${response.code()} ${response.message()}") } suspend fun updateCurrentUser(req: UpdateSettingsReq): SettingsView? { - return try { - val response = api.updateCurrentUser(req) - if (response.isSuccessful) { - response.body() ?: throw Exception("Ошибка обновления пользователя") - } else { - throw Exception("Ошибка обновления пользователя: ${response.code()}") - } - } catch (e: Exception) { - e.printStackTrace() - null - } + val response = api.updateCurrentUser(req) + if (response.isSuccessful) return response.body() + throw Exception("Ошибка обновления пользователя: ${response.code()} ${response.message()}") } suspend fun changePassword(oldPassword: String, newPassword: String) { - try { - val response = api.changePassword(ChangePasswordReq(oldPassword, newPassword)) - if (!response.isSuccessful) { - throw Exception("Ошибка смены пароля: ${response.code()}") - } - } catch (e: Exception) { - e.printStackTrace() - throw e - } + val response = api.changePassword(ChangePasswordReq(oldPassword, newPassword)) + if (!response.isSuccessful) throw Exception("Ошибка смены пароля: ${response.code()} ${response.message()}") } suspend fun deleteCurrentUser(req: DeleteAccountReq) { - try { - val response = api.deleteCurrentUser(req) - if (!response.isSuccessful) { - throw Exception("Ошибка удаления аккаунта: ${response.code()}") - } - } catch (e: Exception) { - e.printStackTrace() - throw e - } + val response = api.deleteCurrentUser(req) + if (!response.isSuccessful) throw Exception("Ошибка удаления аккаунта: ${response.code()} ${response.message()}") } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt index 31c1889..ea2e396 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt @@ -13,6 +13,7 @@ import com.example.goodroad.BuildConfig import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.AuthViewModel import androidx.compose.runtime.saveable.rememberSaveable +import com.example.goodroad.data.network.ApiClient @Composable fun LoginScreen( @@ -32,6 +33,10 @@ fun LoginScreen( LaunchedEffect(loginResult) { if (loginResult?.user != null) { + ApiClient.setCredentials( + phone = formatPhoneForRequest(normalizeRequiredRussianPhone(phone)!!), + password = password + ) onLoginSuccess() } } diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt index c787c41..b883839 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -13,10 +13,10 @@ import com.example.goodroad.ui.viewmodel.UserViewModel @Composable fun UserEditScreen( - vm: UserViewModel, + userViewModel: UserViewModel, onBack: () -> Unit ) { - val user = vm.user.value ?: return + val user = userViewModel.user.value ?: return var firstName by remember { mutableStateOf(user.firstName ?: "") } var lastName by remember { mutableStateOf(user.lastName ?: "") } @@ -56,7 +56,7 @@ fun UserEditScreen( AuthButton( text = "Сохранить" ) { - vm.updateUser( + userViewModel.updateUser( firstName = firstName, lastName = lastName, photoUrl = photoUrl.ifBlank { null } diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt index a151bd7..b7c8f56 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt @@ -21,26 +21,26 @@ fun UserNav(onLogout: () -> Unit) { } } - val vm: UserViewModel = viewModel(factory = factory) + val userViewModel: UserViewModel = viewModel(factory = factory) var screen by remember { mutableStateOf("profile") } when (screen) { "profile" -> UserProfileScreen( - vm = vm, + userViewModel = userViewModel, onEdit = { screen = "edit" }, onDelete = { screen = "delete" }, onLogout = onLogout ) "edit" -> UserEditScreen( - vm = vm, + userViewModel = userViewModel, onBack = { screen = "profile" } ) "delete" -> UserDeleteAccountScreen( onDelete = { - vm.logout() + userViewModel.logout() onLogout() }, onExit = { screen = "profile" } diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt index 98d4279..1c1c2db 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt @@ -4,8 +4,10 @@ import androidx.compose.foundation.layout.* import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.example.goodroad.ui.auth.AuthButton import com.example.goodroad.ui.theme.BackgroundLight @@ -14,54 +16,51 @@ import com.example.goodroad.ui.viewmodel.UserViewModel @Composable fun UserProfileScreen( - vm: UserViewModel, + userViewModel: UserViewModel, onEdit: () -> Unit, onDelete: () -> Unit, onLogout: () -> Unit ) { LaunchedEffect(Unit) { - vm.getCurrentUser() + userViewModel.getCurrentUser() } - val user = vm.user.value - - if (user == null) { - Text("Загрузка...", color = TextPrimary) - return - } - - Surface( - modifier = Modifier.fillMaxSize(), - color = BackgroundLight - ) { - Column( - modifier = Modifier - .fillMaxSize() - .padding(24.dp) - ) { - UserDecor() - - Text( - "Профиль", - style = MaterialTheme.typography.headlineLarge, - color = TextPrimary - ) - - Spacer(Modifier.height(20.dp)) - - UserInfoBlock("Имя", user.firstName ?: "") - UserInfoBlock("Фамилия", user.lastName ?: "") - UserInfoBlock("Роль", user.role ?: "") - - Spacer(Modifier.height(20.dp)) - - AuthButton(text = "Редактировать", onClick = onEdit) - Spacer(Modifier.height(10.dp)) - - AuthButton(text = "Удалить аккаунт", onClick = onDelete) - Spacer(Modifier.height(10.dp)) - - AuthButton(text = "Выйти", onClick = onLogout) + when { + userViewModel.isLoading.value -> { + Text("Загрузка...", color = TextPrimary) + } + userViewModel.errorMessage.value != null -> { + Text("Ошибка: ${userViewModel.errorMessage.value}", color = Color.Red) + } + userViewModel.user.value != null -> { + val user = userViewModel.user.value!! + Surface( + modifier = Modifier.fillMaxSize(), + color = BackgroundLight + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(24.dp) + ) { + UserDecor() + Text( + "Профиль", + style = MaterialTheme.typography.headlineLarge, + color = TextPrimary + ) + Spacer(Modifier.height(20.dp)) + UserInfoBlock("Имя", user.firstName ?: "") + UserInfoBlock("Фамилия", user.lastName ?: "") + UserInfoBlock("Роль", user.role ?: "") + Spacer(Modifier.height(20.dp)) + AuthButton(text = "Редактировать", onClick = onEdit) + Spacer(Modifier.height(10.dp)) + AuthButton(text = "Удалить аккаунт", onClick = onDelete) + Spacer(Modifier.height(10.dp)) + AuthButton(text = "Выйти", onClick = onLogout) + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt index de9beb0..64a21f5 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -3,7 +3,6 @@ package com.example.goodroad.ui.viewmodel import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.example.goodroad.data.network.UserApi import com.example.goodroad.data.repository.UserRepository import com.example.goodroad.ui.user.DeleteAccountReq import com.example.goodroad.ui.user.SettingsView @@ -13,15 +12,19 @@ import kotlinx.coroutines.launch class UserViewModel(private val repository: UserRepository) : ViewModel() { var user = mutableStateOf(null) - private set + var isLoading = mutableStateOf(false) + var errorMessage = mutableStateOf(null) fun getCurrentUser() { viewModelScope.launch { + isLoading.value = true + errorMessage.value = null try { - val realUser = repository.getCurrentUser() - user.value = realUser + user.value = repository.getCurrentUser() } catch (e: Exception) { - e.printStackTrace() + errorMessage.value = e.message ?: "Неизвестная ошибка" + } finally { + isLoading.value = false } } } @@ -29,12 +32,9 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { fun updateUser(firstName: String, lastName: String, photoUrl: String? = null, phone: String? = null) { viewModelScope.launch { try { - val updatedUser = repository.updateCurrentUser( - UpdateSettingsReq(firstName, lastName, photoUrl, phone) - ) - user.value = updatedUser + user.value = repository.updateCurrentUser(UpdateSettingsReq(firstName, lastName, photoUrl, phone)) } catch (e: Exception) { - e.printStackTrace() + errorMessage.value = e.message } } } @@ -44,7 +44,7 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { try { repository.changePassword(oldPassword, newPassword) } catch (e: Exception) { - e.printStackTrace() + errorMessage.value = e.message } } } @@ -55,7 +55,7 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { repository.deleteCurrentUser(DeleteAccountReq(password)) user.value = null } catch (e: Exception) { - e.printStackTrace() + errorMessage.value = e.message } } } From 6863ee8a45c00b9d4b42a07561ae9d9176033921 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Wed, 8 Apr 2026 01:01:30 +0300 Subject: [PATCH 13/65] fix: after registration and login user see profileScreen. after delete user doesn't see login screen yet --- .../example/goodroad/data/network/UserApi.kt | 5 +- .../example/goodroad/ui/auth/LoginScreen.kt | 6 -- .../goodroad/ui/user/UserDeleteAccount.kt | 53 +++++++++++++---- .../com/example/goodroad/ui/user/UserNav.kt | 9 ++- .../goodroad/ui/user/UserProfileScreen.kt | 57 +++++++++++++------ .../goodroad/ui/viewmodel/AuthViewModel.kt | 15 +++-- .../goodroad/ui/viewmodel/UserViewModel.kt | 14 ++++- 7 files changed, 109 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/data/network/UserApi.kt b/app/src/main/java/com/example/goodroad/data/network/UserApi.kt index 23d34e3..b1d8b34 100644 --- a/app/src/main/java/com/example/goodroad/data/network/UserApi.kt +++ b/app/src/main/java/com/example/goodroad/data/network/UserApi.kt @@ -5,6 +5,7 @@ import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.Response import retrofit2.http.GET +import retrofit2.http.HTTP import retrofit2.http.POST import retrofit2.http.PUT @@ -19,6 +20,6 @@ interface UserApi { @POST("/users") suspend fun changePassword(@Body req: ChangePasswordReq): Response - @DELETE("/users") - suspend fun deleteCurrentUser(@Body req : DeleteAccountReq): Response + @HTTP(method = "DELETE", path = "/users", hasBody = true) + suspend fun deleteCurrentUser(@Body req: DeleteAccountReq): Response } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt index ea2e396..7ddb95e 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt @@ -13,7 +13,6 @@ import com.example.goodroad.BuildConfig import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.AuthViewModel import androidx.compose.runtime.saveable.rememberSaveable -import com.example.goodroad.data.network.ApiClient @Composable fun LoginScreen( @@ -33,10 +32,6 @@ fun LoginScreen( LaunchedEffect(loginResult) { if (loginResult?.user != null) { - ApiClient.setCredentials( - phone = formatPhoneForRequest(normalizeRequiredRussianPhone(phone)!!), - password = password - ) onLoginSuccess() } } @@ -65,7 +60,6 @@ fun LoginScreen( loading = true viewModel.login(formatPhoneForRequest(phoneDigits), password) - loading = false } }, footer = { diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt index c8d80ce..fc048c1 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt @@ -1,57 +1,88 @@ package com.example.goodroad.ui.user import androidx.compose.foundation.layout.* -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text +import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.example.goodroad.ui.auth.AuthButton import com.example.goodroad.ui.auth.PlainField import com.example.goodroad.ui.theme.GrayButton import com.example.goodroad.ui.theme.TextPrimary import com.example.goodroad.ui.theme.WhiteSoft - +import com.example.goodroad.ui.viewmodel.UserViewModel @Composable fun UserDeleteAccountScreen( - onDelete: (String) -> Unit, + viewModel: UserViewModel, onExit: () -> Unit ) { var password by remember { mutableStateOf("") } - Column(modifier = Modifier.padding(24.dp)) { + val isLoading by viewModel.isLoading + val errorMessage by viewModel.errorMessage + + Column( + modifier = Modifier + .fillMaxSize() + .padding(24.dp) + ) { UserDecor() + Spacer(modifier = Modifier.height(16.dp)) + Text( "Удаление аккаунта", style = MaterialTheme.typography.headlineLarge, color = TextPrimary ) + Spacer(modifier = Modifier.height(20.dp)) + PlainField( value = password, onValueChange = { password = it }, label = "Пароль" ) - Spacer(Modifier.height(20.dp)) + Spacer(modifier = Modifier.height(20.dp)) AuthButton( - text = "Удалить аккаунт" + text = "Удалить аккаунт", + enabled = !isLoading ) { - onDelete(password) + viewModel.deleteUser(password) { + onExit() + } } - Spacer(Modifier.height(16.dp)) + Spacer(modifier = Modifier.height(16.dp)) AuthButton( text = "Выйти", backgroundColor = GrayButton, - contentColor = WhiteSoft + contentColor = WhiteSoft, + enabled = !isLoading ) { - onExit() + viewModel.logout { + onExit() + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + if (isLoading) { + CircularProgressIndicator(modifier = Modifier.padding(top = 16.dp)) + } + + errorMessage?.let { + Text( + text = it, + color = Color.Red, + modifier = Modifier.padding(top = 16.dp) + ) } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt index b7c8f56..8727972 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt @@ -39,11 +39,10 @@ fun UserNav(onLogout: () -> Unit) { ) "delete" -> UserDeleteAccountScreen( - onDelete = { - userViewModel.logout() - onLogout() - }, - onExit = { screen = "profile" } + viewModel = userViewModel, + onExit = { + screen = "profile" + } ) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt index 1c1c2db..1a2df36 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt @@ -1,11 +1,9 @@ package com.example.goodroad.ui.user import androidx.compose.foundation.layout.* -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp @@ -21,19 +19,29 @@ fun UserProfileScreen( onDelete: () -> Unit, onLogout: () -> Unit ) { - LaunchedEffect(Unit) { - userViewModel.getCurrentUser() + val user by userViewModel.user + val isLoading by userViewModel.isLoading + val errorMessage by userViewModel.errorMessage + + LaunchedEffect(userViewModel) { + if (user == null && !userViewModel.isDeleted) { + userViewModel.getCurrentUser() + } } when { - userViewModel.isLoading.value -> { - Text("Загрузка...", color = TextPrimary) + isLoading -> { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } } - userViewModel.errorMessage.value != null -> { - Text("Ошибка: ${userViewModel.errorMessage.value}", color = Color.Red) + errorMessage != null -> { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text("Ошибка: $errorMessage", color = Color.Red) + } } - userViewModel.user.value != null -> { - val user = userViewModel.user.value!! + user != null -> { + val u = user!! Surface( modifier = Modifier.fillMaxSize(), color = BackgroundLight @@ -50,15 +58,28 @@ fun UserProfileScreen( color = TextPrimary ) Spacer(Modifier.height(20.dp)) - UserInfoBlock("Имя", user.firstName ?: "") - UserInfoBlock("Фамилия", user.lastName ?: "") - UserInfoBlock("Роль", user.role ?: "") + UserInfoBlock("Имя", u.firstName ?: "") + UserInfoBlock("Фамилия", u.lastName ?: "") + UserInfoBlock("Роль", u.role ?: "") Spacer(Modifier.height(20.dp)) AuthButton(text = "Редактировать", onClick = onEdit) Spacer(Modifier.height(10.dp)) - AuthButton(text = "Удалить аккаунт", onClick = onDelete) + AuthButton(text = "Удалить аккаунт") { + onDelete() + } Spacer(Modifier.height(10.dp)) - AuthButton(text = "Выйти", onClick = onLogout) + AuthButton(text = "Выйти") { + userViewModel.logout { + onLogout() + } + } + } + } + } + else -> { + LaunchedEffect(Unit) { + if (userViewModel.isDeleted) { + onLogout() } } } diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt index 238e624..14a1b3b 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.example.goodroad.data.network.AuthResp +import com.example.goodroad.data.network.ApiClient import com.example.goodroad.data.repository.AuthRepository import kotlinx.coroutines.launch @@ -18,14 +19,18 @@ class AuthViewModel : ViewModel() { private val _error = MutableLiveData() val error: LiveData = _error + private val _recoverResult = MutableLiveData() + val recoverResult: LiveData = _recoverResult + fun login(phone: String, password: String) { viewModelScope.launch { try { val response = authRepository.loginUser(phone, password) + ApiClient.setCredentials(phone, password) _loginResult.value = response _error.value = null } catch (e: Exception) { - _error.value = e.message + _error.value = e.message ?: "Ошибка логина" } } } @@ -34,17 +39,15 @@ class AuthViewModel : ViewModel() { viewModelScope.launch { try { val response = authRepository.registerUser(firstName, lastName, phone, password) + ApiClient.setCredentials(phone, password) _loginResult.value = response _error.value = null } catch (e: Exception) { - _error.value = e.message + _error.value = e.message ?: "Ошибка регистрации" } } } - private val _recoverResult = MutableLiveData() - val recoverResult: LiveData = _recoverResult - fun recoverPassword(phone: String, firstName: String, lastName: String, newPassword: String) { viewModelScope.launch { try { @@ -52,7 +55,7 @@ class AuthViewModel : ViewModel() { _recoverResult.value = success _error.value = null } catch (e: Exception) { - _error.value = e.message + _error.value = e.message ?: "Ошибка восстановления пароля" } } } diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt index 64a21f5..40f6689 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -14,6 +14,8 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { var user = mutableStateOf(null) var isLoading = mutableStateOf(false) var errorMessage = mutableStateOf(null) + var isDeleted = false + private set fun getCurrentUser() { viewModelScope.launch { @@ -49,18 +51,26 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { } } - fun deleteUser(password: String) { + fun deleteUser(password: String, onSuccess: () -> Unit) { viewModelScope.launch { + isLoading.value = true + errorMessage.value = null try { repository.deleteCurrentUser(DeleteAccountReq(password)) user.value = null + isDeleted = true + onSuccess() } catch (e: Exception) { errorMessage.value = e.message + } finally { + isLoading.value = false } } } - fun logout() { + fun logout(onSuccess: () -> Unit) { user.value = null + isDeleted = false + onSuccess() } } \ No newline at end of file From a2c70d4c399d3c177ba9917b8aded2cb1a349521 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Wed, 8 Apr 2026 01:07:19 +0300 Subject: [PATCH 14/65] fix: after deleting user see login screen --- .../goodroad/data/network/ApiClient.kt | 31 ++++++++++--------- .../goodroad/ui/user/UserDeleteAccount.kt | 2 -- .../goodroad/ui/viewmodel/UserViewModel.kt | 1 + 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt index 8b12710..71d8b68 100644 --- a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt +++ b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt @@ -22,22 +22,23 @@ object ApiClient { userPassword = password } - private val client = OkHttpClient.Builder() - .addInterceptor(logging) - .addInterceptor { chain -> - val requestBuilder = chain.request().newBuilder() - val phone = userPhone - val password = userPassword - if (!phone.isNullOrBlank() && !password.isNullOrBlank()) { - val credential = Credentials.basic(phone, password) - requestBuilder.addHeader("Authorization", credential) + private val client: OkHttpClient + get() = OkHttpClient.Builder() + .addInterceptor(logging) + .addInterceptor { chain -> + val requestBuilder = chain.request().newBuilder() + val phone = userPhone + val password = userPassword + if (!phone.isNullOrBlank() && !password.isNullOrBlank()) { + val credential = Credentials.basic(phone, password) + requestBuilder.addHeader("Authorization", credential) + } + chain.proceed(requestBuilder.build()) } - chain.proceed(requestBuilder.build()) - } - .connectTimeout(20, TimeUnit.SECONDS) - .readTimeout(20, TimeUnit.SECONDS) - .writeTimeout(20, TimeUnit.SECONDS) - .build() + .connectTimeout(20, TimeUnit.SECONDS) + .readTimeout(20, TimeUnit.SECONDS) + .writeTimeout(20, TimeUnit.SECONDS) + .build() val authApi: AuthApi by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { Retrofit.Builder() diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt index fc048c1..36cf14a 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt @@ -19,7 +19,6 @@ fun UserDeleteAccountScreen( onExit: () -> Unit ) { var password by remember { mutableStateOf("") } - val isLoading by viewModel.isLoading val errorMessage by viewModel.errorMessage @@ -30,7 +29,6 @@ fun UserDeleteAccountScreen( ) { UserDecor() - Spacer(modifier = Modifier.height(16.dp)) Text( diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt index 40f6689..6fd7427 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -18,6 +18,7 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { private set fun getCurrentUser() { + if (isDeleted) return viewModelScope.launch { isLoading.value = true errorMessage.value = null From 53e12a3b9b74a940d1dc02d7dbaa74736fb92151 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Wed, 8 Apr 2026 23:20:15 +0300 Subject: [PATCH 15/65] =?UTF-8?q?fix:=20now=20password=20on=20deleteScreen?= =?UTF-8?q?=20can=20be=20unvisible,=20=D0=B2=D1=85=D0=BE=D0=B4=D0=B8=D0=BC?= =?UTF-8?q?...=20on=20login=20screen=20resets=20after=20the=20server=20res?= =?UTF-8?q?ponse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/goodroad/ui/auth/LoginScreen.kt | 19 +++++-------------- .../goodroad/ui/user/UserDeleteAccount.kt | 8 +++----- .../goodroad/ui/viewmodel/AuthViewModel.kt | 18 +++++++++++++++--- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt index 7ddb95e..97dc753 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt @@ -1,18 +1,16 @@ package com.example.goodroad.ui.auth import androidx.compose.foundation.layout.* +import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel -import com.example.goodroad.BuildConfig +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.livedata.observeAsState import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.AuthViewModel -import androidx.compose.runtime.saveable.rememberSaveable @Composable fun LoginScreen( @@ -24,11 +22,11 @@ fun LoginScreen( var password by rememberSaveable { mutableStateOf("") } var phoneWarning by rememberSaveable { mutableStateOf(null) } var errorText by rememberSaveable { mutableStateOf(null) } - var loading by rememberSaveable { mutableStateOf(false) } val viewModel: AuthViewModel = viewModel() val loginResult by viewModel.loginResult.observeAsState() val error by viewModel.error.observeAsState() + val loading by viewModel.isLoading.observeAsState(initial = false) LaunchedEffect(loginResult) { if (loginResult?.user != null) { @@ -51,14 +49,7 @@ fun LoginScreen( errorText = "Заполните телефон и пароль" return@AuthButton } - - if (BuildConfig.MOCK_AUTH) { - errorText = null - onLoginSuccess() - return@AuthButton - } - - loading = true + errorText = null viewModel.login(formatPhoneForRequest(phoneDigits), password) } }, diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt index 36cf14a..0e97c92 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt @@ -7,10 +7,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.example.goodroad.ui.auth.AuthButton -import com.example.goodroad.ui.auth.PlainField -import com.example.goodroad.ui.theme.GrayButton -import com.example.goodroad.ui.theme.TextPrimary -import com.example.goodroad.ui.theme.WhiteSoft +import com.example.goodroad.ui.auth.PasswordField +import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.UserViewModel @Composable @@ -39,7 +37,7 @@ fun UserDeleteAccountScreen( Spacer(modifier = Modifier.height(20.dp)) - PlainField( + PasswordField( value = password, onValueChange = { password = it }, label = "Пароль" diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt index 14a1b3b..63c8be3 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt @@ -22,40 +22,52 @@ class AuthViewModel : ViewModel() { private val _recoverResult = MutableLiveData() val recoverResult: LiveData = _recoverResult + private val _isLoading = MutableLiveData(false) + val isLoading: LiveData = _isLoading + fun login(phone: String, password: String) { viewModelScope.launch { + _isLoading.value = true + _error.value = null try { val response = authRepository.loginUser(phone, password) ApiClient.setCredentials(phone, password) _loginResult.value = response - _error.value = null } catch (e: Exception) { _error.value = e.message ?: "Ошибка логина" + } finally { + _isLoading.value = false } } } fun register(firstName: String, lastName: String, phone: String, password: String) { viewModelScope.launch { + _isLoading.value = true + _error.value = null try { val response = authRepository.registerUser(firstName, lastName, phone, password) ApiClient.setCredentials(phone, password) _loginResult.value = response - _error.value = null } catch (e: Exception) { _error.value = e.message ?: "Ошибка регистрации" + } finally { + _isLoading.value = false } } } fun recoverPassword(phone: String, firstName: String, lastName: String, newPassword: String) { viewModelScope.launch { + _isLoading.value = true + _error.value = null try { val success = authRepository.recoverPassword(phone, firstName, lastName, newPassword) _recoverResult.value = success - _error.value = null } catch (e: Exception) { _error.value = e.message ?: "Ошибка восстановления пароля" + } finally { + _isLoading.value = false } } } From fe9524bf2173831d70a6bcc1d5bf8a79db836108 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Thu, 9 Apr 2026 00:25:46 +0300 Subject: [PATCH 16/65] feat: correct work edit screen --- .../example/goodroad/data/network/UserApi.kt | 5 +- .../data/repository/UserRepository.kt | 7 +- .../com/example/goodroad/ui/user/UserApi.kt | 13 ++- .../goodroad/ui/user/UserEditScreen.kt | 83 +++++++++++++------ .../goodroad/ui/viewmodel/UserViewModel.kt | 32 ++++--- 5 files changed, 84 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/data/network/UserApi.kt b/app/src/main/java/com/example/goodroad/data/network/UserApi.kt index b1d8b34..8106bcc 100644 --- a/app/src/main/java/com/example/goodroad/data/network/UserApi.kt +++ b/app/src/main/java/com/example/goodroad/data/network/UserApi.kt @@ -15,10 +15,7 @@ interface UserApi { suspend fun getCurrentUser(): Response @PUT("/users") - suspend fun updateCurrentUser(@Body req: UpdateSettingsReq): Response - - @POST("/users") - suspend fun changePassword(@Body req: ChangePasswordReq): Response + suspend fun updateCurrentUser(@Body req: UpdateUserReq): Response @HTTP(method = "DELETE", path = "/users", hasBody = true) suspend fun deleteCurrentUser(@Body req: DeleteAccountReq): Response diff --git a/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt b/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt index f386f01..d14cc9b 100644 --- a/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt +++ b/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt @@ -11,17 +11,12 @@ class UserRepository(private val api: UserApi) { throw Exception("Ошибка загрузки пользователя: ${response.code()} ${response.message()}") } - suspend fun updateCurrentUser(req: UpdateSettingsReq): SettingsView? { + suspend fun updateCurrentUser(req: UpdateUserReq): SettingsView? { val response = api.updateCurrentUser(req) if (response.isSuccessful) return response.body() throw Exception("Ошибка обновления пользователя: ${response.code()} ${response.message()}") } - suspend fun changePassword(oldPassword: String, newPassword: String) { - val response = api.changePassword(ChangePasswordReq(oldPassword, newPassword)) - if (!response.isSuccessful) throw Exception("Ошибка смены пароля: ${response.code()} ${response.message()}") - } - suspend fun deleteCurrentUser(req: DeleteAccountReq) { val response = api.deleteCurrentUser(req) if (!response.isSuccessful) throw Exception("Ошибка удаления аккаунта: ${response.code()} ${response.message()}") diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt b/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt index 35e2f7b..a8c9409 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt @@ -6,14 +6,17 @@ data class UserDto( val firstName: String?, val lastName: String?, val photoUrl: String?, + val phone: String?, val active: Boolean ) -data class UpdateSettingsReq( +data class UpdateUserReq( val firstName: String? = null, val lastName: String? = null, val photoUrl: String? = null, - val phone: String? = null + val phone: String? = null, + val oldPassword: String? = null, + val newPassword: String? = null ) data class SettingsView( @@ -22,14 +25,10 @@ data class SettingsView( val firstName: String?, val lastName: String?, val photoUrl: String?, + val phone: String?, val active: Boolean ) -data class ChangePasswordReq( - val oldPassword: String, - val newPassword: String -) - data class DeleteAccountReq( val password: String ) \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt index b883839..ab48220 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -1,14 +1,17 @@ package com.example.goodroad.ui.user import androidx.compose.foundation.layout.* -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text +import androidx.compose.foundation.verticalScroll +import androidx.compose.foundation.rememberScrollState +import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.example.goodroad.ui.auth.AuthButton import com.example.goodroad.ui.auth.PlainField +import com.example.goodroad.ui.theme.GrayButton import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.theme.WhiteSoft import com.example.goodroad.ui.viewmodel.UserViewModel @Composable @@ -21,8 +24,18 @@ fun UserEditScreen( var firstName by remember { mutableStateOf(user.firstName ?: "") } var lastName by remember { mutableStateOf(user.lastName ?: "") } var photoUrl by remember { mutableStateOf(user.photoUrl ?: "") } + var phone by remember { mutableStateOf(user.phone ?: "") } + var oldPassword by remember { mutableStateOf("") } + var newPassword by remember { mutableStateOf("") } - Column(modifier = Modifier.padding(24.dp)) { + val errorMessage by remember { derivedStateOf { userViewModel.errorMessage.value } } + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(24.dp) + ) { UserDecor() Text( @@ -31,37 +44,55 @@ fun UserEditScreen( color = TextPrimary ) - PlainField( - value = firstName, - onValueChange = { firstName = it }, - label = "Имя" - ) - Spacer(Modifier.height(12.dp)) - - PlainField( - value = lastName, - onValueChange = { lastName = it }, - label = "Фамилия" - ) - - PlainField( - value = photoUrl, - onValueChange = { photoUrl = it }, - label = "URL фото, телефон и пароль(на сервере функцию пофиксить): TODO" - ) + PlainField(value = firstName, onValueChange = { firstName = it }, label = "Имя") + Spacer(Modifier.height(12.dp)) + PlainField(value = lastName, onValueChange = { lastName = it }, label = "Фамилия") + Spacer(Modifier.height(12.dp)) + PlainField(value = photoUrl, onValueChange = { photoUrl = it }, label = "URL фото") + Spacer(Modifier.height(12.dp)) + PlainField(value = phone, onValueChange = { phone = it }, label = "Телефон") + Spacer(Modifier.height(12.dp)) + PlainField(value = oldPassword, onValueChange = { oldPassword = it }, label = "Старый пароль") + Spacer(Modifier.height(12.dp)) + PlainField(value = newPassword, onValueChange = { newPassword = it }, label = "Новый пароль") Spacer(Modifier.height(20.dp)) + AuthButton(text = "Сохранить") { + val oldPass = oldPassword.takeIf { it.isNotBlank() } + val newPass = newPassword.takeIf { it.isNotBlank() } - AuthButton( - text = "Сохранить" - ) { userViewModel.updateUser( firstName = firstName, lastName = lastName, - photoUrl = photoUrl.ifBlank { null } + photoUrl = photoUrl.ifBlank { null }, + phone = phone.ifBlank { null }, + oldPassword = oldPass, + newPassword = newPass + ) + + oldPassword = "" + newPassword = "" + } + + Spacer(Modifier.height(12.dp)) + AuthButton( + text = "Выйти", + backgroundColor = GrayButton, + contentColor = WhiteSoft + ) { + userViewModel.logout { + onBack() + } + } + + if (!errorMessage.isNullOrBlank()) { + Spacer(Modifier.height(12.dp)) + Text( + text = errorMessage ?: "", + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.bodyMedium ) - onBack() } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt index 6fd7427..6c2d18b 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -6,7 +6,7 @@ import androidx.lifecycle.viewModelScope import com.example.goodroad.data.repository.UserRepository import com.example.goodroad.ui.user.DeleteAccountReq import com.example.goodroad.ui.user.SettingsView -import com.example.goodroad.ui.user.UpdateSettingsReq +import com.example.goodroad.ui.user.UpdateUserReq import kotlinx.coroutines.launch class UserViewModel(private val repository: UserRepository) : ViewModel() { @@ -32,20 +32,26 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { } } - fun updateUser(firstName: String, lastName: String, photoUrl: String? = null, phone: String? = null) { - viewModelScope.launch { - try { - user.value = repository.updateCurrentUser(UpdateSettingsReq(firstName, lastName, photoUrl, phone)) - } catch (e: Exception) { - errorMessage.value = e.message - } - } - } - - fun changePassword(oldPassword: String, newPassword: String) { + fun updateUser( + firstName: String, + lastName: String, + photoUrl: String? = null, + phone: String? = null, + oldPassword: String? = null, + newPassword: String? = null + ) { viewModelScope.launch { + errorMessage.value = null try { - repository.changePassword(oldPassword, newPassword) + val req = UpdateUserReq( + firstName = firstName, + lastName = lastName, + photoUrl = photoUrl, + phone = phone, + oldPassword = oldPassword, + newPassword = newPassword + ) + user.value = repository.updateCurrentUser(req) } catch (e: Exception) { errorMessage.value = e.message } From 8bc84ba81a59ee5f043241d747fa93e00702acf7 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Thu, 9 Apr 2026 23:13:34 +0300 Subject: [PATCH 17/65] feat: add icons and validations on edit sreen. fix: structure of files --- .../data/{network => auth}/AuthApi.kt | 2 +- .../data/{network => auth}/AuthModels.kt | 2 +- .../{repository => auth}/AuthRepository.kt | 7 +- .../goodroad/data/network/ApiClient.kt | 2 + .../data/{network => user}/UserApi.kt | 10 +- .../UserApi.kt => data/user/UserModels.kt} | 5 +- .../{repository => user}/UserRepository.kt | 8 +- .../example/goodroad/ui/auth/LoginScreen.kt | 1 + .../goodroad/ui/auth/RecoverPasswordScreen.kt | 1 + .../goodroad/ui/auth/RegisterScreen.kt | 9 +- .../validation/UserValidators.kt} | 2 +- .../goodroad/ui/user/UserEditScreen.kt | 125 +++++++++++++++--- .../com/example/goodroad/ui/user/UserNav.kt | 2 +- .../goodroad/ui/viewmodel/AuthViewModel.kt | 4 +- .../goodroad/ui/viewmodel/UserViewModel.kt | 8 +- 15 files changed, 138 insertions(+), 50 deletions(-) rename app/src/main/java/com/example/goodroad/data/{network => auth}/AuthApi.kt (90%) rename app/src/main/java/com/example/goodroad/data/{network => auth}/AuthModels.kt (94%) rename app/src/main/java/com/example/goodroad/data/{repository => auth}/AuthRepository.kt (74%) rename app/src/main/java/com/example/goodroad/data/{network => user}/UserApi.kt (70%) rename app/src/main/java/com/example/goodroad/{ui/user/UserApi.kt => data/user/UserModels.kt} (91%) rename app/src/main/java/com/example/goodroad/data/{repository => user}/UserRepository.kt (78%) rename app/src/main/java/com/example/goodroad/ui/{auth/AuthValidators.kt => common/validation/UserValidators.kt} (96%) diff --git a/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt b/app/src/main/java/com/example/goodroad/data/auth/AuthApi.kt similarity index 90% rename from app/src/main/java/com/example/goodroad/data/network/AuthApi.kt rename to app/src/main/java/com/example/goodroad/data/auth/AuthApi.kt index a1d4189..db43c8b 100644 --- a/app/src/main/java/com/example/goodroad/data/network/AuthApi.kt +++ b/app/src/main/java/com/example/goodroad/data/auth/AuthApi.kt @@ -1,4 +1,4 @@ -package com.example.goodroad.data.network +package com.example.goodroad.data.auth import retrofit2.Response import retrofit2.http.Body diff --git a/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt b/app/src/main/java/com/example/goodroad/data/auth/AuthModels.kt similarity index 94% rename from app/src/main/java/com/example/goodroad/data/network/AuthModels.kt rename to app/src/main/java/com/example/goodroad/data/auth/AuthModels.kt index 0bce938..81a7f02 100644 --- a/app/src/main/java/com/example/goodroad/data/network/AuthModels.kt +++ b/app/src/main/java/com/example/goodroad/data/auth/AuthModels.kt @@ -1,4 +1,4 @@ -package com.example.goodroad.data.network +package com.example.goodroad.data.auth data class LoginReq( val phone: String, diff --git a/app/src/main/java/com/example/goodroad/data/repository/AuthRepository.kt b/app/src/main/java/com/example/goodroad/data/auth/AuthRepository.kt similarity index 74% rename from app/src/main/java/com/example/goodroad/data/repository/AuthRepository.kt rename to app/src/main/java/com/example/goodroad/data/auth/AuthRepository.kt index b1e2b29..74aa482 100644 --- a/app/src/main/java/com/example/goodroad/data/repository/AuthRepository.kt +++ b/app/src/main/java/com/example/goodroad/data/auth/AuthRepository.kt @@ -1,11 +1,6 @@ -package com.example.goodroad.data.repository +package com.example.goodroad.data.auth import com.example.goodroad.data.network.ApiClient -import com.example.goodroad.data.network.AuthResp -import com.example.goodroad.data.network.LoginReq -import com.example.goodroad.data.network.RecoverPasswordReq -import com.example.goodroad.data.network.RegisterReq -import retrofit2.Response class AuthRepository { private val api = ApiClient.authApi diff --git a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt index 71d8b68..eeb7f76 100644 --- a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt +++ b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt @@ -1,6 +1,8 @@ package com.example.goodroad.data.network import com.example.goodroad.BuildConfig +import com.example.goodroad.data.auth.AuthApi +import com.example.goodroad.data.user.UserApi import okhttp3.Credentials import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor diff --git a/app/src/main/java/com/example/goodroad/data/network/UserApi.kt b/app/src/main/java/com/example/goodroad/data/user/UserApi.kt similarity index 70% rename from app/src/main/java/com/example/goodroad/data/network/UserApi.kt rename to app/src/main/java/com/example/goodroad/data/user/UserApi.kt index 8106bcc..eb5c7c5 100644 --- a/app/src/main/java/com/example/goodroad/data/network/UserApi.kt +++ b/app/src/main/java/com/example/goodroad/data/user/UserApi.kt @@ -1,12 +1,12 @@ -package com.example.goodroad.data.network +package com.example.goodroad.data.user -import com.example.goodroad.ui.user.* -import retrofit2.http.Body -import retrofit2.http.DELETE +import com.example.goodroad.data.user.DeleteAccountReq +import com.example.goodroad.data.user.SettingsView +import com.example.goodroad.data.user.UpdateUserReq import retrofit2.Response +import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.HTTP -import retrofit2.http.POST import retrofit2.http.PUT interface UserApi { diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt b/app/src/main/java/com/example/goodroad/data/user/UserModels.kt similarity index 91% rename from app/src/main/java/com/example/goodroad/ui/user/UserApi.kt rename to app/src/main/java/com/example/goodroad/data/user/UserModels.kt index a8c9409..5c96c2f 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserApi.kt +++ b/app/src/main/java/com/example/goodroad/data/user/UserModels.kt @@ -1,4 +1,7 @@ -package com.example.goodroad.ui.user +package com.example.goodroad.data.user + +class UserModels { +} data class UserDto( val id: String, diff --git a/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt b/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt similarity index 78% rename from app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt rename to app/src/main/java/com/example/goodroad/data/user/UserRepository.kt index d14cc9b..98b6467 100644 --- a/app/src/main/java/com/example/goodroad/data/repository/UserRepository.kt +++ b/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt @@ -1,7 +1,9 @@ -package com.example.goodroad.data.repository +package com.example.goodroad.data.user -import com.example.goodroad.data.network.UserApi -import com.example.goodroad.ui.user.* +import com.example.goodroad.data.user.UserApi +import com.example.goodroad.data.user.DeleteAccountReq +import com.example.goodroad.data.user.SettingsView +import com.example.goodroad.data.user.UpdateUserReq class UserRepository(private val api: UserApi) { diff --git a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt index 97dc753..0ce8339 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.common.validation.* import androidx.lifecycle.viewmodel.compose.viewModel import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.livedata.observeAsState diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt index 2c2c8f7..db82932 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.common.validation.* import androidx.compose.material.icons.filled.Person import androidx.compose.runtime.livedata.observeAsState import androidx.lifecycle.viewmodel.compose.viewModel diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt index be4197e..24a3958 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt @@ -1,27 +1,22 @@ package com.example.goodroad.ui.auth -import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.theme.* import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.common.validation.* import androidx.compose.material.icons.filled.Person import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.livedata.observeAsState import androidx.lifecycle.viewmodel.compose.viewModel import com.example.goodroad.BuildConfig -import com.example.goodroad.data.network.ApiClient -import com.example.goodroad.data.network.RegisterReq import com.example.goodroad.ui.viewmodel.AuthViewModel -import kotlinx.coroutines.launch -import retrofit2.HttpException -import java.io.IOException @Composable fun RegisterScreen( diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthValidators.kt b/app/src/main/java/com/example/goodroad/ui/common/validation/UserValidators.kt similarity index 96% rename from app/src/main/java/com/example/goodroad/ui/auth/AuthValidators.kt rename to app/src/main/java/com/example/goodroad/ui/common/validation/UserValidators.kt index 5df307a..79f0f7f 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthValidators.kt +++ b/app/src/main/java/com/example/goodroad/ui/common/validation/UserValidators.kt @@ -1,4 +1,4 @@ -package com.example.goodroad.ui.auth +package com.example.goodroad.ui.common.validation private val cyrillicInputRegex = Regex("^[\\p{IsCyrillic} -]*$") private val digitsInputRegex = Regex("^\\d*$") diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt index ab48220..df03289 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -7,13 +7,15 @@ import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.auth.AuthButton -import com.example.goodroad.ui.auth.PlainField -import com.example.goodroad.ui.theme.GrayButton -import com.example.goodroad.ui.theme.TextPrimary -import com.example.goodroad.ui.theme.WhiteSoft +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.Photo +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Phone +import com.example.goodroad.ui.auth.* +import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.UserViewModel - +import com.example.goodroad.ui.common.validation.* @Composable fun UserEditScreen( userViewModel: UserViewModel, @@ -24,11 +26,17 @@ fun UserEditScreen( var firstName by remember { mutableStateOf(user.firstName ?: "") } var lastName by remember { mutableStateOf(user.lastName ?: "") } var photoUrl by remember { mutableStateOf(user.photoUrl ?: "") } - var phone by remember { mutableStateOf(user.phone ?: "") } + var phone by remember { mutableStateOf(user.phone?.removePrefix("+") ?: "") } var oldPassword by remember { mutableStateOf("") } var newPassword by remember { mutableStateOf("") } + var firstNameWarning by remember { mutableStateOf(null) } + var lastNameWarning by remember { mutableStateOf(null) } + var phoneWarning by remember { mutableStateOf(null) } + var errorText by remember { mutableStateOf(null) } + val errorMessage by remember { derivedStateOf { userViewModel.errorMessage.value } } + val finalError = errorMessage ?: errorText Column( modifier = Modifier @@ -45,34 +53,115 @@ fun UserEditScreen( ) Spacer(Modifier.height(12.dp)) - PlainField(value = firstName, onValueChange = { firstName = it }, label = "Имя") + PlainField( + value = firstName, + onValueChange = { value -> + when { + !isAllowedCyrillicInput(value) -> firstNameWarning = CYRILLIC_WARNING + value != firstName -> { + firstName = value + firstNameWarning = null + } + } + }, + label = "Имя", + icon = { Icon(imageVector = Icons.Default.Person, contentDescription = null, tint = UrbanBrown) }, + warning = firstNameWarning + ) + Spacer(Modifier.height(12.dp)) - PlainField(value = lastName, onValueChange = { lastName = it }, label = "Фамилия") + PlainField( + value = lastName, + onValueChange = { value -> + when { + !isAllowedCyrillicInput(value) -> lastNameWarning = CYRILLIC_WARNING + value != lastName -> { + lastName = value + lastNameWarning = null + } + } + }, + label = "Фамилия", + icon = { Icon(imageVector = Icons.Default.Person, contentDescription = null, tint = UrbanBrown) }, + warning = lastNameWarning + ) + Spacer(Modifier.height(12.dp)) - PlainField(value = photoUrl, onValueChange = { photoUrl = it }, label = "URL фото") + PlainField( + value = photoUrl, + onValueChange = { photoUrl = it }, + label = "URL фото", + icon = { Icon(imageVector = Icons.Default.Photo, contentDescription = null, tint = UrbanBrown) } + ) + Spacer(Modifier.height(12.dp)) - PlainField(value = phone, onValueChange = { phone = it }, label = "Телефон") + PhoneField( + value = phone, + onValueChange = { value -> + phone = value + phoneWarning = when { + !isAllowedDigitsInput(value) -> PHONE_CHARS_WARNING + value.length > 11 -> PHONE_FORMAT_WARNING + value.isNotEmpty() && value.first() !in listOf('7', '8') -> PHONE_FORMAT_WARNING + else -> null + } + }, + label = "Телефон", + warning = phoneWarning + ) + Spacer(Modifier.height(12.dp)) - PlainField(value = oldPassword, onValueChange = { oldPassword = it }, label = "Старый пароль") + PasswordField( + value = oldPassword, + onValueChange = { oldPassword = it }, + label = "Старый пароль" + ) + Spacer(Modifier.height(12.dp)) - PlainField(value = newPassword, onValueChange = { newPassword = it }, label = "Новый пароль") + PasswordField( + value = newPassword, + onValueChange = { newPassword = it }, + label = "Новый пароль" + ) Spacer(Modifier.height(20.dp)) AuthButton(text = "Сохранить") { + val firstNameNormalized = normalizeRequiredCyrillic(firstName) + if (firstNameNormalized == null) { + firstNameWarning = CYRILLIC_WARNING + errorText = "Имя должно содержать только кириллицу" + return@AuthButton + } + + val lastNameNormalized = normalizeRequiredCyrillic(lastName) + if (lastNameNormalized == null) { + lastNameWarning = CYRILLIC_WARNING + errorText = "Фамилия должна содержать только кириллицу" + return@AuthButton + } + + val phoneDigits = phone.takeIf { it.isNotBlank() }?.let { normalizeRequiredRussianPhone(it) } + if (phone.isNotBlank() && phoneDigits == null) { + phoneWarning = PHONE_FORMAT_WARNING + errorText = "Некорректный телефон" + return@AuthButton + } + val oldPass = oldPassword.takeIf { it.isNotBlank() } val newPass = newPassword.takeIf { it.isNotBlank() } userViewModel.updateUser( - firstName = firstName, - lastName = lastName, + firstName = firstNameNormalized, + lastName = lastNameNormalized, photoUrl = photoUrl.ifBlank { null }, - phone = phone.ifBlank { null }, + phone = phoneDigits?.let { formatPhoneForRequest(it) }, oldPassword = oldPass, newPassword = newPass ) oldPassword = "" newPassword = "" + errorText = null } Spacer(Modifier.height(12.dp)) @@ -86,10 +175,10 @@ fun UserEditScreen( } } - if (!errorMessage.isNullOrBlank()) { + if (!finalError.isNullOrBlank()) { Spacer(Modifier.height(12.dp)) Text( - text = errorMessage ?: "", + text = finalError, color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodyMedium ) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt index 8727972..6e2fbd6 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt @@ -3,7 +3,7 @@ package com.example.goodroad.ui.user import androidx.compose.runtime.* import androidx.lifecycle.viewmodel.compose.viewModel import com.example.goodroad.data.network.ApiClient -import com.example.goodroad.data.repository.UserRepository +import com.example.goodroad.data.user.UserRepository import com.example.goodroad.ui.viewmodel.UserViewModel import androidx.lifecycle.ViewModelProvider import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt index 63c8be3..15eeed2 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt @@ -4,9 +4,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import com.example.goodroad.data.network.AuthResp +import com.example.goodroad.data.auth.AuthResp import com.example.goodroad.data.network.ApiClient -import com.example.goodroad.data.repository.AuthRepository +import com.example.goodroad.data.auth.AuthRepository import kotlinx.coroutines.launch class AuthViewModel : ViewModel() { diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt index 6c2d18b..cf09781 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -3,10 +3,10 @@ package com.example.goodroad.ui.viewmodel import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.example.goodroad.data.repository.UserRepository -import com.example.goodroad.ui.user.DeleteAccountReq -import com.example.goodroad.ui.user.SettingsView -import com.example.goodroad.ui.user.UpdateUserReq +import com.example.goodroad.data.user.UserRepository +import com.example.goodroad.data.user.DeleteAccountReq +import com.example.goodroad.data.user.SettingsView +import com.example.goodroad.data.user.UpdateUserReq import kotlinx.coroutines.launch class UserViewModel(private val repository: UserRepository) : ViewModel() { From 8fe96179b8df4c0c16f5fc51e5bfb7fdb688ff56 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Fri, 10 Apr 2026 00:24:24 +0300 Subject: [PATCH 18/65] feat: displaying user-friendly errors --- .../java/com/example/goodroad/MainActivity.kt | 2 +- .../goodroad/data/auth/AuthRepository.kt | 45 ++++++++++++--- .../goodroad/data/user/UserRepository.kt | 11 ++-- .../com/example/goodroad/ui/auth/AuthApp.kt | 2 +- .../goodroad/ui/auth/RoleStubScreen.kt | 3 +- .../goodroad/ui/user/UserComponents.kt | 3 +- .../com/example/goodroad/ui/user/UserDecor.kt | 5 +- .../goodroad/ui/user/UserEditScreen.kt | 57 ++++++++++++++----- .../goodroad/ui/user/UserProfileScreen.kt | 3 +- .../goodroad/ui/viewmodel/AuthViewModel.kt | 49 ++++++++++++++-- .../goodroad/ui/viewmodel/UserViewModel.kt | 36 ++++++++++-- 11 files changed, 166 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/MainActivity.kt b/app/src/main/java/com/example/goodroad/MainActivity.kt index 5231822..1f6f878 100644 --- a/app/src/main/java/com/example/goodroad/MainActivity.kt +++ b/app/src/main/java/com/example/goodroad/MainActivity.kt @@ -4,7 +4,7 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import com.example.goodroad.ui.auth.* -import com.example.goodroad.ui.theme.GoodRoadTheme +import com.example.goodroad.ui.theme.* class MainActivity : ComponentActivity() { diff --git a/app/src/main/java/com/example/goodroad/data/auth/AuthRepository.kt b/app/src/main/java/com/example/goodroad/data/auth/AuthRepository.kt index 74aa482..7497dff 100644 --- a/app/src/main/java/com/example/goodroad/data/auth/AuthRepository.kt +++ b/app/src/main/java/com/example/goodroad/data/auth/AuthRepository.kt @@ -1,20 +1,49 @@ package com.example.goodroad.data.auth import com.example.goodroad.data.network.ApiClient +import retrofit2.HttpException +import java.io.IOException class AuthRepository { + private val api = ApiClient.authApi - suspend fun loginUser(phone: String, password: String): AuthResp = - api.login(LoginReq(phone, password)) + suspend fun loginUser(phone: String, password: String): AuthResp { + return try { + api.login(LoginReq(phone, password)) + } catch (e: HttpException) { + throw e + } catch (e: IOException) { + throw IOException() + } + } + + suspend fun registerUser( + firstName: String, + lastName: String, + phone: String, + password: String + ): AuthResp { + return try { + api.register(RegisterReq(firstName, lastName, phone, password)) + } catch (e: HttpException) { + throw e + } catch (e: IOException) { + throw IOException() + } + } - suspend fun registerUser(firstName: String, lastName: String, phone: String, password: String): AuthResp = - api.register(RegisterReq(firstName, lastName, phone, password)) + suspend fun recoverPassword( + phone: String, + firstName: String, + lastName: String, + newPassword: String + ): Boolean { + val response = api.recoverPassword( + RecoverPasswordReq(phone, firstName, lastName, newPassword) + ) - suspend fun recoverPassword(phone: String, firstName: String, lastName: String, newPassword: String): Boolean { - val req = RecoverPasswordReq(phone, firstName, lastName, newPassword) - val response = api.recoverPassword(req) if (response.isSuccessful) return true - else throw Exception("Ошибка восстановления пароля: ${response.code()}") + throw HttpException(response) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt b/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt index 98b6467..eafcf26 100644 --- a/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt +++ b/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt @@ -1,26 +1,23 @@ package com.example.goodroad.data.user -import com.example.goodroad.data.user.UserApi -import com.example.goodroad.data.user.DeleteAccountReq -import com.example.goodroad.data.user.SettingsView -import com.example.goodroad.data.user.UpdateUserReq +import retrofit2.HttpException class UserRepository(private val api: UserApi) { suspend fun getCurrentUser(): SettingsView? { val response = api.getCurrentUser() if (response.isSuccessful) return response.body() - throw Exception("Ошибка загрузки пользователя: ${response.code()} ${response.message()}") + throw HttpException(response) } suspend fun updateCurrentUser(req: UpdateUserReq): SettingsView? { val response = api.updateCurrentUser(req) if (response.isSuccessful) return response.body() - throw Exception("Ошибка обновления пользователя: ${response.code()} ${response.message()}") + throw HttpException(response) } suspend fun deleteCurrentUser(req: DeleteAccountReq) { val response = api.deleteCurrentUser(req) - if (!response.isSuccessful) throw Exception("Ошибка удаления аккаунта: ${response.code()} ${response.message()}") + if (!response.isSuccessful) throw HttpException(response) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt index 9c0d404..b4e8de8 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt @@ -9,7 +9,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import com.example.goodroad.ui.theme.BackgroundLight +import com.example.goodroad.ui.theme.* @Composable fun AuthApp( diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt index 839a9bb..7ef7af5 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt @@ -8,8 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.theme.BackgroundLight -import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.theme.* @Composable fun RoleStubScreen( diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt b/app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt index d7d8330..f3138c2 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt @@ -1,5 +1,4 @@ package com.example.goodroad.ui.user -import com.example.goodroad.ui.theme.TextPrimary import androidx.compose.foundation.layout.* import androidx.compose.material3.MaterialTheme @@ -7,7 +6,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.theme.UrbanBrown +import com.example.goodroad.ui.theme.* @Composable fun UserInfoBlock(label: String, value: String?) { diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt b/app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt index 18b6c34..fd3eef3 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt @@ -19,10 +19,7 @@ import androidx.compose.ui.graphics.StrokeJoin import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.theme.BackgroundLight -import com.example.goodroad.ui.theme.SurfaceWarm -import com.example.goodroad.ui.theme.TextPrimary -import com.example.goodroad.ui.theme.UrbanBrown +import com.example.goodroad.ui.theme.* @Composable fun UserDecor() { diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt index df03289..f9d35c3 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -10,12 +10,11 @@ import androidx.compose.ui.unit.dp import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Photo -import androidx.compose.material.icons.filled.Lock -import androidx.compose.material.icons.filled.Phone import com.example.goodroad.ui.auth.* import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.UserViewModel import com.example.goodroad.ui.common.validation.* + @Composable fun UserEditScreen( userViewModel: UserViewModel, @@ -53,6 +52,7 @@ fun UserEditScreen( ) Spacer(Modifier.height(12.dp)) + PlainField( value = firstName, onValueChange = { value -> @@ -65,11 +65,18 @@ fun UserEditScreen( } }, label = "Имя", - icon = { Icon(imageVector = Icons.Default.Person, contentDescription = null, tint = UrbanBrown) }, + icon = { + Icon( + imageVector = Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + }, warning = firstNameWarning ) Spacer(Modifier.height(12.dp)) + PlainField( value = lastName, onValueChange = { value -> @@ -82,19 +89,33 @@ fun UserEditScreen( } }, label = "Фамилия", - icon = { Icon(imageVector = Icons.Default.Person, contentDescription = null, tint = UrbanBrown) }, + icon = { + Icon( + imageVector = Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + }, warning = lastNameWarning ) Spacer(Modifier.height(12.dp)) + PlainField( value = photoUrl, onValueChange = { photoUrl = it }, label = "URL фото", - icon = { Icon(imageVector = Icons.Default.Photo, contentDescription = null, tint = UrbanBrown) } + icon = { + Icon( + imageVector = Icons.Default.Photo, + contentDescription = null, + tint = UrbanBrown + ) + } ) Spacer(Modifier.height(12.dp)) + PhoneField( value = phone, onValueChange = { value -> @@ -111,6 +132,7 @@ fun UserEditScreen( ) Spacer(Modifier.height(12.dp)) + PasswordField( value = oldPassword, onValueChange = { oldPassword = it }, @@ -118,13 +140,24 @@ fun UserEditScreen( ) Spacer(Modifier.height(12.dp)) + PasswordField( value = newPassword, onValueChange = { newPassword = it }, label = "Новый пароль" ) + if (!finalError.isNullOrBlank()) { + Spacer(Modifier.height(12.dp)) + Text( + text = finalError, + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.bodyMedium + ) + } + Spacer(Modifier.height(20.dp)) + AuthButton(text = "Сохранить") { val firstNameNormalized = normalizeRequiredCyrillic(firstName) if (firstNameNormalized == null) { @@ -140,7 +173,9 @@ fun UserEditScreen( return@AuthButton } - val phoneDigits = phone.takeIf { it.isNotBlank() }?.let { normalizeRequiredRussianPhone(it) } + val phoneDigits = + phone.takeIf { it.isNotBlank() }?.let { normalizeRequiredRussianPhone(it) } + if (phone.isNotBlank() && phoneDigits == null) { phoneWarning = PHONE_FORMAT_WARNING errorText = "Некорректный телефон" @@ -165,6 +200,7 @@ fun UserEditScreen( } Spacer(Modifier.height(12.dp)) + AuthButton( text = "Выйти", backgroundColor = GrayButton, @@ -174,14 +210,5 @@ fun UserEditScreen( onBack() } } - - if (!finalError.isNullOrBlank()) { - Spacer(Modifier.height(12.dp)) - Text( - text = finalError, - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.bodyMedium - ) - } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt index 1a2df36..a8032fa 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt @@ -8,8 +8,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.example.goodroad.ui.auth.AuthButton -import com.example.goodroad.ui.theme.BackgroundLight -import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.UserViewModel @Composable diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt index 15eeed2..f2971a1 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt @@ -5,9 +5,11 @@ import androidx.lifecycle.viewModelScope import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.example.goodroad.data.auth.AuthResp -import com.example.goodroad.data.network.ApiClient import com.example.goodroad.data.auth.AuthRepository +import com.example.goodroad.data.network.ApiClient import kotlinx.coroutines.launch +import retrofit2.HttpException +import java.io.IOException class AuthViewModel : ViewModel() { @@ -29,12 +31,13 @@ class AuthViewModel : ViewModel() { viewModelScope.launch { _isLoading.value = true _error.value = null + try { val response = authRepository.loginUser(phone, password) ApiClient.setCredentials(phone, password) _loginResult.value = response } catch (e: Exception) { - _error.value = e.message ?: "Ошибка логина" + _error.value = mapAuthError(e, AuthAction.LOGIN) } finally { _isLoading.value = false } @@ -45,12 +48,13 @@ class AuthViewModel : ViewModel() { viewModelScope.launch { _isLoading.value = true _error.value = null + try { val response = authRepository.registerUser(firstName, lastName, phone, password) ApiClient.setCredentials(phone, password) _loginResult.value = response } catch (e: Exception) { - _error.value = e.message ?: "Ошибка регистрации" + _error.value = mapAuthError(e, AuthAction.REGISTER) } finally { _isLoading.value = false } @@ -61,14 +65,51 @@ class AuthViewModel : ViewModel() { viewModelScope.launch { _isLoading.value = true _error.value = null + try { val success = authRepository.recoverPassword(phone, firstName, lastName, newPassword) _recoverResult.value = success } catch (e: Exception) { - _error.value = e.message ?: "Ошибка восстановления пароля" + _error.value = mapAuthError(e, AuthAction.RECOVER) } finally { _isLoading.value = false } } } + + private fun mapAuthError(e: Exception, action: AuthAction): String { + return when (e) { + is HttpException -> when (e.code()) { + 400 -> when (action) { + AuthAction.RECOVER -> "Неверные данные для восстановления" + else -> "Проверьте введённые данные" + } + + 401 -> "Неверный номер телефона или пароль" + 403 -> when (action) { + AuthAction.REGISTER -> "Регистрация запрещена" + else -> "Доступ запрещён" + } + + 404 -> when (action) { + AuthAction.RECOVER -> "Пользователь не найден" + else -> "Пользователь не найден" + } + + 409 -> "Пользователь с таким номером уже существует" + 500 -> "Сервер временно недоступен" + else -> "Ошибка операции" + } + + is IOException -> "Проверьте подключение к интернету" + + else -> "Неизвестная ошибка" + } + } + + private enum class AuthAction { + LOGIN, + REGISTER, + RECOVER + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt index cf09781..a677d96 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -3,29 +3,34 @@ package com.example.goodroad.ui.viewmodel import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.example.goodroad.data.user.UserRepository import com.example.goodroad.data.user.DeleteAccountReq import com.example.goodroad.data.user.SettingsView import com.example.goodroad.data.user.UpdateUserReq +import com.example.goodroad.data.user.UserRepository import kotlinx.coroutines.launch +import retrofit2.HttpException +import java.io.IOException class UserViewModel(private val repository: UserRepository) : ViewModel() { var user = mutableStateOf(null) var isLoading = mutableStateOf(false) var errorMessage = mutableStateOf(null) + var isDeleted = false private set fun getCurrentUser() { if (isDeleted) return + viewModelScope.launch { isLoading.value = true errorMessage.value = null + try { user.value = repository.getCurrentUser() } catch (e: Exception) { - errorMessage.value = e.message ?: "Неизвестная ошибка" + errorMessage.value = mapUserError(e) } finally { isLoading.value = false } @@ -41,7 +46,9 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { newPassword: String? = null ) { viewModelScope.launch { + isLoading.value = true errorMessage.value = null + try { val req = UpdateUserReq( firstName = firstName, @@ -51,9 +58,13 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { oldPassword = oldPassword, newPassword = newPassword ) + user.value = repository.updateCurrentUser(req) + } catch (e: Exception) { - errorMessage.value = e.message + errorMessage.value = mapUserError(e) + } finally { + isLoading.value = false } } } @@ -62,13 +73,14 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { viewModelScope.launch { isLoading.value = true errorMessage.value = null + try { repository.deleteCurrentUser(DeleteAccountReq(password)) user.value = null isDeleted = true onSuccess() } catch (e: Exception) { - errorMessage.value = e.message + errorMessage.value = mapUserError(e) } finally { isLoading.value = false } @@ -80,4 +92,20 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { isDeleted = false onSuccess() } + + private fun mapUserError(e: Exception): String { + return when (e) { + is HttpException -> when (e.code()) { + 400 -> "Некорректные данные профиля" + 401 -> "Вы не авторизованы" + 403 -> "Нет прав для выполнения действия" + 404 -> "Пользователь не найден" + 409 -> "Телефон уже используется другим пользователем" + 500 -> "Сервер временно недоступен" + else -> "Ошибка операции" + } + is IOException -> "Проверьте подключение к интернету" + else -> e.message ?: "Неизвестная ошибка" + } + } } \ No newline at end of file From 2906824ca7736390d963e88026b1525983bcca55 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sat, 11 Apr 2026 01:34:22 +0300 Subject: [PATCH 19/65] fix(auth-ui): stabilize auth screens and request states --- .../com/example/goodroad/ui/auth/AuthApp.kt | 8 +-- .../goodroad/ui/auth/AuthComponents.kt | 31 ++++++--- .../example/goodroad/ui/auth/LoginScreen.kt | 36 ++++++---- .../goodroad/ui/auth/RecoverPasswordScreen.kt | 66 +++++++++---------- .../goodroad/ui/auth/RegisterScreen.kt | 34 ++++++---- .../goodroad/ui/viewmodel/AuthViewModel.kt | 21 ++---- 6 files changed, 110 insertions(+), 86 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt index b4e8de8..11b5fb3 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt @@ -1,5 +1,4 @@ package com.example.goodroad.ui.auth -import com.example.goodroad.ui.user.UserNav import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Surface @@ -9,7 +8,8 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.theme.BackgroundLight +import com.example.goodroad.ui.user.UserNav @Composable fun AuthApp( @@ -25,8 +25,8 @@ fun AuthApp( ) { composable(LOGIN_ROUTE) { LoginScreen( - onLoginSuccess = { - navController.navigate(USER_HOME_ROUTE) { + onLoginSuccess = { role -> + navController.navigate(homeRoute(role)) { popUpTo(LOGIN_ROUTE) { inclusive = true } launchSingleTop = true } diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt index 3d1f29f..432f910 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt @@ -1,14 +1,29 @@ package com.example.goodroad.ui.auth -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import com.example.goodroad.ui.theme.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.graphics.Color +import com.example.goodroad.ui.theme.AlertRed +import com.example.goodroad.ui.theme.SafeGreen +import com.example.goodroad.ui.theme.SafeRoute +import com.example.goodroad.ui.theme.BackgroundLight +import com.example.goodroad.ui.theme.TextSecondary +import com.example.goodroad.ui.theme.UrbanBrown @Composable fun AuthButton( @@ -26,10 +41,10 @@ fun AuthButton( .height(54.dp), shape = RoundedCornerShape(16.dp), colors = ButtonDefaults.buttonColors( - containerColor = SafeRoute, - contentColor = BackgroundLight, - disabledContainerColor = SafeRoute.copy(alpha = 0.6f), - disabledContentColor = BackgroundLight + containerColor = backgroundColor, + contentColor = contentColor, + disabledContainerColor = backgroundColor.copy(alpha = 0.6f), + disabledContentColor = contentColor ) ) { Text( diff --git a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt index 0ce8339..a6dd622 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt @@ -1,21 +1,35 @@ package com.example.goodroad.ui.auth -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.common.validation.* import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.livedata.observeAsState -import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.common.validation.PHONE_FORMAT_WARNING +import com.example.goodroad.ui.common.validation.formatPhoneForRequest +import com.example.goodroad.ui.common.validation.isAllowedDigitsInput +import com.example.goodroad.ui.common.validation.isValidRussianPhoneDigits +import com.example.goodroad.ui.common.validation.normalizeRequiredRussianPhone +import com.example.goodroad.ui.theme.UrbanBrown import com.example.goodroad.ui.viewmodel.AuthViewModel @Composable fun LoginScreen( - onLoginSuccess: () -> Unit, + onLoginSuccess: (String) -> Unit, onSignUp: () -> Unit, onForgotPassword: () -> Unit ) { @@ -30,8 +44,8 @@ fun LoginScreen( val loading by viewModel.isLoading.observeAsState(initial = false) LaunchedEffect(loginResult) { - if (loginResult?.user != null) { - onLoginSuccess() + loginResult?.user?.role?.let { role -> + onLoginSuccess(role) } } @@ -66,7 +80,7 @@ fun LoginScreen( value = phone, onValueChange = { value -> when { - !isAllowedDigitsInput(value) -> phoneWarning = PHONE_CHARS_WARNING + !isAllowedDigitsInput(value) -> phoneWarning = PHONE_FORMAT_WARNING value.length > 11 -> phoneWarning = PHONE_FORMAT_WARNING value.isNotEmpty() && value.first() !in listOf('7', '8') -> phoneWarning = PHONE_FORMAT_WARNING else -> { diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt index db82932..d329135 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt @@ -1,17 +1,28 @@ package com.example.goodroad.ui.auth -import android.util.Log import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.common.validation.* +import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person -import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.material3.Icon import androidx.lifecycle.viewmodel.compose.viewModel -import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.common.validation.CYRILLIC_WARNING +import com.example.goodroad.ui.common.validation.PHONE_CHARS_WARNING +import com.example.goodroad.ui.common.validation.PHONE_FORMAT_WARNING +import com.example.goodroad.ui.common.validation.formatPhoneForRequest +import com.example.goodroad.ui.common.validation.isAllowedCyrillicInput +import com.example.goodroad.ui.common.validation.isAllowedDigitsInput +import com.example.goodroad.ui.common.validation.normalizeRequiredCyrillic +import com.example.goodroad.ui.common.validation.normalizeRequiredRussianPhone +import com.example.goodroad.ui.theme.UrbanBrown import com.example.goodroad.ui.viewmodel.AuthViewModel @Composable @@ -29,25 +40,9 @@ fun RecoverPasswordScreen( var errorText by rememberSaveable { mutableStateOf(null) } val viewModel: AuthViewModel = viewModel() - val recoverResult by viewModel.recoverResult.observeAsState() val error by viewModel.error.observeAsState() - LaunchedEffect(recoverResult) { - if (recoverResult == true) { - errorText = null - firstName = "" - lastName = "" - phone = "" - newPassword = "" - confirmPassword = "" - firstNameWarning = null - lastNameWarning = null - phoneWarning = null - errorText = "Пароль успешно изменен. Теперь можно войти." - } - } - AuthScreenFrame( title = "Смена пароля", subtitle = "Для восстановления введите имя, фамилию, номер телефона и новый пароль.", @@ -82,8 +77,7 @@ fun RecoverPasswordScreen( return@AuthButton } - Log.d("RecoverPassword", "phone=$phoneDigits, first=$firstNameNormalized, last=$lastNameNormalized") - + errorText = null viewModel.recoverPassword( phone = formatPhoneForRequest(phoneDigits), firstName = firstNameNormalized, @@ -112,11 +106,13 @@ fun RecoverPasswordScreen( } }, label = "Имя", - icon = { androidx.compose.material3.Icon( - imageVector = androidx.compose.material.icons.Icons.Default.Person, - contentDescription = null, - tint = UrbanBrown - ) }, + icon = { + Icon( + imageVector = Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + }, warning = firstNameWarning ) @@ -134,11 +130,13 @@ fun RecoverPasswordScreen( } }, label = "Фамилия", - icon = { androidx.compose.material3.Icon( - imageVector = androidx.compose.material.icons.Icons.Default.Person, - contentDescription = null, - tint = UrbanBrown - ) }, + icon = { + Icon( + imageVector = Icons.Default.Person, + contentDescription = null, + tint = UrbanBrown + ) + }, warning = lastNameWarning ) @@ -177,8 +175,6 @@ fun RecoverPasswordScreen( label = "Подтвердите пароль" ) - Spacer(Modifier.height(12.dp)) - AuthStatusText(text = error ?: errorText) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt index 24a3958..388406f 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt @@ -1,21 +1,30 @@ package com.example.goodroad.ui.auth -import com.example.goodroad.ui.theme.* -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Person +import androidx.compose.material3.Icon import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.common.validation.* -import androidx.compose.material.icons.filled.Person -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.livedata.observeAsState import androidx.lifecycle.viewmodel.compose.viewModel import com.example.goodroad.BuildConfig +import com.example.goodroad.ui.common.validation.CYRILLIC_WARNING +import com.example.goodroad.ui.common.validation.PHONE_CHARS_WARNING +import com.example.goodroad.ui.common.validation.PHONE_FORMAT_WARNING +import com.example.goodroad.ui.common.validation.formatPhoneForRequest +import com.example.goodroad.ui.common.validation.isAllowedCyrillicInput +import com.example.goodroad.ui.common.validation.isAllowedDigitsInput +import com.example.goodroad.ui.common.validation.normalizeRequiredCyrillic +import com.example.goodroad.ui.common.validation.normalizeRequiredRussianPhone +import com.example.goodroad.ui.theme.UrbanBrown import com.example.goodroad.ui.viewmodel.AuthViewModel @Composable @@ -32,10 +41,11 @@ fun RegisterScreen( var lastNameWarning by rememberSaveable { mutableStateOf(null) } var phoneWarning by rememberSaveable { mutableStateOf(null) } var errorText by rememberSaveable { mutableStateOf(null) } - var loading by rememberSaveable { mutableStateOf(false) } + val viewModel: AuthViewModel = viewModel() val registerResult by viewModel.loginResult.observeAsState() val error by viewModel.error.observeAsState() + val loading by viewModel.isLoading.observeAsState(initial = false) LaunchedEffect(registerResult) { registerResult?.user?.role?.let { role -> @@ -113,8 +123,8 @@ fun RegisterScreen( }, label = "Имя", icon = { - androidx.compose.material3.Icon( - imageVector = androidx.compose.material.icons.Icons.Default.Person, + Icon( + imageVector = Icons.Default.Person, contentDescription = null, tint = UrbanBrown ) @@ -139,8 +149,8 @@ fun RegisterScreen( }, label = "Фамилия", icon = { - androidx.compose.material3.Icon( - imageVector = androidx.compose.material.icons.Icons.Default.Person, + Icon( + imageVector = Icons.Default.Person, contentDescription = null, tint = UrbanBrown ) diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt index f2971a1..4804a17 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt @@ -1,11 +1,11 @@ package com.example.goodroad.ui.viewmodel -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import com.example.goodroad.data.auth.AuthResp +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.example.goodroad.data.auth.AuthRepository +import com.example.goodroad.data.auth.AuthResp import com.example.goodroad.data.network.ApiClient import kotlinx.coroutines.launch import retrofit2.HttpException @@ -62,19 +62,8 @@ class AuthViewModel : ViewModel() { } fun recoverPassword(phone: String, firstName: String, lastName: String, newPassword: String) { - viewModelScope.launch { - _isLoading.value = true - _error.value = null - - try { - val success = authRepository.recoverPassword(phone, firstName, lastName, newPassword) - _recoverResult.value = success - } catch (e: Exception) { - _error.value = mapAuthError(e, AuthAction.RECOVER) - } finally { - _isLoading.value = false - } - } + _recoverResult.value = null + _error.value = "Восстановление пароля пока не подключено на сервере" } private fun mapAuthError(e: Exception, action: AuthAction): String { From ba563acb1cb00d73b6593aba79c68eeffc5de4ec Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sat, 11 Apr 2026 01:36:53 +0300 Subject: [PATCH 20/65] fix(profile): align user screens with current backend contract --- .../com/example/goodroad/data/user/UserApi.kt | 11 ++- .../example/goodroad/data/user/UserModels.kt | 9 +-- .../goodroad/data/user/UserRepository.kt | 5 ++ .../goodroad/ui/user/UserEditScreen.kt | 76 +++++++++++++++---- .../com/example/goodroad/ui/user/UserNav.kt | 16 ++-- .../goodroad/ui/viewmodel/UserViewModel.kt | 43 +++++++++-- 6 files changed, 120 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/data/user/UserApi.kt b/app/src/main/java/com/example/goodroad/data/user/UserApi.kt index eb5c7c5..dda3345 100644 --- a/app/src/main/java/com/example/goodroad/data/user/UserApi.kt +++ b/app/src/main/java/com/example/goodroad/data/user/UserApi.kt @@ -1,13 +1,12 @@ package com.example.goodroad.data.user -import com.example.goodroad.data.user.DeleteAccountReq -import com.example.goodroad.data.user.SettingsView -import com.example.goodroad.data.user.UpdateUserReq import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.HTTP +import retrofit2.http.POST import retrofit2.http.PUT +import retrofit2.http.Query interface UserApi { @@ -17,6 +16,12 @@ interface UserApi { @PUT("/users") suspend fun updateCurrentUser(@Body req: UpdateUserReq): Response + @POST("/users") + suspend fun changePassword( + @Query("oldPassword") oldPassword: String, + @Query("newPassword") newPassword: String + ): Response + @HTTP(method = "DELETE", path = "/users", hasBody = true) suspend fun deleteCurrentUser(@Body req: DeleteAccountReq): Response } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/data/user/UserModels.kt b/app/src/main/java/com/example/goodroad/data/user/UserModels.kt index 5c96c2f..220a4ed 100644 --- a/app/src/main/java/com/example/goodroad/data/user/UserModels.kt +++ b/app/src/main/java/com/example/goodroad/data/user/UserModels.kt @@ -1,15 +1,11 @@ package com.example.goodroad.data.user -class UserModels { -} - data class UserDto( val id: String, val role: String, val firstName: String?, val lastName: String?, val photoUrl: String?, - val phone: String?, val active: Boolean ) @@ -17,9 +13,7 @@ data class UpdateUserReq( val firstName: String? = null, val lastName: String? = null, val photoUrl: String? = null, - val phone: String? = null, - val oldPassword: String? = null, - val newPassword: String? = null + val phone: String? = null ) data class SettingsView( @@ -28,7 +22,6 @@ data class SettingsView( val firstName: String?, val lastName: String?, val photoUrl: String?, - val phone: String?, val active: Boolean ) diff --git a/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt b/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt index eafcf26..8ae40fb 100644 --- a/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt +++ b/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt @@ -16,6 +16,11 @@ class UserRepository(private val api: UserApi) { throw HttpException(response) } + suspend fun changePassword(oldPassword: String, newPassword: String) { + val response = api.changePassword(oldPassword, newPassword) + if (!response.isSuccessful) throw HttpException(response) + } + suspend fun deleteCurrentUser(req: DeleteAccountReq) { val response = api.deleteCurrentUser(req) if (!response.isSuccessful) throw HttpException(response) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt index f9d35c3..cd057a9 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -1,31 +1,56 @@ package com.example.goodroad.ui.user -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.verticalScroll +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Photo -import com.example.goodroad.ui.auth.* -import com.example.goodroad.ui.theme.* +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.auth.AuthButton +import com.example.goodroad.ui.auth.PasswordField +import com.example.goodroad.ui.auth.PhoneField +import com.example.goodroad.ui.auth.PlainField +import com.example.goodroad.ui.common.validation.CYRILLIC_WARNING +import com.example.goodroad.ui.common.validation.PHONE_CHARS_WARNING +import com.example.goodroad.ui.common.validation.PHONE_FORMAT_WARNING +import com.example.goodroad.ui.common.validation.formatPhoneForRequest +import com.example.goodroad.ui.common.validation.isAllowedCyrillicInput +import com.example.goodroad.ui.common.validation.isAllowedDigitsInput +import com.example.goodroad.ui.common.validation.normalizeRequiredCyrillic +import com.example.goodroad.ui.common.validation.normalizeRequiredRussianPhone +import com.example.goodroad.ui.theme.GrayButton +import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.theme.UrbanBrown +import com.example.goodroad.ui.theme.WhiteSoft import com.example.goodroad.ui.viewmodel.UserViewModel -import com.example.goodroad.ui.common.validation.* @Composable fun UserEditScreen( userViewModel: UserViewModel, - onBack: () -> Unit + onBack: () -> Unit, + onLogout: () -> Unit ) { val user = userViewModel.user.value ?: return var firstName by remember { mutableStateOf(user.firstName ?: "") } var lastName by remember { mutableStateOf(user.lastName ?: "") } var photoUrl by remember { mutableStateOf(user.photoUrl ?: "") } - var phone by remember { mutableStateOf(user.phone?.removePrefix("+") ?: "") } + var phone by remember { mutableStateOf("") } var oldPassword by remember { mutableStateOf("") } var newPassword by remember { mutableStateOf("") } @@ -127,10 +152,17 @@ fun UserEditScreen( else -> null } }, - label = "Телефон", + label = "Новый телефон", warning = phoneWarning ) + Spacer(Modifier.height(4.dp)) + Text( + text = "Оставьте поле пустым, если номер менять не нужно.", + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown + ) + Spacer(Modifier.height(12.dp)) PasswordField( @@ -147,6 +179,13 @@ fun UserEditScreen( label = "Новый пароль" ) + Spacer(Modifier.height(4.dp)) + Text( + text = "Чтобы сменить пароль, заполните оба поля. Иначе оставьте их пустыми.", + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown + ) + if (!finalError.isNullOrBlank()) { Spacer(Modifier.height(12.dp)) Text( @@ -201,14 +240,21 @@ fun UserEditScreen( Spacer(Modifier.height(12.dp)) + AuthButton( + text = "Назад", + backgroundColor = GrayButton, + contentColor = WhiteSoft, + onClick = onBack + ) + + Spacer(Modifier.height(12.dp)) + AuthButton( text = "Выйти", backgroundColor = GrayButton, contentColor = WhiteSoft ) { - userViewModel.logout { - onBack() - } + userViewModel.logout(onLogout) } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt index 6e2fbd6..e1a40ec 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt @@ -1,12 +1,15 @@ package com.example.goodroad.ui.user -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import com.example.goodroad.data.network.ApiClient import com.example.goodroad.data.user.UserRepository import com.example.goodroad.ui.viewmodel.UserViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.compose.runtime.Composable @Composable fun UserNav(onLogout: () -> Unit) { @@ -35,14 +38,13 @@ fun UserNav(onLogout: () -> Unit) { "edit" -> UserEditScreen( userViewModel = userViewModel, - onBack = { screen = "profile" } + onBack = { screen = "profile" }, + onLogout = onLogout ) "delete" -> UserDeleteAccountScreen( viewModel = userViewModel, - onExit = { - screen = "profile" - } + onExit = onLogout ) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt index a677d96..8fbbf8a 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -3,6 +3,7 @@ package com.example.goodroad.ui.viewmodel import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.example.goodroad.data.network.ApiClient import com.example.goodroad.data.user.DeleteAccountReq import com.example.goodroad.data.user.SettingsView import com.example.goodroad.data.user.UpdateUserReq @@ -50,17 +51,41 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { errorMessage.value = null try { + val current = user.value + val phoneToUpdate = phone?.takeIf { it.isNotBlank() } + val hasPasswordChange = !oldPassword.isNullOrBlank() || !newPassword.isNullOrBlank() + + if (hasPasswordChange && (oldPassword.isNullOrBlank() || newPassword.isNullOrBlank())) { + throw IllegalArgumentException("Для смены пароля заполните оба поля") + } + val req = UpdateUserReq( - firstName = firstName, - lastName = lastName, - photoUrl = photoUrl, - phone = phone, - oldPassword = oldPassword, - newPassword = newPassword + firstName = firstName.takeIf { it != current?.firstName }, + lastName = lastName.takeIf { it != current?.lastName }, + photoUrl = photoUrl.takeIf { it != current?.photoUrl }, + phone = phoneToUpdate ) - user.value = repository.updateCurrentUser(req) + val hasProfileChanges = req.firstName != null || + req.lastName != null || + req.photoUrl != null || + req.phone != null + + if (!hasProfileChanges && !hasPasswordChange) { + throw IllegalArgumentException("Нет изменений для сохранения") + } + + if (hasProfileChanges) { + user.value = repository.updateCurrentUser(req) + if (req.phone != null) { + ApiClient.updateCredentials(phone = req.phone) + } + } + if (hasPasswordChange) { + repository.changePassword(oldPassword!!, newPassword!!) + ApiClient.updateCredentials(password = newPassword) + } } catch (e: Exception) { errorMessage.value = mapUserError(e) } finally { @@ -76,6 +101,7 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { try { repository.deleteCurrentUser(DeleteAccountReq(password)) + ApiClient.clearCredentials() user.value = null isDeleted = true onSuccess() @@ -88,13 +114,16 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { } fun logout(onSuccess: () -> Unit) { + ApiClient.clearCredentials() user.value = null isDeleted = false + errorMessage.value = null onSuccess() } private fun mapUserError(e: Exception): String { return when (e) { + is IllegalArgumentException -> e.message ?: "Некорректные данные" is HttpException -> when (e.code()) { 400 -> "Некорректные данные профиля" 401 -> "Вы не авторизованы" From 25400721220f72d36e73015228801144d861474e Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sat, 11 Apr 2026 01:39:05 +0300 Subject: [PATCH 21/65] fix(network): update api client auth handling --- .../com/example/goodroad/data/network/ApiClient.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt index eeb7f76..6456047 100644 --- a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt +++ b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt @@ -24,6 +24,20 @@ object ApiClient { userPassword = password } + fun updateCredentials(phone: String? = null, password: String? = null) { + if (!phone.isNullOrBlank()) { + userPhone = phone + } + if (!password.isNullOrBlank()) { + userPassword = password + } + } + + fun clearCredentials() { + userPhone = null + userPassword = null + } + private val client: OkHttpClient get() = OkHttpClient.Builder() .addInterceptor(logging) From 1e3b474c3a9111f66f0b5ef2bea94fbaa60fb34a Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sat, 11 Apr 2026 01:39:42 +0300 Subject: [PATCH 22/65] chore(gradle): add gradle wrapper jar --- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43764 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..1b33c55baabb587c669f562ae36f953de2481846 GIT binary patch literal 43764 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 Date: Sun, 12 Apr 2026 00:11:43 +0300 Subject: [PATCH 23/65] fix(auth): connect recover-password screen to backend --- .../com/example/goodroad/data/auth/AuthApi.kt | 7 ++-- .../goodroad/ui/auth/RecoverPasswordScreen.kt | 4 +- .../goodroad/ui/viewmodel/AuthViewModel.kt | 41 +++++++++++-------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/data/auth/AuthApi.kt b/app/src/main/java/com/example/goodroad/data/auth/AuthApi.kt index db43c8b..a5c88d8 100644 --- a/app/src/main/java/com/example/goodroad/data/auth/AuthApi.kt +++ b/app/src/main/java/com/example/goodroad/data/auth/AuthApi.kt @@ -1,8 +1,7 @@ package com.example.goodroad.data.auth -import retrofit2.Response -import retrofit2.http.Body -import retrofit2.http.POST +import retrofit2.* +import retrofit2.http.* interface AuthApi { @@ -13,5 +12,5 @@ interface AuthApi { suspend fun register(@Body req: RegisterReq): AuthResp @POST("/auth/recover-password") - suspend fun recoverPassword(@Body req: RecoverPasswordReq) : Response + suspend fun recoverPassword(@Body req: RecoverPasswordReq): Response } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt index d329135..4e7bdad 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt @@ -42,6 +42,7 @@ fun RecoverPasswordScreen( val viewModel: AuthViewModel = viewModel() val recoverResult by viewModel.recoverResult.observeAsState() val error by viewModel.error.observeAsState() + val successText = if (recoverResult == true) "Пароль успешно восстановлен" else null AuthScreenFrame( title = "Смена пароля", @@ -176,5 +177,6 @@ fun RecoverPasswordScreen( ) AuthStatusText(text = error ?: errorText) + AuthSuccessText(text = successText) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt index 4804a17..11ebc71 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt @@ -1,11 +1,7 @@ package com.example.goodroad.ui.viewmodel -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.example.goodroad.data.auth.AuthRepository -import com.example.goodroad.data.auth.AuthResp +import androidx.lifecycle.* +import com.example.goodroad.data.auth.* import com.example.goodroad.data.network.ApiClient import kotlinx.coroutines.launch import retrofit2.HttpException @@ -34,7 +30,7 @@ class AuthViewModel : ViewModel() { try { val response = authRepository.loginUser(phone, password) - ApiClient.setCredentials(phone, password) + ApiClient.updateCredentials(phone, password) _loginResult.value = response } catch (e: Exception) { _error.value = mapAuthError(e, AuthAction.LOGIN) @@ -51,7 +47,7 @@ class AuthViewModel : ViewModel() { try { val response = authRepository.registerUser(firstName, lastName, phone, password) - ApiClient.setCredentials(phone, password) + ApiClient.updateCredentials(phone, password) _loginResult.value = response } catch (e: Exception) { _error.value = mapAuthError(e, AuthAction.REGISTER) @@ -62,8 +58,21 @@ class AuthViewModel : ViewModel() { } fun recoverPassword(phone: String, firstName: String, lastName: String, newPassword: String) { - _recoverResult.value = null - _error.value = "Восстановление пароля пока не подключено на сервере" + viewModelScope.launch { + _isLoading.value = true + _error.value = null + _recoverResult.value = null + + try { + val ok = authRepository.recoverPassword(phone, firstName, lastName, newPassword) + _recoverResult.value = ok + } catch (e: Exception) { + _error.value = mapAuthError(e, AuthAction.RECOVER) + _recoverResult.value = false + } finally { + _isLoading.value = false + } + } } private fun mapAuthError(e: Exception, action: AuthAction): String { @@ -74,17 +83,17 @@ class AuthViewModel : ViewModel() { else -> "Проверьте введённые данные" } - 401 -> "Неверный номер телефона или пароль" + 401 -> when (action) { + AuthAction.RECOVER -> "Имя, фамилия или телефон не совпадают" + else -> "Неверный номер телефона или пароль" + } + 403 -> when (action) { AuthAction.REGISTER -> "Регистрация запрещена" else -> "Доступ запрещён" } - 404 -> when (action) { - AuthAction.RECOVER -> "Пользователь не найден" - else -> "Пользователь не найден" - } - + 404 -> "Пользователь не найден" 409 -> "Пользователь с таким номером уже существует" 500 -> "Сервер временно недоступен" else -> "Ошибка операции" From 9fe0187597f1d135b474814d03e5c6d8b21e276b Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sun, 12 Apr 2026 00:12:08 +0300 Subject: [PATCH 24/65] refactor(network): remove setCredentials and keep updateCredentials --- .../java/com/example/goodroad/data/network/ApiClient.kt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt index 6456047..737feb3 100644 --- a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt +++ b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt @@ -3,8 +3,7 @@ package com.example.goodroad.data.network import com.example.goodroad.BuildConfig import com.example.goodroad.data.auth.AuthApi import com.example.goodroad.data.user.UserApi -import okhttp3.Credentials -import okhttp3.OkHttpClient +import okhttp3.* import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory @@ -19,11 +18,6 @@ object ApiClient { private var userPhone: String? = null private var userPassword: String? = null - fun setCredentials(phone: String, password: String) { - userPhone = phone - userPassword = password - } - fun updateCredentials(phone: String? = null, password: String? = null) { if (!phone.isNullOrBlank()) { userPhone = phone From 3c0b359070286459f69ed0ba48c4f40d9be01ef3 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sun, 12 Apr 2026 00:12:30 +0300 Subject: [PATCH 25/65] feat(profile): add avatar upload api and update user data layer --- .../com/example/goodroad/data/user/UserApi.kt | 12 ++-- .../example/goodroad/data/user/UserModels.kt | 4 ++ .../goodroad/data/user/UserRepository.kt | 7 ++ .../goodroad/ui/viewmodel/UserViewModel.kt | 64 +++++++++++++++++-- 4 files changed, 77 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/data/user/UserApi.kt b/app/src/main/java/com/example/goodroad/data/user/UserApi.kt index dda3345..588d319 100644 --- a/app/src/main/java/com/example/goodroad/data/user/UserApi.kt +++ b/app/src/main/java/com/example/goodroad/data/user/UserApi.kt @@ -1,12 +1,8 @@ package com.example.goodroad.data.user +import okhttp3.MultipartBody import retrofit2.Response -import retrofit2.http.Body -import retrofit2.http.GET -import retrofit2.http.HTTP -import retrofit2.http.POST -import retrofit2.http.PUT -import retrofit2.http.Query +import retrofit2.http.* interface UserApi { @@ -22,6 +18,10 @@ interface UserApi { @Query("newPassword") newPassword: String ): Response + @Multipart + @POST("/users/avatar") + suspend fun uploadAvatar(@Part file: MultipartBody.Part): Response + @HTTP(method = "DELETE", path = "/users", hasBody = true) suspend fun deleteCurrentUser(@Body req: DeleteAccountReq): Response } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/data/user/UserModels.kt b/app/src/main/java/com/example/goodroad/data/user/UserModels.kt index 220a4ed..0394b80 100644 --- a/app/src/main/java/com/example/goodroad/data/user/UserModels.kt +++ b/app/src/main/java/com/example/goodroad/data/user/UserModels.kt @@ -16,6 +16,10 @@ data class UpdateUserReq( val phone: String? = null ) +data class AvatarUploadResp( + val photoUrl: String +) + data class SettingsView( val id: String, val role: String, diff --git a/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt b/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt index 8ae40fb..1df6475 100644 --- a/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt +++ b/app/src/main/java/com/example/goodroad/data/user/UserRepository.kt @@ -1,5 +1,6 @@ package com.example.goodroad.data.user +import okhttp3.MultipartBody import retrofit2.HttpException class UserRepository(private val api: UserApi) { @@ -21,6 +22,12 @@ class UserRepository(private val api: UserApi) { if (!response.isSuccessful) throw HttpException(response) } + suspend fun uploadAvatar(file: MultipartBody.Part): AvatarUploadResp? { + val response = api.uploadAvatar(file) + if (response.isSuccessful) return response.body() + throw HttpException(response) + } + suspend fun deleteCurrentUser(req: DeleteAccountReq) { val response = api.deleteCurrentUser(req) if (!response.isSuccessful) throw HttpException(response) diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt index 8fbbf8a..5c4b65c 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -1,15 +1,18 @@ package com.example.goodroad.ui.viewmodel +import android.content.Context +import android.net.Uri import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.goodroad.data.network.ApiClient -import com.example.goodroad.data.user.DeleteAccountReq -import com.example.goodroad.data.user.SettingsView -import com.example.goodroad.data.user.UpdateUserReq -import com.example.goodroad.data.user.UserRepository +import com.example.goodroad.data.user.* import kotlinx.coroutines.launch +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.asRequestBody import retrofit2.HttpException +import java.io.File import java.io.IOException class UserViewModel(private val repository: UserRepository) : ViewModel() { @@ -17,6 +20,7 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { var user = mutableStateOf(null) var isLoading = mutableStateOf(false) var errorMessage = mutableStateOf(null) + var successMessage = mutableStateOf(null) var isDeleted = false private set @@ -49,6 +53,7 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { viewModelScope.launch { isLoading.value = true errorMessage.value = null + successMessage.value = null try { val current = user.value @@ -86,6 +91,8 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { repository.changePassword(oldPassword!!, newPassword!!) ApiClient.updateCredentials(password = newPassword) } + + successMessage.value = "Профиль успешно сохранен" } catch (e: Exception) { errorMessage.value = mapUserError(e) } finally { @@ -94,10 +101,47 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { } } + fun uploadAvatar(context: Context, uri: Uri, onSuccess: (String) -> Unit) { + viewModelScope.launch { + isLoading.value = true + errorMessage.value = null + successMessage.value = null + + try { + val resolver = context.contentResolver + val mimeType = resolver.getType(uri) ?: "image/*" + val extension = MimeTypeMap.resolveExtension(mimeType) + val tempFile = File.createTempFile("avatar_upload", extension, context.cacheDir) + resolver.openInputStream(uri)?.use { input -> + tempFile.outputStream().use { output -> + input.copyTo(output) + } + } ?: throw IllegalArgumentException("Не удалось прочитать выбранный файл") + + val requestBody = tempFile.asRequestBody(mimeType.toMediaTypeOrNull()) + val part = MultipartBody.Part.createFormData("file", tempFile.name, requestBody) + val response = repository.uploadAvatar(part) + ?: throw IllegalStateException("Сервер не вернул ссылку на фото") + + onSuccess(response.photoUrl) + tempFile.delete() + } catch (e: Exception) { + errorMessage.value = mapUserError(e) + } finally { + isLoading.value = false + } + } + } + + fun clearSuccessMessage() { + successMessage.value = null + } + fun deleteUser(password: String, onSuccess: () -> Unit) { viewModelScope.launch { isLoading.value = true errorMessage.value = null + successMessage.value = null try { repository.deleteCurrentUser(DeleteAccountReq(password)) @@ -118,6 +162,7 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { user.value = null isDeleted = false errorMessage.value = null + successMessage.value = null onSuccess() } @@ -137,4 +182,15 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { else -> e.message ?: "Неизвестная ошибка" } } + + private object MimeTypeMap { + fun resolveExtension(mimeType: String): String { + return when (mimeType) { + "image/jpeg" -> ".jpg" + "image/png" -> ".png" + "image/webp" -> ".webp" + else -> ".tmp" + } + } + } } \ No newline at end of file From 306611f0732dc7176c313611084f946c902aadc4 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sun, 12 Apr 2026 00:12:57 +0300 Subject: [PATCH 26/65] fix(profile-ui): restore phone label and update edit profile flow --- .../goodroad/ui/user/UserEditScreen.kt | 164 +++++++++++++----- 1 file changed, 121 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt index cd057a9..d0de751 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -1,42 +1,28 @@ package com.example.goodroad.ui.user -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import android.net.Uri +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Photo -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.auth.AuthButton -import com.example.goodroad.ui.auth.PasswordField -import com.example.goodroad.ui.auth.PhoneField -import com.example.goodroad.ui.auth.PlainField -import com.example.goodroad.ui.common.validation.CYRILLIC_WARNING -import com.example.goodroad.ui.common.validation.PHONE_CHARS_WARNING -import com.example.goodroad.ui.common.validation.PHONE_FORMAT_WARNING -import com.example.goodroad.ui.common.validation.formatPhoneForRequest -import com.example.goodroad.ui.common.validation.isAllowedCyrillicInput -import com.example.goodroad.ui.common.validation.isAllowedDigitsInput -import com.example.goodroad.ui.common.validation.normalizeRequiredCyrillic -import com.example.goodroad.ui.common.validation.normalizeRequiredRussianPhone -import com.example.goodroad.ui.theme.GrayButton -import com.example.goodroad.ui.theme.TextPrimary -import com.example.goodroad.ui.theme.UrbanBrown -import com.example.goodroad.ui.theme.WhiteSoft +import coil.compose.AsyncImage +import com.example.goodroad.ui.auth.* +import com.example.goodroad.ui.common.validation.* +import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.UserViewModel @Composable @@ -45,14 +31,17 @@ fun UserEditScreen( onBack: () -> Unit, onLogout: () -> Unit ) { + val context = LocalContext.current val user = userViewModel.user.value ?: return var firstName by remember { mutableStateOf(user.firstName ?: "") } var lastName by remember { mutableStateOf(user.lastName ?: "") } var photoUrl by remember { mutableStateOf(user.photoUrl ?: "") } + var selectedPhotoUri by remember { mutableStateOf(null) } var phone by remember { mutableStateOf("") } var oldPassword by remember { mutableStateOf("") } var newPassword by remember { mutableStateOf("") } + var confirmNewPassword by remember { mutableStateOf("") } var firstNameWarning by remember { mutableStateOf(null) } var lastNameWarning by remember { mutableStateOf(null) } @@ -62,6 +51,17 @@ fun UserEditScreen( val errorMessage by remember { derivedStateOf { userViewModel.errorMessage.value } } val finalError = errorMessage ?: errorText + val photoPickerLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.GetContent() + ) { uri -> + if (uri != null) { + selectedPhotoUri = uri + userViewModel.uploadAvatar(context, uri) { uploadedUrl -> + photoUrl = uploadedUrl + } + } + } + Column( modifier = Modifier .fillMaxSize() @@ -126,17 +126,74 @@ fun UserEditScreen( Spacer(Modifier.height(12.dp)) - PlainField( - value = photoUrl, - onValueChange = { photoUrl = it }, - label = "URL фото", - icon = { - Icon( - imageVector = Icons.Default.Photo, - contentDescription = null, - tint = UrbanBrown - ) + Text( + text = "Фото профиля", + style = MaterialTheme.typography.bodyMedium, + color = TextPrimary + ) + + Spacer(Modifier.height(8.dp)) + + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + when { + selectedPhotoUri != null -> { + AsyncImage( + model = selectedPhotoUri, + contentDescription = null, + modifier = Modifier + .size(120.dp) + .clip(CircleShape), + contentScale = ContentScale.Crop + ) + } + + photoUrl.isNotBlank() -> { + AsyncImage( + model = photoUrl, + contentDescription = null, + modifier = Modifier + .size(120.dp) + .clip(CircleShape), + contentScale = ContentScale.Crop + ) + } + + else -> { + Surface( + modifier = Modifier.size(120.dp), + shape = CircleShape, + color = WhiteSoft, + tonalElevation = 2.dp + ) { + Box(contentAlignment = Alignment.Center) { + Icon( + imageVector = Icons.Default.Photo, + contentDescription = null, + tint = UrbanBrown, + modifier = Modifier.size(36.dp) + ) + } + } + } } + } + + Spacer(Modifier.height(12.dp)) + + AuthButton( + text = "Выбрать фото" + ) { + photoPickerLauncher.launch("image/*") + } + + Spacer(Modifier.height(4.dp)) + Text( + text = "Фотография загружается как файл на сервер, а в профиле сохраняется ссылка на нее.", + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown ) Spacer(Modifier.height(12.dp)) @@ -152,7 +209,7 @@ fun UserEditScreen( else -> null } }, - label = "Новый телефон", + label = "Телефон", warning = phoneWarning ) @@ -179,9 +236,17 @@ fun UserEditScreen( label = "Новый пароль" ) + Spacer(Modifier.height(12.dp)) + + PasswordField( + value = confirmNewPassword, + onValueChange = { confirmNewPassword = it }, + label = "Подтвердите новый пароль" + ) + Spacer(Modifier.height(4.dp)) Text( - text = "Чтобы сменить пароль, заполните оба поля. Иначе оставьте их пустыми.", + text = "Чтобы сменить пароль, заполните старый пароль и дважды введите новый.", style = MaterialTheme.typography.bodySmall, color = UrbanBrown ) @@ -223,6 +288,18 @@ fun UserEditScreen( val oldPass = oldPassword.takeIf { it.isNotBlank() } val newPass = newPassword.takeIf { it.isNotBlank() } + val confirmPass = confirmNewPassword.takeIf { it.isNotBlank() } + + if (!newPass.isNullOrBlank() || !confirmPass.isNullOrBlank() || !oldPass.isNullOrBlank()) { + if (oldPass.isNullOrBlank() || newPass.isNullOrBlank() || confirmPass.isNullOrBlank()) { + errorText = "Для смены пароля заполните все три поля" + return@AuthButton + } + if (newPass != confirmPass) { + errorText = "Новые пароли не совпадают" + return@AuthButton + } + } userViewModel.updateUser( firstName = firstNameNormalized, @@ -235,6 +312,7 @@ fun UserEditScreen( oldPassword = "" newPassword = "" + confirmNewPassword = "" errorText = null } @@ -257,4 +335,4 @@ fun UserEditScreen( userViewModel.logout(onLogout) } } -} \ No newline at end of file +} From 52e8be06ae08d049cf145ad235f33d3341269fa9 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sun, 12 Apr 2026 00:13:24 +0300 Subject: [PATCH 27/65] build(android): add dependencies for profile photo upload --- app/build.gradle | 1 + settings.gradle | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 3d054e3..d880b89 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -79,6 +79,7 @@ dependencies { implementation 'com.squareup.retrofit2:retrofit:2.11.0' implementation 'com.squareup.retrofit2:converter-gson:2.11.0' implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0' + implementation 'io.coil-kt:coil-compose:2.7.0' debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' diff --git a/settings.gradle b/settings.gradle index 5f2196b..1018558 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,4 +15,4 @@ dependencyResolutionManagement { } rootProject.name = 'GoodRoad-Client' -include ':app' +include ':app' \ No newline at end of file From 82dc78a051c3bee22492f05531917ab4f3c92540 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Sun, 12 Apr 2026 01:44:26 +0300 Subject: [PATCH 28/65] add: ObstacleSelectScreen and button to switch to the map --- .../goodroad/ui/auth/AuthComponents.kt | 2 +- .../com/example/goodroad/ui/maps/MapsNav.kt | 14 ++ .../goodroad/ui/maps/ObstacleSelectScreen.kt | 147 ++++++++++++++++++ .../com/example/goodroad/ui/theme/Color.kt | 2 + .../com/example/goodroad/ui/user/UserNav.kt | 22 ++- .../goodroad/ui/user/UserProfileScreen.kt | 83 ++++++++-- .../goodroad/ui/viewmodel/MapsViewModel.kt | 15 ++ 7 files changed, 263 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/com/example/goodroad/ui/maps/MapsNav.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/viewmodel/MapsViewModel.kt diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt index 432f910..63006ef 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt @@ -28,7 +28,7 @@ import com.example.goodroad.ui.theme.UrbanBrown @Composable fun AuthButton( text: String, - backgroundColor: Color = SafeRoute, + backgroundColor: Color = SafeGreen, contentColor: Color = BackgroundLight, enabled: Boolean = true, onClick: () -> Unit diff --git a/app/src/main/java/com/example/goodroad/ui/maps/MapsNav.kt b/app/src/main/java/com/example/goodroad/ui/maps/MapsNav.kt new file mode 100644 index 0000000..f67c2d5 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/maps/MapsNav.kt @@ -0,0 +1,14 @@ +package com.example.goodroad.ui.maps + +import androidx.compose.runtime.Composable + +@Composable +fun MapsNav( + onBackToProfile: () -> Unit, + onSaveObstacles: (List) -> Unit +) { + ObstacleSelectScreen( + onBackToProfile = onBackToProfile, + onSave = onSaveObstacles + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt b/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt new file mode 100644 index 0000000..a09a54c --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt @@ -0,0 +1,147 @@ +package com.example.goodroad.ui.maps + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.example.goodroad.ui.auth.AuthButton +import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.user.UserDecor + +data class Obstacle( + val id: String, + val name: String +) + +@Composable +fun ObstacleSelectScreen( + onBackToProfile: () -> Unit, + onSave: (List) -> Unit +) { + val obstacles = remember { + listOf( + Obstacle("1", "Ямы да ухабы"), + Obstacle("2", "Лежачие полицейские...спят, устали"), + Obstacle("3", "Ремонт дороги...пока что можно только летать"), + Obstacle("4", "Скользкая дорога...пол помыли"), + Obstacle("5", "Перекрытие дороги...фильм снимают"), + Obstacle("6", "Авария...дискотека"), + Obstacle("7", "Пробка...от вина") + ) + } + + var selected by remember { mutableStateOf(setOf()) } + + val scrollState = rememberScrollState() + + Surface( + modifier = Modifier.fillMaxSize(), + color = BackgroundLight + ) { + + Box(modifier = Modifier.fillMaxSize()) { + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scrollState) + .padding(24.dp) + ) { + + UserDecor() + + Text( + text = "Выбор препятствий", + style = MaterialTheme.typography.headlineLarge, + color = TextPrimary + ) + + Spacer(modifier = Modifier.height(20.dp)) + + Text( + text = "Выберите препятствия, которые вас интересуют:", + style = MaterialTheme.typography.bodyLarge, + color = UrbanBrown + ) + + Spacer(modifier = Modifier.height(20.dp)) + + obstacles.forEach { obstacle -> + val checked = obstacle.id in selected + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp), + verticalAlignment = Alignment.CenterVertically + ) { + + Checkbox( + checked = checked, + onCheckedChange = { isChecked -> + selected = if (isChecked) { + selected + obstacle.id + } else { + selected - obstacle.id + } + }, + colors = CheckboxDefaults.colors( + checkedColor = UrbanBrown, + uncheckedColor = UrbanBrown, + checkmarkColor = WhiteSoft + ) + ) + + Spacer(modifier = Modifier.width(10.dp)) + + Text( + text = obstacle.name, + style = MaterialTheme.typography.bodyLarge, + color = UrbanBrown + ) + } + } + + Spacer(modifier = Modifier.height(30.dp)) + + AuthButton( + text = "Сохранить" + ) { + onSave(selected.toList()) + } + + Spacer(modifier = Modifier.height(10.dp)) + + AuthButton( + text = "Назад в профиль", + backgroundColor = GrayButton, + contentColor = WhiteSoft + ) { + onBackToProfile() + } + } + + Box( + modifier = Modifier + .fillMaxHeight() + .width(6.dp) + .align(Alignment.CenterEnd) + .background(UrbanBrown.copy(alpha = 0.25f)) + ) + + Box( + modifier = Modifier + .width(6.dp) + .height(60.dp) + .offset(y = (scrollState.value * 0.2f).dp) + .align(Alignment.TopEnd) + .background(UrbanBrown) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/theme/Color.kt b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt index 24a2d96..dbc5ea9 100644 --- a/app/src/main/java/com/example/goodroad/ui/theme/Color.kt +++ b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt @@ -12,6 +12,7 @@ val UrbanBrown = Color(0xFFA28A72) val BackgroundLight = Color(0xFFF7F5F0) val SurfaceWarm = Color(0xFFEEE7DD) val BorderWarm = Color(0xFFD8CEC0) +val SoftYellow = Color(0xFFF2C94C) val MapBackground = Color(0xFFF3EFE7) val Buildings = Color(0xFFDCCFBE) @@ -23,6 +24,7 @@ val BalancedRoute = Color(0xFF4F87C9) val FastRoute = Color(0xFF7C6BCB) val Obstacle = Color(0xFFD56B63) + val TextPrimary = Color(0xFF2F2B28) val TextSecondary = Color(0xFF7A6F66) val WhiteSoft = Color(0xFFFFFBF7) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt index e1a40ec..680cdf1 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt @@ -1,19 +1,18 @@ package com.example.goodroad.ui.user -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.* import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import com.example.goodroad.data.network.ApiClient import com.example.goodroad.data.user.UserRepository +import com.example.goodroad.ui.maps.MapsNav import com.example.goodroad.ui.viewmodel.UserViewModel @Composable fun UserNav(onLogout: () -> Unit) { + val api = ApiClient.userApi + val factory = object : ViewModelProvider.Factory { override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(UserViewModel::class.java)) { @@ -29,11 +28,13 @@ fun UserNav(onLogout: () -> Unit) { var screen by remember { mutableStateOf("profile") } when (screen) { + "profile" -> UserProfileScreen( userViewModel = userViewModel, onEdit = { screen = "edit" }, onDelete = { screen = "delete" }, - onLogout = onLogout + onLogout = onLogout, + onSelectObstacles = { screen = "obstacles" } ) "edit" -> UserEditScreen( @@ -46,5 +47,14 @@ fun UserNav(onLogout: () -> Unit) { viewModel = userViewModel, onExit = onLogout ) + + "obstacles" -> MapsNav( + onBackToProfile = { + screen = "profile" + }, + onSaveObstacles = { selected -> + screen = "profile" + } + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt index a8032fa..b44c403 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt @@ -6,7 +6,9 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.example.goodroad.ui.auth.AuthButton import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.UserViewModel @@ -16,7 +18,8 @@ fun UserProfileScreen( userViewModel: UserViewModel, onEdit: () -> Unit, onDelete: () -> Unit, - onLogout: () -> Unit + onLogout: () -> Unit, + onSelectObstacles: () -> Unit ) { val user by userViewModel.user val isLoading by userViewModel.isLoading @@ -30,17 +33,20 @@ fun UserProfileScreen( when { isLoading -> { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } + errorMessage != null -> { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("Ошибка: $errorMessage", color = Color.Red) } } + user != null -> { val u = user!! + Surface( modifier = Modifier.fillMaxSize(), color = BackgroundLight @@ -50,36 +56,83 @@ fun UserProfileScreen( .fillMaxSize() .padding(24.dp) ) { + UserDecor() + Text( "Профиль", style = MaterialTheme.typography.headlineLarge, color = TextPrimary ) + Spacer(Modifier.height(20.dp)) - UserInfoBlock("Имя", u.firstName ?: "") - UserInfoBlock("Фамилия", u.lastName ?: "") - UserInfoBlock("Роль", u.role ?: "") + + Card( + colors = CardDefaults.cardColors( + containerColor = UrbanBrown.copy(alpha = 0.08f) + ), + modifier = Modifier.fillMaxWidth() + ) { + Column( + modifier = Modifier.padding(16.dp) + ) { + + Text( + text = "${u.firstName ?: ""} ${u.lastName ?: ""}", + fontSize = 22.sp, + fontWeight = FontWeight.Bold, + color = UrbanBrown + ) + + Spacer(Modifier.height(6.dp)) + + Text( + text = "Роль: ${u.role ?: ""}", + fontSize = 16.sp, + color = UrbanBrown.copy(alpha = 0.8f) + ) + } + } + Spacer(Modifier.height(20.dp)) + + AuthButton( + text = "Выбрать препятствия", + backgroundColor = SoftYellow , + contentColor = WhiteSoft + ) { + onSelectObstacles() + } + + Spacer(modifier = Modifier.height(10.dp)) + + AuthButton( + text = "Перейти на карту", + backgroundColor = SoftYellow , + contentColor = WhiteSoft + ) { + } + + Spacer(Modifier.height(10.dp)) + AuthButton(text = "Редактировать", onClick = onEdit) + Spacer(Modifier.height(10.dp)) - AuthButton(text = "Удалить аккаунт") { - onDelete() - } + + AuthButton(text = "Удалить аккаунт", onClick = onDelete) + Spacer(Modifier.height(10.dp)) + AuthButton(text = "Выйти") { - userViewModel.logout { - onLogout() - } + userViewModel.logout { onLogout() } } } } } + else -> { LaunchedEffect(Unit) { - if (userViewModel.isDeleted) { - onLogout() - } + if (userViewModel.isDeleted) onLogout() } } } diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/MapsViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/MapsViewModel.kt new file mode 100644 index 0000000..3b453a5 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/MapsViewModel.kt @@ -0,0 +1,15 @@ +package com.example.goodroad.ui.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.State + +class MapsViewModel : ViewModel() { + + private val _selectedObstacles = mutableStateOf>(emptyList()) + val selectedObstacles: State> = _selectedObstacles + + fun setSelectedObstacles(selected: List) { + _selectedObstacles.value = selected + } +} \ No newline at end of file From a2bc4c1c1b94b8fa595488734a2fbd297ca1a931 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sun, 12 Apr 2026 02:59:12 +0300 Subject: [PATCH 29/65] chore: add note about theme color --- app/src/main/java/com/example/goodroad/ui/theme/Color.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/example/goodroad/ui/theme/Color.kt b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt index dbc5ea9..4df9735 100644 --- a/app/src/main/java/com/example/goodroad/ui/theme/Color.kt +++ b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt @@ -7,7 +7,7 @@ val SafeGreen = Color(0xFF6FAE8A) val InclusiveViolet = Color(0xFF8B7AC6) val AlertRed = Color(0xFFD56B63) -val UrbanBrown = Color(0xFFA28A72) +val UrbanBrown = Color(0xFFA28A72) // 0xFF7A6F66 val BackgroundLight = Color(0xFFF7F5F0) val SurfaceWarm = Color(0xFFEEE7DD) From 3225c5d356f3cff145433652f082c4734e6d6e83 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sun, 12 Apr 2026 03:00:34 +0300 Subject: [PATCH 30/65] fix: user's edit screen now more cutie --- .../goodroad/ui/user/UserEditScreen.kt | 122 ++++++++++++------ 1 file changed, 85 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt index d0de751..0cf5364 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -3,10 +3,10 @@ package com.example.goodroad.ui.user import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person @@ -20,9 +20,25 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import coil.compose.AsyncImage -import com.example.goodroad.ui.auth.* -import com.example.goodroad.ui.common.validation.* -import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.auth.AuthButton +import com.example.goodroad.ui.auth.AuthSuccessText +import com.example.goodroad.ui.auth.PasswordField +import com.example.goodroad.ui.auth.PhoneField +import com.example.goodroad.ui.auth.PlainField +import com.example.goodroad.ui.common.validation.CYRILLIC_WARNING +import com.example.goodroad.ui.common.validation.PHONE_CHARS_WARNING +import com.example.goodroad.ui.common.validation.PHONE_FORMAT_WARNING +import com.example.goodroad.ui.common.validation.formatPhoneForRequest +import com.example.goodroad.ui.common.validation.isAllowedCyrillicInput +import com.example.goodroad.ui.common.validation.isAllowedDigitsInput +import com.example.goodroad.ui.common.validation.normalizeRequiredCyrillic +import com.example.goodroad.ui.common.validation.normalizeRequiredRussianPhone +import com.example.goodroad.ui.theme.BackgroundLight +import com.example.goodroad.ui.theme.BorderWarm +import com.example.goodroad.ui.theme.GrayButton +import com.example.goodroad.ui.theme.TextPrimary +import com.example.goodroad.ui.theme.UrbanBrown +import com.example.goodroad.ui.theme.WhiteSoft import com.example.goodroad.ui.viewmodel.UserViewModel @Composable @@ -49,8 +65,32 @@ fun UserEditScreen( var errorText by remember { mutableStateOf(null) } val errorMessage by remember { derivedStateOf { userViewModel.errorMessage.value } } + val successMessage by remember { derivedStateOf { userViewModel.successMessage.value } } + val isLoading by remember { derivedStateOf { userViewModel.isLoading.value } } val finalError = errorMessage ?: errorText + val hasProfileChanges by remember(firstName, lastName, photoUrl, phone, selectedPhotoUri, user) { + derivedStateOf { + firstName != (user.firstName ?: "") || + lastName != (user.lastName ?: "") || + photoUrl != (user.photoUrl ?: "") || + phone.isNotBlank() || + selectedPhotoUri != null + } + } + + val hasPasswordChanges by remember(oldPassword, newPassword, confirmNewPassword) { + derivedStateOf { + oldPassword.isNotBlank() || newPassword.isNotBlank() || confirmNewPassword.isNotBlank() + } + } + + val canSave by remember(hasProfileChanges, hasPasswordChanges, isLoading) { + derivedStateOf { + (hasProfileChanges || hasPasswordChanges) && !isLoading + } + } + val photoPickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.GetContent() ) { uri -> @@ -71,7 +111,7 @@ fun UserEditScreen( UserDecor() Text( - "Редактирование профиля", + text = "Редактирование профиля", style = MaterialTheme.typography.headlineLarge, color = TextPrimary ) @@ -82,7 +122,9 @@ fun UserEditScreen( value = firstName, onValueChange = { value -> when { - !isAllowedCyrillicInput(value) -> firstNameWarning = CYRILLIC_WARNING + !isAllowedCyrillicInput(value) -> { + firstNameWarning = CYRILLIC_WARNING + } value != firstName -> { firstName = value firstNameWarning = null @@ -106,7 +148,9 @@ fun UserEditScreen( value = lastName, onValueChange = { value -> when { - !isAllowedCyrillicInput(value) -> lastNameWarning = CYRILLIC_WARNING + !isAllowedCyrillicInput(value) -> { + lastNameWarning = CYRILLIC_WARNING + } value != lastName -> { lastName = value lastNameWarning = null @@ -126,12 +170,6 @@ fun UserEditScreen( Spacer(Modifier.height(12.dp)) - Text( - text = "Фото профиля", - style = MaterialTheme.typography.bodyMedium, - color = TextPrimary - ) - Spacer(Modifier.height(8.dp)) Box( @@ -181,21 +219,36 @@ fun UserEditScreen( } } - Spacer(Modifier.height(12.dp)) - - AuthButton( - text = "Выбрать фото" + Spacer(Modifier.height(16.dp)) + + OutlinedButton( + onClick = { photoPickerLauncher.launch("image/*") }, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + shape = RoundedCornerShape(16.dp), + colors = ButtonDefaults.outlinedButtonColors( + containerColor = BackgroundLight, + contentColor = UrbanBrown + ), + border = ButtonDefaults.outlinedButtonBorder.copy( + brush = androidx.compose.ui.graphics.SolidColor(BorderWarm) + ) ) { - photoPickerLauncher.launch("image/*") + Icon( + imageVector = Icons.Default.Photo, + contentDescription = null, + tint = UrbanBrown, + modifier = Modifier.size(22.dp) + ) + Spacer(Modifier.width(12.dp)) + Text( + text = "Выбрать фото профиля", + style = MaterialTheme.typography.titleMedium, + color = UrbanBrown + ) } - Spacer(Modifier.height(4.dp)) - Text( - text = "Фотография загружается как файл на сервер, а в профиле сохраняется ссылка на нее.", - style = MaterialTheme.typography.bodySmall, - color = UrbanBrown - ) - Spacer(Modifier.height(12.dp)) PhoneField( @@ -251,6 +304,8 @@ fun UserEditScreen( color = UrbanBrown ) + AuthSuccessText(text = successMessage) + if (!finalError.isNullOrBlank()) { Spacer(Modifier.height(12.dp)) Text( @@ -262,7 +317,10 @@ fun UserEditScreen( Spacer(Modifier.height(20.dp)) - AuthButton(text = "Сохранить") { + AuthButton( + text = if (isLoading) "Сохраняем..." else "Сохранить", + enabled = canSave + ) { val firstNameNormalized = normalizeRequiredCyrillic(firstName) if (firstNameNormalized == null) { firstNameWarning = CYRILLIC_WARNING @@ -324,15 +382,5 @@ fun UserEditScreen( contentColor = WhiteSoft, onClick = onBack ) - - Spacer(Modifier.height(12.dp)) - - AuthButton( - text = "Выйти", - backgroundColor = GrayButton, - contentColor = WhiteSoft - ) { - userViewModel.logout(onLogout) - } } -} +} \ No newline at end of file From 4a964a9bb8ec5236e3c22935722204909652fee3 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sun, 12 Apr 2026 03:31:24 +0300 Subject: [PATCH 31/65] style(imports): clean up and shorten imports --- .../goodroad/data/network/ApiClient.kt | 3 +- .../com/example/goodroad/ui/auth/AuthApp.kt | 5 +--- .../goodroad/ui/auth/AuthComponents.kt | 22 ++------------ .../com/example/goodroad/ui/auth/AuthDecor.kt | 8 ++--- .../example/goodroad/ui/auth/AuthFields.kt | 11 ++++--- .../example/goodroad/ui/auth/LoginScreen.kt | 20 ++----------- .../goodroad/ui/auth/RecoverPasswordScreen.kt | 14 ++------- .../goodroad/ui/auth/RegisterScreen.kt | 16 ++-------- .../goodroad/ui/auth/RoleStubScreen.kt | 3 +- .../goodroad/ui/maps/ObstacleSelectScreen.kt | 13 ++++----- .../com/example/goodroad/ui/theme/Theme.kt | 5 +--- .../goodroad/ui/user/UserComponents.kt | 2 +- .../com/example/goodroad/ui/user/UserDecor.kt | 9 ++---- .../goodroad/ui/user/UserDeleteAccount.kt | 9 +++--- .../goodroad/ui/user/UserEditScreen.kt | 29 ++++--------------- .../com/example/goodroad/ui/user/UserNav.kt | 3 +- .../goodroad/ui/user/UserProfileScreen.kt | 9 +++--- .../goodroad/ui/viewmodel/AuthViewModel.kt | 5 ++-- .../goodroad/ui/viewmodel/UserViewModel.kt | 3 +- 19 files changed, 50 insertions(+), 139 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt index 737feb3..6e1a87f 100644 --- a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt +++ b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt @@ -3,12 +3,11 @@ package com.example.goodroad.data.network import com.example.goodroad.BuildConfig import com.example.goodroad.data.auth.AuthApi import com.example.goodroad.data.user.UserApi -import okhttp3.* import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit - +import okhttp3.* object ApiClient { private val logging = HttpLoggingInterceptor().apply { diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt index 11b5fb3..540f1be 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthApp.kt @@ -5,12 +5,9 @@ import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.rememberNavController +import androidx.navigation.compose.* import com.example.goodroad.ui.theme.BackgroundLight import com.example.goodroad.ui.user.UserNav - @Composable fun AuthApp( navController: NavHostController = rememberNavController() diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt index 63006ef..4b69798 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt @@ -1,30 +1,14 @@ package com.example.goodroad.ui.auth -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.theme.AlertRed -import com.example.goodroad.ui.theme.SafeGreen -import com.example.goodroad.ui.theme.SafeRoute -import com.example.goodroad.ui.theme.BackgroundLight -import com.example.goodroad.ui.theme.TextSecondary -import com.example.goodroad.ui.theme.UrbanBrown - +import com.example.goodroad.ui.theme.* @Composable fun AuthButton( text: String, diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt index 3f38ee9..47195a7 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthDecor.kt @@ -1,9 +1,5 @@ package com.example.goodroad.ui.auth -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.* -import androidx.compose.ui.graphics.* -import com.example.goodroad.ui.theme.* import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.shape.RoundedCornerShape @@ -17,6 +13,10 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.* +import androidx.compose.ui.graphics.* +import com.example.goodroad.ui.theme.* @Composable fun AuthScreenFrame( title: String, diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt index 0582f8b..e7fa192 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt @@ -1,10 +1,5 @@ package com.example.goodroad.ui.auth -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.text.input.* -import com.example.goodroad.ui.theme.* -import androidx.compose.material.icons.filled.* import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.shape.RoundedCornerShape @@ -14,7 +9,11 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.unit.dp import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.interaction.MutableInteractionSource - +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.text.input.* +import com.example.goodroad.ui.theme.* +import androidx.compose.material.icons.filled.* @Composable fun PhoneField( value: String, diff --git a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt index a6dd622..90b1caa 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt @@ -1,32 +1,18 @@ package com.example.goodroad.ui.auth -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.* import androidx.compose.material3.Text import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel -import com.example.goodroad.ui.common.validation.PHONE_FORMAT_WARNING -import com.example.goodroad.ui.common.validation.formatPhoneForRequest -import com.example.goodroad.ui.common.validation.isAllowedDigitsInput -import com.example.goodroad.ui.common.validation.isValidRussianPhoneDigits -import com.example.goodroad.ui.common.validation.normalizeRequiredRussianPhone +import com.example.goodroad.ui.common.validation.* import com.example.goodroad.ui.theme.UrbanBrown import com.example.goodroad.ui.viewmodel.AuthViewModel - @Composable fun LoginScreen( onLoginSuccess: (String) -> Unit, diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt index 4e7bdad..cfaa4e7 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt @@ -1,12 +1,12 @@ package com.example.goodroad.ui.auth +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -14,17 +14,9 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.material3.Icon import androidx.lifecycle.viewmodel.compose.viewModel -import com.example.goodroad.ui.common.validation.CYRILLIC_WARNING -import com.example.goodroad.ui.common.validation.PHONE_CHARS_WARNING -import com.example.goodroad.ui.common.validation.PHONE_FORMAT_WARNING -import com.example.goodroad.ui.common.validation.formatPhoneForRequest -import com.example.goodroad.ui.common.validation.isAllowedCyrillicInput -import com.example.goodroad.ui.common.validation.isAllowedDigitsInput -import com.example.goodroad.ui.common.validation.normalizeRequiredCyrillic -import com.example.goodroad.ui.common.validation.normalizeRequiredRussianPhone +import com.example.goodroad.ui.common.validation.* import com.example.goodroad.ui.theme.UrbanBrown import com.example.goodroad.ui.viewmodel.AuthViewModel - @Composable fun RecoverPasswordScreen( onLogin: () -> Unit diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt index 388406f..2ed30ed 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt @@ -1,11 +1,7 @@ package com.example.goodroad.ui.auth -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -16,17 +12,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.example.goodroad.BuildConfig -import com.example.goodroad.ui.common.validation.CYRILLIC_WARNING -import com.example.goodroad.ui.common.validation.PHONE_CHARS_WARNING -import com.example.goodroad.ui.common.validation.PHONE_FORMAT_WARNING -import com.example.goodroad.ui.common.validation.formatPhoneForRequest -import com.example.goodroad.ui.common.validation.isAllowedCyrillicInput -import com.example.goodroad.ui.common.validation.isAllowedDigitsInput -import com.example.goodroad.ui.common.validation.normalizeRequiredCyrillic -import com.example.goodroad.ui.common.validation.normalizeRequiredRussianPhone +import com.example.goodroad.ui.common.validation.* import com.example.goodroad.ui.theme.UrbanBrown import com.example.goodroad.ui.viewmodel.AuthViewModel - @Composable fun RegisterScreen( onRegisterSuccess: (String) -> Unit, diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt index 7ef7af5..b8bafc3 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RoleStubScreen.kt @@ -1,6 +1,5 @@ package com.example.goodroad.ui.auth -import androidx.compose.foundation.layout.* import androidx.compose.foundation.background import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -8,8 +7,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import androidx.compose.foundation.layout.* import com.example.goodroad.ui.theme.* - @Composable fun RoleStubScreen( title: String, diff --git a/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt b/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt index a09a54c..ad0dfcd 100644 --- a/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt @@ -1,18 +1,15 @@ package com.example.goodroad.ui.maps -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.foundation.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.example.goodroad.ui.auth.AuthButton -import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.user.UserDecor - +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import com.example.goodroad.ui.theme.* data class Obstacle( val id: String, val name: String diff --git a/app/src/main/java/com/example/goodroad/ui/theme/Theme.kt b/app/src/main/java/com/example/goodroad/ui/theme/Theme.kt index c7a03cf..d87037b 100644 --- a/app/src/main/java/com/example/goodroad/ui/theme/Theme.kt +++ b/app/src/main/java/com/example/goodroad/ui/theme/Theme.kt @@ -1,11 +1,8 @@ package com.example.goodroad.ui.theme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.lightColorScheme +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color - private val GoodRoadLightColors = lightColorScheme( primary = SafeRoute, onPrimary = WhiteSoft, diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt b/app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt index f3138c2..52473ed 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserComponents.kt @@ -1,11 +1,11 @@ package com.example.goodroad.ui.user -import androidx.compose.foundation.layout.* import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import androidx.compose.foundation.layout.* import com.example.goodroad.ui.theme.* @Composable diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt b/app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt index fd3eef3..f01f295 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserDecor.kt @@ -2,7 +2,6 @@ package com.example.goodroad.ui.user import androidx.compose.foundation.Canvas import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme @@ -11,16 +10,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Path -import androidx.compose.ui.graphics.PathEffect -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.StrokeCap -import androidx.compose.ui.graphics.StrokeJoin +import androidx.compose.ui.graphics.* import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.foundation.layout.* import com.example.goodroad.ui.theme.* - @Composable fun UserDecor() { Box( diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt index 0e97c92..f16ebca 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt @@ -1,16 +1,15 @@ package com.example.goodroad.ui.user -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.example.goodroad.ui.auth.AuthButton import com.example.goodroad.ui.auth.PasswordField -import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.UserViewModel - +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import com.example.goodroad.ui.theme.* @Composable fun UserDeleteAccountScreen( viewModel: UserViewModel, diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt index 0cf5364..5482ad6 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -3,7 +3,6 @@ package com.example.goodroad.ui.user import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -11,8 +10,6 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Photo -import androidx.compose.material3.* -import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -20,27 +17,13 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import coil.compose.AsyncImage -import com.example.goodroad.ui.auth.AuthButton -import com.example.goodroad.ui.auth.AuthSuccessText -import com.example.goodroad.ui.auth.PasswordField -import com.example.goodroad.ui.auth.PhoneField -import com.example.goodroad.ui.auth.PlainField -import com.example.goodroad.ui.common.validation.CYRILLIC_WARNING -import com.example.goodroad.ui.common.validation.PHONE_CHARS_WARNING -import com.example.goodroad.ui.common.validation.PHONE_FORMAT_WARNING -import com.example.goodroad.ui.common.validation.formatPhoneForRequest -import com.example.goodroad.ui.common.validation.isAllowedCyrillicInput -import com.example.goodroad.ui.common.validation.isAllowedDigitsInput -import com.example.goodroad.ui.common.validation.normalizeRequiredCyrillic -import com.example.goodroad.ui.common.validation.normalizeRequiredRussianPhone -import com.example.goodroad.ui.theme.BackgroundLight -import com.example.goodroad.ui.theme.BorderWarm -import com.example.goodroad.ui.theme.GrayButton -import com.example.goodroad.ui.theme.TextPrimary -import com.example.goodroad.ui.theme.UrbanBrown -import com.example.goodroad.ui.theme.WhiteSoft +import com.example.goodroad.ui.auth.* +import com.example.goodroad.ui.common.validation.* +import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.UserViewModel - +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* @Composable fun UserEditScreen( userViewModel: UserViewModel, diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt index 680cdf1..d87fb4a 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt @@ -1,13 +1,12 @@ package com.example.goodroad.ui.user -import androidx.compose.runtime.* import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import com.example.goodroad.data.network.ApiClient import com.example.goodroad.data.user.UserRepository import com.example.goodroad.ui.maps.MapsNav import com.example.goodroad.ui.viewmodel.UserViewModel - +import androidx.compose.runtime.* @Composable fun UserNav(onLogout: () -> Unit) { diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt index b44c403..744eccc 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt @@ -1,8 +1,5 @@ package com.example.goodroad.ui.user -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -10,9 +7,11 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.goodroad.ui.auth.AuthButton -import com.example.goodroad.ui.theme.* import com.example.goodroad.ui.viewmodel.UserViewModel - +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import com.example.goodroad.ui.theme.* @Composable fun UserProfileScreen( userViewModel: UserViewModel, diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt index 11ebc71..9b8af06 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt @@ -1,12 +1,11 @@ package com.example.goodroad.ui.viewmodel -import androidx.lifecycle.* -import com.example.goodroad.data.auth.* import com.example.goodroad.data.network.ApiClient import kotlinx.coroutines.launch import retrofit2.HttpException import java.io.IOException - +import androidx.lifecycle.* +import com.example.goodroad.data.auth.* class AuthViewModel : ViewModel() { private val authRepository = AuthRepository() diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt index 5c4b65c..2a8ea3c 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -6,7 +6,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.goodroad.data.network.ApiClient -import com.example.goodroad.data.user.* import kotlinx.coroutines.launch import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody @@ -14,7 +13,7 @@ import okhttp3.RequestBody.Companion.asRequestBody import retrofit2.HttpException import java.io.File import java.io.IOException - +import com.example.goodroad.data.user.* class UserViewModel(private val repository: UserRepository) : ViewModel() { var user = mutableStateOf(null) From 452c759ac4e3701fd5c97fd0e9c4498b48789d80 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sun, 12 Apr 2026 15:45:46 +0300 Subject: [PATCH 32/65] fix(user-delete): separate delete flow from back navigation --- .../goodroad/ui/user/UserDeleteAccount.kt | 21 ++++++------------- .../com/example/goodroad/ui/user/UserNav.kt | 1 + 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt index f16ebca..e3fe4de 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt @@ -13,7 +13,8 @@ import com.example.goodroad.ui.theme.* @Composable fun UserDeleteAccountScreen( viewModel: UserViewModel, - onExit: () -> Unit + onExit: () -> Unit, + onBack: () -> Unit ) { var password by remember { mutableStateOf("") } val isLoading by viewModel.isLoading @@ -56,21 +57,11 @@ fun UserDeleteAccountScreen( Spacer(modifier = Modifier.height(16.dp)) AuthButton( - text = "Выйти", - backgroundColor = GrayButton, + text = "Назад в профиль", + backgroundColor = UrbanBrown, contentColor = WhiteSoft, - enabled = !isLoading - ) { - viewModel.logout { - onExit() - } - } - - Spacer(modifier = Modifier.height(16.dp)) - - if (isLoading) { - CircularProgressIndicator(modifier = Modifier.padding(top = 16.dp)) - } + onClick = onBack + ) errorMessage?.let { Text( diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt index d87fb4a..b829f10 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt @@ -44,6 +44,7 @@ fun UserNav(onLogout: () -> Unit) { "delete" -> UserDeleteAccountScreen( viewModel = userViewModel, + onBack = { screen = "profile" }, onExit = onLogout ) From 236fdb8a087e62f4d05e1b60d19551cf972de786 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sun, 12 Apr 2026 15:48:20 +0300 Subject: [PATCH 33/65] fix(profile-ui): refine profile and edit screen behavior --- .../main/java/com/example/goodroad/ui/user/UserEditScreen.kt | 4 ++-- .../java/com/example/goodroad/ui/user/UserProfileScreen.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt index 5482ad6..46cf792 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -360,8 +360,8 @@ fun UserEditScreen( Spacer(Modifier.height(12.dp)) AuthButton( - text = "Назад", - backgroundColor = GrayButton, + text = "Назад в профиль", + backgroundColor = UrbanBrown, contentColor = WhiteSoft, onClick = onBack ) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt index 744eccc..0fe7098 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt @@ -97,7 +97,7 @@ fun UserProfileScreen( AuthButton( text = "Выбрать препятствия", - backgroundColor = SoftYellow , + backgroundColor = UrbanBrown, contentColor = WhiteSoft ) { onSelectObstacles() @@ -107,7 +107,7 @@ fun UserProfileScreen( AuthButton( text = "Перейти на карту", - backgroundColor = SoftYellow , + backgroundColor = UrbanBrown, contentColor = WhiteSoft ) { } From a46891e5db067bb115d231abe4aa2a3e7a0f4aa2 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Sun, 12 Apr 2026 15:49:04 +0300 Subject: [PATCH 34/65] style(ui): update colors and obstacle selection screen --- .../com/example/goodroad/ui/maps/ObstacleSelectScreen.kt | 4 ++-- app/src/main/java/com/example/goodroad/ui/theme/Color.kt | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt b/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt index ad0dfcd..07e93b8 100644 --- a/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt @@ -61,7 +61,7 @@ fun ObstacleSelectScreen( Spacer(modifier = Modifier.height(20.dp)) Text( - text = "Выберите препятствия, которые вас интересуют:", + text = "Выберите препятствия, которые хотите избегать:", style = MaterialTheme.typography.bodyLarge, color = UrbanBrown ) @@ -116,7 +116,7 @@ fun ObstacleSelectScreen( AuthButton( text = "Назад в профиль", - backgroundColor = GrayButton, + backgroundColor = UrbanBrown, contentColor = WhiteSoft ) { onBackToProfile() diff --git a/app/src/main/java/com/example/goodroad/ui/theme/Color.kt b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt index 4df9735..41b5a68 100644 --- a/app/src/main/java/com/example/goodroad/ui/theme/Color.kt +++ b/app/src/main/java/com/example/goodroad/ui/theme/Color.kt @@ -3,16 +3,15 @@ package com.example.goodroad.ui.theme import androidx.compose.ui.graphics.Color val PrimaryBlue = Color(0xFF4F87C9) -val SafeGreen = Color(0xFF6FAE8A) +val SafeGreen = Color(0xFF549671) val InclusiveViolet = Color(0xFF8B7AC6) val AlertRed = Color(0xFFD56B63) -val UrbanBrown = Color(0xFFA28A72) // 0xFF7A6F66 +val UrbanBrown = Color(0xFF887058) val BackgroundLight = Color(0xFFF7F5F0) val SurfaceWarm = Color(0xFFEEE7DD) val BorderWarm = Color(0xFFD8CEC0) -val SoftYellow = Color(0xFFF2C94C) val MapBackground = Color(0xFFF3EFE7) val Buildings = Color(0xFFDCCFBE) From 9003c1241a59f2780fc94a3052e7021f5c51bda0 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 03:03:59 +0300 Subject: [PATCH 35/65] feat(reviews): add review API integration and state management --- .../goodroad/data/network/ApiClient.kt | 24 ++- .../example/goodroad/data/review/ReviewApi.kt | 25 +++ .../goodroad/data/review/ReviewModels.kt | 47 ++++ .../goodroad/data/review/ReviewRepository.kt | 59 +++++ .../ui/reviews/ReviewDetailsScreen.kt | 0 .../goodroad/ui/reviews/ReviewFormScreen.kt | 0 .../example/goodroad/ui/reviews/ReviewUi.kt | 0 .../goodroad/ui/reviews/UserReviewsScreen.kt | 0 .../goodroad/ui/viewmodel/ReviewsViewModel.kt | 202 ++++++++++++++++++ 9 files changed, 347 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/com/example/goodroad/data/review/ReviewApi.kt create mode 100644 app/src/main/java/com/example/goodroad/data/review/ReviewModels.kt create mode 100644 app/src/main/java/com/example/goodroad/data/review/ReviewRepository.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/reviews/ReviewDetailsScreen.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/reviews/ReviewFormScreen.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/reviews/UserReviewsScreen.kt create mode 100644 app/src/main/java/com/example/goodroad/ui/viewmodel/ReviewsViewModel.kt diff --git a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt index 6e1a87f..961a83f 100644 --- a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt +++ b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt @@ -2,12 +2,14 @@ package com.example.goodroad.data.network import com.example.goodroad.BuildConfig import com.example.goodroad.data.auth.AuthApi +import com.example.goodroad.data.review.ReviewApi import com.example.goodroad.data.user.UserApi +import okhttp3.* import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit -import okhttp3.* + object ApiClient { private val logging = HttpLoggingInterceptor().apply { @@ -49,21 +51,23 @@ object ApiClient { .writeTimeout(20, TimeUnit.SECONDS) .build() - val authApi: AuthApi by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { - Retrofit.Builder() + private fun retrofit(): Retrofit { + return Retrofit.Builder() .baseUrl(BuildConfig.GOODROAD_SERVER_URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build() - .create(AuthApi::class.java) + } + + val authApi: AuthApi by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + retrofit().create(AuthApi::class.java) } val userApi: UserApi by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { - Retrofit.Builder() - .baseUrl(BuildConfig.GOODROAD_SERVER_URL) - .client(client) - .addConverterFactory(GsonConverterFactory.create()) - .build() - .create(UserApi::class.java) + retrofit().create(UserApi::class.java) + } + + val reviewApi: ReviewApi by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + retrofit().create(ReviewApi::class.java) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/data/review/ReviewApi.kt b/app/src/main/java/com/example/goodroad/data/review/ReviewApi.kt new file mode 100644 index 0000000..7db2e75 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/review/ReviewApi.kt @@ -0,0 +1,25 @@ +package com.example.goodroad.data.review + +import retrofit2.Response +import retrofit2.http.* + +interface ReviewApi { + + @GET("/reviews/own") + suspend fun getOwnReviews(): Response> + + @GET("/reviews/points") + suspend fun getOwnReviewPoints(): Response + + @POST("/reviews") + suspend fun createReview(@Body req: UpsertReviewReq): Response + + @PATCH("/reviews/{id}") + suspend fun updateReview( + @Path("id") reviewId: String, + @Body req: UpsertReviewReq + ): Response + + @DELETE("/reviews/{id}") + suspend fun deleteReview(@Path("id") reviewId: String): Response +} diff --git a/app/src/main/java/com/example/goodroad/data/review/ReviewModels.kt b/app/src/main/java/com/example/goodroad/data/review/ReviewModels.kt new file mode 100644 index 0000000..6c280ad --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/review/ReviewModels.kt @@ -0,0 +1,47 @@ +package com.example.goodroad.data.review + +data class ReviewAddress( + val country: String, + val region: String, + val localityType: String, + val city: String, + val street: String, + val house: String, + val placeName: String? = null +) + +data class ReviewObstacle( + val obstacleType: String, + val severity: Short +) + +data class UpsertReviewReq( + val latitude: Double, + val longitude: Double, + val address: ReviewAddress, + val rating: Short, + val obstacles: List, + val comment: String? = null, + val photoUrls: List = emptyList() +) + +data class ReviewCardResp( + val id: String, + val featureId: String, + val address: ReviewAddress, + val latitude: Double, + val longitude: Double, + val rating: Short, + val obstacles: List, + val comment: String? = null, + val photoUrls: List = emptyList(), + val status: String, + val createdAt: String, + val awardedPoints: Int, + val moderatorComment: String? = null +) + +data class ReviewPointsResp( + val totalPoints: Int, + val approvedReviews: Long +) diff --git a/app/src/main/java/com/example/goodroad/data/review/ReviewRepository.kt b/app/src/main/java/com/example/goodroad/data/review/ReviewRepository.kt new file mode 100644 index 0000000..7a73cce --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/review/ReviewRepository.kt @@ -0,0 +1,59 @@ +package com.example.goodroad.data.review + +import com.example.goodroad.data.user.UserApi +import okhttp3.MultipartBody +import retrofit2.HttpException + +class ReviewRepository( + private val reviewApi: ReviewApi, + private val userApi: UserApi +) { + + suspend fun getOwnReviews(): List { + val response = reviewApi.getOwnReviews() + if (response.isSuccessful) { + return response.body().orEmpty() + } + throw HttpException(response) + } + + suspend fun getOwnReviewPoints(): ReviewPointsResp? { + val response = reviewApi.getOwnReviewPoints() + if (response.isSuccessful) { + return response.body() + } + throw HttpException(response) + } + + suspend fun createReview(req: UpsertReviewReq): ReviewCardResp? { + val response = reviewApi.createReview(req) + if (response.isSuccessful) { + return response.body() + } + throw HttpException(response) + } + + suspend fun updateReview(reviewId: String, req: UpsertReviewReq): ReviewCardResp? { + val response = reviewApi.updateReview(reviewId, req) + if (response.isSuccessful) { + return response.body() + } + throw HttpException(response) + } + + suspend fun deleteReview(reviewId: String) { + val response = reviewApi.deleteReview(reviewId) + if (!response.isSuccessful) { + throw HttpException(response) + } + } + + suspend fun uploadReviewPhoto(file: MultipartBody.Part): String { + val response = userApi.uploadAvatar(file) + if (!response.isSuccessful) { + throw HttpException(response) + } + val body = response.body() ?: throw IllegalStateException("Сервер не вернул ссылку на фото") + return body.photoUrl + } +} diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewDetailsScreen.kt b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewDetailsScreen.kt new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewFormScreen.kt b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewFormScreen.kt new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/UserReviewsScreen.kt b/app/src/main/java/com/example/goodroad/ui/reviews/UserReviewsScreen.kt new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/ReviewsViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/ReviewsViewModel.kt new file mode 100644 index 0000000..6ba093f --- /dev/null +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/ReviewsViewModel.kt @@ -0,0 +1,202 @@ +package com.example.goodroad.ui.viewmodel + +import android.content.Context +import android.net.Uri +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.goodroad.data.review.* +import kotlinx.coroutines.launch +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.asRequestBody +import retrofit2.HttpException +import java.io.File +import java.io.IOException + +class ReviewsViewModel(private val repository: ReviewRepository) : ViewModel() { + + var reviews = mutableStateOf>(emptyList()) + private set + + var points = mutableStateOf(null) + private set + + var isLoading = mutableStateOf(false) + private set + + var isSubmitting = mutableStateOf(false) + private set + + var isPhotoUploading = mutableStateOf(false) + private set + + var errorMessage = mutableStateOf(null) + private set + + var successMessage = mutableStateOf(null) + private set + + fun loadReviews() { + viewModelScope.launch { + isLoading.value = true + errorMessage.value = null + + try { + points.value = repository.getOwnReviewPoints() + reviews.value = repository.getOwnReviews() + } catch (e: Exception) { + errorMessage.value = mapReviewError(e) + } finally { + isLoading.value = false + } + } + } + + fun createReview(req: UpsertReviewReq, onSuccess: () -> Unit) { + viewModelScope.launch { + isSubmitting.value = true + errorMessage.value = null + successMessage.value = null + + try { + repository.createReview(req) + successMessage.value = "Отзыв отправлен на модерацию" + points.value = repository.getOwnReviewPoints() + reviews.value = repository.getOwnReviews() + onSuccess() + } catch (e: Exception) { + errorMessage.value = mapReviewError(e) + } finally { + isSubmitting.value = false + } + } + } + + fun updateReview(reviewId: String, req: UpsertReviewReq, onSuccess: () -> Unit) { + viewModelScope.launch { + isSubmitting.value = true + errorMessage.value = null + successMessage.value = null + + try { + repository.updateReview(reviewId, req) + successMessage.value = "Изменения сохранены. Отзыв снова отправлен на модерацию" + points.value = repository.getOwnReviewPoints() + reviews.value = repository.getOwnReviews() + onSuccess() + } catch (e: Exception) { + errorMessage.value = mapReviewError(e) + } finally { + isSubmitting.value = false + } + } + } + + fun deleteReview(reviewId: String, onSuccess: () -> Unit) { + viewModelScope.launch { + isSubmitting.value = true + errorMessage.value = null + successMessage.value = null + + try { + repository.deleteReview(reviewId) + successMessage.value = "Отзыв удален" + points.value = repository.getOwnReviewPoints() + reviews.value = repository.getOwnReviews() + onSuccess() + } catch (e: Exception) { + errorMessage.value = mapReviewError(e) + } finally { + isSubmitting.value = false + } + } + } + + fun uploadReviewPhotos( + context: Context, + uris: List, + onSuccess: (List) -> Unit + ) { + if (uris.isEmpty()) return + + viewModelScope.launch { + isPhotoUploading.value = true + errorMessage.value = null + successMessage.value = null + + try { + val resolver = context.contentResolver + val uploadedUrls = mutableListOf() + + for (uri in uris) { + val mimeType = resolver.getType(uri) ?: "image/*" + val extension = MimeTypeMap.resolveExtension(mimeType) + val tempFile = File.createTempFile("review_photo", extension, context.cacheDir) + + try { + resolver.openInputStream(uri)?.use { input -> + tempFile.outputStream().use { output -> + input.copyTo(output) + } + } ?: throw IllegalArgumentException("Не удалось прочитать выбранный файл") + + val requestBody = tempFile.asRequestBody(mimeType.toMediaTypeOrNull()) + val part = MultipartBody.Part.createFormData("file", tempFile.name, requestBody) + uploadedUrls += repository.uploadReviewPhoto(part) + } finally { + tempFile.delete() + } + } + + successMessage.value = if (uploadedUrls.size == 1) { + "Фотография добавлена" + } else { + "Фотографии добавлены" + } + onSuccess(uploadedUrls) + } catch (e: Exception) { + errorMessage.value = mapReviewError(e) + } finally { + isPhotoUploading.value = false + } + } + } + + fun clearMessages() { + errorMessage.value = null + successMessage.value = null + } + + fun clearSuccessMessage() { + successMessage.value = null + } + + private fun mapReviewError(e: Exception): String { + return when (e) { + is IllegalArgumentException -> e.message ?: "Некорректные данные отзыва" + is HttpException -> when (e.code()) { + 400 -> "Проверьте поля отзыва" + 401 -> "Вы не авторизованы" + 404 -> "Отзыв или препятствие не найдены" + 409 -> "Такой отзыв уже существует" + 413 -> "Файл слишком большой" + 500 -> "Сервер временно недоступен" + else -> "Не удалось выполнить операцию с отзывом" + } + is IOException -> "Проверьте подключение к интернету" + else -> e.message ?: "Неизвестная ошибка" + } + } + + private object MimeTypeMap { + fun resolveExtension(mimeType: String): String { + return when (mimeType) { + "image/jpeg" -> ".jpg" + "image/png" -> ".png" + "image/webp" -> ".webp" + else -> ".tmp" + } + } + } +} From 52f2cffc131de911f86184f0be753b0dbf546a06 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 03:04:32 +0300 Subject: [PATCH 36/65] feat(reviews): add navigation to user reviews from profile --- .../com/example/goodroad/ui/user/UserNav.kt | 73 +++++++++++++++++-- .../goodroad/ui/user/UserProfileScreen.kt | 26 +++++-- 2 files changed, 83 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt index b829f10..37b5822 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt @@ -1,39 +1,52 @@ package com.example.goodroad.ui.user +import androidx.compose.runtime.* import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import com.example.goodroad.data.network.ApiClient +import com.example.goodroad.data.review.* import com.example.goodroad.data.user.UserRepository import com.example.goodroad.ui.maps.MapsNav -import com.example.goodroad.ui.viewmodel.UserViewModel -import androidx.compose.runtime.* +import com.example.goodroad.ui.reviews.* +import com.example.goodroad.ui.viewmodel.* + @Composable fun UserNav(onLogout: () -> Unit) { - val api = ApiClient.userApi + val userApi = ApiClient.userApi + val reviewApi = ApiClient.reviewApi val factory = object : ViewModelProvider.Factory { override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(UserViewModel::class.java)) { @Suppress("UNCHECKED_CAST") - return UserViewModel(UserRepository(api)) as T + return UserViewModel(UserRepository(userApi)) as T + } + if (modelClass.isAssignableFrom(ReviewsViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return ReviewsViewModel(ReviewRepository(reviewApi, userApi)) as T } throw IllegalArgumentException("Unknown ViewModel class") } } val userViewModel: UserViewModel = viewModel(factory = factory) + val reviewsViewModel: ReviewsViewModel = viewModel(factory = factory) var screen by remember { mutableStateOf("profile") } + var selectedReview by remember { mutableStateOf(null) } when (screen) { - "profile" -> UserProfileScreen( userViewModel = userViewModel, onEdit = { screen = "edit" }, onDelete = { screen = "delete" }, onLogout = onLogout, - onSelectObstacles = { screen = "obstacles" } + onSelectObstacles = { screen = "obstacles" }, + onOpenReviews = { + selectedReview = null + screen = "reviews" + } ) "edit" -> UserEditScreen( @@ -52,9 +65,53 @@ fun UserNav(onLogout: () -> Unit) { onBackToProfile = { screen = "profile" }, - onSaveObstacles = { selected -> + onSaveObstacles = { screen = "profile" } ) + + "reviews" -> UserReviewsScreen( + reviewsViewModel = reviewsViewModel, + onBack = { screen = "profile" }, + onAddReview = { + selectedReview = null + screen = "review_form" + }, + onOpenDetails = { + selectedReview = it + screen = "review_details" + }, + onEditReview = { + selectedReview = it + screen = "review_form" + } + ) + + "review_form" -> ReviewFormScreen( + reviewsViewModel = reviewsViewModel, + initialReview = selectedReview, + onBack = { screen = "reviews" }, + onSaved = { + selectedReview = null + screen = "reviews" + } + ) + + "review_details" -> selectedReview?.let { review -> + ReviewDetailsScreen( + review = review, + reviewsViewModel = reviewsViewModel, + onBack = { screen = "reviews" }, + onEdit = { screen = "review_form" }, + onDeleted = { + selectedReview = null + screen = "reviews" + } + ) + } ?: run { + LaunchedEffect(Unit) { + screen = "reviews" + } + } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt index 0fe7098..83a0e87 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt @@ -1,5 +1,8 @@ package com.example.goodroad.ui.user +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -7,18 +10,17 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.goodroad.ui.auth.AuthButton -import com.example.goodroad.ui.viewmodel.UserViewModel -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.* import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.viewmodel.UserViewModel + @Composable fun UserProfileScreen( userViewModel: UserViewModel, onEdit: () -> Unit, onDelete: () -> Unit, onLogout: () -> Unit, - onSelectObstacles: () -> Unit + onSelectObstacles: () -> Unit, + onOpenReviews: () -> Unit ) { val user by userViewModel.user val isLoading by userViewModel.isLoading @@ -55,7 +57,6 @@ fun UserProfileScreen( .fillMaxSize() .padding(24.dp) ) { - UserDecor() Text( @@ -75,7 +76,6 @@ fun UserProfileScreen( Column( modifier = Modifier.padding(16.dp) ) { - Text( text = "${u.firstName ?: ""} ${u.lastName ?: ""}", fontSize = 22.sp, @@ -114,6 +114,16 @@ fun UserProfileScreen( Spacer(Modifier.height(10.dp)) + AuthButton( + text = "Мои отзывы", + backgroundColor = SafeGreen, + contentColor = WhiteSoft + ) { + onOpenReviews() + } + + Spacer(Modifier.height(10.dp)) + AuthButton(text = "Редактировать", onClick = onEdit) Spacer(Modifier.height(10.dp)) @@ -135,4 +145,4 @@ fun UserProfileScreen( } } } -} \ No newline at end of file +} From 3709e2558f4a35793ccf659fa6ef716896d2ca1b Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 03:05:03 +0300 Subject: [PATCH 37/65] feat(reviews-ui): implement review list, form and details screens --- .../ui/reviews/ReviewDetailsScreen.kt | 161 +++++++ .../goodroad/ui/reviews/ReviewFormScreen.kt | 414 ++++++++++++++++++ .../example/goodroad/ui/reviews/ReviewUi.kt | 278 ++++++++++++ .../goodroad/ui/reviews/UserReviewsScreen.kt | 195 +++++++++ 4 files changed, 1048 insertions(+) diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewDetailsScreen.kt b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewDetailsScreen.kt index e69de29..269c1e2 100644 --- a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewDetailsScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewDetailsScreen.kt @@ -0,0 +1,161 @@ +package com.example.goodroad.ui.reviews + +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.example.goodroad.data.review.* +import com.example.goodroad.ui.auth.* +import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.user.UserDecor +import com.example.goodroad.ui.viewmodel.ReviewsViewModel + +@Composable +fun ReviewDetailsScreen( + review: ReviewCardResp, + reviewsViewModel: ReviewsViewModel, + onBack: () -> Unit, + onEdit: () -> Unit, + onDeleted: () -> Unit +) { + val isSubmitting by reviewsViewModel.isSubmitting + val errorMessage by reviewsViewModel.errorMessage + var showDeleteDialog by remember { mutableStateOf(false) } + + if (showDeleteDialog) { + AlertDialog( + onDismissRequest = { showDeleteDialog = false }, + title = { + Text( + text = "Вы уверены?", + color = TextPrimary + ) + }, + text = { + Text( + text = "Отзыв будет удален без возможности восстановления.", + color = UrbanBrown + ) + }, + confirmButton = { + TextButton( + onClick = { + showDeleteDialog = false + reviewsViewModel.deleteReview(review.id, onDeleted) + } + ) { + Text("Да", color = AlertRed) + } + }, + dismissButton = { + TextButton(onClick = { showDeleteDialog = false }) { + Text("Нет", color = UrbanBrown) + } + } + ) + } + + Surface( + modifier = Modifier.fillMaxSize(), + color = BackgroundLight + ) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(24.dp) + ) { + UserDecor() + + Text( + text = "Подробности отзыва", + style = MaterialTheme.typography.headlineLarge, + color = TextPrimary + ) + + Spacer(Modifier.height(20.dp)) + + OutlinedCard( + modifier = Modifier.fillMaxWidth(), + border = BorderStroke(2.dp, moderationStatusColor(review.status)), + colors = CardDefaults.outlinedCardColors(containerColor = BackgroundLight) + ) { + Column(modifier = Modifier.padding(16.dp)) { + ReviewCardSummary(review) + + Spacer(Modifier.height(16.dp)) + ReviewInfoRow("Координаты", "${review.latitude}, ${review.longitude}") + Spacer(Modifier.height(12.dp)) + ReviewInfoRow("Комментарий", review.comment?.ifBlank { "—" } ?: "—") + Spacer(Modifier.height(12.dp)) + ReviewInfoRow( + "Комментарий модератора", + review.moderatorComment?.ifBlank { "—" } ?: "—" + ) + + Spacer(Modifier.height(16.dp)) + Text( + text = "Фотографии", + style = MaterialTheme.typography.titleMedium, + color = UrbanBrown + ) + Spacer(Modifier.height(8.dp)) + ReviewPhotosStrip(review.photoUrls) + + Spacer(Modifier.height(16.dp)) + Text( + text = "Препятствия", + style = MaterialTheme.typography.titleMedium, + color = UrbanBrown + ) + Spacer(Modifier.height(8.dp)) + review.obstacles.forEach { obstacle -> + Text( + text = "${obstacleLabel(obstacle.obstacleType)} — ${obstacleSeverityText(obstacle.severity.toInt())}", + style = MaterialTheme.typography.bodyLarge, + color = TextPrimary + ) + Spacer(Modifier.height(4.dp)) + } + } + } + + AuthStatusText(errorMessage) + + Spacer(Modifier.height(20.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + ReviewSquareActionButton( + text = "Редактировать", + backgroundColor = SafeGreen, + modifier = Modifier.weight(1f) + ) { + onEdit() + } + ReviewSquareActionButton( + text = if (isSubmitting) "Удаляем..." else "Удалить", + backgroundColor = AlertRed, + modifier = Modifier.weight(1f), + enabled = !isSubmitting + ) { + showDeleteDialog = true + } + } + + Spacer(Modifier.height(12.dp)) + + ReviewSquareActionButton( + text = "Назад к отзывам", + backgroundColor = UrbanBrown, + modifier = Modifier.fillMaxWidth() + ) { + onBack() + } + } + } +} diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewFormScreen.kt b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewFormScreen.kt index e69de29..aec1ebb 100644 --- a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewFormScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewFormScreen.kt @@ -0,0 +1,414 @@ +package com.example.goodroad.ui.reviews + +import android.content.Context +import android.location.Address +import android.location.Geocoder +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Photo +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp +import com.example.goodroad.data.review.* +import com.example.goodroad.ui.auth.* +import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.user.UserDecor +import com.example.goodroad.ui.viewmodel.ReviewsViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.util.Locale + +@Composable +fun ReviewFormScreen( + reviewsViewModel: ReviewsViewModel, + initialReview: ReviewCardResp?, + onBack: () -> Unit, + onSaved: () -> Unit +) { + val context = LocalContext.current + val scope = rememberCoroutineScope() + val isEdit = initialReview != null + val reviewKey = initialReview?.id ?: "new" + + var placeName by remember(reviewKey) { mutableStateOf(initialReview?.address?.placeName ?: "") } + var latitude by remember(reviewKey) { mutableStateOf(initialReview?.latitude?.toString() ?: "") } + var longitude by remember(reviewKey) { mutableStateOf(initialReview?.longitude?.toString() ?: "") } + var rating by remember(reviewKey) { mutableStateOf(initialReview?.rating?.toInt()) } + var comment by remember(reviewKey) { mutableStateOf(initialReview?.comment ?: "") } + var formError by remember(reviewKey) { mutableStateOf(null) } + val photoUrls = remember(reviewKey) { + mutableStateListOf().apply { + addAll(initialReview?.photoUrls.orEmpty()) + } + } + + val obstacleSeverities = remember(reviewKey) { + mutableStateMapOf().apply { + ReviewObstacleTypes.forEach { type -> + val current = initialReview?.obstacles + ?.firstOrNull { it.obstacleType == type } + ?.severity + ?.toInt() + ?: 0 + put(type, current) + } + } + } + + val isSubmitting by reviewsViewModel.isSubmitting + val isPhotoUploading by reviewsViewModel.isPhotoUploading + val serverError by reviewsViewModel.errorMessage + + val photoPickerLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.GetMultipleContents() + ) { uris -> + if (uris.isNotEmpty()) { + reviewsViewModel.uploadReviewPhotos(context, uris) { uploadedUrls -> + photoUrls.addAll(uploadedUrls) + } + } + } + + Surface( + modifier = Modifier.fillMaxSize(), + color = BackgroundLight + ) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(24.dp) + ) { + UserDecor() + + Text( + text = if (isEdit) "Редактирование отзыва" else "Новый отзыв", + style = MaterialTheme.typography.headlineLarge, + color = TextPrimary + ) + + Spacer(Modifier.height(20.dp)) + + PlainField( + value = placeName, + onValueChange = { placeName = it }, + label = "Название места" + ) + + Spacer(Modifier.height(8.dp)) + + Text( + text = "Название места можно не заполнять.", + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown + ) + + Spacer(Modifier.height(16.dp)) + + PlainField( + value = latitude, + onValueChange = { latitude = it }, + label = "Широта", + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal) + ) + + Spacer(Modifier.height(12.dp)) + + PlainField( + value = longitude, + onValueChange = { longitude = it }, + label = "Долгота", + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal) + ) + + Spacer(Modifier.height(12.dp)) + + Text( + text = "Адрес будет определен автоматически по введенным координатам.", + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown + ) + + Spacer(Modifier.height(8.dp)) + + Text( + text = "Координаты должны быть в числовом формате. Можно использовать точку или запятую.", + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown + ) + + Spacer(Modifier.height(20.dp)) + + Text( + text = "Оценка", + style = MaterialTheme.typography.titleMedium, + color = UrbanBrown + ) + Spacer(Modifier.height(8.dp)) + SeveritySelector( + value = rating, + range = 1..5, + onValueChange = { rating = it } + ) + + Spacer(Modifier.height(20.dp)) + + Text( + text = "Препятствия и их тяжесть", + style = MaterialTheme.typography.titleMedium, + color = UrbanBrown + ) + Spacer(Modifier.height(8.dp)) + Text( + text = "0 — нет такого препятствия, 1 — слабая тяжесть, 2 — средняя тяжесть, 3 — сильная тяжесть.", + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown + ) + Spacer(Modifier.height(4.dp)) + Text( + text = "Хотя бы у одного препятствия тяжесть должна быть больше 0.", + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown + ) + + ReviewObstacleTypes.forEach { type -> + Spacer(Modifier.height(12.dp)) + Text( + text = obstacleLabel(type), + style = MaterialTheme.typography.bodyLarge, + color = TextPrimary + ) + Spacer(Modifier.height(6.dp)) + SeveritySelector( + value = obstacleSeverities[type] ?: 0, + range = 0..3, + onValueChange = { obstacleSeverities[type] = it } + ) + } + + Spacer(Modifier.height(20.dp)) + + TextField( + value = comment, + onValueChange = { comment = it }, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 56.dp), + label = { + Text( + text = "Комментарий", + color = UrbanBrown + ) + }, + textStyle = MaterialTheme.typography.bodyLarge.copy(color = TextPrimary), + singleLine = false, + minLines = 1, + maxLines = 5, + colors = TextFieldDefaults.colors( + focusedContainerColor = BackgroundLight, + unfocusedContainerColor = BackgroundLight, + focusedIndicatorColor = SafeGreen, + unfocusedIndicatorColor = BorderWarm, + focusedLabelColor = UrbanBrown, + unfocusedLabelColor = UrbanBrown, + cursorColor = SafeGreen + ) + ) + + Spacer(Modifier.height(16.dp)) + + OutlinedButton( + onClick = { photoPickerLauncher.launch("image/*") }, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + colors = ButtonDefaults.outlinedButtonColors( + containerColor = BackgroundLight, + contentColor = UrbanBrown + ), + border = BorderStroke(1.dp, BorderWarm) + ) { + Icon( + imageVector = Icons.Default.Photo, + contentDescription = null, + tint = UrbanBrown + ) + Spacer(Modifier.width(12.dp)) + Text( + text = if (isPhotoUploading) "Загружаем фото..." else "Добавить фотографии", + color = UrbanBrown + ) + } + + Spacer(Modifier.height(12.dp)) + + ReviewPhotosStrip( + photoUrls = photoUrls, + onRemove = { photoUrls.remove(it) } + ) + + AuthStatusText(formError ?: serverError) + + Spacer(Modifier.height(20.dp)) + + AuthButton( + text = when { + isPhotoUploading -> "Загружаем фото..." + isSubmitting -> "Сохраняем..." + isEdit -> "Сохранить изменения" + else -> "Отправить отзыв" + }, + enabled = !isSubmitting && !isPhotoUploading + ) { + scope.launch { + val validationError = validateReviewForm( + latitude = latitude, + longitude = longitude, + rating = rating, + obstacleSeverities = obstacleSeverities + ) + + if (validationError != null) { + formError = validationError + return@launch + } + + val lat = latitude.trim().replace(',', '.').toDouble() + val lon = longitude.trim().replace(',', '.').toDouble() + + val generatedAddress = resolveReviewAddress( + context = context, + latitude = lat, + longitude = lon, + placeName = placeName, + fallbackAddress = initialReview?.address + ) + + formError = null + reviewsViewModel.clearMessages() + + val request = UpsertReviewReq( + latitude = lat, + longitude = lon, + address = generatedAddress, + rating = rating!!.toShort(), + obstacles = ReviewObstacleTypes.map { type -> + ReviewObstacle( + obstacleType = type, + severity = (obstacleSeverities[type] ?: 0).toShort() + ) + }, + comment = comment.trim().ifBlank { null }, + photoUrls = photoUrls.toList() + ) + + if (isEdit) { + reviewsViewModel.updateReview(initialReview!!.id, request, onSaved) + } else { + reviewsViewModel.createReview(request, onSaved) + } + } + } + + Spacer(Modifier.height(10.dp)) + + AuthButton( + text = "Назад к отзывам", + backgroundColor = UrbanBrown + ) { + onBack() + } + } + } +} + +private fun validateReviewForm( + latitude: String, + longitude: String, + rating: Int?, + obstacleSeverities: Map +): String? { + val lat = latitude.trim().replace(',', '.').toDoubleOrNull() + val lon = longitude.trim().replace(',', '.').toDoubleOrNull() + if (lat == null || lon == null) { + return "Введите корректные координаты" + } + if (lat < -90.0 || lat > 90.0 || lon < -180.0 || lon > 180.0) { + return "Координаты выходят за допустимый диапазон" + } + if (rating == null) { + return "Поставьте оценку отзыву" + } + if (obstacleSeverities.values.none { it > 0 }) { + return "Укажите тяжесть хотя бы для одного препятствия" + } + return null +} + +private suspend fun resolveReviewAddress( + context: Context, + latitude: Double, + longitude: Double, + placeName: String, + fallbackAddress: ReviewAddress? +): ReviewAddress = withContext(Dispatchers.IO) { + val normalizedPlaceName = placeName.trim().ifBlank { null } + val baseAddress = fallbackAddress ?: ReviewAddress( + country = "Россия", + region = "Регион не указан", + localityType = "город", + city = "Населенный пункт не указан", + street = "Улица не указана", + house = "Без номера", + placeName = normalizedPlaceName + ) + val geocoder = Geocoder(context, Locale("ru")) + + return@withContext try { + val rawAddress = geocoder.getFromLocation(latitude, longitude, 1)?.firstOrNull() + if (rawAddress == null) { + baseAddress.copy(placeName = normalizedPlaceName) + } else { + ReviewAddress( + country = rawAddress.countryName?.takeIf { it.isNotBlank() } + ?: baseAddress.country, + region = listOf(rawAddress.adminArea, rawAddress.subAdminArea) + .firstNotBlank() + ?: baseAddress.region, + localityType = detectLocalityType(rawAddress, baseAddress), + city = listOf(rawAddress.locality, rawAddress.subLocality, rawAddress.subAdminArea, rawAddress.adminArea) + .firstNotBlank() + ?: baseAddress.city, + street = listOf(rawAddress.thoroughfare, rawAddress.subLocality, rawAddress.featureName) + .firstNotBlank() + ?: baseAddress.street, + house = listOf(rawAddress.subThoroughfare, rawAddress.premises) + .firstNotBlank() + ?: baseAddress.house, + placeName = normalizedPlaceName + ) + } + } catch (_: Exception) { + baseAddress.copy(placeName = normalizedPlaceName) + } +} + +private fun List.firstNotBlank(): String? { + return firstOrNull { !it.isNullOrBlank() }?.trim() +} + +private fun detectLocalityType(address: Address, fallbackAddress: ReviewAddress): String { + return when { + !address.locality.isNullOrBlank() -> "город" + !address.subAdminArea.isNullOrBlank() -> "район" + else -> fallbackAddress.localityType + } +} diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt index e69de29..2039368 100644 --- a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt +++ b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt @@ -0,0 +1,278 @@ +package com.example.goodroad.ui.reviews + +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.sp +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.example.goodroad.data.review.* +import com.example.goodroad.ui.theme.* +import java.time.Instant +import java.time.ZoneId +import java.time.format.DateTimeFormatter + +val ReviewObstacleTypes = listOf( + "CURB", + "STAIRS", + "ROAD_SLOPE", + "POTHOLES", + "SAND", + "GRAVEL" +) + +fun obstacleLabel(type: String): String { + return when (type) { + "CURB" -> "Бордюр" + "STAIRS" -> "Лестницы" + "ROAD_SLOPE" -> "Наклон дороги" + "POTHOLES" -> "Ямы" + "SAND" -> "Песок" + "GRAVEL" -> "Гравий" + else -> type + } +} + +fun obstacleSeverityText(severity: Int): String { + return when (severity) { + 0 -> "нет" + 1 -> "слабая тяжесть" + 2 -> "средняя тяжесть" + 3 -> "сильная тяжесть" + else -> severity.toString() + } +} + +fun moderationStatusText(status: String): String { + return when (status) { + "APPROVED" -> "Одобрен" + "REJECTED" -> "Отклонен" + else -> "На модерации" + } +} + +fun moderationStatusColor(status: String): Color { + return when (status) { + "APPROVED" -> SafeGreen + "REJECTED" -> AlertRed + else -> UrbanBrown + } +} + +fun buildAddressLine(address: ReviewAddress): String { + val parts = listOf( + address.country, + address.region, + address.localityType, + address.city, + address.street, + address.house, + address.placeName + ) + return parts.filterNot { it.isNullOrBlank() }.joinToString(", ") +} + +fun formatReviewDate(raw: String): String { + return try { + val instant = Instant.parse(raw) + val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") + formatter.format(instant.atZone(ZoneId.systemDefault())) + } catch (_: Exception) { + raw.take(10) + } +} + +@Composable +fun ReviewInfoRow(label: String, value: String) { + Column(modifier = Modifier.fillMaxWidth()) { + Text( + text = label, + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown + ) + Text( + text = value, + style = MaterialTheme.typography.bodyLarge, + color = TextPrimary + ) + } +} + +@Composable +fun SeveritySelector( + value: Int?, + range: IntRange, + onValueChange: (Int) -> Unit +) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + range.forEach { item -> + val selected = item == value + OutlinedButton( + onClick = { onValueChange(item) }, + border = BorderStroke(1.dp, if (selected) SafeGreen else BorderWarm), + colors = ButtonDefaults.outlinedButtonColors( + containerColor = if (selected) SafeGreen.copy(alpha = 0.12f) else Color.Transparent, + contentColor = if (selected) SafeGreen else UrbanBrown + ), + shape = RoundedCornerShape(20.dp), + modifier = Modifier.weight(1f), + contentPadding = PaddingValues(horizontal = 0.dp, vertical = 12.dp) + ) { + Text(item.toString()) + } + } + } +} + +@Composable +fun ReviewStatusBadge(status: String) { + val color = moderationStatusColor(status) + Surface( + color = color.copy(alpha = 0.08f), + contentColor = color, + shape = RoundedCornerShape(18.dp), + border = BorderStroke(1.dp, color) + ) { + Box( + modifier = Modifier.padding(horizontal = 14.dp, vertical = 8.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = moderationStatusText(status), + style = MaterialTheme.typography.bodyMedium + ) + } + } +} + +@Composable +fun ReviewCardSummary(review: ReviewCardResp) { + Column(modifier = Modifier.fillMaxWidth()) { + ReviewInfoRow("Адрес", buildAddressLine(review.address)) + Spacer(Modifier.height(8.dp)) + ReviewInfoRow("Оценка", review.rating.toString()) + Spacer(Modifier.height(8.dp)) + ReviewInfoRow("Баллы", review.awardedPoints.toString()) + Spacer(Modifier.height(8.dp)) + ReviewInfoRow("Дата", formatReviewDate(review.createdAt)) + Spacer(Modifier.height(12.dp)) + ReviewStatusBadge(review.status) + } +} + +@Composable +fun ReviewActionButton( + text: String, + modifier: Modifier = Modifier, + backgroundColor: Color = SafeGreen, + enabled: Boolean = true, + onClick: () -> Unit +) { + Button( + onClick = onClick, + enabled = enabled, + modifier = modifier.height(56.dp), + colors = ButtonDefaults.buttonColors( + containerColor = backgroundColor, + contentColor = WhiteSoft, + disabledContainerColor = backgroundColor.copy(alpha = 0.6f), + disabledContentColor = WhiteSoft + ), + shape = RoundedCornerShape(16.dp) + ) { + Text( + text = text, + style = MaterialTheme.typography.labelLarge, + fontWeight = FontWeight.SemiBold, + maxLines = 1, + softWrap = false, + textAlign = TextAlign.Center + ) + } +} + +@Composable +fun ReviewSquareActionButton( + text: String, + modifier: Modifier = Modifier, + backgroundColor: Color = SafeGreen, + enabled: Boolean = true, + onClick: () -> Unit +) { + Button( + onClick = onClick, + enabled = enabled, + modifier = modifier.height(64.dp), + colors = ButtonDefaults.buttonColors( + containerColor = backgroundColor, + contentColor = WhiteSoft, + disabledContainerColor = backgroundColor.copy(alpha = 0.6f), + disabledContentColor = WhiteSoft + ), + shape = RoundedCornerShape(18.dp) + ) { + Text( + text = text, + fontSize = 17.sp, + fontWeight = FontWeight.SemiBold, + maxLines = 1, + softWrap = false, + textAlign = TextAlign.Center + ) + } +} + +@Composable +fun ReviewPhotosStrip( + photoUrls: List, + onRemove: ((String) -> Unit)? = null +) { + if (photoUrls.isEmpty()) { + Text( + text = "Фото не добавлены", + style = MaterialTheme.typography.bodyMedium, + color = UrbanBrown + ) + return + } + + Row( + modifier = Modifier + .fillMaxWidth() + .horizontalScroll(rememberScrollState()), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + photoUrls.forEach { url -> + Column { + AsyncImage( + model = url, + contentDescription = null, + modifier = Modifier + .size(120.dp) + .clip(RoundedCornerShape(16.dp)), + contentScale = ContentScale.Crop + ) + if (onRemove != null) { + TextButton(onClick = { onRemove(url) }) { + Text( + text = "Убрать", + color = UrbanBrown + ) + } + } + } + } + } +} diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/UserReviewsScreen.kt b/app/src/main/java/com/example/goodroad/ui/reviews/UserReviewsScreen.kt index e69de29..f80efaa 100644 --- a/app/src/main/java/com/example/goodroad/ui/reviews/UserReviewsScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/reviews/UserReviewsScreen.kt @@ -0,0 +1,195 @@ +package com.example.goodroad.ui.reviews + +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.example.goodroad.data.review.* +import com.example.goodroad.ui.auth.* +import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.user.UserDecor +import com.example.goodroad.ui.viewmodel.ReviewsViewModel +import kotlinx.coroutines.delay + +@Composable +fun UserReviewsScreen( + reviewsViewModel: ReviewsViewModel, + onBack: () -> Unit, + onAddReview: () -> Unit, + onOpenDetails: (ReviewCardResp) -> Unit, + onEditReview: (ReviewCardResp) -> Unit +) { + val reviews by reviewsViewModel.reviews + val isLoading by reviewsViewModel.isLoading + val errorMessage by reviewsViewModel.errorMessage + val successMessage by reviewsViewModel.successMessage + + val approvedCount = reviews.count { it.status == "APPROVED" } + val rejectedCount = reviews.count { it.status == "REJECTED" } + val pendingCount = reviews.count { it.status == "PENDING" } + + LaunchedEffect(Unit) { + reviewsViewModel.loadReviews() + } + + LaunchedEffect(successMessage) { + if (successMessage != null) { + delay(10_000) + reviewsViewModel.clearSuccessMessage() + } + } + + Surface( + modifier = Modifier.fillMaxSize(), + color = BackgroundLight + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(24.dp) + ) { + UserDecor() + + Text( + text = "Мои отзывы", + style = MaterialTheme.typography.headlineLarge, + color = TextPrimary + ) + + Spacer(Modifier.height(20.dp)) + + OutlinedCard( + modifier = Modifier.fillMaxWidth(), + border = BorderStroke(1.dp, UrbanBrown.copy(alpha = 0.4f)), + colors = CardDefaults.outlinedCardColors( + containerColor = UrbanBrown.copy(alpha = 0.06f) + ) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = "Статистика по отзывам", + style = MaterialTheme.typography.titleMedium, + color = UrbanBrown + ) + Spacer(Modifier.height(8.dp)) + Text( + text = "Одобренных отзывов: $approvedCount", + style = MaterialTheme.typography.bodyLarge, + color = UrbanBrown + ) + Text( + text = "Отклоненных отзывов: $rejectedCount", + style = MaterialTheme.typography.bodyLarge, + color = UrbanBrown + ) + Text( + text = "На модерации: $pendingCount", + style = MaterialTheme.typography.bodyLarge, + color = UrbanBrown + ) + } + } + + Spacer(Modifier.height(16.dp)) + + AuthButton(text = "Добавить отзыв") { + reviewsViewModel.clearMessages() + onAddReview() + } + + Spacer(Modifier.height(10.dp)) + + AuthButton( + text = "Назад в профиль", + backgroundColor = UrbanBrown + ) { + onBack() + } + + AuthSuccessText(successMessage) + AuthStatusText(errorMessage) + + Spacer(Modifier.height(16.dp)) + + if (isLoading) { + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } else if (reviews.isEmpty()) { + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + contentAlignment = Alignment.Center + ) { + Text( + text = "Пока нет ни одного отзыва", + style = MaterialTheme.typography.bodyLarge, + color = UrbanBrown + ) + } + } else { + LazyColumn( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + items(reviews, key = { it.id }) { review -> + ReviewListItem( + review = review, + onOpenDetails = { onOpenDetails(review) }, + onEdit = { onEditReview(review) } + ) + } + } + } + } + } +} + +@Composable +private fun ReviewListItem( + review: ReviewCardResp, + onOpenDetails: () -> Unit, + onEdit: () -> Unit +) { + OutlinedCard( + modifier = Modifier.fillMaxWidth(), + border = BorderStroke(2.dp, moderationStatusColor(review.status)), + colors = CardDefaults.outlinedCardColors(containerColor = BackgroundLight) + ) { + Column(modifier = Modifier.padding(16.dp)) { + ReviewCardSummary(review) + + Spacer(Modifier.height(12.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + ReviewActionButton( + text = "Редактировать", + backgroundColor = UrbanBrown, + modifier = Modifier.weight(1f) + ) { + onEdit() + } + ReviewActionButton( + text = "Подробнее", + backgroundColor = SafeGreen, + modifier = Modifier.weight(1f) + ) { + onOpenDetails() + } + } + } + } +} From 0acee5ca9aa9946b55f85ba7d95542aa4ef212d4 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 03:25:21 +0300 Subject: [PATCH 38/65] fix: viewing of adress --- .../example/goodroad/ui/reviews/ReviewUi.kt | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt index 2039368..0226cf1 100644 --- a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt +++ b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt @@ -69,16 +69,38 @@ fun moderationStatusColor(status: String): Color { } fun buildAddressLine(address: ReviewAddress): String { - val parts = listOf( - address.country, - address.region, - address.localityType, - address.city, - address.street, - address.house, - address.placeName - ) - return parts.filterNot { it.isNullOrBlank() }.joinToString(", ") + val parts = mutableListOf() + + address.country + ?.takeIf { it.isNotBlank() } + ?.let { parts += it } + + address.region + ?.takeIf { it.isNotBlank() } + ?.let { parts += it } + + val locality = listOfNotNull( + address.localityType?.takeIf { it.isNotBlank() }, + address.city?.takeIf { it.isNotBlank() } + ).joinToString(" ") + + if (locality.isNotBlank()) { + parts += locality + } + + address.street + ?.takeIf { it.isNotBlank() } + ?.let { parts += "ул. $it" } + + address.house + ?.takeIf { it.isNotBlank() } + ?.let { parts += "д. $it" } + + address.placeName + ?.takeIf { it.isNotBlank() } + ?.let { parts += it } + + return parts.joinToString(", ") } fun formatReviewDate(raw: String): String { From 8d2d1be8378691bce5def184284ab3444c496fe5 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 22:00:27 +0300 Subject: [PATCH 39/65] fix(auth): auto-hide status messages on auth screens --- .../goodroad/ui/auth/AuthComponents.kt | 22 +++++++++++++++---- .../example/goodroad/ui/auth/LoginScreen.kt | 8 ++++++- .../goodroad/ui/auth/RecoverPasswordScreen.kt | 21 ++++++++++++++---- .../goodroad/ui/auth/RegisterScreen.kt | 14 +++++++++--- .../goodroad/ui/viewmodel/AuthViewModel.kt | 8 +++++++ 5 files changed, 61 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt index 4b69798..9a7a71f 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthComponents.kt @@ -3,12 +3,14 @@ package com.example.goodroad.ui.auth import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.example.goodroad.ui.theme.* +import kotlinx.coroutines.delay + @Composable fun AuthButton( text: String, @@ -72,10 +74,16 @@ fun AuthFooter( @Composable fun AuthStatusText( - text: String? + text: String?, + onTimeout: (() -> Unit)? = null ) { if (text == null) return + LaunchedEffect(text) { + delay(5_000) + onTimeout?.invoke() + } + Spacer(Modifier.height(12.dp)) Text( text = text, @@ -86,14 +94,20 @@ fun AuthStatusText( @Composable fun AuthSuccessText( - text: String? + text: String?, + onTimeout: (() -> Unit)? = null ) { if (text == null) return + LaunchedEffect(text) { + delay(5_000) + onTimeout?.invoke() + } + Spacer(Modifier.height(12.dp)) Text( text = text, style = MaterialTheme.typography.bodySmall, color = SafeGreen ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt index 90b1caa..d735a97 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/LoginScreen.kt @@ -107,6 +107,12 @@ fun LoginScreen( } } - AuthStatusText(text = error ?: errorText) + AuthStatusText( + text = error ?: errorText, + onTimeout = { + errorText = null + viewModel.clearError() + } + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt index cfaa4e7..d412756 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RecoverPasswordScreen.kt @@ -106,7 +106,8 @@ fun RecoverPasswordScreen( tint = UrbanBrown ) }, - warning = firstNameWarning + warning = firstNameWarning, + maxLength = NAME_MAX_LENGTH ) Spacer(Modifier.height(12.dp)) @@ -130,7 +131,8 @@ fun RecoverPasswordScreen( tint = UrbanBrown ) }, - warning = lastNameWarning + warning = lastNameWarning, + maxLength = NAME_MAX_LENGTH ) Spacer(Modifier.height(12.dp)) @@ -168,7 +170,18 @@ fun RecoverPasswordScreen( label = "Подтвердите пароль" ) - AuthStatusText(text = error ?: errorText) - AuthSuccessText(text = successText) + AuthStatusText( + text = error ?: errorText, + onTimeout = { + errorText = null + viewModel.clearError() + } + ) + AuthSuccessText( + text = successText, + onTimeout = { + viewModel.clearRecoverResult() + } + ) } } diff --git a/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt index 2ed30ed..78ddeef 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/RegisterScreen.kt @@ -117,7 +117,8 @@ fun RegisterScreen( tint = UrbanBrown ) }, - warning = firstNameWarning + warning = firstNameWarning, + maxLength = NAME_MAX_LENGTH ) Spacer(Modifier.height(12.dp)) @@ -143,7 +144,8 @@ fun RegisterScreen( tint = UrbanBrown ) }, - warning = lastNameWarning + warning = lastNameWarning, + maxLength = NAME_MAX_LENGTH ) Spacer(Modifier.height(12.dp)) @@ -189,6 +191,12 @@ fun RegisterScreen( label = "Подтвердите пароль" ) - AuthStatusText(text = error ?: errorText) + AuthStatusText( + text = error ?: errorText, + onTimeout = { + errorText = null + viewModel.clearError() + } + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt index 9b8af06..94e8e89 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/AuthViewModel.kt @@ -74,6 +74,14 @@ class AuthViewModel : ViewModel() { } } + fun clearError() { + _error.value = null + } + + fun clearRecoverResult() { + _recoverResult.value = null + } + private fun mapAuthError(e: Exception, action: AuthAction): String { return when (e) { is HttpException -> when (e.code()) { From e8aca83a0611e0b09d1cfbb14d0cc7489350d4e6 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 22:01:29 +0300 Subject: [PATCH 40/65] fix(input): add max-length limits for auth fields --- .../example/goodroad/ui/auth/AuthFields.kt | 43 ++++++++++++------- .../ui/common/validation/UserValidators.kt | 7 +++ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt b/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt index e7fa192..e4147b2 100644 --- a/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt +++ b/app/src/main/java/com/example/goodroad/ui/auth/AuthFields.kt @@ -1,25 +1,27 @@ package com.example.goodroad.ui.auth -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.ui.Modifier -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.ui.unit.dp -import androidx.compose.foundation.LocalIndication -import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.* +import androidx.compose.foundation.interaction.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.* +import androidx.compose.foundation.text.* +import androidx.compose.material.icons.* +import androidx.compose.material.icons.filled.* import androidx.compose.material3.* import androidx.compose.runtime.* +import androidx.compose.ui.* import androidx.compose.ui.text.input.* +import androidx.compose.ui.unit.* +import com.example.goodroad.ui.common.validation.* import com.example.goodroad.ui.theme.* -import androidx.compose.material.icons.filled.* + @Composable fun PhoneField( value: String, onValueChange: (String) -> Unit, label: String, - warning: String? = null + warning: String? = null, + maxLength: Int = PHONE_MAX_LENGTH ) { PlainField( value = value, @@ -34,6 +36,7 @@ fun PhoneField( ) }, warning = warning, + maxLength = maxLength, prefix = { Text( text = "+", @@ -47,7 +50,8 @@ fun PhoneField( fun PasswordField( value: String, onValueChange: (String) -> Unit, - label: String + label: String, + maxLength: Int = PASSWORD_MAX_LENGTH ) { var visible by remember { mutableStateOf(false) } @@ -68,6 +72,7 @@ fun PasswordField( tint = UrbanBrown ) }, + maxLength = maxLength, trailing = { Icon( imageVector = if (visible) { @@ -93,17 +98,23 @@ fun PlainField( value: String, onValueChange: (String) -> Unit, label: String, + modifier: Modifier = Modifier, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, visualTransformation: VisualTransformation = VisualTransformation.None, icon: @Composable (() -> Unit)? = null, trailing: @Composable (() -> Unit)? = null, prefix: @Composable (() -> Unit)? = null, - warning: String? = null + warning: String? = null, + maxLength: Int = Int.MAX_VALUE ) { TextField( value = value, - onValueChange = onValueChange, - modifier = Modifier.fillMaxWidth(), + onValueChange = { newValue -> + if (newValue.length <= maxLength) { + onValueChange(newValue) + } + }, + modifier = modifier.fillMaxWidth(), label = { Text( text = label, @@ -143,4 +154,4 @@ fun PlainField( ), shape = RoundedCornerShape(18.dp) ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/goodroad/ui/common/validation/UserValidators.kt b/app/src/main/java/com/example/goodroad/ui/common/validation/UserValidators.kt index 79f0f7f..d8b79e5 100644 --- a/app/src/main/java/com/example/goodroad/ui/common/validation/UserValidators.kt +++ b/app/src/main/java/com/example/goodroad/ui/common/validation/UserValidators.kt @@ -9,6 +9,13 @@ const val CYRILLIC_WARNING = "Допустимы только кириллица const val PHONE_CHARS_WARNING = "Допустимы только цифры. Знак + добавляется автоматически" const val PHONE_FORMAT_WARNING = "Введите российский номер: 11 цифр, первая — 7 или 8" +const val NAME_MAX_LENGTH = 80 +const val PHONE_MAX_LENGTH = 11 +const val PASSWORD_MAX_LENGTH = 100 +const val PLACE_NAME_MAX_LENGTH = 180 +const val COMMENT_MAX_LENGTH = 1000 +const val COORDINATE_MAX_LENGTH = 20 + fun isAllowedCyrillicInput(value: String): Boolean { return cyrillicInputRegex.matches(value) } From 3f691cea277c00af94df12557a36f071bf3e6921 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 22:01:52 +0300 Subject: [PATCH 41/65] fix(obstacles): align obstacle API and models with backend --- .../goodroad/data/network/ApiClient.kt | 21 +++++++++++------- .../goodroad/data/obstacle/ObstacleApi.kt | 15 +++++++++++++ .../goodroad/data/obstacle/ObstacleModels.kt | 11 ++++++++++ .../data/obstacle/ObstacleRepository.kt | 22 +++++++++++++++++++ 4 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/example/goodroad/data/obstacle/ObstacleApi.kt create mode 100644 app/src/main/java/com/example/goodroad/data/obstacle/ObstacleModels.kt create mode 100644 app/src/main/java/com/example/goodroad/data/obstacle/ObstacleRepository.kt diff --git a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt index 961a83f..d3ea79b 100644 --- a/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt +++ b/app/src/main/java/com/example/goodroad/data/network/ApiClient.kt @@ -1,14 +1,15 @@ package com.example.goodroad.data.network import com.example.goodroad.BuildConfig -import com.example.goodroad.data.auth.AuthApi -import com.example.goodroad.data.review.ReviewApi -import com.example.goodroad.data.user.UserApi +import com.example.goodroad.data.auth.* +import com.example.goodroad.data.obstacle.* +import com.example.goodroad.data.review.* +import com.example.goodroad.data.user.* import okhttp3.* -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory -import java.util.concurrent.TimeUnit +import okhttp3.logging.* +import retrofit2.* +import retrofit2.converter.gson.* +import java.util.concurrent.* object ApiClient { @@ -67,7 +68,11 @@ object ApiClient { retrofit().create(UserApi::class.java) } + val obstacleApi: ObstacleApi by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + retrofit().create(ObstacleApi::class.java) + } + val reviewApi: ReviewApi by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { retrofit().create(ReviewApi::class.java) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/goodroad/data/obstacle/ObstacleApi.kt b/app/src/main/java/com/example/goodroad/data/obstacle/ObstacleApi.kt new file mode 100644 index 0000000..4a7c5bc --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/obstacle/ObstacleApi.kt @@ -0,0 +1,15 @@ +package com.example.goodroad.data.obstacle + +import retrofit2.* +import retrofit2.http.* + +interface ObstacleApi { + + @GET("/users/obstacles") + suspend fun getUserObstaclePolicies(): Response> + + @PUT("/users/obstacles") + suspend fun replaceUserObstaclePolicies( + @Body req: ReplaceObstaclePolicyReq + ): Response> +} diff --git a/app/src/main/java/com/example/goodroad/data/obstacle/ObstacleModels.kt b/app/src/main/java/com/example/goodroad/data/obstacle/ObstacleModels.kt new file mode 100644 index 0000000..e5bb2b0 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/obstacle/ObstacleModels.kt @@ -0,0 +1,11 @@ +package com.example.goodroad.data.obstacle + +data class ObstaclePolicyItem( + val obstacleType: String, + val selected: Boolean, + val maxAllowedSeverity: Short? +) + +data class ReplaceObstaclePolicyReq( + val items: List +) diff --git a/app/src/main/java/com/example/goodroad/data/obstacle/ObstacleRepository.kt b/app/src/main/java/com/example/goodroad/data/obstacle/ObstacleRepository.kt new file mode 100644 index 0000000..3f1aabb --- /dev/null +++ b/app/src/main/java/com/example/goodroad/data/obstacle/ObstacleRepository.kt @@ -0,0 +1,22 @@ +package com.example.goodroad.data.obstacle + +import retrofit2.* + +class ObstacleRepository(private val api: ObstacleApi) { + + suspend fun getUserObstaclePolicies(): List { + val response = api.getUserObstaclePolicies() + if (response.isSuccessful) { + return response.body().orEmpty() + } + throw HttpException(response) + } + + suspend fun replaceUserObstaclePolicies(req: ReplaceObstaclePolicyReq): List { + val response = api.replaceUserObstaclePolicies(req) + if (response.isSuccessful) { + return response.body().orEmpty() + } + throw HttpException(response) + } +} From e1ca971d6a833f482a27512194e30b405141a3a6 Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 22:02:17 +0300 Subject: [PATCH 42/65] fix(obstacles): update obstacle selection screen behavior --- .../goodroad/ui/maps/ObstacleSelectScreen.kt | 223 +++++++++++++----- .../goodroad/ui/viewmodel/MapsViewModel.kt | 82 ++++++- 2 files changed, 243 insertions(+), 62 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt b/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt index 07e93b8..e70bfec 100644 --- a/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/maps/ObstacleSelectScreen.kt @@ -1,38 +1,67 @@ package com.example.goodroad.ui.maps import androidx.compose.foundation.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.example.goodroad.ui.auth.AuthButton -import com.example.goodroad.ui.user.UserDecor import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* +import androidx.compose.ui.* +import androidx.compose.ui.unit.* +import com.example.goodroad.data.obstacle.* +import com.example.goodroad.ui.auth.* import com.example.goodroad.ui.theme.* -data class Obstacle( - val id: String, - val name: String +import com.example.goodroad.ui.user.* +import com.example.goodroad.ui.viewmodel.* + +data class ObstacleOption( + val obstacleType: String, + val title: String +) + +private val ServerObstacleOptions = listOf( + ObstacleOption("CURB", "Бордюры"), + ObstacleOption("STAIRS", "Лестницы"), + ObstacleOption("ROAD_SLOPE", "Наклон дороги"), + ObstacleOption("POTHOLES", "Ямы"), + ObstacleOption("SAND", "Песок"), + ObstacleOption("GRAVEL", "Гравий") ) @Composable fun ObstacleSelectScreen( + mapsViewModel: MapsViewModel, onBackToProfile: () -> Unit, - onSave: (List) -> Unit + onSaved: () -> Unit ) { - val obstacles = remember { - listOf( - Obstacle("1", "Ямы да ухабы"), - Obstacle("2", "Лежачие полицейские...спят, устали"), - Obstacle("3", "Ремонт дороги...пока что можно только летать"), - Obstacle("4", "Скользкая дорога...пол помыли"), - Obstacle("5", "Перекрытие дороги...фильм снимают"), - Obstacle("6", "Авария...дискотека"), - Obstacle("7", "Пробка...от вина") - ) + val policies by mapsViewModel.policies + val isLoading by mapsViewModel.isLoading + val isSaving by mapsViewModel.isSaving + val errorMessage by mapsViewModel.errorMessage + val successMessage by mapsViewModel.successMessage + + val selectedMap = remember { + mutableStateMapOf().apply { + ServerObstacleOptions.forEach { put(it.obstacleType, false) } + } } - var selected by remember { mutableStateOf(setOf()) } + val severityMap = remember { + mutableStateMapOf().apply { + ServerObstacleOptions.forEach { put(it.obstacleType, 1) } + } + } + + LaunchedEffect(Unit) { + mapsViewModel.loadPolicies() + } + + LaunchedEffect(policies) { + if (policies.isNotEmpty()) { + policies.forEach { item -> + selectedMap[item.obstacleType] = item.selected + severityMap[item.obstacleType] = item.maxAllowedSeverity?.toInt() ?: 1 + } + } + } val scrollState = rememberScrollState() @@ -40,16 +69,13 @@ fun ObstacleSelectScreen( modifier = Modifier.fillMaxSize(), color = BackgroundLight ) { - Box(modifier = Modifier.fillMaxSize()) { - Column( modifier = Modifier .fillMaxSize() .verticalScroll(scrollState) .padding(24.dp) ) { - UserDecor() Text( @@ -61,55 +87,145 @@ fun ObstacleSelectScreen( Spacer(modifier = Modifier.height(20.dp)) Text( - text = "Выберите препятствия, которые хотите избегать:", + text = "Отметьте препятствия, которых нужно избегать, и укажите максимальную допустимую тяжесть.", style = MaterialTheme.typography.bodyLarge, color = UrbanBrown ) + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "1 — слабая тяжесть, 2 — средняя тяжесть, 3 — сильная тяжесть.", + style = MaterialTheme.typography.bodySmall, + color = UrbanBrown + ) + Spacer(modifier = Modifier.height(20.dp)) - obstacles.forEach { obstacle -> - val checked = obstacle.id in selected + if (isLoading && policies.isEmpty()) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 24.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator(color = SafeGreen) + } + } + + ServerObstacleOptions.forEach { obstacle -> + val checked = selectedMap[obstacle.obstacleType] == true + val severity = severityMap[obstacle.obstacleType] ?: 1 - Row( + Column( modifier = Modifier .fillMaxWidth() - .padding(vertical = 6.dp), - verticalAlignment = Alignment.CenterVertically + .padding(vertical = 8.dp) ) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Checkbox( + checked = checked, + onCheckedChange = { isChecked -> + selectedMap[obstacle.obstacleType] = isChecked + mapsViewModel.clearMessages() + }, + colors = CheckboxDefaults.colors( + checkedColor = UrbanBrown, + uncheckedColor = UrbanBrown, + checkmarkColor = WhiteSoft + ) + ) - Checkbox( - checked = checked, - onCheckedChange = { isChecked -> - selected = if (isChecked) { - selected + obstacle.id - } else { - selected - obstacle.id - } - }, - colors = CheckboxDefaults.colors( - checkedColor = UrbanBrown, - uncheckedColor = UrbanBrown, - checkmarkColor = WhiteSoft + Spacer(modifier = Modifier.width(10.dp)) + + Text( + text = obstacle.title, + style = MaterialTheme.typography.bodyLarge, + color = UrbanBrown ) - ) + } - Spacer(modifier = Modifier.width(10.dp)) + if (checked) { + Spacer(modifier = Modifier.height(8.dp)) - Text( - text = obstacle.name, - style = MaterialTheme.typography.bodyLarge, - color = UrbanBrown - ) + Text( + text = "Максимальная допустимая тяжесть", + style = MaterialTheme.typography.bodyMedium, + color = UrbanBrown + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + (1..3).forEach { value -> + FilterChip( + selected = severity == value, + onClick = { + severityMap[obstacle.obstacleType] = value + mapsViewModel.clearMessages() + }, + label = { + Text( + text = value.toString(), + style = MaterialTheme.typography.bodyLarge + ) + }, + colors = FilterChipDefaults.filterChipColors( + selectedContainerColor = SafeGreen.copy(alpha = 0.18f), + selectedLabelColor = SafeGreen, + containerColor = BackgroundLight, + labelColor = UrbanBrown + ), + border = FilterChipDefaults.filterChipBorder( + enabled = true, + selected = severity == value, + borderColor = if (severity == value) SafeGreen else BorderWarm, + selectedBorderColor = SafeGreen + ) + ) + } + } + } } } + AuthStatusText( + text = errorMessage, + onTimeout = mapsViewModel::clearMessages + ) + AuthSuccessText( + text = successMessage, + onTimeout = mapsViewModel::clearMessages + ) + Spacer(modifier = Modifier.height(30.dp)) AuthButton( - text = "Сохранить" + text = if (isSaving) "Сохраняем..." else "Сохранить", + enabled = !isSaving && !isLoading ) { - onSave(selected.toList()) + val items = ServerObstacleOptions.map { obstacle -> + val selected = selectedMap[obstacle.obstacleType] == true + ObstaclePolicyItem( + obstacleType = obstacle.obstacleType, + selected = selected, + maxAllowedSeverity = if (selected) { + (severityMap[obstacle.obstacleType] ?: 1).toShort() + } else { + null + } + ) + } + + mapsViewModel.savePolicies(items) { + onSaved() + } } Spacer(modifier = Modifier.height(10.dp)) @@ -117,7 +233,8 @@ fun ObstacleSelectScreen( AuthButton( text = "Назад в профиль", backgroundColor = UrbanBrown, - contentColor = WhiteSoft + contentColor = WhiteSoft, + enabled = !isSaving ) { onBackToProfile() } @@ -141,4 +258,4 @@ fun ObstacleSelectScreen( ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/MapsViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/MapsViewModel.kt index 3b453a5..421ff9d 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/MapsViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/MapsViewModel.kt @@ -1,15 +1,79 @@ package com.example.goodroad.ui.viewmodel -import androidx.lifecycle.ViewModel -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.State +import androidx.compose.runtime.* +import androidx.lifecycle.* +import com.example.goodroad.data.obstacle.* +import kotlinx.coroutines.launch +import retrofit2.* +import java.io.* -class MapsViewModel : ViewModel() { +class MapsViewModel(private val repository: ObstacleRepository) : ViewModel() { - private val _selectedObstacles = mutableStateOf>(emptyList()) - val selectedObstacles: State> = _selectedObstacles + var policies = mutableStateOf>(emptyList()) + private set - fun setSelectedObstacles(selected: List) { - _selectedObstacles.value = selected + var isLoading = mutableStateOf(false) + private set + + var isSaving = mutableStateOf(false) + private set + + var errorMessage = mutableStateOf(null) + private set + + var successMessage = mutableStateOf(null) + private set + + fun loadPolicies() { + viewModelScope.launch { + isLoading.value = true + errorMessage.value = null + + try { + policies.value = repository.getUserObstaclePolicies() + } catch (e: Exception) { + errorMessage.value = mapObstacleError(e) + } finally { + isLoading.value = false + } + } + } + + fun savePolicies(items: List, onSuccess: () -> Unit) { + viewModelScope.launch { + isSaving.value = true + errorMessage.value = null + successMessage.value = null + + try { + val req = ReplaceObstaclePolicyReq(items) + policies.value = repository.replaceUserObstaclePolicies(req) + successMessage.value = "Настройки препятствий сохранены" + onSuccess() + } catch (e: Exception) { + errorMessage.value = mapObstacleError(e) + } finally { + isSaving.value = false + } + } + } + + fun clearMessages() { + errorMessage.value = null + successMessage.value = null + } + + private fun mapObstacleError(e: Exception): String { + return when (e) { + is HttpException -> when (e.code()) { + 400 -> "Проверьте выбранные препятствия и уровень тяжести" + 401 -> "Вы не авторизованы" + 403 -> "Нет прав для выполнения действия" + 500 -> "Сервер временно недоступен" + else -> "Не удалось сохранить настройки препятствий" + } + is IOException -> "Проверьте подключение к интернету" + else -> e.message ?: "Неизвестная ошибка" + } } -} \ No newline at end of file +} From a2aab7c13544c7cef7786fb82c28b071dc4fd15e Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 22:02:42 +0300 Subject: [PATCH 43/65] fix(navigation): keep obstacle screen open after saving --- .../com/example/goodroad/ui/maps/MapsNav.kt | 28 ++++++++++++--- .../com/example/goodroad/ui/user/UserNav.kt | 36 ++++++++++++------- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/maps/MapsNav.kt b/app/src/main/java/com/example/goodroad/ui/maps/MapsNav.kt index f67c2d5..b506a74 100644 --- a/app/src/main/java/com/example/goodroad/ui/maps/MapsNav.kt +++ b/app/src/main/java/com/example/goodroad/ui/maps/MapsNav.kt @@ -1,14 +1,34 @@ package com.example.goodroad.ui.maps -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* +import androidx.lifecycle.* +import androidx.lifecycle.viewmodel.compose.* +import com.example.goodroad.data.network.* +import com.example.goodroad.data.obstacle.* +import com.example.goodroad.ui.viewmodel.* @Composable fun MapsNav( onBackToProfile: () -> Unit, - onSaveObstacles: (List) -> Unit + onSaved: () -> Unit ) { + val api = ApiClient.obstacleApi + + val factory = object : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(MapsViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return MapsViewModel(ObstacleRepository(api)) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } + } + + val mapsViewModel: MapsViewModel = viewModel(factory = factory) + ObstacleSelectScreen( + mapsViewModel = mapsViewModel, onBackToProfile = onBackToProfile, - onSave = onSaveObstacles + onSaved = onSaved ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt index 37b5822..20a476c 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserNav.kt @@ -1,27 +1,41 @@ package com.example.goodroad.ui.user -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import com.example.goodroad.data.network.ApiClient -import com.example.goodroad.data.review.* +import com.example.goodroad.data.review.ReviewCardResp +import com.example.goodroad.data.review.ReviewRepository import com.example.goodroad.data.user.UserRepository import com.example.goodroad.ui.maps.MapsNav -import com.example.goodroad.ui.reviews.* -import com.example.goodroad.ui.viewmodel.* +import com.example.goodroad.ui.reviews.ReviewDetailsScreen +import com.example.goodroad.ui.reviews.ReviewFormScreen +import com.example.goodroad.ui.reviews.UserReviewsScreen +import com.example.goodroad.ui.viewmodel.ReviewsViewModel +import com.example.goodroad.ui.viewmodel.UserViewModel @Composable fun UserNav(onLogout: () -> Unit) { - val userApi = ApiClient.userApi val reviewApi = ApiClient.reviewApi - val factory = object : ViewModelProvider.Factory { + val userFactory = object : ViewModelProvider.Factory { override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(UserViewModel::class.java)) { @Suppress("UNCHECKED_CAST") return UserViewModel(UserRepository(userApi)) as T } + throw IllegalArgumentException("Unknown ViewModel class") + } + } + + val reviewsFactory = object : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(ReviewsViewModel::class.java)) { @Suppress("UNCHECKED_CAST") return ReviewsViewModel(ReviewRepository(reviewApi, userApi)) as T @@ -30,8 +44,8 @@ fun UserNav(onLogout: () -> Unit) { } } - val userViewModel: UserViewModel = viewModel(factory = factory) - val reviewsViewModel: ReviewsViewModel = viewModel(factory = factory) + val userViewModel: UserViewModel = viewModel(factory = userFactory) + val reviewsViewModel: ReviewsViewModel = viewModel(factory = reviewsFactory) var screen by remember { mutableStateOf("profile") } var selectedReview by remember { mutableStateOf(null) } @@ -65,9 +79,7 @@ fun UserNav(onLogout: () -> Unit) { onBackToProfile = { screen = "profile" }, - onSaveObstacles = { - screen = "profile" - } + onSaved = {} ) "reviews" -> UserReviewsScreen( @@ -114,4 +126,4 @@ fun UserNav(onLogout: () -> Unit) { } } } -} +} \ No newline at end of file From 083186014de166194861e3285ee1fc9417cc991e Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 22:03:09 +0300 Subject: [PATCH 44/65] fix(reviews): improve review form and details flow --- .../ui/reviews/ReviewDetailsScreen.kt | 17 +- .../goodroad/ui/reviews/ReviewFormScreen.kt | 202 +++++++++++------- .../goodroad/ui/viewmodel/ReviewsViewModel.kt | 4 + 3 files changed, 133 insertions(+), 90 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewDetailsScreen.kt b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewDetailsScreen.kt index 269c1e2..d87692d 100644 --- a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewDetailsScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewDetailsScreen.kt @@ -4,13 +4,13 @@ import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp +import androidx.compose.ui.* +import androidx.compose.ui.unit.* import com.example.goodroad.data.review.* import com.example.goodroad.ui.auth.* import com.example.goodroad.ui.theme.* -import com.example.goodroad.ui.user.UserDecor -import com.example.goodroad.ui.viewmodel.ReviewsViewModel +import com.example.goodroad.ui.user.* +import com.example.goodroad.ui.viewmodel.* @Composable fun ReviewDetailsScreen( @@ -46,7 +46,7 @@ fun ReviewDetailsScreen( reviewsViewModel.deleteReview(review.id, onDeleted) } ) { - Text("Да", color = AlertRed) + Text("Да", color = UrbanBrown) } }, dismissButton = { @@ -113,7 +113,7 @@ fun ReviewDetailsScreen( Spacer(Modifier.height(8.dp)) review.obstacles.forEach { obstacle -> Text( - text = "${obstacleLabel(obstacle.obstacleType)} — ${obstacleSeverityText(obstacle.severity.toInt())}", + text = "${obstacleLabel(obstacle.obstacleType)}: ${obstacleSeverityText(obstacle.severity.toInt())}", style = MaterialTheme.typography.bodyLarge, color = TextPrimary ) @@ -122,7 +122,10 @@ fun ReviewDetailsScreen( } } - AuthStatusText(errorMessage) + AuthStatusText( + text = errorMessage, + onTimeout = reviewsViewModel::clearErrorMessage + ) Spacer(Modifier.height(20.dp)) diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewFormScreen.kt b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewFormScreen.kt index aec1ebb..d8a9e4e 100644 --- a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewFormScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewFormScreen.kt @@ -1,30 +1,28 @@ package com.example.goodroad.ui.reviews -import android.content.Context -import android.location.Address -import android.location.Geocoder -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts +import android.content.* +import android.location.* +import androidx.activity.compose.* +import androidx.activity.result.contract.* import androidx.compose.foundation.* import androidx.compose.foundation.layout.* -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Photo +import androidx.compose.foundation.text.* +import androidx.compose.material.icons.* +import androidx.compose.material.icons.filled.* import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.unit.dp +import androidx.compose.ui.* +import androidx.compose.ui.platform.* +import androidx.compose.ui.text.input.* +import androidx.compose.ui.unit.* import com.example.goodroad.data.review.* import com.example.goodroad.ui.auth.* +import com.example.goodroad.ui.common.validation.* import com.example.goodroad.ui.theme.* -import com.example.goodroad.ui.user.UserDecor -import com.example.goodroad.ui.viewmodel.ReviewsViewModel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import java.util.Locale +import com.example.goodroad.ui.user.* +import com.example.goodroad.ui.viewmodel.* +import kotlinx.coroutines.* +import java.util.* @Composable fun ReviewFormScreen( @@ -67,6 +65,15 @@ fun ReviewFormScreen( val isPhotoUploading by reviewsViewModel.isPhotoUploading val serverError by reviewsViewModel.errorMessage + var isPreparingSubmit by remember(reviewKey) { mutableStateOf(false) } + val submitInProgress = isSubmitting || isPreparingSubmit + + LaunchedEffect(isSubmitting) { + if (isSubmitting) { + isPreparingSubmit = false + } + } + val photoPickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.GetMultipleContents() ) { uris -> @@ -100,7 +107,8 @@ fun ReviewFormScreen( PlainField( value = placeName, onValueChange = { placeName = it }, - label = "Название места" + label = "Название места", + maxLength = PLACE_NAME_MAX_LENGTH ) Spacer(Modifier.height(8.dp)) @@ -113,21 +121,28 @@ fun ReviewFormScreen( Spacer(Modifier.height(16.dp)) - PlainField( - value = latitude, - onValueChange = { latitude = it }, - label = "Широта", - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal) - ) - - Spacer(Modifier.height(12.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + PlainField( + value = latitude, + onValueChange = { latitude = it }, + label = "Широта", + maxLength = COORDINATE_MAX_LENGTH, + modifier = Modifier.weight(1f), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal) + ) - PlainField( - value = longitude, - onValueChange = { longitude = it }, - label = "Долгота", - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal) - ) + PlainField( + value = longitude, + onValueChange = { longitude = it }, + label = "Долгота", + maxLength = COORDINATE_MAX_LENGTH, + modifier = Modifier.weight(1f), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal) + ) + } Spacer(Modifier.height(12.dp)) @@ -198,7 +213,11 @@ fun ReviewFormScreen( TextField( value = comment, - onValueChange = { comment = it }, + onValueChange = { value -> + if (value.length <= COMMENT_MAX_LENGTH) { + comment = value + } + }, modifier = Modifier .fillMaxWidth() .heightIn(min = 56.dp), @@ -255,65 +274,82 @@ fun ReviewFormScreen( onRemove = { photoUrls.remove(it) } ) - AuthStatusText(formError ?: serverError) + AuthStatusText( + text = formError ?: serverError, + onTimeout = { + formError = null + reviewsViewModel.clearMessages() + } + ) Spacer(Modifier.height(20.dp)) AuthButton( text = when { isPhotoUploading -> "Загружаем фото..." - isSubmitting -> "Сохраняем..." + submitInProgress -> "Сохраняем..." isEdit -> "Сохранить изменения" else -> "Отправить отзыв" }, - enabled = !isSubmitting && !isPhotoUploading + enabled = !submitInProgress && !isPhotoUploading ) { scope.launch { - val validationError = validateReviewForm( - latitude = latitude, - longitude = longitude, - rating = rating, - obstacleSeverities = obstacleSeverities - ) - - if (validationError != null) { - formError = validationError - return@launch - } - - val lat = latitude.trim().replace(',', '.').toDouble() - val lon = longitude.trim().replace(',', '.').toDouble() - - val generatedAddress = resolveReviewAddress( - context = context, - latitude = lat, - longitude = lon, - placeName = placeName, - fallbackAddress = initialReview?.address - ) - - formError = null - reviewsViewModel.clearMessages() - - val request = UpsertReviewReq( - latitude = lat, - longitude = lon, - address = generatedAddress, - rating = rating!!.toShort(), - obstacles = ReviewObstacleTypes.map { type -> - ReviewObstacle( - obstacleType = type, - severity = (obstacleSeverities[type] ?: 0).toShort() - ) - }, - comment = comment.trim().ifBlank { null }, - photoUrls = photoUrls.toList() - ) - - if (isEdit) { - reviewsViewModel.updateReview(initialReview!!.id, request, onSaved) - } else { - reviewsViewModel.createReview(request, onSaved) + isPreparingSubmit = true + var sentToViewModel = false + + try { + val validationError = validateReviewForm( + latitude = latitude, + longitude = longitude, + rating = rating, + obstacleSeverities = obstacleSeverities + ) + + if (validationError != null) { + formError = validationError + return@launch + } + + val lat = latitude.trim().replace(',', '.').toDouble() + val lon = longitude.trim().replace(',', '.').toDouble() + + val generatedAddress = resolveReviewAddress( + context = context, + latitude = lat, + longitude = lon, + placeName = placeName, + fallbackAddress = initialReview?.address + ) + + formError = null + reviewsViewModel.clearMessages() + + val request = UpsertReviewReq( + latitude = lat, + longitude = lon, + address = generatedAddress, + rating = rating!!.toShort(), + obstacles = ReviewObstacleTypes.map { type -> + ReviewObstacle( + obstacleType = type, + severity = (obstacleSeverities[type] ?: 0).toShort() + ) + }, + comment = comment.trim().ifBlank { null }, + photoUrls = photoUrls.toList() + ) + + sentToViewModel = true + + if (isEdit) { + reviewsViewModel.updateReview(initialReview!!.id, request, onSaved) + } else { + reviewsViewModel.createReview(request, onSaved) + } + } finally { + if (!sentToViewModel) { + isPreparingSubmit = false + } } } } @@ -411,4 +447,4 @@ private fun detectLocalityType(address: Address, fallbackAddress: ReviewAddress) !address.subAdminArea.isNullOrBlank() -> "район" else -> fallbackAddress.localityType } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/ReviewsViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/ReviewsViewModel.kt index 6ba093f..a84f980 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/ReviewsViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/ReviewsViewModel.kt @@ -172,6 +172,10 @@ class ReviewsViewModel(private val repository: ReviewRepository) : ViewModel() { successMessage.value = null } + fun clearErrorMessage() { + errorMessage.value = null + } + private fun mapReviewError(e: Exception): String { return when (e) { is IllegalArgumentException -> e.message ?: "Некорректные данные отзыва" From f169ea199ad1d0d284c07857e2dc1a89ee2c8a2a Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 22:03:42 +0300 Subject: [PATCH 45/65] fix(reviews-ui): polish review cards and obstacle text --- .../example/goodroad/ui/reviews/ReviewUi.kt | 31 +++++++++---------- .../goodroad/ui/reviews/UserReviewsScreen.kt | 27 +++++++--------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt index 0226cf1..055680a 100644 --- a/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt +++ b/app/src/main/java/com/example/goodroad/ui/reviews/ReviewUi.kt @@ -2,24 +2,21 @@ package com.example.goodroad.ui.reviews import androidx.compose.foundation.* import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.shape.* import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.sp -import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage +import androidx.compose.runtime.* +import androidx.compose.ui.* +import androidx.compose.ui.draw.* +import androidx.compose.ui.graphics.* +import androidx.compose.ui.layout.* +import androidx.compose.ui.text.font.* +import androidx.compose.ui.text.style.* +import androidx.compose.ui.unit.* +import coil.compose.* import com.example.goodroad.data.review.* import com.example.goodroad.ui.theme.* -import java.time.Instant -import java.time.ZoneId -import java.time.format.DateTimeFormatter +import java.time.* +import java.time.format.* val ReviewObstacleTypes = listOf( "CURB", @@ -90,11 +87,11 @@ fun buildAddressLine(address: ReviewAddress): String { address.street ?.takeIf { it.isNotBlank() } - ?.let { parts += "ул. $it" } + ?.let { parts += it } address.house ?.takeIf { it.isNotBlank() } - ?.let { parts += "д. $it" } + ?.let { parts += it } address.placeName ?.takeIf { it.isNotBlank() } diff --git a/app/src/main/java/com/example/goodroad/ui/reviews/UserReviewsScreen.kt b/app/src/main/java/com/example/goodroad/ui/reviews/UserReviewsScreen.kt index f80efaa..72bbfe5 100644 --- a/app/src/main/java/com/example/goodroad/ui/reviews/UserReviewsScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/reviews/UserReviewsScreen.kt @@ -5,15 +5,13 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.* import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp +import androidx.compose.ui.* +import androidx.compose.ui.unit.* import com.example.goodroad.data.review.* import com.example.goodroad.ui.auth.* import com.example.goodroad.ui.theme.* -import com.example.goodroad.ui.user.UserDecor -import com.example.goodroad.ui.viewmodel.ReviewsViewModel -import kotlinx.coroutines.delay +import com.example.goodroad.ui.user.* +import com.example.goodroad.ui.viewmodel.* @Composable fun UserReviewsScreen( @@ -36,13 +34,6 @@ fun UserReviewsScreen( reviewsViewModel.loadReviews() } - LaunchedEffect(successMessage) { - if (successMessage != null) { - delay(10_000) - reviewsViewModel.clearSuccessMessage() - } - } - Surface( modifier = Modifier.fillMaxSize(), color = BackgroundLight @@ -110,8 +101,14 @@ fun UserReviewsScreen( onBack() } - AuthSuccessText(successMessage) - AuthStatusText(errorMessage) + AuthSuccessText( + text = successMessage, + onTimeout = reviewsViewModel::clearSuccessMessage + ) + AuthStatusText( + text = errorMessage, + onTimeout = reviewsViewModel::clearErrorMessage + ) Spacer(Modifier.height(16.dp)) From b538465a33429e24944129062721a69f6e69e8ff Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Mon, 13 Apr 2026 22:04:17 +0300 Subject: [PATCH 46/65] fix(profile): refine edit, photo, and delete account flows --- .../goodroad/ui/user/UserDeleteAccount.kt | 32 +++++++++++-------- .../goodroad/ui/user/UserEditScreen.kt | 30 ++++++++++------- .../goodroad/ui/user/UserProfileScreen.kt | 16 +++++----- .../goodroad/ui/viewmodel/UserViewModel.kt | 8 ++++- 4 files changed, 52 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt index e3fe4de..5719b53 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt @@ -1,15 +1,17 @@ package com.example.goodroad.ui.user +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.example.goodroad.ui.auth.AuthButton +import com.example.goodroad.ui.auth.AuthStatusText import com.example.goodroad.ui.auth.PasswordField -import com.example.goodroad.ui.viewmodel.UserViewModel -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.* import com.example.goodroad.ui.theme.* +import com.example.goodroad.ui.viewmodel.UserViewModel +import kotlinx.coroutines.delay + @Composable fun UserDeleteAccountScreen( viewModel: UserViewModel, @@ -20,6 +22,13 @@ fun UserDeleteAccountScreen( val isLoading by viewModel.isLoading val errorMessage by viewModel.errorMessage + LaunchedEffect(errorMessage) { + if (!errorMessage.isNullOrBlank()) { + delay(5_000) + viewModel.clearMessages() + } + } + Column( modifier = Modifier .fillMaxSize() @@ -60,15 +69,12 @@ fun UserDeleteAccountScreen( text = "Назад в профиль", backgroundColor = UrbanBrown, contentColor = WhiteSoft, - onClick = onBack + onClick = { + viewModel.clearMessages() + onBack() + } ) - errorMessage?.let { - Text( - text = it, - color = Color.Red, - modifier = Modifier.padding(top = 16.dp) - ) - } + AuthStatusText(text = errorMessage) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt index 46cf792..97798c8 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserEditScreen.kt @@ -122,7 +122,8 @@ fun UserEditScreen( tint = UrbanBrown ) }, - warning = firstNameWarning + warning = firstNameWarning, + maxLength = NAME_MAX_LENGTH ) Spacer(Modifier.height(12.dp)) @@ -148,7 +149,8 @@ fun UserEditScreen( tint = UrbanBrown ) }, - warning = lastNameWarning + warning = lastNameWarning, + maxLength = NAME_MAX_LENGTH ) Spacer(Modifier.height(12.dp)) @@ -287,16 +289,20 @@ fun UserEditScreen( color = UrbanBrown ) - AuthSuccessText(text = successMessage) - - if (!finalError.isNullOrBlank()) { - Spacer(Modifier.height(12.dp)) - Text( - text = finalError, - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.bodyMedium - ) - } + AuthSuccessText( + text = successMessage, + onTimeout = { + errorText = null + userViewModel.clearMessages() + } + ) + AuthStatusText( + text = finalError, + onTimeout = { + errorText = null + userViewModel.clearMessages() + } + ) Spacer(Modifier.height(20.dp)) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt index 83a0e87..2a1d959 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt @@ -33,18 +33,12 @@ fun UserProfileScreen( } when { - isLoading -> { + isLoading && user == null -> { Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } - errorMessage != null -> { - Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Text("Ошибка: $errorMessage", color = Color.Red) - } - } - user != null -> { val u = user!! @@ -139,10 +133,16 @@ fun UserProfileScreen( } } + errorMessage != null -> { + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text("Ошибка: $errorMessage", color = Color.Red) + } + } + else -> { LaunchedEffect(Unit) { if (userViewModel.isDeleted) onLogout() } } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt index 2a8ea3c..17e7eb5 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.goodroad.data.network.ApiClient +import com.example.goodroad.data.user.* import kotlinx.coroutines.launch import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody @@ -13,7 +14,7 @@ import okhttp3.RequestBody.Companion.asRequestBody import retrofit2.HttpException import java.io.File import java.io.IOException -import com.example.goodroad.data.user.* + class UserViewModel(private val repository: UserRepository) : ViewModel() { var user = mutableStateOf(null) @@ -136,6 +137,11 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { successMessage.value = null } + fun clearMessages() { + errorMessage.value = null + successMessage.value = null + } + fun deleteUser(password: String, onSuccess: () -> Unit) { viewModelScope.launch { isLoading.value = true From 35557883d070609d898d4de0c28297deefbc4775 Mon Sep 17 00:00:00 2001 From: VictoriaGrudtsyna <148629595+VictoriaGrudtsyna@users.noreply.github.com> Date: Mon, 13 Apr 2026 23:31:54 +0300 Subject: [PATCH 47/65] =?UTF-8?q?fix:=20=D1=80=D0=B5=D0=B4=D0=B0=D0=BA?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20->=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BF=D1=80=D0=BE=D1=84=D0=B8=D0=BB=D1=8C.=20fi?= =?UTF-8?q?x:=20new=20error=20map=20for=20delete=20account?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../goodroad/ui/user/UserDeleteAccount.kt | 6 ++- .../goodroad/ui/user/UserProfileScreen.kt | 2 +- .../goodroad/ui/viewmodel/UserViewModel.kt | 38 ++++++++++++++----- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt index 5719b53..067f2e7 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserDeleteAccount.kt @@ -52,6 +52,10 @@ fun UserDeleteAccountScreen( label = "Пароль" ) + Spacer(modifier = Modifier.height(8.dp)) + + AuthStatusText(text = errorMessage) + Spacer(modifier = Modifier.height(20.dp)) AuthButton( @@ -74,7 +78,5 @@ fun UserDeleteAccountScreen( onBack() } ) - - AuthStatusText(text = errorMessage) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt index 2a1d959..abe96de 100644 --- a/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt +++ b/app/src/main/java/com/example/goodroad/ui/user/UserProfileScreen.kt @@ -118,7 +118,7 @@ fun UserProfileScreen( Spacer(Modifier.height(10.dp)) - AuthButton(text = "Редактировать", onClick = onEdit) + AuthButton(text = "Редактировать профиль", onClick = onEdit) Spacer(Modifier.height(10.dp)) diff --git a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt index 17e7eb5..b8b20fe 100644 --- a/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/example/goodroad/ui/viewmodel/UserViewModel.kt @@ -112,6 +112,7 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { val mimeType = resolver.getType(uri) ?: "image/*" val extension = MimeTypeMap.resolveExtension(mimeType) val tempFile = File.createTempFile("avatar_upload", extension, context.cacheDir) + resolver.openInputStream(uri)?.use { input -> tempFile.outputStream().use { output -> input.copyTo(output) @@ -120,6 +121,7 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { val requestBody = tempFile.asRequestBody(mimeType.toMediaTypeOrNull()) val part = MultipartBody.Part.createFormData("file", tempFile.name, requestBody) + val response = repository.uploadAvatar(part) ?: throw IllegalStateException("Сервер не вернул ссылку на фото") @@ -133,15 +135,6 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { } } - fun clearSuccessMessage() { - successMessage.value = null - } - - fun clearMessages() { - errorMessage.value = null - successMessage.value = null - } - fun deleteUser(password: String, onSuccess: () -> Unit) { viewModelScope.launch { isLoading.value = true @@ -155,7 +148,7 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { isDeleted = true onSuccess() } catch (e: Exception) { - errorMessage.value = mapUserError(e) + errorMessage.value = mapDeleteError(e) } finally { isLoading.value = false } @@ -171,6 +164,15 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { onSuccess() } + fun clearSuccessMessage() { + successMessage.value = null + } + + fun clearMessages() { + errorMessage.value = null + successMessage.value = null + } + private fun mapUserError(e: Exception): String { return when (e) { is IllegalArgumentException -> e.message ?: "Некорректные данные" @@ -188,6 +190,22 @@ class UserViewModel(private val repository: UserRepository) : ViewModel() { } } + private fun mapDeleteError(e: Exception): String { + return when (e) { + is HttpException -> when (e.code()) { + 400 -> "Введите корректный пароль" + 401 -> "Неверный пароль" + 403 -> "Нет прав для удаления аккаунта" + 404 -> "Аккаунт уже удалён или не найден" + 409 -> "Невозможно удалить аккаунт (есть связанные данные)" + 500 -> "Сервер временно недоступен" + else -> "Не удалось удалить аккаунт" + } + is IOException -> "Проверьте подключение к интернету" + else -> e.message ?: "Неизвестная ошибка" + } + } + private object MimeTypeMap { fun resolveExtension(mimeType: String): String { return when (mimeType) { From 55c18adadee006c13e92cca5c3e979bc05e259aa Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Tue, 14 Apr 2026 03:46:11 +0300 Subject: [PATCH 48/65] refactor: Readme --- README.md | 193 ++++++++++++++++++++++++++------------------- images/preview.png | Bin 0 -> 171793 bytes 2 files changed, 114 insertions(+), 79 deletions(-) create mode 100644 images/preview.png diff --git a/README.md b/README.md index cfc474f..4b31e10 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,44 @@ -![Превью](images/readme_preview.png) +![Превью](images/preview.png) ## GoodRoad (Android client) **Авторы**: ```Городкова Ксения```, ```Грудцына Виктория```, ```Толстограева Виктория``` -## Описание проекта - -**GoodRoad** — это мобильное приложение для построения пеших инклюзивных маршрутов. В отличие от стандартной навигации по кратчайшему пути, приложение подстраивает путь под ограничения здоровья пользователя. Если полностью безбарьерного маршрута нет, GoodRoad показывает проблемные участки и дает возможность запросить помощь волонтера. - -Данные о доступности инфраструктуры собираются пользователями в игровом формате с системой бонусов. Запросы на помощь находятся в отдельной ленте. - -## Основная функциональность - -- **Профиль пользователя:** - - Выбор препятствий, которые нужно избегать при построении маршрута; - - Интерактивные задания, связанные с оценкой объектов и волонтерством; - - Рейтинг и система начисления баллов за отзывы и волонтерство; - - Лента заявок на помощь с фильтрами по местоположению и времени; - - Подача заявки на улучшение инфраструктуры. -- **Профиль модератора:** - - Обработка заявок о помощи: система реагирования на критические ситуации; - - Модерация отзывов; -- **Карта и маршруты:** - - Влияние отзывов на построение пути. - - Построение 3 вариантов маршрута: **доступный**, **быстрый**, **компромиссный**; - - Выделение проблемных участков и карточки объектов с деталями; - - История маршрутов и сохранение последнего построенного пути. - -## MVP - -- **Регистрация / вход / восстановление доступа.** -- **Маршрутизация:** - - Выбор избегаемых препятствий в профиле; - - Построение маршрута; - - Отображение маршрута и проблемных участков; - - Хранение препятствий (тип и тяжесть). -- **Отзывы:** - - Чек-лист для оценивания; - - Прикрепление фотографии (опционально); - - Учет отзывов в маршрутизации. -- **Модерация:** - - Проверка отзывов; - - Управление младшими модераторами (в случае главного). +## Краткое описание проекта + +**GoodRoad** — мобильное приложение для построения пеших инклюзивных маршрутов с учетом состояния дороги, препятствий на пути и персональных ограничений пользователя. + +Обычный навигатор часто показывает только самый короткий или быстрый путь, но не объясняет, насколько этот путь удобен на практике. На маршруте могут встретиться лестницы, высокие бордюры, ямы, крутые участки дороги и другие барьеры. GoodRoad решает именно эту задачу: приложение помогает заранее понять, подходит ли путь конкретному человеку, показывает проблемные места и позволяет собирать отзывы о доступности дорог и объектов. + +Сейчас проект представлен в формате MVP. В этой версии мы показываем основной сценарий работы приложения: пользователь настраивает ограничения, строит маршрут с учетом препятствий и оставляет отзывы, а модератор проверяет пользовательский контент. + +## Возможности для каждого типа пользователя (MVP) + +### Пользователь +- Регистрация, вход, восстановление пароля; +- Просмотр и редактирование профиля; +- Выбор препятствий, которые нужно избегать при построении маршрута, и их тяжесть; +- Построение маршрута с учетом ограничений пользователя; +- Создание, редактирование и удаление отзывов о состоянии дороги или объекта; +- Добавление фотографий к отзыву; +- Просмотр статуса модерации отзыва и начисленных за него баллов. + +### Модератор +- Просмотр отзывов, отправленных на проверку; +- Одобрение и отклонение отзывов; +- Работа с модерацией пользовательского контента. + +### Главный модератор +- Все возможности обычного модератора; +- Добавление и удаление других модераторов. + +## Что планируем добавить к защите + +- Дополнительные сценарии для построения маршрута; +- Расширение работы с картой и подсветкой проблемных участков; +- Взаимодействие с волонтерами: их поиск, связь и оказание помощи людям с ограниченными возможностями; +- Расширение системы баллов в игровом формате: выполнение заданий, связанных с волонтерством и сбором данных о доступности дорог и объектов; +- Расширение модераторского сценария. ## Наши продукты @@ -54,57 +51,95 @@ - Временно недоступно + Скоро тут появится ссылка +## Как запустить backend в Docker +На данный момент доступна локальная проверка клиентского приложения. Для этого рядом должен находиться серверный репозиторий [GoodRoad-Server](https://github.com/GoodRoad-Project/GoodRoadServer). +Ожидаемая структура каталогов: +```text +GoodRoad-Client/ +GoodRoad-Server/ +``` -## Технологии +### 1. Подготовьте `.env` +Из корня клиентского репозитория выполните: - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Основные технологии и инструменты (клиент)
КомпонентТехнологии / Инструменты
UIAndroid SDK, Material Components/ Jetpack Compose
КартыMapbox Android SDK (слои для маршрутов и препятствий)
ГеопозицияAndroid Location API
Оффлайн-данныеRoom (кэш профиля, ограничений, препятствий и последнего маршрута)
ГеометрияJTS (офлайн-операции с геометрией при учете препятствий)
+```bash +cp docker/.env.backend.example docker/.env.backend +``` -*Технологии серверной части: [в отдельном репозитории](https://github.com/GoodRoad-Project/GoodRoadServer).* +При необходимости можно изменить значения в `docker/.env.backend`. -## Архитектура клиента +### 2. Запустите контейнеры +Из корня `GoodRoad-Client`: + +```bash +docker compose --env-file docker/.env.backend -f docker-compose.backend.yml up --build +``` -**Временно недоступно** +После запуска поднимутся PostgreSQL + PostGIS, а также backend GoodRoad на Spring Boot. -## Безопасность +### 3. Используйте сервер из Android-клиента +В клиенте используется адрес: -**Временно недоступно** +```text +http://10.0.2.2:8080/ +``` -## Структура проекта +Этот адрес подходит для Android-эмулятора. Если запускаете приложение на физическом устройстве, адрес сервера нужно заменить на локальный IP компьютера в вашей сети. -| **Схема клиентского приложения**
![Архитектура приложения](images/android_scheme.png) | -|:----------------------------------------------------------------------------------------:| +## Технологии клиента + +| Категория | Технологии | Назначение | +|---|---|---| +| Язык | Kotlin | Основной язык клиентского приложения | +| UI | Jetpack Compose, Material 3 | Построение экранов и компонентов интерфейса | +| Навигация | Navigation Compose | Переходы между экранами и сценариями | +| Состояние экрана | ViewModel, LiveData | Хранение состояния и обработка логики экранов | +| Асинхронность | Kotlin Coroutines | Выполнение сетевых и фоновых операций | +| Сеть | Retrofit, OkHttp | Работа с REST API | +| JSON | Gson Converter | Сериализация и десериализация DTO | +| Изображения | Coil | Загрузка и отображение изображений | + +## Архитектура клиента -*Схемы серверной части: [в отдельном репозитории](https://github.com/GoodRoad-Project/GoodRoadServer)*. \ No newline at end of file +Клиент реализован как Android-приложение на Jetpack Compose. Основной поток взаимодействия внутри клиента: **надо добавить** + +### Слои клиентской архитектуры + +| Слой | Что включает | Назначение | +|----|---|---| +| UI | `ui/auth`, `ui/user`, `ui/maps`, `ui/reviews`, `ui/theme` | Экраны, компоненты интерфейса, навигационные сценарии | +| ViewModel | `AuthViewModel`, `UserViewModel`, `MapsViewModel`, `ReviewsViewModel` | Состояние экранов, обработка действий пользователя, связь UI и данных | +| Data | `data/auth`, `data/user`, `data/obstacle`, `data/review`, `data/network` | Работа с DTO, репозиториями и сетевыми запросами | +| Network | `ApiClient`, Retrofit API interfaces | Подключение к backend, настройка base URL, сериализация и HTTP-клиент | + +### Структура проекта + +```text +app/src/main/java/com/example/goodroad/ +├── MainActivity.kt +├── data/ +│ ├── auth/ +│ ├── network/ +│ ├── obstacle/ +│ ├── review/ +│ └── user/ +└── ui/ + ├── auth/ + ├── common/ + ├── maps/ + ├── reviews/ + ├── theme/ + ├── user/ + └── viewmodel/ +``` + +## Схема клиентского приложения + +Ниже приведена схема экранов и основных пользовательских переходов, которая используется как ориентир для клиентской части проекта. Зеленым обозначены блоки, реализованные к MVP. + +![Схема клиентского приложения](images/android_scheme.png) diff --git a/images/preview.png b/images/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..fe0336e2ae677026370d698aeae7eb07874561a5 GIT binary patch literal 171793 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 U(uC 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%0A3@rOq{mPkCa3JvF|0sx*ST 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�x>A~9I*t%jKG$jm-W}D$GUT(7b)ZpX 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 xZRD-(lJFnH5 Date: Tue, 14 Apr 2026 03:48:17 +0300 Subject: [PATCH 49/65] refactor: readme preview --- images/preview.png | Bin 171793 -> 281408 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/images/preview.png b/images/preview.png index fe0336e2ae677026370d698aeae7eb07874561a5..bd5814da19a86287e20520c44d3b65099da6b3b9 100644 GIT binary patch literal 281408 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℞dP8ai4*xpK4=p1nGU=G=Ut*=!=%4cQM6|9qOl^hbDHu*cP zMM-E_52ou+tP2~1TUQLoKT4JpAeND^LAAdjFxE=5R9bq{Jea+LYOnX+@8#o2_EI{9 zaydEiade%`umSav6P_v7#I`+xdhk=4yfc6 zHD1Xbs5z;FhmOt)er7F4UXNJ^*Yn4p%P*47;TS=#Oa5hhyqqNB=VP1bSxmo^+ls^E z4YsE{K$B0HU=Ae%T>476Q8})Irr?u=OSY|Z<{fdY-$Jw@T!fbm)3*b_Q*_W&=1qGR z;#t!d^8LuO^23gBZX9ad9<@}ZIvwxWwxn(O@I^#m{l8w;mGnhrf>XBCbm>Y^u<}Cf zOti|NKbT1$ok97C51dp00>}HFIGD3PONhJdf3;ucTk(eQk{WMd_%yU@v=>z!X8Wu} zM+Eg5ameKu@R)_m!Po4U9Y`jO$EhkLO6PO(Zp3?>%xuwWb6(RmYEr3JLb%{leA1EJ zI2Wk%nuQhd!4wy`IzdkmC#1#k_~`c+9+ku;jc7YlNh3gZ=0E;V|L=4MT?V-=6zU`g z6Cee2TzkxO?)Z!{b85p(ZGj;FDiC%MqUZRB1GNzX5-cg^VsI*oN0F&4grJX0Y!1i%xET+#GmzoBmZj2nP2zWQK3~ox*=az%x&xy@my$2jzMFf!IDHvT zwlBk3G?m=zLG`G&?BsdfUdQ3_9yeb<;ro}5xcdAFH{UHdz(BLM97J7vWgBqC^M zn$~zCnP`Og+Wm)SnV^ic_3H>ut)D!*A!lfsMaaw%K!V3J1?ZeZ_O3090ulwzQ)w$R z|ARHyFUQgvH+ystN_|Z`I_s{Of}EXZb-NPW-js|%n`T8Nf0kJW4~u32y=f=;Yfcn6 zzzPLZ#X*@k=p+iyDs%Rceb6D7O_7c3o2tjuKiQC;EocAe5HD>(AlV)oPF|eh{LOoO z`1yBu`@?VW^87VUUc5jDH!efzBHNfja}@uwAMzTs9z4+xPk6e&#r@4SzJL3It1q8$ z`~4dpA8r}A1C!ksw~D8c-o#Vu*X1LmX6(a>g#gw~baP3#_`{nrN?q|y!~vkU$rxS<)Ez( z893-+@`9yqbVlDgn7GU2Y^3`bd{2KQ0?6%v?dcweryJz@1bDfHmqN$~YL&e9>@k;H z6b^#0Qy&^`K5qbqEHVHiFk?9%{~-@&$`(lvP$mX%0)8en7TMoa;z$Y4F8vktp6x6X zNag|iE$m%rX`*w}x#oM6Hm4GQ?k}fVWs=nR z9D3I@DqHB2fAl={fPDC%56{HGkRv2Fa$Q2ZeVan^&sniNc*U3dHt)KkzzKmCyW!_K zIqWLS+W5;c3XvzNqP#&iwRdew=4A>~;2c`}VIlDDk5nouOOr5rw$z(c023lG7RGmV zM8Tt?F{6^nIgFbI)@CSl z7Cm)OBPXIXfh1jjoJDNCmK7!vUD zCnYR$Kq3N5+e>)7tEdBcl~T!=sY~@me3BkYI5aB`CJj=%*77}%S?E!j;8F|vH9Ktu zRR)I2iHuDnM1wFCFWVo%GcOt6=5;7zbAXxN9LFYQ#F-DMlEE|#11o(O4Cp(N#&6E5fhx~R+5A^!F{1^FGe7aR zR9Oi+2*dQ9d~9OtW(*jKx9LY~-Q%A06Xoq_oN`u7*D>p;WRcq_x|YRK8=zeIdnEvV zd07u6WLWE)-kuy5D|%y$KTG%c&WsyfHb}u2Ciui_dWHn^;Mjkm_ZhyO0S4$mMi3vr z$}f^d;#LmB5r0*uglER7K9iT!?rZ2A)m<+|DrMWQ~FBnk(KnkVHJ*> zc(pY;!FM;@+4tM0KjH4`Gw!ahupXXD)^~daDeD~c=_n=8=mXPioa_$!^eF5s(?0BW zJM_*YmndU~U%;0>Q36-GhcwVklx80Jq5_6x1Q5$YTjGrQ(sl@~y>viM5Q$UvbL@cd z9BI!`?rWW$OTc}Ff>Jv^w5OE$p42Js*TFUAowR9ooK^?V444+Ji>AW_KnZ6%+vbPD zy-Q|wf7>=~W)@YE))gGW%Z{|Jwe9veIXT7o36t%{}WzazCqjViF?i9&exF9 z=X^QohzK4{My8w+D=IwA1h$PwGF^Sa&9^W3e)R>n->>j+e}|{1dyZEx^lm3qyq64a z#cbj+*|I6%gXNANv|kBjCxW<&Nk0(R#1#Wb$zmU+kJfix4(`crkFKQvwDatbM@YGH zOLC}LM$&-o!|X2@tviWl@>BwVX1lF_7A-IES=ECs{IBV_sI7bVYC5MZA?0!hB(Sb) z1@R7=7tPH3>YW>k*=ABkS?aml?OAQ2pui6K;lLfUL(3g98ZO>^z)!#Z3;g)oKg0Ru zTVPqx*8>tGL53RP2#_dW(=U^)DU~C=sjcgVy;$)bWnuL~@{Mnpv`7KJ+XsL?;LHq3V}Z@&e))zsE$K zs<--zg!bwP>Nwc12uyw#Gz(9dOI}=QD}9#Y2~@yO{ox5Q`ljOP+O>Q5tF&*5LzUF+ z;{Va-DAjJ!5dY!d{EM`5g&(#d;42ysXBUmcZs*Di>dSzRlHNeDzC9*qP(s|R%!y@R zJjP0EW5j_&HYHrs_fTds!C;qJPOT?e_7WuiMj_x1elP37W5RishI($uaJcuN@ zINgRvXINl^Ero1+V8+R~h9Mi5p@?bh#^uZxyktc9@fXPC*zN6DHYVi7bXRf(;Tiy- zgdsru8aKq14b?nOCa~Br>Bx5#Kvc_=DZa0GyhtcOS3fB%NBpZ=to<&U_(k>9-}2;$#GGlmXwLjk=xn!1l!=&s~(cTz_KYq6m1 zsK3_3VI)kYrx&fa4yaT$7z7}*0t^#?bYpN={4Bk&ZM1>={a$)ie4?>Mi~z37kk{|M zBhk@Q`*z6AOa$X!bD(D9Qn)PrWyVP+8|G@Qf)}G*@37oEag%nQEZ9ySFMGT^e~mXE ze!`m%KjHGtdz_rT!lKQ;x)alL`xB3idxI~BeDJfuqr=WI`THdON?D*=^dpXjP4CX@#@lg3=PdV_dVOzc9FJ-@YMDWqJ6e@^M`xM@B9K>~se^V7^ zQM2LVK{@*g+E?=klQRs|GP@F^BwSk{W@9 zN6T8azG3T$lNV>WeES1_`%nH$y!-Jt*uOX}hnT}75@!4il*kU0?|E!n&4)F3KjSgf zPeYdvT|X|u@SK-qU&O5+03pOjtF|9%tJB)!56YG#`$!7#{KwWq~E))X}7!?UrOQ z)GwTD)iq5xMUZ#sr>pO1JQO1ZgM27o-IF)+JM2R_LC0T6_c=*UyQ<0u@w(~Ko{cjg z`ZoH+Y)khM2vyXMN>BADyIE~T$meEkkUHisLTlQCvemO=*SPEeA%C|#U^;<^$-k%yDhM9?QrtXvd?$_gER()WuBpcWkbx~J2WZqe z>B+WG43Jues|im}-i|R@Tr>e2vXlSeU;p!DBf~-T>~T3O;CBk40_seJ|t#H908tyokj@O=aZ9u@F3$*UFSL z3u=wRW#i=b>l%Fs-GIM3xROD4c{|yi^e|&YWnVoE^)c=A+WVo#T*}nWpE8i8Y&>?A z+Zdn6LdltTI(5g&(OEViIP2`PBdP3Gy8)UX>2#0`I|@@Q*u8j(-TrLMFt?plGB3(N z4HbZ$UMH7;DzPcvLGc|R-ceh`N1^oymlo<0e%FJX8_GobhzdY8Y94xB6)C^r6#0pg?U-16}h zuPWiLF%o{$(Bm5r>0JV=FCL5?8R1X61w-Ssyy^dC5cyp*SkwJLVtabP&G#?(`tgsr z{_+vG->-0ge~Z4ZJo6WHW_Sb+0pl@Fo@phIMu0O)U52$0zA;uh(c|46cXC!02|y=( zx3J{9>qwDf`5gS7po3t?Y9t&xdICFb_~Mc;x6&Gujt-D$ob{u1m7I@E@lVi+O&N1v zy))q&nn9!pOMluSw}Tp*`Bs3oZ3^haYo^JEEm$cuU~qur8FTjUSweqKZG4!?nP(>| zQaWsD3t}NGh0(MvPxPc?bbk2`?|%M0-v9htoWFjDlhZSxO>18<>jvphB1%#=VsQA5 z^r`j6psAiIeK1#Bh-k&*uFn!k3SK!0pvHJU-rITMvk) z^|aGWzI`7$#+s>DX+M()O7;j=vb_@iy5FlRh1F$j%YGDjedZ+x)0RaP-6;8?3Iu+0 z|Ju07a7bUKH}w(UQ`Ttb`m}T)0iY+(yH*gBk|Xna?OU@@w_wsEO?$17&nLdOo^c8G zv{Bi%ZA{2Wo?9q6hK3g}Uex<3p4p$A;iupK6a4ni|0yoszDF#3m72?60|cvVg>|?7 z@(KQHnKRGx-X#1(&~@4V+s>Pp>C^d2jCVyspiJHrys9JenFB>2*C!mFzGHp5ukv{g zfB11-Nw`$SftQh_5*jt53gcdVDS^X%bv)5D?Q3M%o)5ivc=89*0rCil%>Pk5HEb(p zJ7!*Yu;WJ*gj%Lct`Wph087ox3RWekY7ZNLerJ=j>k`#^ahONLaKAO0w z#zmQiJVhz~!aZeCvb5=b#T%*yEq?W+o@t@J%;UlZlenxM7sU<;Egp2s>I`GrH_u z2$tdwZ5hsPCY)BJC8e>N(%&ll+7R=lL^EFFqM3Wo>UeSgMzenpA=>a`BL=b?4}TG zSr?9-i~+a?%XE-(w6XxIRMAG;ifshU6K#dd))+Xm(?REsU{a-N$Lo_*w1s8Y(e@H1 z;n@n%loM<Bo*f3;**y zyNy=z9?PIrQC&}wx6Kj$#aD#Ot7e?#(b%i)f>A9L-_lqg`k!>ZT&tNf+0)!hNf23(?`Io^*-|%#Qhr8=<`1n%jedu}JLd3{|hc()guEh!g&^>{vo-(xuAPQb7T1NAyl= zzll)B8d?X4>A0X!qSpbAMsO53bX7h1)B@`1z_KEt*x+&`nS8Y(-hpZXlM zOP4yKT;;$TJ83|E2(5)SJ!&ipOj-=yKO8tS<+E?MZR479*>%ov+G!YkXdFhUBw5D{ z?e^TuW7}lkV?pe9I6r@jHy?h&>vtdU=KW7NIX_3YvpEHj5M6$KU=rUcP>V&@vC=MV;uPEKfFl!p*N=q{Gmi zevr1pr=5uHhHg4)LHs-2q^z+GTbRMm)ZRd7(j<}F6P_Nf>MUmBLpm8j7L%>9zY7eX zD%B_KgDJQ)tfH)6EC0|$qK=;Z=dl!$@xsJM)w>fs&+QWXC+yzUdGZ5wmbY!1!5FxQ z@b79awmkn{eF)b2-RuxJs#b7&KCc%k->&=w`}33&P6SQ;`ku$9Ng?*7&q42@c7~S! zm`DcvKuwU%F-I!#LtU-S!1VM8n#l(o5GhJnltl9w4N{Gr{8Gsc<01QWZnqQNBjGXd z8>6gRy~1`~at;8|b_gdNL7bO9(`W$tQ*5aN&}_cnfrmF{gWh!OV`-;t*e28KAKX84QOL{9K zqcylT7Ghot8ktMP(hP!2hf@Ki*i}QdA=43-%f(5GXn-B``TAhS|>n zmNn9;oZ1MMWGL}46xMjQCy%vqBUZ%QdQgJE9KzY-SaxW;6YNgTcpOx%<)&jeV4D=; zbeX3TW6g+_qh64r7>=yl+|{;>%(1CnRgR0B1`FzQlCmYw??As@Npk&f#OIU#@cu2uB42=}o;+O(T&k)pRTV zQ5;I3hEqnolV3Q5i3d2+8ozllHPgefc{bd)Tqp2s;3vKGY1A^;>8* zPCF7uWtzJjA2|SaV#L2O1p;*H`D{RE*cuN?Y-PzUN!qhx*LJnG)ctPSeHjW!{F_;8 z@vh0jN$F|W)|uwCxyDPtdx7vI3*!P1FRH>MwEJ-LjAkaeBvslbT^iE+oMk4R`nFbS zd8YE8Id~!g1#udaXcKLR_doosBctBo;>{1(y*NcItOB6ElueLMwZ6`Rk$h&JlY5DT zDp$nI(0cC7Im!9Ui;_fMul51*dHFngR`^9I*n?q_%*qO9U$H$s;;}J<-l(L0E{wf8mnbPzY$#=K>5!l>s~@K#_g9Grw4_Lwv zoq#0SH67>g_LGQR>sZb3cP$lcP4ScDKLKDQh*?TMk_j|I^m*71@y=Mm{=Q-*pzOV{ zLi&g%UV}YokD?!wj7}7g&Wv?<&+z*F3Y^)w5T);c2To@qwS%>OA%F=5Ivy+=h@kQ( zqj)AVfyrU+I*x%y<_y66pAF0?SA(()&Zfx-Nf1JTshNOxi9Z^z6)A;l=hpBym-ESy zR9hvV1jm}9356Kx?q;~$ZV8Ov=R*AWln2?Kbd3SpDKV)%T2IHAW#;&cAb4X}@A z<4n?Yw2i^X8YjTPnhVt2FX$Qt*KkJWkDweT?>~iWof!XbeS%qwIc+_R5)2 z)_Pv|r8iB_lE-BGO`8ezzTxib8@_)01HOLx1Fk-QM6L%kbQ}(cX93|D3)`X;@XTI0 zHF8F_qw;7cYJ$g0Nj3+sag(k^J4J7sN>=5px24Iha131~iGId93$UX`I+h;n9m|f1 zM|cgV`fiOSrk*XNWAAeC@%*O5FTxv9a(NMKua0JeJ&QK3Xk7o9O8TaF5XmYdbDnvm z>nz~VF^nl(EXy9vCt)O)Asy@I5rEp!Gq?4Pw#Px!x{hNbS2;mvFVAuD<^$gU_#3?W z;YVD&euw4cq=I#`zjdN<2aq3#5v)8){uW&b+HJan{)u3Y4uIf)`% zv2u2DHPWt4h+L9raT*YDZFzX+wrq}kE&ZNJe`VKpK;t+fhLZ>-C7L$*q>dpzS#{Bw z@0gbFd2O=!vT0JmYc9>>PIhpDg-$aAV2(2sqz{~sS1B8bzG@fT=H+*k=k@S}W&Z;2 ze*7K&?63YSy#MKU*u6Nd1pMF<^NDTtpljU+c`<^%+2D_3KJYz#69s19MZeU$P#8I2 zN!ehbm+**3OZ$?zKW zfdk7q`H6{vVUImiOuv8Tl?3AqCJDIlGbf(NJ59@G4X^pJ+S3FblM$@cMbL{$;g-5h z&nhul{3yz7wIqSMYp$TmX`+;RmOO|`4r!)aP}UNLXo&yxKmE((syi}aFQF7e_*_Yq zZ<~q2!BBQ=Ht;gEEmTGz0c_f|!VJWzbP|>dTqR&q{KS7ADe-U?M+oFW)l|}CW)9wK zmC2Z1-z9_6SFGz3nvN5K3?~4dyR1y+V75`(=b%>qXM19>WcpR&DDa;;o!>(p=~r_!KX?iNTS+q-Y>|>KO{GK=5<&fndd&(B^293QR3ko zNj#F~Cs5?@98%Pe(6GrS0Ai^nGb@+MtlZuRcf0f9d7f()2{=&sMufYYUAvpz&CDDa z@^|t~DL=JGxc#c}&9MNQm^;{4sKUhfn~hs5+2SKbhz+IsGpy2<1xpr;(^zy3oQqik zQ2v>%^X1Y<`c-{R@48PmI`KCrhZz@osM;qCT(A7{`BOZ7{2d;DcONgFJi+Pu6_7jE z4kNHc;B-0x0j!4uGPUqVMIxWJw6)K1JM9)o`I8iT)=9@BXSf1^96jN})@Uvu_8+;k}z4vmchJ)+V zGp0E^0{gZTp0;4$cbv`}j#oGF&ifzX!%x4!dmnv@+wZ)~WkJe3G5AhrC@_pt@|EPg zMo(=Pk3b88?{gC)277Ev zmCOLto2j+S2%I-oYh)(2^O=rzbHXOk)+65k_;Y;m^S{EoAAO2g4_r^)_i5JhMWPu$ ze{x^85tk~Go&zuAG0t5>mx_&bZ(e_Itqby!GH`5j;^8-VaXjM&`62h$IKO&^?er4R zzC7kp{x|rCn?NafA{$u^%fVHkR<>&B9=o&C;$+l#KLlG1KD53yOgr-%vyv%3pssc~ zvfr0hXd!dc@K2dT1yW|-OgqR^Wh5`-Q;9DSCgNZm|72X(m?t>_xUnNHc7jDd4~qXiDpD+5FjchWvWqd>5Q6N$-$NFnEB= zW!SfAV_ar(Z>PauL)Uw`tP^Lf@flZw1EpMR!?VsH2c-ACdwD9$U$oF5Eg23-(?8LP z0QRw_DkO+rcGfxn${!K@9%GxUA97TrX5!MeE_spe(GgPs{W1RLOusjRl-;EAeL&S( zzy6Q^Tawwv23#Ecm>5_dw)31VqC|ku6Qh$gc6N?BW=WBcD$^h`>pAes&PlshiQt$# z*w+vmPY6a7!bcDZ*}+{hjd~)1$6h788{mK$V5Eg>i3!UPP75&M;K)@{ET_B>3kze& z=T;w$B{V5#5t^iY6`p4XOTs<`c9b63Nk-`162QSYj8r#Ht-?#r4tLN1weXrAu)zyO zU<{*_%Un9|zFA%it`ifpmBLaKxfDq;p-Z~R^vrB@Wz#Ol&M{D!mgR_LJs@<3a`^n@ zNzAD%{@f*o8v3f<6V)o}B$SOer#1~C^bYxp-%LjvHAPRoec3FZOJ({kYw+X^(wF=| z*g}9081%mS2F7GFU!^cWUN2nu{&>(BFTOztVbgv7AY~*a>mCN4jDx8+$&%Oa{E6HY zZ2rUFQ$s_|-+%hfh?EX_pe}KATo4BFSbFA-2s;B)8f%~?0f5new#)IkJ%j3quo);| zocVH}9+2Rio?xe=q-@Y`!mP)x@4hAsjDHFxBM2ajiEqYBtu>(U*iYAZ`QjNK-v1hp z?tO)q&!6Ia{h9$sl>{!!O6O0Jd&)S4XKWVf>EDEgS9G0 zm_?(0?l>HdfQngo5n8DQ+QVjHE#Z0=W&Wkipw zU`yzI{a*KJT9VeR?vju($Z=S(AaK5Zi6@UAWVNss68 z@UuuPW&aFVAezJHjcb?p!n5R{wT8oCMc+C98q*vv&q{AiCAosF%aF8<6{-~6B~wlG z^?c1`eLRjWgEHW;F;T#^>8pY|0oh-w*r4q|T%NA4vFYsV<#2`1e*ANM{?os}+aG+4 zSXXAIttX0}v^PCAviX>22rcxyct-M-;sx7vrjzyLj4z0vsG>{_+C%Ke!D+~rwBb*B zk-x@V1y1vPC3kG67dXBC9@x(=pa2luvmu;tlI_3-OHQaO34hlT6+yywE^Lh9wAvXo zUqk12kKP$Tq%9xH20AE6n=O;D75)?$sY^CDKnK+@34-zk{EL5w^fd{QvLU2zG8w!B z`=-Ow1mCoWP|5l9v&`eL?dQu_*gc_|FL+&RBCO(qWoY0Lh_YYFw6N$B=SxIYxe-@K}5? z=Kav7eXSkD)`CHDAAA)keKiGHK%CRw65YkWga3^)@u&Lse}>e!BRxkIMbXWEN#C?* zmT9bZlo5~0dMj8Vm=Hkx=KuQNGpE{rDoaMFi6{WQW0OP041(D`nI(`C%p4P!&p=K^ zJ1q!&2II2`Yv$ovAQ41wAs7LtLeRz%as<_?`ji8|pKHStBe|kzDnTYWOayZrzHJ9T-&b64$S#M~E&bpElKN(^O z>g-fkSqX?c(qb?ypB2OS7%r8wBekiCXv1-|TGLE=DQa{fWS*?MCNNF>!6>$~)e*+0 z1SJ_I>J=?q`QNf)J>JB6xQXR(K!na@W^aWHm`=qH8nY!SA$UNA+LM#rY=mgSv5)|R zm)#9@$WT`Pk$gsDlX6YDnT5`PkykD!1b}oH{IxLL9RL9T^hrcPRF>jRD-qAAhRYu% zBN3%kOKLJlYZ{rnJR5hsNDD750r_zX@VvP$oBb1qrQeEXdS4T3p;EYK-=>T& z@||J#vmn+B*3^U_nsj*yx%F+HIQ3rPg!hHTg2jX3N5R&2yngi@ubw}{qX*yM!Pj5m z)r)7yzF}DwtV_eT?Lcc-f}huQ#jI}*h=NIr?R~bQ$Qt$h&su!P6 ztg|vf$8*s}T4}wf`HIJmqC9a~44skl`GnlNHe(V`nr7-Zy@8Ul(!EQ!fm!cV zc`Gw1pboF-yffr{B_s>ZxbJ%hV!?8}g`0QY#wTC=6d!*2Biy?CHjj;xk(j+}nG%_= z=AI!0fMnQnAt1Idc^5zWdCjR1UsHaCtIj6unJkiI`^Tr-9Y)7l<+-v0_r2qI;}$;n^b7p>%fH0iAAEuYxG|qx z2jYw+s+?;kT1gl!%d;M(NbyF<#{$`k5KqmH#%_Dn__EHl zuTYhhx`b!H&-p-=nn}&=qsY>ZB|y1R6LZoorF%p2>3#>!>A(e?37(dmD1npHQUdD~ z^M?Zf2nCfpW(o>s>9MkLb()K0wgrQpbV4Wl24kLI@fSD^Z*p_*u ztxUo>E1a^Y=v4AOVHkiCQ?h@2zt73a8Xw10xKza`>1H`HXJ6`TA;e12VTV7tjP_WC&*e);L5O)$$8wIjaa68m~|N%@|y z;(vX&f|&sJ$oW)g1B!yne+P_uZ73>D((FL}`yF>R@tQ_j7Y}RM^M|l6^&v5JQcX7D7QZ~Ydi7(BuGAc4PZ%F^9K9hGD zh|@>PuHY?Md!u#!;`aEQ<)@yg&qF_2)_Cb%t7W~zArHB+6FT~2kVTkdOTv@k_1kp# zI<8+l$K(57tKHu>*KK1Q{?fM7w?N#+`9yyuLcoDV?uylfG}&$*EBQb!9z zLP*JlbVfP8P*6#_)+1&miFnldLAFDtj|m)!j1D@h5FWWKG&BqHH_DMX!2Bql;1_X* zi7%K1kjCfPmSZAbF>Ffd{jxK1PNK0#AspT7Xyu1kZ!GJ9eM?7}&&(6XDZ#PxYeQ?Q zEEUBZIIwJ_e1@!dF}2Ijlsn=UW&aKy2@pr!!CF z+<`pdpti?I`f7QAon6mEkS!W~R~-um%ZWEiW+1)-0&2rF4*>C-|M%Gf*)K zH)K$-ntHS3L zVaYkrV8SD?%Xx^YbTJ@dlTqVw83$&1wr6G-pOIeA#fMU5FU!g@7(yhNB-mUhklK`D z7$xKxE2YWtS0pmga5!8cmLrzK4XlSN?x?M22(vb(6WlIKGBu442;{6Z0Vzp815AKSFB@-m9wdZR3U6GRAlSM(E_f|t)6{8Q>wi;x zCEhU|ksc$z7fyW5pv2hqxv;1J%D|XlgLrbfID9{)v3r8_bYwka+?Rav*=fK91KZK3 zo|;~$+(I;~QfbrL?Z<%A6BfkBmJ2^`T&4DO7#Ht%_00)Dx8BrSh3^3e<(`kzfT&+* zN)Q(wcPLn4ox-(WVwPnF!osQ}g66_4xA@9n7Q+CFtj)ujw*!La#b_R6*qHIcM<5g1cIH}Y?P}eWai(4+iEHyRy~}ygf$5HIiqv1g+GrH_otrH|+>>r{vPqg>%D*d>A}G_$ zKm2u3A{H{4c$B?EC^Qm%>j+J#s!|8mjBcS-)vMdbf zC|kU5LpF9FStX~D@)P9OSziT->}r$Dz^%J)1OKOpm5 zdKaL)G5EN!>{aW9Nk8%wyOXx4KqQxD=t(T?0F=`xTRKr|g)=ptf>$(i*7%}&jPgt; zgL9}IGzMD6OthYIf2JRK8B=gsa4Bgv_<>_& zb>x`Kj0gJ@9;55SIpGvw>9Oaa z!K>tT+TJ>jZ+|kbrGV#TMFSRuF%RZT`>^z>< ziNOk7IGG}tv^P}z`ak}!R4+1uhLV>foLYk>oa~A`mY~$80AzraQ_YB#1$-#7U3KMv zlve~1R}P{8D}y|-tZS7J5En@$AmwEtC$gxN5~v*p$pj46}1@(htN?lc|^>JIEsf^wzsT$Uk-Y(6ee7@;=qT;Dq2$ws*Uvu%i*ZX0UZyu z9HgKKGx5#IIuubdFPi@fxoLb^3$U``f}bv~)Vt$fIx-UCH*n0drxBg=>jon=Ru0wh z6m-HapJaA%Ox8mwxN!kB(Q1HeXxVtQf!~XF({K>$>}x330heBj0V5|2v)?eRik5ZF z9qLobs#=z^FFu6jU>^67i7dJs?~F7PUjZ1Ny2ga$@i^FFxKZOr;oy=ld~>3kCR}2` zV-ap-lleW5;{r9fL~zQ^XH#-C<^MI8h>J~Tckd}7te@o>bpjq`!I z=4+>ctxdgp*G3@HSG)I;aK=(bb&3yclym5YMQ9)ZXp`=wPNOcfj)w3X(aOoTt~Rc- z_j1y?X;k-_R?%r0h3mQ&JzL|Xzxg;F8Dw4kMg%Y&p^+;2 z_PTE&Tk^p^wlfWE2hj#U4yT1dp9HjVX9YDQupDmS&RcKey^lY^N1y%}Z@v3I4mWNA zu?#1L(6Ddy&pCSVX9|CVzb%{OL*j$aIvO_|Mz!PS__l(-o*g`>%rV0Ts1y~o_HRKc8Qc%FBwWe}s%{dB)d3SHmAE}ai??*rz zig48O1MyUs6T<8_fM_@znEmpyicD`ZDtn>=Q+Ari@6GEtRpOe_@YdTO;733C5}*F~ zXSns&JJ^dp<0|~vj;;9OO$m&%_1kl6~yG zG0$^O7#1(FPuAw4*EiKciGVLjUvX<&!Kb4L*#wG$9@8?&>eFz=YJi4C3+)Mt((WcRHJ%G#&YY&V22uV>`5g3`A&8!ghH3zKJ+omsSW= zrZ<(fg{tL9seVY(8k<-IJcPLOT@vqbf^?(3cur`t|BW~8OE5o#wdvZ`#yeb)HO zpven*@ICe$Z{Ua*fab{yIj6IeWk6|6`il#2 zJ#p7p>Y@l_-|+H>CwThkJAD7}0iHg2i0fA`X&}g$FtSqXEQirer>*#IA)nB&)^SG_ zwI0Wuw4hb$_gtn*V?l5xiZ@e+v_?|z^_|AJaR%`dlIY=@WICRu@3rw2*C7)_o*27y z!@6*tG@TfZF+g~VhGuDH*=pifU;Z%Frqcwu_X_bt>ZLyR&dQ2m}R zOU97c(V6ucX|Hx?B1}(HPd4PfM4lUOsz@?R;HxS4an&r_3v@H7?}} z8q2M5?WGMmfL(KVn#0Va+5w$G501`#m8?qsGPw*PB!|!(M*fmPpOhIcaTsSKbD6<# zURITC({7+_ zu!B26HX5ewwx3>NzkZ?Y<&YJ~`GYrBd&4#jRp9KqmfB5oJVCm|O{VBl88q@IaWHgj zmp#p9I6m?v?Gp0=pI63CZTOu~f1dl<&Wt(GdZ-``@y`3UaqVzv?}mb(W5+ZPNAMUb zRwBM>-x@Eh&T#Ke0O{MX4+aXQ1up{t))~;L($5<=1}SS-C$1cp(gu?(RQaoH9f-R8AhNnwfz#Nsj+?EqNLE*IKE z`)Lz+o0@2@Zc3JqJo*M)&TEW-tqml?;VTcrc#SNC~@*8l-B(OvXu(aL9%C zqPhEk@u6oGG$^A{{gyJ;uWX8RFaDntJ-?fdbJr6sK*YGk&_h`p;Sg6Ir(QrvF_Wh_ z1~(YtwlDGKXCv6BK&i{TG&}Rr_#T3I8Tcj)Y%c_-8g!d73Q_Qn5%>tEn5L@I%2Ey59*uOP}Kff zeD3dX#gF8mZ+NB5@{{kr#nVR*@cPwDZ09o$hZXC(*3QY+Lphaqu^igjB~9eYc#(bt zB{b&DQ4+SRdS_{kYoe!z$wHo6_+C@lv&)ycksCf)p(Ep``-mw$ z%ZekyK(#=+ygX>e>$hL)v}0Mq)R-jQCNcnvW& zzu{N*WBo&!lB`$`$~Y=RWyri`*NizjV!N|!i#N8@Ydn4U08bu#gU8=}i&xK|iZ6^K z!b8K7e79dLJtH4+)Kh<+PoOhsrz)D(xPfJAo{f;9S2AJH#VfLtN+)?UQS+1>l)dw~ ztWZ`ClA+cXY&$pYbCpP{3WM1&2B4@1jrWn=gO)lVo!Cg6udlJ~+%KkSXMF%Top;1? zz}p{ujGz3)U*oeM{}jg?x6mhD6sq=0=?>Cb$dR2hQX2LWAys0G#s-Q3lRAmEAi6e3nH&JLY@R&XlEmGDxLF93fIb#Rut_h)(h+DU+s zB^eeLwn8SzkfEJWnPIgttNQcoKux}6d>by~tUwVSJQf2svfym!_|5?;vmMfE;j*Jd zEWvT8?%qsVHS7o=vITaMWlJpq|m>K>AA{1XI(RHsV1DZ#poyCgDG$T-dpyWuk%Gybww=neYSv(RgMu z+JdXA+h{Axup^o_({brXaqorkY#|T=hO9`SF8cNR#YYB}d`Mn14b+C-@a9<%{_2|B zc3-~SuG=ZW(_`lU8SWWWz2m8Fe#S$nFX~4v@>eS*-W09|h_f-tiDeiwgvoG^$)Fei zPe!u34EXquWepJh2AF)TCJH`hIYU9H*b^VF$@5PdQd@@E<0p>hu?$&P>-Dc?mi@31 z)0t~^cf5Z76i@DdgU1iP!L#ol2g5o)M&HJ zDp;aYDVJ`US9)jGdRbbPGIQ7M@orR!7NlR8(XR3~(4lU@UAc!tz91IOHQG~QS-5#t zAji6FWz(WHI+ZryWQc5?Yl9=W4%;~Q#Ew*U%w?)NE-OpNlg`dWg^w_CpvZcw98DRZ z>0N|LJ1Dd4zOyH`eH+fW%Ha2nyP6~9KuS-C=AE<8c~)ud>2yXWaQn_(y!+wD_~b`F z#fP7Mf!lAt11u~0KGx8hzq|r3!#(Bh4;!b%En}+C;NZ` zi^Pxkhz%n(COgM$TYGN2k!*tI9`Y(3v#`yZD${L}R|QZLyhnVRpM*D!bDsY*sh~U` zQy=o!Ta+H4zBj+jEM>khoNR7(sKrMSATOynF-B!C=UZ0RLcHVUrXd^>8={%yl6Z6= zMB^k>{I0Q6ezKBEgD)gOURt4jK&qW2F3B4JlBaAifh%81dV@G2om=rW;h@U2QIzUm z&tT|sSjH9nC9iuUJIbB=V6Psg115Zqb`{4#cAGXZ&S@37_%z|Neijs??(*TUBQ-B_QGNedC5V%c_ZHOn4C#s(s%|D9tPpmy>!3Cs0{dpRx=j zzn$%2U?IswO=ozTVch?aWoCy-GiA+-k@#*X7?1%(?_*=Ga_~cqNePr<6AcvKrCJWO zC9rRdEV1u~Wj$g&-T-37a=5~>9wjV6d{)Gm2vA=L8r+YS7EE5=Q z)+PQM^y5XI(}bz96X=okhJP<)jNhvtk3)DRT!UAJ;-Hb&M7xBKPH@kBr|{~&CVrqC z=y@q6%f`q!u{V-!=$m>+6NE485f0t(5-vt&FL4@DwBG!~xO2a~Qzihm2Px=+%adr0 z#N%Z90$WdrzIu*$8Wj@0tUq}9DSsXOk#dxy1@`j^FP=WZ_Yd#m+2aTJ;mISMUccmq zTau~JWE+iZR_8z-gNAg1jIZ!g1R|x3Za%*-gC=Ly5SG4_9t)j4ZD+DMTdgB;_MMa3 z;`MH+b;AJ8=QCi#D|PN@orO$8WnH+t_c$(Z9Jd7VO@s^!@LcA7-_Szu+A(R|<_BS+ zSX4U3W6NZPi}{jOS~M7KTp;M66*HO7Cnw?Znslo5>|F6R;l7>~jH5ZhTsl!tM^U9@ z!{g-8JzgF`ww=#-{{3Uz`@?VW@Y_FNyMB%Rd`1MY9LQJX71|NC7G3(v1~r{t&f68} z@w_E$rYbeuX3w#f&J_fgG#Z9BmmKi(x^jo)^I4_4p`~6zX}Wr%wK}W3me)BmJ?Vf< z9hnoX@@TflOn?1HLJ!@)?<$Yf5mpeKnRJWq+88Gqo4f(SGVq{`ekAWv!6yz z-Q;h?5ucNV>vxL6#1~=H%{S%{wN!jy?w&6_{f3?wc(Xk!K2Hm#-z6g!908VPZ>N{Y z{VZqD;Eg%U#Ns&lfO(d5q8p&iI>F6X|fs47=Kay_=|2)d@To~jFw#n)P$leMc z?WYYL0Vk%0Ze+D20~DV3OCD3D;9A$r`@&J-PdwJm7sj9XKx1YSOu<5SqA~G`HXZkr z3_eh4k+*q@%UEmA@sD|cj=6C{FgOM$;M@o0$JHfv$T?>7+*n@=(Xp$Ving))5Y8g7 zHg2NDwZNlYF9luNiqOdrIi0%dAQtNgA4$jN2h5!xw^nBlxn%UC}T(+?+eaOOmw|Zz%4lU|?togM)*X5&44R znUsEZJM3oIpuIMtlMRTda({jY;=lff|5=;(1=_kIg{Z!MNz5Xq(n4fxvv!Aj2HJ|I0t#utcYX*tmto7M8xn}vDgtXf#(QPG<|m@N@dg& zyyBCpMq7%?_?z%H!S$ovpx6lZO)BF8)#N}3I6dkKJqC_~ z&8aETt#Idcw4+U;P6mU2(8Z6!t@M>Ks*e|wRYL2t3Rdz4X)5OziN2rl>W8Oz{NQUm zy7x7nKm8upuU;ZG(LngjOd6z9>Gk=19_JE+x)!x=kD1+sjy2MK$})8^_nNRV#PyH6 zHVa9}xDp+Egs(}M}Fmc?|x>Oqn&~aMIsGG<9ZU}}D zk7Rair!!9H6QXndDrBJS+Ua|_aTD*n_c1>C(NFNLg7-23D2@bu9GY}cz{Z<2k;J*f*SC zKf(F*l9iBcQTegPYPryndjOMAwskEB;p!DvP=I!x_jJh4(o%LWIWy0T#)gT_4!Avk zAY29wmGH8Pf(})yfU~O#i3?R22zLy~P)A{sRgxrV4+eLgHXL?vk*OW$-KX$eV=?XJ zcVt%9yKLwQ|JF%=TKULtNZa$nMkRhcKMz|x?R+I|q6pcPd8rklu@W|wCu7Kc?+iTh z`=GIq&ZQKZ8!Grf3t)^ll#sH+u05LejR8J8&nSJUsxfEaHMi0BGjAkdUeGd6@+HUb z`I-0>-*?krEu_kulhso#c(jvs9TVB?BO2|%?uw0|V2dlN%62&CUi$h5p64-6dsQ(} z00g+fTGq0w`M9e~+jsCP`?CW-k`A;#3daBXAO4s0<^(Kr;4uRhaF;c+99xkJ)$v1H zb5k(PKvKk()rpAgI28REii*#fe`v5(1f+8yR0cFiAb|@bOc5rQH?UO;FC%0&su+dn zK5|OL@mK)xthC^6ZSZcyQ#td6!kml>Dilo$Vz8K^Z>_OEGe8UP_&u0`c;K3Viui~b zc#lZxySCYHOF8!?AR2dsmHF7XU{U07tZSBIor&YsEv$zd2xXR|t!N8zQO7)48753D zK}{?e7bz!;FPXT6pRdgK&3iry%6!D8lc&|LD>wMQMrU{=Q~o~6GWBGj z#!~GGS1HN7z>}O9!C99X7Riu;fHt%vq~Irf@CCxoiBi2^I4A`M4WEe@(uSSBQ(*)Be2pKT ze21rx?%~nBZ}9!2?{K<)t#L5&&*;6ztNxrNb?|cEwlefX6)~-GLg<~*@2s;|3Cpr@ zk|2DZbcV4V(1aoyQb!LR<^R@InX59sWpIK~Yfr-6nH!`W>cBef8k%w{mbPFyEZEMR z*w=N%@pwdPH*mwofEA4{@{Z;VRYy5v+(K)z12~^g=smG4(#c6@mC<@SyaApc&C*t2 z-_9jtgBR?8+aM(_?D+17>?mO=y))jjvPA}T|bMjPsA^`4<8bI(%Rw$dZJ{WIAH97Pq4JXby!5>N=SgtKy zg)0lN^g21fIw8iUNx@P`%sRrCaR@dweXK8hmw|u?@<&7B#nUIa|JCns|Eu5Q#nUGU z2Tp3!JHi1OyO3SsK!2A(hEP`D2C3KE0iyGm|6v1F9H-Dhl9Cv^J=-j^U=O;#%p&ZQpV8 z_S^XKmw$sVzWgO_+Cn8v@4|-y2IDC{2N0$J&3ft(E*o$;F!hb_C*2MuI`9VV zyEZC|D)m;Ig9|3y(hmx#0cJYwM zk)6TbKd}kju555FTcO@IfP=?54v%(#FJ9m`jX{ASB}TFl7o79uJEem@Bctni(fkJF zbmXmgP(fjCHaOOS1BBVQF&|LZa32-hAqJN;;TRLduQ*BgiJ~F(Ds@l%`v3f2l933f z`dLbmG90YRX5+_`Gds3(H=?KPL{|{F))vHaK!TA?E6r4N(rki?I1S*l`z*P6!e`k+ zt1N_!cC9za^w#IJd@Rv5qe=lSE7v1SkgdcVg*mK@#Ewk#BnX;VO?0>2 zrp&TSO}Nu0i9luK^3?$og_LK5hBnb*yL(0cybGNTmjI0}PPAh^xs63LFWLczt6Ml+ z-NdT%lzqe$g^NsW4 zB^Ty7zgKLdwlE3DxQ4Q4umz<=&DR_O2K*sM^bMuTFH`xl6?MU?9w!(`hoQRo{CH=y zqyG7p-n~>TDfm4m2VDG~%XJE7_p2Y-PEcX_k%O5=5t!w8#sXDDZm86ceGO;RT&LzuiExJ&~X9%V#3 zXs0%dPU0}qSN)23)K7uFpYh`96FhnNEx!5vZ}9x_L+tw*+xd)8#1nFkBk07X45B9a zq26M)H&Yufw!k9CIkYQlCbLUWW~@*6UgT(dEUf~D%&ax7CEokabAv(nJx&{O=JE4> zu8D71790*ooVPQ!^CtX}RLnH%7!K(_^U1#Nh{pAoNR})|0PBH)Q-&kUu=*X@etTS;u9C%hLShkuq zx5_ybzjSTD8QMjeJjT8@EtSdMao*0jaqBKV`q9tuqc4An4?g(}ha0!B`#k7L2F#n6 zmvlcc$;pr-%UJPvRdI<&>tj7B{vXe!^e=aQ6)z+WGQBdMoOPBp?=&xz;8s88$D!lv z+4Owj_I0m*b02{S&f_n>e}r#-|64q~_eWg6c+Tu+1AvCMvcm3sJ|PvHi4gCn47G(j zxbNGU&Ra+@VcX$wv+As!~nq(Vome_T7~a zgSNCihEo-w4)xl7Xy3ohc59ZGmk7c zFI6Fa9=yS|zU~7+Id`)T3Iw2a{;bB3EG2AxPJA$U&9J%^Q`t_k<&w5&NZ-%;uV1vs z0PPeAP`jgT$}#CDn@MALcSS=xba2(Kcfr^5q6e|Vpa}!M-X)uJkF1ZO2h6t`Hrp~j z$S*%2A1SRsDvfB#4H!Um6_n zFa??jq2-o(lyl-J&-R#l&C9fF3J%+g!o$*->_}HurAoD(b)|9^&O(`%iK%jmkddIl zQs`8Ad$K&tG)rq-bltdC!T_-?N7#nZOXplnDMiK4vmi>zIcg)2j26U{rPX)XVC?ME zNZ=$z0wabdb)omQFZUM^yP1*H%vMq&*;UHkD04O|3t`b_q zVtiUT)MqzqXMyGLp~)b0PaHP*OM|WUOnWOUPl|%8XkjH%d@&Rpg<3Ef@N(*=OJB|R z?05WTKIEE-$r<$frBGD`P?v)w9G3$p9P`Z6W6b13wviRtv-X#PICrUj3gGFx$6+N% zUq6_cn;hDDMX(XaLLk6?^$?$yVr$}9D8UZ zv+kWkLAqKev7L_9l}rvYYNNBtFlqf8v&LK#zN`nl_0IeF=(C^T^DqArci#OF2%gEC zWI)N<2xJ#;h~$HJrC0r1snQXgC@vBocV)e2`6C~5oC(T+dD|U~_j9-llB3dLHRlVi z3fx)tIOnsyk!>m%Eu)!}d=0%A5$NLaWog*1U*pMxukp?Ae~TyIeT(z!7YyVy;(n_t zE*@98_g$OZ*&#D59+9(e10w#vUGR(_U`eWi>{=*py+xj-UBc%l+yswO8#tw%wk#@F zH43$#OjRAYf8l6dR`RLzURNNAeOhjI?v((bwH52SU^|~HXoaBD&LAK8j+@#6H*Vd* zCqMcbe)1Q8g}2}T7#UoDXg;(|5iHwQx@TK0#FQ60eZnI+PU4z$){o*H*;64W*0()R znTZ0(qJ^Fi|C`rXPORTGtraYF&GYn*{qzdwS5JX{mO;LhJ{kPLKIy#D8S1ss)!?mp z-<_Cs35a*{nU|oboE4y*2V9IvLnNJRJ{a=bV(5(&jMkULt?(z>Ks;;S8-4K_N7QN# zjD`F!xpj#zf=*D1u5_ZLj~tLB`Y-yhJl32mdCzp^;>Ze9Y^sIggHQ%LL|ks1Htc%K zxry(g8-~)kVxgu7n4nU5@KiQE34)1HwxM}i&6VCIe<%Pjoz_DfF~Hmu%;4Dh{IW9n zmlc2@4Tw7>@N}G+ab^8wo*{hZF9mH(;~Yy$Mv#QbIaYazF=pRU0d<5OQ1U;=&F6dH zX-!Slf26Cbj^*?Z{V*&xkHNt+>q)}Fxs|R52^LC(S?BaR6@VmawzIVH8DXDvl^y3K zlt?gp95O0<-+9JYXZbcEbm#1p3@Im8gJ?lTOM$2Gw&l{f^aP*Dk$9lncIGn?#Uw;M zCjh`@V1v=&$ZpqKYLyk4V1^Y^5c_@}?KiHwprVrSP0`_moJBd9gqM8~i)_aNLqvE> zdEWQ4#u2H&KXkfx$G@`O@UXFKjy|MNL>dMn^u)V@9ASD{^{m z=%9DmAMwk85Me4MJoJA-VL=SzLEj4&5@dp!SIT%YVTbVQH^WkWs9yn6ib~H9KDE*r zN`wm5Sk`ZDui6c@>oTUPO}$~^_xSvo&(&k3M^#-+#?AClmLgY6V~FC#fxEgb)VdWu zJCs-vC$m1Xi2-8;dwjo)r_0PkXrn2%HDJlf*(f~e>%bKTuorSS-4_b&G(ls5aTTmY z0aPN|8qhmlK7WQM5AWg8y|3`(!MC`6`5dr8p=A~4vvic!$0ibo151d1{P_nmf+14w&M1z$!sgh2|9C$#1-r!3rXh6#(`U+fHL$6-Apm0?b8Qiqfg zr@57~%gPWho1pHc0qY%!Q;w9YMr;@(SgP+V&$6!EsI=2TZVK=iTibTl^GYI2fq=7? zJsb`PoVT;){=jd4t)J5zU2@9f#p!pFAim_c0} zHT98$w#*Vv{A9`mVI_|^siQTj*_FJBX%3?P@qB0b=iEq#&dCO{ohy4fmo&CT<;0Xl z20)cDmnSAP4m~3L4<01^6)b_A%)WEf!DX1lJ?AUpjlcQiiG61U*dm{zcqkfS@rK0v zdGHM=51gMATyvF5r~VuC;)y+emm3s&6U_v?9~L=ulB+_VW{sJb#kZ2tnks@m)b!A( zxCkb8&5xkai^jvoC2zQ&+4i+u+8&hsV|-3N6=WC9<58(Lg?3pPr8sY=0+>Xh@w0E* zZI3XpfhM8208oUUh(aFH1R+L_Vq`LmnVr#DwM}*c0PW-)W1{sS)c?M7(R*$6 zQ@ONv$xboz9G!xc^sJQ0=*s$Tn=(au#|l;kYVW(Uy_{f7gpd;-!3-1Zh|0jHfy*cm z1WQ)-S*4jAD1|Di$Smj--W9Q|Sl25o$6MT8d~rEAaloL30s+prmGV^t$&R}4krs9f zs!3n}*lHb46oV#%e~d@IAsk;AMx#udm;X*e1C-!-heX&hvg1}ppWaIghXe>v7K6PZ zqDpVnKs`^q2!a`m3AEJnTt6`a3xn_Vt~Eg!yOnep@>Cr;uwWY0uL5J>*`WBUF^R_f z%|~)c7ThlFi+V0)KvG^rH!BM%^f4KcFZ56KS$IkoM!8Saq%=Rno(r}5l5%JOO{x>V zAs8_kd3DXa)U+9fU)F=hXS1_7^QLrSk-<&YWqd;XHu%@iSs%do&w1m+9p~4t@Z{lr zJoxsH`2NASc>VG@Lgl@b3(5`+2nP{4F)-_62hHV`iSwo-F-mXovnRfYs0=J|tPQ@r zxtGT!9!=>M>dTp@HO9`YD0!)4JM+jS^5ZbN?98pINa{Ns(zZ-z604kR+bDBfi_J3B zh`@S~5eCg43Nl8tY1|rny-ztOjvTD?*toG)w7@}g-M()XB%q@V-ai}*|He`FY1xc} zkjnHmowG}vGb`+M=&3Bdvwcl_P4wPXmfmo6^A6sA??Zg@#n15W2Or~j^A@5t?3)Y{ z(-s^Db)B8l!Gq)T;8*hLkj2yu%IqXXZlr$W+%%l=@jH1*kE{e_E;9DH?s({t`e7Z33Jg(&Msi$Qm zrwr+)-Rrk~$J!PqJH+42{A+yL-^I>!AaFXLD07Wv;piRb^SSsLpbf;#H?Z}N^>70p zeflGO`OCk>`=5P*4%;>&hocSj?pEAlV=iBKO_UmWJPJNWwFSxi$jLoyvruy($F|vB$tRrmO z3SP;lX~J>IzG*z)n1K{a6)H1LyN=5jL$>h1=diWiFYg-;X9&kq5Ta^0r-s{fpwkA8 zj1GL}s7!VBsU1+3Ud!>R_hoa)hA}My4baZm?9%qF_Mp*tOD_p03?#`ht+{bi=^b?O z^B9|UYA%|~HnDB*wX;5rh{WQ!D)K`A%C>0)kEb0p)l$kn@s4<;DARJXSl-5Pr!egAd6UtPu9)N`_`f+<}g+00g-EKF3bDQ|H06nt}m*9wjyc z71jYoRLKr?f^8T3ko+93f)ZUr<7cH9d9Q*5snaUVL&6D{1_+OWnZYHFH>}6(=ZQvy z%~v3P{U85TQOXRPDgz8<5vXjnI3gmWpe*a1m4bG0>!XC1 zQC1#7wQr1WEX#^m4usS17qVrbx;Gi30u)d*rM0DYxnxO!n3E5Z8EwQ$u~?p{*ywmt zXdVnDF)(Dv&x!(41TZ0$MUPM>6F^%JXv+c1;RX&@w-M`+ zJ8KJWUxp*hoCPP5<*27RD}&v?Q8|o>i$5x}QKdg@jwC03ETA(E{c=ujAg@b7ppcI! z5Hxl=*lI!L=+%xCwlmwNf%)OhYqaC>4Wo#%Y-e5*^&8Hp8ZI89V4A0f<6wiwiqGXC ze4cGUZ5U3DmAK#-hQbB_t{0Lb^ly5X*^}{P)jR(MHmMQSF zEDN@cYrrCTeyp=Z&XUcn?K0;~I)|2R20e(=1hZnPU?O}Rlmd8O%jiwR{9c65tL20Q z>A_v+#jooD%d${fynD3Tf%!pzcnVDD{&wbwHC@%`4Itb9!mU(_{+ytz9(==^}=C#zz%JT1h+iFf(*A?q} zkh8*Ucxx<+waoVfGHA>$$1A-3?nn6e^B?2g4?f1Nx9$R!akT#DcxE|8(g5IZW3CwN zT2f0!&OaAulYw~go3H78T!6poDNGQhqC8NoH z%!f_#0QVn(b>R;0C_9iN@B6T&Y{-^;)!Jsjb7^Aiyv}8A#E<`m0zwR&58cHeiq|gF zzTqAOEWOUs!{h~L{`S4&d^!OMEGtXE&s70(z`D|r;heN!y}E%2@l{-z)JbXF zYThLcJqJ&cz=!#5*IMTDYp^gz^JU*TKSvBMF2_!KSlLA4YS>zU=F2Hi6$&)=oT@!R znI^s==h&41wADjZG{gs=;m*+9$HxN=FyMz6n>i7FC5NUm@_|=@@82OD@{?dAADRdF zjy1)U&GmDz-DSLp25xiyx9CNjL-tnoT|V&G4zmVh1 ziP#f93C_uZL=#@c44wc0M(16U!U0KEiT4R;-tF6YVkn6DS^5)-&_u{_R>s4Ga5O4B ziK$I!t#wYNCkBRupmWNoi4o1dp}v~eD#~$dTA$+Q=WHAi4!{w*%yRn4TOUX3R#n2_R6R$*Q;w!+|YhcPW zTjPiqix#E$IFVVwk)EQFaL1@7aU}r7|K<}paD-+|x`pQfyO@Pf*ZNoSfg*-{lcO=; zCkI{Qsdhbd^#wuUd(C_(JP|BCN2G%n>7Sao+zx8O6CPuf2~pA72($dKkuGJ=byjUe zg(q^6MuX!>o`EOzIA~t{pzngwaB~#AYE%j+cz}Jh;g7S;o)!?oaIcrjEkl$a;j7|ipR2e zfP6>(hN=aKXEB9KbVW1bmvd$+ID1$R+)-5yP6v;6Y|Fw-=W@i|_dmi%pZ*vhe)<`1 z-hNxhKx)G?OnReuP4kP5{?c8R0m9F})=SdMldSW%REEV|JPbScC#k517%sgnwK^7ld_t^@a%|@vS#R)AJF)`SnXY{_Y;`fBkzree@mn)2r&E zX%}eIntD>YzNqpl6$sM~Z;2DvL=2s6jFYDe}|;0MX`#E0#`zMUa_%c%*W(g>Xc2pW77z>>;=xJGZ4V>u#RfE z!wr1&*-!BEU;Yg~{Pc4yT7p5{DH)&i7(ZalqewtmNCv~mSL8kMiMYt$0NYIFN&lT3 z6XAX^sQVQ!c}|(&8`t>Gaj5lVsqAcn0GzuT65I82oL)Xg-x*w>?&w&W5<7h_N1hq4 zU?q)9eg$8s8il8Ifb;`(b1Fkgy{A(I+6pxC1mPU=o*EAMPvyKM4E3bOhQb>&+^i~? zvzPumuuaG)?{>6O)s;<2Aa0(-XM1HP2Zo7**%2PPx7rk4Y3Hk7%R%x?@stio?BxKb z#=;i{=wmZo_A}bEE}6!e>>nV17EGn<{Jh=}?tMlu<<(3GF_bH$Mi83s8>h1Koz<;A z=gqcgS2@7DEC+;+RIEUhV2q}DQMM(_^O5pr^tf;yQ&yhO5+CF#jwc1rqC{O9-afL! zo)_8P*slo?t&-0Q$teA)ei?iukI|nYoK_>G7je}}^-!h4D8flFi$Nw_pE7-rG7=|d=rWv)M7Br>TI{5XJ~rBZT=%?*JSl<{e7~gK27mJ!>-IbK<6&6iRWrO<3n?@=ZnQ z#%N|J_?#~r4PYaV`bRmwjCMi7X!NB4PKF>CRiDu$(kq%`>CF)55h?iC5+CLyD#HQDEZS%3`0H{ zIGOpX__>Jm#&6=YdR|#zPLxsn*85oIAVe{O8^J9IdgArV=XmnneLQ>g0FS@>7O!4B zL#to|vs_*`b2^{6?7G$k6aHmkLY}Y7DEp}Ms%@AWt@d~ z>*A0;C_E7#*?|r^bS)a%y3|^_Sr+L$f+d~GyuuT|%FPpWV$V7=8UWh^OGk+-X4+hK z9i=KGDjgttoq@+Lx<9yrFvz)u6h8_|t97VxgP7o*{4%^Raafd+DD?Wo6vWQlq6n$ud=WjU1N zEXO!NSota5W(M&KZOd908{%ITxgk7iTtFh)TCz)=D6k+Z8#m+$>nq;p+`xIB;vpCM8Sc7~$iJP{c$= z4BIp@?NEpzN@y7gA&f*aCS~IFu&wdX^323~xB`?hUDhis>lNZ~#6jmNvnB10Eqn}k zqOzilN^5dVGAIRd;H=<%Q5yk8jrzV|Lzq4Wn#smQKh8?sCUgJJxoLG01cB|_WkH8d__V2;p zFK}9un7^|n^OV;_B0FBa_yJG8yN4&=J;3wtAK}FhPZ7CmgO0@Uz)hW)A?7g=P_`$v zrZBZWXwlBkh9fm$dkg`ueGc@UM-;V)T9;HhHi)C5l_p%H(6G$>1sP`!3>sfNYKlg8 zs*J3n+Tvk4;pCB+N}JbZVKyd`rJv`G22lh~rxUU#4(rNHg&blH7A=@TZ;NzMUdE8= zn2`S>#+lUq9T>+}(Gaj+ViSk;AoXMeJkdAGt+SJd!%>bjvyJENgms~l&v(f_`_7Ff z8Cf@PCt_J~^Y&f5{oV)o=(8W={f|Gx@zz^tZ5f7!`O-WW6fR25;D1Vo_}H~dhlZZdV12qZp$)I<9Jg~SEu~zam+Y@WaskDX&ipoGCMuS*EPN*9ufe8 zWyF0u8c&YbRC*smKG~)GDT+HM?^#h0w6%-oXbMtr~Y*Yy&9+ zxoEGGoxJ8Cssmzv*W)Bf$@7-U-aD4%PysRHAZ*yyQV8<-t}Nx^eF>%|GvREB^b(vk zQJbmk50et+C2ms2IK-(}ge{x#jzcVSHPw*z>V_fdvDY=)3tq|NBcv_e<$IqgdFH>Nq? z4>uoK3RkjCHmh)JUJq4S?tOzd!VNiIrak$_xa?d)q~MeWG0B3ecEGuRfWPCsSI zPCLij9FP}IDybKoe<|;B=0QJ&rk?d7E%7&T7tuMz<*cpPuj#)G>u=MgOF-&NUVoyp1*;+}zNC;#Z{C5b`# z^%>S}1b+D&31Des72BxdNoTDmyMLC|OwSplg?WuXdqXzc%c2?Bzc0^k))j_@)g(iA z*k7v$Am_f*j*I{N@BZJpEHt>c(`$C#f+=$`{9qa>jEE|2ZH*hgFcL!pf7{MvRSlLC znATW`z}dw}&~B$vG*qB2(;kYtElkf7XTwP?r7Gd-eaE^U*{0Ou-nA}m;kvec-$s^t z%CZw=B9kyrNcV;l2nt6TI5y&l!+obw3`mJ-{a>$=3}-|*p|0x%OJO$uKEzI z;I%LlD#iEw%zrNUY?29tvYgp*{qi}UK6!|T_rJxHhxhUFho=Y`eXNS%=2m;}I3AAx zhNI|py(ew{L%qT7*467_dBjn)#v^hFS@E!WX4|;SHTj6}+41d_#Wah%ufcQHb9{)7 zgEM7K>w!l91GkITQPJq<%~Y9p0^GEV_6-*8s@;1R4j3ryof$D^rgdV2WL8{=wr~S4 zJH^&7Bq_5b3_hyd6#U)tmC8&5&W>;dhKWH`jZYD8kKNgQ2Py#IeC9e(mea~OTe#s3 z4M6spSn<|-@8i=izQo6${RFq)en)$AC>yBYu;~HIg$HNpv^>0V%{*LEY~s{qXNK}p zbiaJ$@~il$m+bI239;aL<9WOxx6@S`WR_3yUGb$fMMzG8IU`5oFjSVD`x(z3J;431 zeuwYweTD0nFAxf*wx!i3dJrF^^voi8q|PM`ki(gy(Jp|3eUcXj>^59`GRWn3$Zwk8 zy%x^Sns#PoW~ZU|QT?ziYh|zZZNsi()s{uFv2)`d(=BC$(rF*-a<*E25*sdPG@adqo0{QR%~A^zeY{A1jD>m8mKzHgL0&3Tu4 znDnO%GgE~ad_455-wE0mqI)>&YP%$}c!QZq@o>oTl}!n;J_C8QS2(9g2Z#>rT2(mW zgCQ4o=>FV13EGdRAMpCcV>&9rJ%bxlwSnXxax6m05*WtfXC+etv>gHL{Ve;%Ht;?& ztKuok24EwCXoSY>y*vmL#P8lyuic_=K%7I z6KKxF)3*Vj4I!JK2MwoOAZ^%Y4?PxEUR@63~hF@S_HjWK5#IJ_Wvd4>>#Jp&}ZK0DxOat=?Abyq(vc2Ra zYHNu9{Qv!z$<3=4F zfGkH?N``L}7DZM=r3S^AMzFG`P&vfn%?|V%^@02JdJ%4*z|yCwR21XbkiF9hX5Y>f z)5zMD-7bQQ3HSXx?JlXwRU6U7a=`J%ZI)dxM;xwhpe;ub&DiiSY>lN^HmCAsvH2EK~``D#C!c#&-p!P$Ofar zJ&9IyVn?aYcu0o5OQ2NwAPx|}68;jZx-}*kw#Bz7FjP{csV-086YsYOMP|wOp)peV z4Sr3%+k%<6!h1~)6DU&RDdF~gwuyOkg`|{xSq}K5+R=EbZPBc* zHA`qh@r1^mcFXy*vC^lBw3?VEW`C#c!e?Yac&uZj9=Twx$+zA_CWtc|Ts7HSfvQgu z*)OmOwIy^4ZuV`(8UeKI*v_x<{QJlF_K&~CqkG@r_3M`mFe)pwuH1q2eAYVVx;T;S z8_(r6_$i~lJ1B|7x-M9kLuGlFWl=VkaNFVJgr~|Gb+vacB{{4IV0N6P7iqAMphpWO z6m;1^X4XLy;l3jl8FriWnfi<1WU|y6JnI(nqm9V`bu+-~B4^Kvf3!&m`DKh}QJHpE zp^U$A}l?wo5=K?Oh+3yswfsm+#n{1j~o${=kDOiRveYd~xJL#4L8C*$%CSZ!Lu zRZA*3h;2I~wb>XzWA=DFV%vDu^321AnR3**i}tLi1(!ZGFJwwL28>hRX34(c^zsQ#ufInF5Wxx=@`xONaV=yAde4~ztJew+!C+^MW8v5g zXI2Sxr(U#<65qkLG`M!&c!6l#wPW=dN7On{+98w;%IFv~ogO(yVf&I$6Q}HZ;Lkb& z<&0Ap1gGo~+XMVR;bh;?9t#ROz&zn31bc>;yaXLmUn%_gz1kpZcAO_Y0F5!r&Ue+6 zZ9dzZIw7TSVBAjS_U?z8l6VCB1vtNQIVH4=lO^^82I?Fj=qGvO-|~4lLfiGd%HG9c z6)dm}F(Jt3=#!YocJafq9;S>w%a0cY=MhT0X-vSd?|F^!kN<$w4(O=ms&Yovcj-cp zNHQaAX*{~0S(}YPauLz6)SfFGC##JWm7nKL(aG>to0`9CsiV|EYMx_|P62HyI`%ES z9&(z!q$-ne(Q*EF|1!xGEa8Ej0cAzAYqvTNQv%Z(*RYjQhNM%Y(qtq?>GZ;gtPuwp zS6yu*8Oco#79cVJO+EnRJhS|Qj1|tj>FLB!s;y1VVohE|64xL&in4H5Z%w*wStam< z*HONRZZk`CSoue8`K2Y zlWby#b24xMaIJzST}SL=I&+V>A<=~px@pOFX)Noh00J!8fs9smqyA)2Wf_6Id(nmm zCK}c|n3QXZm$n*w5^eBe(LB_Bmd5cs@dvd~cJDJ455S^r+bF&mUL zXyT1Eykp!nlFU;U-olxGD}^x_mdc)dXx^PKK3SOKx0(zzzCqmS-dHQ?@sVcJnDi{^ zIN;`k#)){K>FjhVaL7S5f0)*TX48Zy17h^2$fln3#5>m3=D-`Q5J_he*tdm~X+jV9 z=q-*lr3Z|AvTLdx(t@A~8tIsZj{SVbi)WAV$KU)t9zXa7&wuzH*RNjUuq)))mRI_stccN=eu^nV#UyvQeXsG9<;p{&sPd`7f}RTDb-VCdSO9f!jK zEm#)Yg7`6PGr%!9jo2=}ZMrCgZ`f3-MvNdP6vN zfPwrkzP`}uP6}%flFLSO_qEHq3Bu$RrZ1%8;_NO zckvq=h$!?AEeyTA?*VKTplDq=X#4gGUEpOD$`-p8NeumABs z!)HJF5~<1;_7THKhcaCL+4D8L&&9+ax@p5G9MU+zQaQ*TL`uU~gwjtWepn1wsa@JF}lpCdPvJ|od@(mM9 z{i1YQFqwWSTd)Ek!;xtV&kSZ2j>@U!Tv|_DI>>B`n-%)(@4-8Z&mgWP1j=T*5+|nO zRntiA_SCVcvY{YHEh}M0oDO;rFXjAb+?FYr90ARTwEgYeIqS@4wXC6cmbq*GWMw9^ ztaMg6&vU%`E9Zywb|!R^6U}pm4>4@1RR0k{o;RPr$uH0zsmBgy_#%$ z1ws|JSxZeVBP}RzlpFF8%l-Ae1r{vD6KD+$3rn~~d-^_?K@alq2)0=N>=$Qm^Vt@p zOGj44tKnuTzE4Zy26V>Ih3+eP??{z>hbvyif4fTXE3p|U$C>T%InH15;aWpxjJ4XH z`*RqcWj`dd_q4GHSx*L`hVIfk08%H;#DD&G|1w+CnSf#tAh{05XI*Y55olxqh`6Ct z0Xi6P8OSt{@Whp!(RTsgoW0%7j2eLxa0*(DV+^*@Swpo?~*5ggASIjK8<%-OJl+P$dp>Yv) zqhyBPG-NXiNZ=Cq*;$wjMgQ)}%}&ZiyBAKZInhjUS&B=pG!6#L0cY{}{!lb_sK}U? zm*ADNQopG`i9y}t`1Owz24Nq{J2alz0nVf#^EnIHgma|6tG%#rDCkhI5gH2KET}|+ zUK?YX5iRE?r6j0-tR!e0LO7)&X8R4?q1J?|%3(-g@^#EXNzdgLSxgy_|0|p14(O&ISjF z|1=&3Et%;?(#hjk!C;m}Lf4R4$)Dv?&yY4mwCM!P(xflk24NN(*%%8284 zzoTKf+q%dX16DS|3V_xEr&rJM-8X;0*T4G>o<95*+x2Utf+EX0PVXpNAURJZMrNme zv&|u%v4O{>VS)WbCmAZ)rn9`$dG2=j+1I+z;fA$sW$4T+n#ap@CY z?5AlB{kENvU^y!^2RU19RAwgjZIeSznuqip?VVNG@?n@m=aNSNspS%P-hLln{_=0} zqo4mJj<@dASRD8ISnj9?Rr=SF&P0YLxi*U%h^SsPu6pWT7Qh;!djG}rTIsjY~ zO&gkc8+J4y=n6d1<`hl4GGsa;kNlZ<&s!VQAkD^A_pA{WDxiNM0O#=PW7b63-_LoU=q-K9lun z(QTZsZBY8ol? zEN+!(hkPf}9;A)rf^tmxX$z|-3MR>dpF`(9Arvz5l5GS%INA?f>iLqvVWTt!K|QY4 zRNYhM>J3cpFuBXVM2A09y)o8H`&~KC5p)jC05(e2!<7a}Lp>Fx%~DVrn69xA7KMNc z)dqp0_>f>vhT6pdgV_o@6^oDbkOH6p#&AN-K6ZkHFbXtbY0c3RzC)&`u{pA8x-Z!5 zbdDYVCWBoQSR%$*%Ewz+4oAeoZQChO>W@I_lLWBJMuwq5<`q)KEH>oo$5JXTl~5N! zM)X%hcp*ekg-kU!2xvj(@8eH6P@yae<&qE3X_ug1IHr7irblAX=%O$22R~7$tbhXU zYcfLgoxD1fq7_2HAV%j-vW!L=_k_y|WQ@mxSktK{aF^}PnNvPzJo~pvDbxf{z72R? z4ciFNc=7w>tubDYfw)tksvsMaw&A?MxA72MN})~%oU`(Be|Bg+6o1q|#Fa)Y9G8OR z*XenF(9=_E!^we-T4$$=_nMBSoB8FUg^AV#nDS`aYfKZLMKhk<24QPj|9JiK86H3Q z8V|nyJ)S;%fR`_L{%cdfi)&p*KZnDiGE^zY!Wq-un|>*{*i_2Aoi~;v$&iJQVv@ra zc5YK#qzTATU;;j}xl7~n7mv#bqTb{4p17HjqE0j~`8b*F%u@kU#`zpDgmxG9edC^n z0ZUewl1q|<>pE>A+hE{&;AQ2}S6r7o^kAlg0b|oih;Oa`NK+nB)qKpBGS8u-xG)(r zeIGifPse9@Y>r?zt?kTA;zGNdcP+>1oks-TynPq%fBYkS^7)T(_x%siR<2*)r_6Lp z2hzA4n`Ball0Q8T07WP29ls|XXQK)Ozfe|c=pbE^VfIgcA)lKu{0l5W1p|K?da?9^ zbAjrvaL0&>xzBfl#v!qB!AJk)AV*`89#!~0)tYL*{*6W@tkdi zwy6A3>W+P*QD1uis466s<;>JxGQqRSBRDQP`OF-i zPbY+{?L?p6JGS0&^Y&f*RCC4ACfaI6z`Ci_8TCz9@6zxpJiZf(O*9GsRLI)m%Bcva53jT@LBjj{~4sdAP#wPSDTK3*(qR|Qvv@o0GgRM`otSdU#b-m8P8@CjMe~>6 z_miw{;l^k5H4HCllC~G7UC0Rs<7Z902wyhvNz`sKGVbggXTn8qk7s z0p(8B2eQ>U)|r3&UHVVHtYtSfNBCWiL2&Z{+Au%`3znu*ZDKK1(JDcO9V01sl?q9I zB&bBVf$*jWE**rT6|JH!V~r0Lk|tIkd7?Bsk`cFFkt-TtMk=gSlSUr1dsJyGJEUMV zO?a!<0B90z0Jn)>S8UsfOxv0!RuYPigr2wZYzx<(EC(#dn^=#xupF+q`AbIa>}Taj z3Q(D)$_)UQE_S&cHc-U;U^d6hkVvh&7{*GMkdo=uhj8LaHFE?nNPh(g07?It0EU9p z50mlDR%IBu3@@liBWRdoiU@}>9A5W)$-o7hCcGII3Yju?<}pna?voP+oy@_jgq7e+ zAcS)(ubu;TD^v=b+Vy2cA==Nkc+4`BeSNH8WYTdP7bn_qko%_FL@#inO@g4 zXiWo3u$0ltu@bN2$S*6tF;3yD9M`dIB0!nvON7nnHCz|C6ubkpQL%8LM?4`(cNC7L z)Y*}r8^%(|al&k}DfLC~4E}8UMjbKn zwPk;4ql7I;2;yA8?b}WVC>pMA-om>devS`5{Q~cN@G*`zZgHa|%dW;$BBHTbU!~TQ zH4TPbs{omKpivf4a_?41MC+)ioojvhS${O1r@6JBQhWL~)1i_{))5y^8)m_pc4)Y1 z69=w$au9edyO?xrwM+Ochp(Rhfcszn4qyN7zu@JwCuq>7YivRRj2)|XLWeU7sj9CE zYy*d7#cp1P>>1BJ3OLaiB%cg`pwXk+jF(sP@JhtHo1Lp$q2+V(pkWAQ4 zZGthy%MsjEmv;7)OpvnHl=ukPrcx$fzKHcc z>=$I$8dy~{r2~A*rYq3vB#(n~-k^-I%cW}4&%zO;$1mEM?bsr!Z5;k(^(r0a5HEKJ z9&6*}aTJ_wg{FOyiZ{HWm-LJAnW^llPCP0Yr9T6}wx8+53eV(u`x|`Tcv%+mO=W(` z(V~s2SZ&XDSDA_So;!uvX|%_o!dZKeMkC@Wy1)CrF?>^KM^&!GeXmYX1waaR#I$Jm7JK%H;+`Y&@mJ zK~Ww^avA9)AXbunAM5-$SzOl8(}5LHHP9$z1cgeIK}E94_?UzsxjD{ZJuvFEohu4v zCvRP8;AQ8tj6{*?8DI%+Owbw*H*Vu_bsKFxaDBI!8N$P00Y{poWL0Nxfg*2F;Bq3o zol!(R4GbfcPCrW`y#So;s8Xd4CRV&A6VKM#9 z`RbRz8=`{bM|O@C_c_{~FJ~e~j$h(8~&!$2IY6-jv}Q2wb18v2WZp z+nXu2NgkVyPAZ2^lQ2MomHS69!>}xL-jKk0;2G}QzA>;iwYhg1*~`N7oT14iytz>D z8S*kW6LKb^gMGDVIG@i`W;D11caR^TjJj#vr>w#8cpxm2@9teCHOYhw(c|HOWm&YI zSi5fLj@;XB|SA*OljCW*QC@|7J>$wJ1j-f@=ru zXk{i2ha-Tj4beO?lGlcANC5AB_$fa6{HM5a=WX12>up@!xJ3g)>(4w#nVYQ>tdF3% zNH@i{GJq(&L$8tf3(hEm)O>$=Spp~n3DE2VCJ8~wrHBjlU2l_?3F)kQOYhcwwOBfb zcLg<4dfjwD^&mZAed%|5$Mf$W;ojH3!^3aC!HZ{)k$s~}l$`(81Ir4xZNt9rSQf6S zXK+Y{lKL_D)rQfF+ioJt0Sl`j^1k_;r$D)rlXZ1@REp*GTl8{FWLIv*tTarxUg_gHKJ_q}CQ( zU0tC?Lnsa!>wwkm~KK&8U7M6@dvM-(t(;%rt$5>JG?m#*qLMKLfN*H2Kir ziMjvzlC3qvV0@WRW3KqoYnSW6#YbS`g;h!1G!dcorc>Fr6G_z33M^u5;MCfoWI4g? z;DDXDVc^}_%AxNG$KWyZ1KSv5ay!I1$A>AHl1Uq%+Jo2VLpJcFTj+3e8D|FZ>gN!c z0*~OE3N#OS82cBI2PLnfUD*c?)Htv`_Qi9Khs`0H zcJAS1y&pmQp?7U+HiWNuc&uMnmEy#=WW~;4LFr}yWvkjIGCvHb5egoWA4qr8(ECG_ zjqm?@Cv6ajw#ufN#UZO-LNO|>QbEE9IACIM%08k zO$J8vxP(X%-QS7OdWZwu$&jsaVLO#M6$w>aaWtb&lYrX!u5L=8DA?<|l298riQ!iI zoV2WTjO^ZB7IB5;aE0~iHV#)eDM0J72$Z!J){}vlTEET0ThBq-Qt;IsF-B;Ht9B*_ zpjuAIB({)A5QKjUF$r&4A)EowBxj~lUm=5Qki?Urp&sL7`ySDt38X~Fb6zVu=uhFb zgEKA%R@kvUE^1T@Mmp5?VWFG*l@BrV>#!y^~`zFMtZS`WCLbXjEq71lKfh zV#0Q1*QT+~wbN{`>qsl|CIf(cZpV7y#zau*k@<@g7I9`ALj1kE98_H3>KA9W zP#J1E|I4~!S-444sI)um%ujSh(pf#MhcaBu6`>8c08V0?`nq}J3O!Xys$Izgpwqo_ zawT2m{MUvboIDpT*Fo|f%O-;l3NWMtg8s}oZe4ja6u@A`zB6lgI358g11{z~otce4 ztOwk@eFty9`#x^ozKa`o?&94KKgQLK+uSHlJLz(sA}98q+<-2r<5~nVSOF#E2@YO1 zIvv2WE`fRTLXOBIlo!oO$*e%g_~0eEv5W%D^lQT$_UZQY&jnn1-?CKmnt5LQExDHD zYW`(Y4p2q}_R|T^o_vRIfB#!Ny#EzWuV136A|j^1T$&fn(;ByTL&vlRy#<2{r6;tE zAyrM`_3;ig_3ot+4eMc)YbPjc)1qudPx3m~WwYvo zdW$k)I_^75T|-rKEW@O{(=fcupk%Ugr3FjZL&2xh>4bIV#zH;0Lx0Ik3%vFA`}pZ! z{tZ6=*gNk zJp}S;>0a>+$O`oT+^=+`^a*T3YYdzZ6sVD}G&Vox8vU+)MyhI2cnW2=&66oVqWDaS zC$~GE5xi0WDV&;CupCX}lh9V8w%Y9b;8{5lq?@W4v=2lk9eU%x{E&D)Cnp0eq0bCT|k`cmMW(NETi(q6;W95EvFGsyl6aUz`k%Sep zTT=wqrHfQTDZ`ILWGNH}Ed{7&Qe}@Y-?~p{r2KvHS`sM%ZQ6s>xo350kv=JgWjS)= zhxN#;^5KeEWksYRqlAp9OL&;XXHw?G4V@(MoIxM|lrRxbR2)U;du1HU)(0~5VaP{QOgU40ddZ*ILMh7!+`}pc!6uvzZ`QrpTTGt+Zz*KwW+Q+ zo*~qim;*mM(gseF00rI85p3hpW4D6hIPElzR%Lh9v@=7Um>t7!Kq%nFjSJtcru&f6};}K7FNk+M8p&fI!nFt_g_;_t_LK~0BG55~F zmhVu1=kpoI!?BL0>Z%$t-ceOW3{sMQ+DX$!=)NhFZ)b~G2{{1N6YMXfvt4d$n3#d# zJg`hUfv^*A*QaZ>&wyxlZhU6F5!wm7wJ4{G21Ft8VonQiv$1tu>xjj^Gje}*^EPhX zc^fxw-@)B?-^bl|-pBFgEvyI5CE+TLp0$LB^AqQ};$w9b0-|&i2yc=(>b_r~UI45? z9htW#pD)X`bqmNH#m8!hSIL+E;SZUVJ*ly($G(_0n)*Z?QF=CU%3e=GO+hE`sUKe5I#9b2f$VZ zqnOR`q(`GUwbpdsc4PqSvf^|;A{HqfgoDHyEdeA7RY_KrD2gi z;Fu{OT*5K-O6aNc(7|)loekp@LAf%GqXn*R-ocN5@hklF7yke^-g*n$t_%<$`)Kfw zWsN+?cFYoRKxd+H@r76+!cLBZKT`(MOyM~YAP=jJ0P*c?C-f*`Anm5t*(QlDz9gOo z4AX<6JMp4lB7nZ(`o%-^?e%0h3+~G5sytWoI{Amt`=0xx<4Ibj=1*uY=<~W_!L=+b z;SL%1&i*aSfx27{geb%QIUHq+M-qol{3MGUB>)Cs0opvXo^$puJt75ViB~yz(i^4$ z-zO|Rd-Y3vQ73n8R>pSuKkhpq=P{;?vmJ5qxh94fAkkDtGE{D`ckY&18|dUH*B}iW zmi37B>K1pvKHLPBR@>N&3|h7==WIxFBjsxZ z5N{nXe|Ux`kM85qw_oG&gKzQT`BOwD)|G*~uFNh}9vs>%V+7o&thbG&&FgwVfa{$( z*~nn%R`kpWF>5@^e<8zS(GH>Kjm{e#OJ5vw^A6daXBjdjC%>j}<{|<3|cYsNEO1|g0M9=#3Ebo^8f&+AT zEpHBpgy&xI2WdLq8JF~m9EiT3@cno9@XhakgU9#3!RhphdQv>S?=F$I;g{TiOS|pv z+A+QK#^*kfYZF zD=D^p!*V#{^Pl_zU;gz!!rLEwjD*T3A&MCWN=2N|BS%&B5)Xx3F&uClv zNW2yiESc9kd{9B5>2V~`lj9{!#{ZB7&b87lRJCeT&*=GTc!D3s+xZnvufDJK%qqg* z8>zh@8TGpXtQfoX;4{|U(CJ6m*&w3y=*OZjm<_%R@ zY9l#rG$z_e{u$_y4I;;5;8gQh(>iYl4LpapfHy&FZ9yywh$?9Ss4|C80sxkFknK0x zV&8V~hht~m)u!WLyX3JaWvO&a1n1GFq(zNe{OKTr?G1Eh5HcqQ#bn<;2o&uIQM++p~VWAx6^Bm0WNBhav}+~1^k zZbk|PV7OLU_Oj{Sxpv-SPQ5?y$GL1=5nAe(z^={9S~P8Xta;YKk_xztHP;MkNT1kE zR#i=S|06Oy`De0Oc%+!^3^31if(DzFsfE^^r;HbtU)TUrc0fu1%I>g*fkdmg ztjUSS3mtY1af-r{XeP3a3L%s%R?O3Tax<{Q4XnpoI9%PpvT_IPG5PXS2^oBVy$nTV z38-`wO%Y@2AH7F@2ss91QnqI!z-zF~$k61+R{Z?9Saz~dsHE5jzW5FWKjh5u#;l|- zt{8%9nwQd^qVqK&P2=FOhl2&sVP~@gcj`#vsVDh!;VdqK2g2UqCp}L^qsc;(W7vtC z+7~kkp{A>#5Gdv`hR58!jRrnQrA#^(ZUj^HC0L@2;bCx6DTqBpO#; z$m5C5=M%H7yUrz6hSW|_k#{OuYQqcf(BBcmxi1f>rVjxcq8No_*s6hy0z{ zZI@in*);2u-WAZ%j=`xce`{Rx3H9&iX|(T-s{ks!4(Y@QmYo_#JDA7A0sG!@KC@K% zu&&Co5zdrIRRxmz6c<&-p6f*!rKi{tR*KpfH=P8+SG)+WH*EnNj?Bz4V8WfVxt4gG zd)u_`HOr}GiTAeY++3BQEz62^IpBOgbA7CKCpMlTBa%2&S^6^1Ys@n28#6VB^*~++ z|2c0PmZfp6bOPt?OlN;RsKj??0Crt*b@Mjvyv3~a?YG{=-FM%^)s34veL)V`g~4q- z%^ocug*@q!aZMA289;nk`e?pi&8VG8S6GiYD_VNPa-q51wB&i|)JUW0z|sQyMu+*{ zAAg5O_rAibm(S5agJYL&+jbhHWcs> zpbm_=KPvF%Kn~%j1J8<&+Mht>-R2Ef_bu83Veg4|-v0=H{g3}&`1p&TYaO#QfZ`RR zTC6ji-b`&MsRuG=BgFHp=B~02rnxtWbdL!byklPSJLVY_-{{|DA4GwJb1oAe8#xgd z1McEJ@q5HH287@RB6pl$Kg0R;a{$i78fL-s#++)TIeB0w0^-j?s_|>v)XvABRI#va z20q!Z?Hlrdg34aEJ7p}RO=a2&^cgN!LMD7dqrjAxS!(YAfX-rKEUV!~$CftlWJ`*+WI@gw zEx31x9qpp;)ShW!{BZ0F_6qjENY=1_+bE>BKimF8D39c4!NiLFooX*Q=a?thhMuq; zS-diAaL=O=oZmAO>q32lp6GN`2#<7NAOO>MKO_4lJBmi{t|KU$d5TCOUSz|xWufCP zdkEro+JxThivftCxssKU*TF~Iki^++A7iVf0QReISG|LOnu=SduM$LNZy$YXE8wPSFZ zbq#_>UW6up4J>z0+L}0|7@B~}-fOKJ8It5kP~lO)1%ebqv$ubdMbR#ijwY8tF>1D~ zS6B`=(AFcC^@!zg6U%yJ)Su1soe&&$JZpr6%Em%W8Aq3tk$7YRGPjf_00cMSu~HcS z#>E7rW(@wg#K2|{uCHJuhq#?_N2X|@-TLJ81KtQi2=&M%q)`sE?xme{jyJ#vugxQm zE#)A2a`71bj-FODO>ayQD1gT#k?O*ug{lwZVa88L$+9{3Z(&ip#$kk_ZsygT0ys7R z017!vY-(xo4uYdBU`#L%{4qSJc$^=->tEC7B|;e2m$Dhz3&3 z1cu;h%&Bpbrc(3;P|^^wVhr%Cbt$g!*~g&#SaydJl_72P3NOBj{{EqiVAFiO;Wi*V z5pG59F#xpAvhI}UPVW1T z{j7D5$^@;;S{n}~fb;3Zl3^_=+4l{*4BjTZFE)gw{1=soI;eL%USaPYr|WB^C#RrM z-gRy=zf)dIhuFCEPNXs}4u=EFv7|SbHjKz7hnBklk4P;uRtgfx*k>s;`LQdK9$p>; z20H*nT6K=|(i(cw5ilgFx1#~WA_ z6!36}Ps1q#>kR{61Fz2_PwCx|(Ml0)oF^o!NG(6oSL##U{u5!9ruNMebkxHwfWBSh z@q=&i_3wX!@4vf`{dA28$_TV}zVAEMb!FMGU5ClJ(H~cRGLUXYXA5zb-3- zSDItFZrPb-$)@;?a!WdJ2*g>FEx_Opv)W4|-$IoyCBGGjB@RM3+a+TTz-`;c_%>Ks z)RfUN&a%@vBArR^b#+EsHdmAWRZt~V>Bl7xTsy6|2}uO5ZoP%S{F{G@pZ?-kSa05D z`S?7?mIFY3xFB=sSk4^~T$h9vkC}bQ*x*wms8|wOjhkQWRM1*sqQJ6i-r`^X#JNv% zVHwJLMq``DQnCW^e!&vR?KNJ%c#OVX^UxIC4DH)6&!wjIcxbYH#1jw>Dj=?`X-X$I z=zMb_A~nCI;0eur0GBG-c^Pf!gc=7>odriIgUEA%IZoTDtJ-0<6PoMA{KS`?4Km<7 zXz98^TfQoXLUY|rchW`s1_?KO+`IPR4UYfLsOHM3ds1=$xJ0 zM7gKlWhR*4Jx=;3!nP`qqHwKjuExgx6_lIykI+NIu?+_o_?7r1j#_KOX-(#;>HiQn z?$|x8)HqsOkhQNy07{ajg6?5wTehd|7!-&2WRS6God7ON#+{seEUwJ zt!K;Gut%<_RnVXeIUGhr@0}ZONZ|SWP#jH+M2Ev0dyL2(8GA(0s0@O~L>*j$eFyDu z80+1{1XK=U1i>&OUzdU!)FKp4jlu8oVx)tD#dpo{WGGDzNHO?$jyN7sY3eX5BpE}V z(CoAn4h6&Hm=jcaa|F5=fj8L)nZoU{J2M5_WPcZ97xT|>@iEcCN^-i)502y&oRzt! zAWO3srdW9palgprS^~pfi31bAmfqM*zL!`sFix_w66?;H%%^ z$#?g0e*Frm484c7cN&(Am=a!3rl<1>+xaZVRI)7NoEvs=eKBEF8I+W4Dg%2^NhI|a z*AUa88+f4;&hq4XR}pisvsw@1yh0GPYmW3OI$w|(N2)k@fQEk4ZOdj#tb7U6U!rj2n=aYEXbDHoA;=8Ld0m#aPW-_DB zfWgqy>$;*X+BiyQLch6r6rbgoNV}0m;*p398lO*RAR4Z2-o(|7Te$Pid-&j^Pw~$C zAL7>CcUj)O9MHir+gKYMCb=xtF5OY$?XmbX`Ml=ZnDmhN&RMjfCNzhrE$dT%c4n#t zu%E8+@ZKNrhhP6^eE;wPw(W$qu_~nN%wx_2Hi$@%J}o8a;Cjrib7Dsi z!mW406%Wbd<0u!FGystC7|h-+i)=63RdUd2AJOeR?;F;`6+ZjP&+)5&^3U+jho572 z<{Cr`%WDW8yb}26*w;tj8UgC2pYh!uGh^{xu#*J_GfU=LXH6wA(JB| zeq35(P+CT|`OY|OZ3T1$9@`r8-y*nOF=JbBw{sVocPCv8@j}X>Hf}R@43YF>Dmdz3 zZ?`kpEJ+1+*?3)ZkK;MhN(klr*!2ZVR zI>sYmldT`xG;p$y@h6p)dKR3=XP)-BX@__0;@jpRfS&c*YWh0X6LPdg%MgAusizA@ z*;jmqfdt~3$)1#5P^>E&XX=dPa`L=RUC+qxDx4+x@nUZu`(Dxby8=U*bg;8JW<>!v zl@lN#n)JlBooWsve{u8DOzTdKUv!->kJoZ*7)e{&M<{36I1yWP@{52*s$v1McLweX zdE~9a7&+XuPX#5-Ln++{*(h+;lgA*rOAbUmSf)wO_`84eKg!?Y1Ds^kkYNXrhEr8AX}H@Gp2 z6oI1{%d!GqM^OMeRW64fV0 z3kf18eG#x65-~+F?NETkBO9hr8C8t%IAXLc3sR9CG6Z2PVYLAm`V8%ME?CQ3fpK6r7P|nuR$+KnuSXy66W=lfT{2yKMr;{w^9f2h3SX1+;O{BJ+ztTWl0i2SQ6r?%*Rf5f z*Dvwp!8f?~yWimPci-a058osAy_{9!yEgaIy1TWlNS#C6R2J7eGmQMZYX@68Id*JQ znO0|ai*Ln0%d%D$tZVZvI(w7ht|}X(j*D`pco`A#W#J|hd*6}DsHOVrasZ9~rC}TwSn(N%nOgYXpD!Yujm`f$AQi0=bv0$I)LeI4_61gs! zW3WuxX`x=rgR z_`rXQH@$w!@LCTEsmbQX?~p9j$taeAnK$(k0uhPxt7mxi{1I}e6TpD#2wJArXtvRuey`Bmr#f+a9TP@#G`2u_|*wXVq&oZGRFEk|K>i06=zIrtPQs z-Y7fmDyI%`jvRqSgDg60Zwd!K*y-$SvQ0n^y&j=c#H|y)1!Ru|e`lhPcou`8?`M^_gUK%d5I?2-fTpFX zeebII0HM@SJfdiWf&SGgCL8_u4OwZC~TaB5~W^0-4p~eT_Kr<=)`{6A2Z72O$QMM?38&|9uV&u zuS%~uS12ImxyY}2P6_fh=}34&CGF7qa$lTtHD=isKvc%r_)loQo^`&1|KhFqyMOyH zvMt&jQtN+~m0NE2&S(WMIbK3gDJ^P&F*$CM`a< zqZGu3g^^uJ844aoL&iiQ;D_L2IN}%Z3Nus$)0!2&thR=8&I#kq(zCHKiuVB5Sf46MOH|CRS>jbC$F7076Qz+Elv%KCi#- z`%mGJkz!$l$Dwu%W1-TH44WZM^tZ;ZYcdql285Nn+t6n;DAApB#pQ5!`c(7>7kFML%nR%86eahR@rw#w}48i9#SJ+CKHgRwmR>G(KNlg?Uu zvY+w8)5m!D%~yEv_3!Zf`-kY;xnz@gv+@@YsjqZ!jIm*0GFHxKZmtsws(GCys|h*U zX1r*(Pyfyl5nN|Icdd6>-YDhdcW)nU}_kL;j(SXV>CG*gt065 zWL=cnAS5$&w3Uz68I?9IbOv((+xd(>Z=$G4Z1tKSXU*iuc#bGp&=EYGbdxd)!n|!8 z&KqG_v?g@#n=&hWzS(%yzs+#y=%IC85sg{mecQDAFw3~tdFSehzcR_^G=c{#&SWkN zVIf~Ti@?vVX~%6vsKb?5ivlIdbyr=L2BEBT6K+!*hb@igSVNl%Mx#Lo1#h@+Y{0N@ z8?IkpBU;1pc&N#cWi=biWP?nj^E-48y&TkpPyO#OQ zZ`28v`2^?Y%V+-F<+0H~TwCDu>ILq7^;`Vm@Ba?Zo;;udCi&<)vraa)yGurOv<|C5 zXe4FlW>#IBf>B4(P$}J%G>9kRt)Zdg`sEXxU;TjI&*BXkz2RN@ zgPAPI_L<6uqwG4uMO^y%5d@)aAvhZMp`mR-ydx8soDjq4aisJf#kR}VP_=>aV7b=! z#F*nbhAFe@%&YqGdtKE#t?8r~wLyM&c5~1&KpUwi1AtBQfv?%dTdG9d2lFR_`zheTUHQ~bpsQl648thCBK(}BCi~vzhPRgEX#GE*LJb&yBd#h zL;R0%z_jHj;kkjw%W1Pa1#xUPC;+kYW;9n3)kLJdjaJm{u1JLg?zp=w>51(VHq(2P{runiH~%tqdG=@=&$ z6E2#{4cf{z%EucxUfo8lNAB(>hnwQ*41uGoGz=*$R&F*Nsa$44R)1DB!Im8{A~Cev zVvtaffoE|kVJgMLiHMVp`q!jX3b_m&zh(y3*b0KyzQR`>GY+AB0^(yB;3)EHYykO2m>bGjcm0?6A2{-OFMk*ICOjd%=?ZaNcjROM36$}s z$_S=BIzKIwwzdt_a`HvE&@FI)kH*+ipSo-yLrd$ zlE1sCFrK{RNjY+vc>Urz9^L;M-+uL5JbUyVu3x?Ya>wC#KvxD4IgH`%sJ)EM>l2mb zx^Va9-OkIhpdoO5ea&5GC!V4Mriu>^4$H!gG4`FCOO;{ah^Q%N10aB9Ravr(xX2oXN|sbEtPZGHxrqk&&F!ZRdK{r8VqeX3~`g zM0c07%aa(8!0~WEEDJY%^A4U#dE8u!cU0bYuA3z@I;r3f^jxWGuwgP}I%Ev3 z%YxT0e!w@s`!&A$-G9XoPaXof<9JwbJ~N<|;2QAM=6N<=p<|ZzeN%93%*C6|D_<1c zO6gYX;MN+B$0Pc_v+X2JR_%VhC(HZ~ha_Ue5KIq=N%qt9Xv` zAhHQ@5zKz~kO!7f)0`}^AY^N8J zf0cC;B81vYaE`11o}DcDY!P;bi3?{+qhOYP0LxgT={Ox{X5|2dbWKXuNOw9&8XZS# zy(735C9k?R)dIj1WBPDJhny_6xfX(RB!{i48!wBn(mK=_3Z#XtQ$;;)Y+dJ;AoUY_2t)XtBQ$sIdJAD1+WyUOfkgbWJo0Z6?>P z7s=3~0r}551HGf~l-Cf2fIc} z3*Pi}mR)$X4;clE&>a;|n% z75#`a+6Sf!8C9Q!_XK_3_)q`lf5_nwx@34{G1!2%Q&;JsZYUit#Hu{8XIC1XlMtZD zQk8{hJw%Quad-60il8l6k2i6=y2Twx*Q1nzkT^v!0%M&q8BK6YXgp~cL9?RtHHfdP z1a54^L&ZHcqsf;7L`53lL}{h6xLjQCGtf5(4e09QqXsofWntK10zDTm}t8LS4`rCa#;* zIE33KT-Pq3>`;F+34%ODBVPuzVAKQko0SMWX*(io7>n1)dxUX}-M>#w58U~(;@}s- z8`=wt#wi3hg9v<*bO+NjK+&K`#+!Q3FBLU-zZ_V1s|pMU45Yv+vg`L{z|H*Taa0Ms zXq&>HuQV?jG{I=PL^L2f&eyN-^znCic#lU&y?*f&`}qV&f20gyMB`4$J1a=M25>|b znL+Bx^wjtyPsq+4n0M}w?P%q?ay{#CqBcZuqT}&R&g#;joQ+S?FOVUglpbBYCQ_Ne z${>{yq8+K_ST>ghh0nC-uEV=F_yM3OvK?HP*~y>~%?6Em{0ou}J$Httq0xKCvW&@x zl_=z)%A&eol@=rAI2TWo_xrvxm=Sb5I7aO(oHj5BC-oEAh|hBFg65jGEI6OfXpP5X z(Yc_JbT}M{yG$GoM_gSUvFNB5RG`344|&z|3TaRL&}m*g_W6d&g+^{IvS(#Bg zpD80s`gT_~y>Oz;dg)?NR+=23&}ML5M+|Le?I0>R_kGHWJL{mV%HCNXo)`fEsKo5* z#x2~w`!?Qr?*qL3{s(yby$^Bg&RraiS2WHAOX+y^X&9{^ITqAIW!P?KynOKt55D;$ zzWUv7@chXm1Ui~30oHZF>H4g!dZQDqIiGfof(3m?b}l(+7ugrmYnL(4ssaVAbX-+i zR3a}PNXRCUBh^|%EDN^tS$xdh>q8sran5iVp|i!#C@nHg&M9Z7F2{lmO0P>M{A^GmvBMg-ba_oHv@JMK0*Nl+m2sI- z8xMuwWse+(```moE&2f2OqiB{2?~Jp@o(09P!^JM&pE7X-t60JoUflEcWxGEJBSXe zjB`8uaxe%5X`RNCc$k6ftaEui-y=K+$~LUcshFK3-!SXu5it=%2gNpPud}`7c-o*B z?fZTvnx}nDvIt|j4M64IO~IIyY}dRXM>!nm5w(dMITtl&^0TW&Fzukv7fF%k3bD>3 zk*rhd*~$Ly3ZO(~hn?wG^{M-GV4~4+OC)W&Hh544YD3>Axb()!6tN3Ld($8=C2l5J z!NuHRo!BpINmlqM0ig}PAV-v??V~O7k7Uik7>s!#W?NLx*v74(QYCQ63$&LAe@_N# zDO2>#Xu}Qp7Smu|4h&dA&)J4Gok5x>=#MQ;Wy^gZRUPupdeU}kZIM3Krj0RgSZf}p zZQHlKf)EJJJ$gszQt330zO*f3yuf%X-q&vq!iaVytM&=J=Rj?QDw)-9HD-wtyI3ff z>c4#gUb;RwFT?==IG{lu0@lC1d8HkYynoN`mDbJJ4u&#EhS>OO()^i-$=@# z9Qcl(=ToR?-=G!8rv+46VdJ3zi6bZpN!B0sSkf;o7CazsPe zygip$Ijd&;MgZIO3D2HB!Go`VkB8rWg;y`0V&6{OEQNrSOl4}lwX$imGiltNPdsjb z6&@pd+?>VAWLc!-9NDq&lw&)DHn8?x>wbmT?y_D%L^|83vJT@w({PtHOlOk z{U44;EXvL^W6rf`YN2>Hle}I*)!wll4+sQK*UU^Zs?ErGPp-A4+_R0gL~RDgWlY+w zTQdG(1|SK%+(SBNfDD0i=KT8q5Gr3bwE^!p@F^5i)qp-zwdv5#%|O z9AIaj&v7k3Ah{x=l|eXojw4?$&S!yiinPWg zC+fy}UOY*C2|$LWA0`d0MzcB*!semVCN_dQsBq8qf zY?3Chy`qw0b|F6Qvfqoh#S_Ajb%M#zv=JW5rDcO_y?cnqtsmV_Ypk*$Us%2d&OE;v z#%^3|oqLJ9VBoSO(uXu1W!`Qx!H=OY!yg#F!FCMf%yOZ>;FCxTw}=zbQp#4h7y1cX&aCw%!BbYo-zu957Y1v z;W7cV73+FLTaP&0xP#?zRl;I{=6EcK#;h2u&Vb}I7tZtKR9K)%EFwLP5I%@3J*;+$ zE@M(5?T7<@*710d2oIEip07X;S?m!x0Nf~3+UX9&&WE&nAU!VG$VCBRqV%so~ zA!3FmGBgH{OHp|;tS3PCJ2?!(A+r)*`(hY};wGr`%8y;UZz^IATP2}Jr`nng z`1)ORb+oejkWwSfCS${02LVW+qEPCb{W?Nwcr1LAvX7+Ef(b_i$aGADC|XT?;z`c* z6(0N+CqdJktqf@!eGX-zz9EHd+?j43!v*h4aTrn?Lrs&3@hj&9q9Y2faj`8KAE|NM z(Ciyt{_s5>eDg=#`{N(*!}kwyzJ7@oz~OK#dO>HswrMju;}Bk3m`Q>;BT^S-8zboSieIIjHG(y^X4^wmB$ZHl5jjEBiy=}Bi{U5|)x zNv-XYVK=j4Ldz8GAWJtGZyb|kUbZp7?~u)wn{=(_BT;Z9@?y z-00DCANaSv8oHJ*O~Lo1&h{An-TXk@ktS-97NYDK2j@74(9h6d{i)=X7!-`+uh8!f zght~UV(+%>>cb>+0+LC{foA(7I6#<8BL+Xij->Q(8a&|+lW&x=n^JHG08Di3JNHvD zwQ7clxACEB!Cg!?7DBY;!ucXk1=(jEq&6_f>>H~E*ik#TOENw7&| z8~wz)%N1xC+2~L^HmI%GH7UQzfUTlh07MsvO(1&DOz!t%(Z&TiN@55au}a|@?@>r6 zI+3D&WLz4bOKTwmiJ)u3(SII8S|O=_75SO`{-+LyuGb6dn(&{YT77HnpbneKnG-A2Fj3txXPG^(& z3LFmy8t)3^wkdi`V?qj6ut0_?yo{hCnA#XhrIvkXws2R*!1xOp^=+ZE1(hOecz~Qm z2DxZ>>{e*`Fj!ox!-tf9; z;UqJ0I-da<2Qn*uH0ZBO-6~cy-hol+sSPzopJmTH= zKE&O3-^by2h4(-D6z_iYFwu|Q_%OP0~?>w)7U!MXt ziNw1feuBUJn}3QgzWgQHfy)*mCx25Hb=bt4cA_CYEQ8xPuwDQ(pUm^1$2#>u5%N5# zdCK!+=^&Thx-CyD$^SGbWXgz+(0t@YDZx?8Rs;sDHDA4P`PYa5_G_G8KEe6?Y9tI4 zXyTc+ToT0g$uDlD#%D-^dWQiAIY^)$)SzTXGQp9ca{#!KXy_HlF+*pDJCRt{qkeaZ zH~Wia)iFIp9_cMx+jeF1W=|cV>Yl!PXL^7~_(@1~dEJifHh3Oz1a^6t&Q*h&|OeP1iY%9{&ycK1icgnP!80&2ECn}@JF*uk=TemCF z9kvfO_O>j5^b7f)n}ZIy=lQ@hj#|^M*w#sEE3+eX8b;1pOxf_p18s+mK@Q0dpC!hI z(`#f4^Ad3L+`gT;fuolt)KO(nN%inM=;$#xY(C^WUhcALxeW2TXGi9a1}zP-V{3cQ zJ_~qp&b$nOQ|2l6y?10VK-~MVdwh8(d()l#Mtp>1qW2Bi>5sKlL8gMIk@o;|5ox7; zPW;_^l%nM2aKnBx9YR6eOa>5$a@l$cd@PMS@Ykl6>yfra?}-2OZ~n)G1j%*$1fI<8 zQtiWfBt!TdPAL_Crtvb6K}w zvoRZvpbcIo_#8X&z+q95vQ3l2qlP9Dj(4K_=oC3@knx-*6c7^{toj(E*M0v>IO!;k z_B_TG+&pepf&h@<#&U*d^kcX}IFxWD-{CD&*L_OKPoJCCSM}|Tmroz#;r*}h=)t#m z_T(W>uU{fN*U0c(%f$8T*MPHShY^WQ(Pz_{$~Hg&X0cAE^C*)QmMo+D-sOajGM`<6 ziHKl%tZ>q^nx)qUlYum`$V;H1qZdypS)+Pv*}>nc8S_HjGE@480x>*v~wo z%N*8I!4L)SdiulwNPiNyUCs&(LB76tgRRaTsMRDVZ{t>LPuUO0!LsP1Hg5Ay-(LI5 zX6Q^4S0M)}m0gVhj>jA5*>V2=*Y&1Lw;b1%*xFfj&ILe_;4BIpK#(8_j^dz}T57v( zha>#Mui#hkL--*)ynx@p(dxm9aQIJ0t0lGMXgSns4gfLZ&bae;sw%gCthILLcPW>| zi}RgRm6f^YDR*975jQOa*T89I7SMc-;%Cyj=_wG{j;(mr@}c*RWu;td&5hh>bW-QJ z8#0Z6eIE_T3_CNwak|%8sgl)}LzcD5T!pjR3PdpAG#R?$dj>3&jg@g0>;7@)##Mx1<*ac8o9YQAT5FYd?+(0C_H}GW2eU1#Pn+uX7)7 zl`x8Mc4d?o{SE-zzM&rnP8tv3p8XWfS+DTD1TXnf^ONh|4-ua){wz_(8Ih zA&(&=-001+k2EhB3Z85Z$bEXLCU%evPCY{!Ysh42I{BTrKeA&vzkqLk=lgi`$G^Z! zuY7?Hm-sv;nSEV}kS9x~i{Y#feWjFGKu!y^?$UXDmhhDQQm-41j$tsvOmCnvu%58K z^>3nWUYeKYF~-sNT2oH?*8DMU4dii!%ac3UFHfoWJ%2elrguk0sb3S2o~6x3edvJx zvak^)s=9y1T>#~Y(q^}QF@A%!6~>XH0BDW$q5vX0IH0T(>i>R z#J;H-DfvuW8g@6lk`GcgS7)QprJV{d<(s6|XF06P^Xhr9%RGWr4Dv^~)3G!Yg9Fwl zNjhfpwgj>vbTWdSe6wpvZ`q43rEH`qhB9W-D9P)ab0-}Cb z={mulWFw!@+lnO@(MFRypHyVJ;{8oFbc}PHyQ65T3SmS>Wo+eWabXrvfU^`s009e5 zH(tVWdVx-}O5!|e%)&bo%xEW(CPiX~01RUyAtaovH~3!~XN*QQ5($46-c&Lq<>PTV zf4+wXwgi9#Un+{iR}4i$_=*S>ktaOrg+e3+r@qo{&d&N<9q%G|5{{uogL99?t)V%D z8QBrKX+*Ed#No)GuT*rZHI^iCKr)1$ppXKaT=;!sP6JmIrXn~w8P`i<1`Z8F{x-f7 zI02UcP0`%>Ua!5nGkfYy`$+&sG{snUl=X|lPl`zL+qCklNsP5XG|i_e2xi6uva%h7 zKEJCs22HjjnQ>_TD5`^G(W-}sB;b90n35o3+~KaCKA z@kA`T6PV#Gqsp*Nt+lm=>^q)4`~;uexruwXKg7LTAK~efha=*rCP&B4Y#|W1x?J&h zDaWNPJQl76w#yY#S;kaA?br_-8|jiInkq%sLLxYytcW83P}ySIljY-yXiv!jOO_x* z4hZD{GdRqi(a|J~37z6`McBKKqKd%2v%EDEIG=oGvm@zjoRpJ;3S}gTXi^7{l%jK% zbH>{S;Z!PX_|P#~(a~}29gDkEOTG|~q}yes9Jso=;#eVAg4yHdW0drFSCsv_)911< z!CQDRt&z|8nNHWNyrq)+fsT$F=NmYm*xLf4=ag)ArV=u4_?gGp(24K3apMLOiS5EY zT~tX7ic&taM1NEckT+X(8s$B`_tBwN@M1=CltUU4j6VqHses0D z9BM|#agKdMYbxc3dcV4?QTOzo=+06rcs4S#!y$kIAeDuaSaBvBpfkq|Dq*~+TYb8D zP;$?4Cii1EH@<62If}dX_8J!H%8OX&_;xkX(w@$clX2O&<$)#l%%r$tp@q(Nwho@? zX<6NASnq1A-e@xJEwji&qOVMxv=`Z98ZU0*RR+3bmEUXKwJG9Hsf*(nlJPBa@z!pX)w-*Cr>@bKr=+SEDN-5JU>+THjNKc;21^{~pN+u0>7+ z636xw+p`DAWaFchtl+v$;T+MGb7=>+Ksoeys{3^`W^tlM`&(%@kf-4QW_dj;U<8%b7QQ0ZNj{-pmSi66igtaELS6 zM@{AVa)NpJ8OqC4eBg?sC&}H&jCeIS)6sWS+j%-RNQ^$zLEZ-J6 zVGcU0B)BIl3=-rG+Uwf#qD*lrhzm883kEfDu^A^S!Hamnvu8qk~I?kli z)*AQA>xfiVHV|IKv3RX#RVX)AT0x(IKuTveRTLllUguClwo^~bIgWdccI@oiFOZI5 zU|^J$lb0leKz?;4fdk3Ci@wYkqKc~2kPhV71su+Q&X((nzyGuUC27cep8^GAlVLuL z2u0|W4T)$#V?%<|`2{TJ7qP4_psnXAa;6te**d<}i>08Qqd>6WPAaC^KuJ56I>(m+ zRS^tD1pI7W*O-m}6Y2{Qg*OD;94(~;#=PuY>%aU;%OS!z&=AI)&jbfR^W;Y|lB0ds zl_8%Ov3t&)%~>)DGf{Gh!4#so=6M{?!G@OrJRKtQcE2l74We`r0-l(#CN9X!9qbngeor@!ITiM(6A0tuy9LbSqRhHt7OU5 z3%_G!@%wM#&PVU#>7xhOwhO}N9V^RgTptHJ z)3z4aw_Oe}m1HzV;FqOgQwb-8pRQ8k?Q-E-iYLX_f)j%Z#wpNrL=v--ocqyvj&8|2 zq{>@K&+CcD%7_XI1db@v8Mv;*mc}eschop#qzksD6=t|PZoF^<(O9k>U?$t8yp~~E z6)oib{g{H5T653D=|snbSvy6Sh0=u1Q5G!{HsoVETf|SgJk`n!q(cW0vs2mh?C3j{ zZOVx!JQ_=Zk3&wi_}rz3P#0kx^F`daB%Te~PA4i@;*bUdz`f&u%OUM}lU~g0$HDUR zIq10`8%`_FXe2LtK6X4+HZT4OvIWF-sFNFUGDrl0tXk?-! z8&16+*e`^IOMU}%unikK(HxTwJZYJH!hnV|H>C9lb_@4=7X_cVpN}{_b{bj$ur{8V z-Ij$-x|E4q)I9DGPD2i4lf!;iqqd8Vfs4irH(c_pfI-->rPp4;u3pXlfKB6vvbL}T z-qv#T2n)(Hq?-&SPUkamta+2iMbpYOhhWbHGj-W)Y(s$^fb#m-S;atpBaT_lPpDZ> zbv5W?86@SyA>+5Jx&oFrx#yRw`|fQFyt~M69QIR z`r7u9zTo<;ulOB@F!epJE2O_H9z0?FW;t4y)+{ZcuWZb!Ns^v1KR`Nuo|6b;JYRB~ zzg@S7I2MpRYU=6%wyVeNKU|qkD5FV81W>xua%5g}YEHoTeOXQ=Ti5jl*Q3taz6zF} zkMx2C?wmO-G^x_UMmuq6Ij%k&f`{cg-!h{ZI)B>PTjM5zrKMet0tCaZt%sbq zvhKnAO+;CE7o9^r1^PTsIf9)$K?lx6Gw{f{%W)}syGE|{nY`eNN%3GZ;7Fb$ABXKX zCLrgk%K=Y2n9+Eq{74M(;(KcK@)h`(1n(y!N5W)05I&l5FE$<5Q3HP9*x<3VUPdnzSdM` ze~=C>R|q5GIhENZ@3uNGo%WqhDA2Zt5WjRKJiN}D%WTIQHk{Caujq_l%P*R~GlFQ7 zIUuSajUD7U7)xN*ZxOaJK-hUm!!8C)m;dp9`0xKGBRZO#5dW{Z3Z?Xb1+kuSdf_v8 z;pNZa{Hf34{L(8}&!0jpG;jzEIaReDJ2E2vLe-$Oi3hLU8^Op4Z=(^~ni_OyLwpN6 zA!C4H|)d zjPK;y{WrfW1PeAcwCC^mDBsrXSQb$jJ~S#q%p~3(2oSM*&cRjbkBMOQu312jr?@^w z_e51*ub$6Zd#gQ-1J)=>MA#Gr$NU5|{=)kVC-^-91z0Gsjf*VjTC{HA4C0WlCW9-l zHqp&^Z|WXi!kfP_F7+`7pf!IzW=4IUF6#rwxdsEnmpzngOy6qkm2K4xo^<%maX&?vgJq`LRIdxORkTY?0TF zimWb3Img`{7$uZvv@-BG=scxFw%yJsl@#Q3W|Dv5l5he~M#7L=QM~M+OHtK8qUK_! zB_uOb<=DruBM&L$iC$6-9($Fi4*2nJ!o$v%-l|tp7w}#=4$m3VZE0BKoE-fSzKDOq zj&zwSAwKr0)AaPRF=y@FR%ci%{Kd`LI5Xj!e8@6d?KhQ-P69&X6Hja)H)zePP0g1u z=D0rMC^R=sQUR-uzF%*Y6Pi{TL7;pzjTqiuBMl9$M!|tqj@+S6>8+`xT4l3Q=Ozova)nUh@=K{Besysf~P`7ae{aUi2$t*+>) zfYXcNnrOAOG|J4zGRphkzRXkpf0$^!Y1b`8nwV!$&lNAgqc> z3&Q%-Ltp>HqjTM&+4>2*)!4eu;h-GRyP@??e~7mI%rNwR=k3A)V&-N2t@D|;t4FFR z^6`CqFX=@_f639&bPIx!-v>Fpl%1n84Dg(NKj$88B`*SHxSJ*@t5l_jklq`gZF_}a z!7QBUwBW@nwa1rhQgb57qY;>(p5k~KyPhAJJ2%ZNFqX|hn~&pg-Sz#1S7xB8Ur}Ys z`p)Y&JT!yZ*6_0gr|?v?G2vR|7}CS^MXuW}8ocmgo-?c|A9R~D%@eGOv^;moCqIu& zWin0wvwb##neaO&T@y;`7$!|YKhiqKA}vW?D6?J*C*_K!v`*oRYaLl*!-Dv4gATL? za7M(P@JZNftf?x7>mTEKO8h6)fG)APnpCt%n}Hefnxka!2w)m`GmQ;IT(j4TcA~0> z4a-W8>)FgMnwn&C!4i~3{!X(fI+c$LmGsxz8-u%TVbzIGTPYgV3mm+OzyGuUd6>=~ z5zIdKe&Fb2-nN`^y73~G^Gn>j{9I=^1S2E<%=HXKXXN|kBuy@>U@3uJL<@1$lxY-M zak4^gMcE+Wa#Dk^lOtLg(IE2s{oF7>Gr1Qu6)Z5po1&VS#sm)&gkhm|6S3gmnt4>l z1eMu@g?z9?ngT(LDfswC!^{&WTp3wpFoA2JA)$=5pDRWu3?glmaXDfm;cXH|KtXDh za1Uj~`Gg33;B~;6>*e@Hod~dmBH>GhOhz9Q98b-E_)f!{pS>PlH0b>qS|2c&mGlYj z;ZX#Sj4#9A**W-;VJy+G64DC(6cWCPW7W@k7r*Cd8gH65f4e@%bmmP8b1aK%E}ArU z1k)6bxgLF2I2`jNtw;!^14qMnr12q|RVFEq4fpQe!iR7F8aLm63y&Xs0_1^p;d!yC zvq@WPxVpMxgBG333N`d`sUzo1ibz$YAl)^Z>+33|Oc|&BTG!@*t3;4=g$8?}lD{Q!X=gD&x^y*7 z1V3}$%zP~p+$rbT+3rCX9BVysPmcM8S#&wrazc;Zaa!F4+=`BL7DX>~E-A;AF;up< zH98mjemJ(l{j2H*o~j0j2rw=(#`pgMjBe+gMb(+kDYSgmL4Us8IH#<|6~UvL9qnI%2{#X```n zL3llO%VW!K^5}lqrm|hGjc1vW@>=kmaK47dRaX!rA$fo?W>0r{PM+m0{--RaJP6^F zV~XZZgrQ4|318x~_r$)riH^2aIBRYkD{~$0DKK3jJ}0kn&-BvRz>M;VI?n4j4wlAd zT4GVIGq5_zQq3QhuR+I~!Es2JkOw*E(GA;9>|cJ0-;HuiMGi<{I=PxH z>8Ilgm&fK?`skzy2N10gHx6uxc7>Z7Ou=R95Gcz|SzP;Xo2o$r zl!2Um(LC|3@lut0vS-R2EPIm~e3!b%bGA@sl3oIcq?7B?VA6_@fZVwzpln>Y*F{^N zdxtvq_q~E9knO>ySi~j$F546i&TDSLH5g{za>R=?X5V62nDvDMpRKv?O|purDKnjX zw)uE8*(XIK=6BH$#}~{sN9&5(2QL10<#AX>2?wdHGpmVadJ`O7X{g^aIcKhqwAL2w z<%Z;n*6+C)X8w(QMce~+@>Nj>kz!Roj^t~vJIl>xCq zUW!w2Ga;Ed0Y`$8_);ceen*6qiPpxy`MWO*MIolXFNIV{<1>b6y1bM(i&n<-bp_u+ z2Tero2yD3Q+f1W*_`Tpr4@HsrlxHUffNDU*QY$Y^) z@jM1)aq|3}NfzX~o#poL#Pjug$EZV)iQy-HAZ?g;D4eXU=4bua<^-OQhOtnrC|dv&!5xadWfS` zN%wFlMC%8gUhcD$k*@v9i)g(Z0vMJOn+7VVps4`(cZ3@f$>5RFfVvr{qr@tiI@~Zw z*)(S;O+SX|A^9-qQ2U}3b?4d`G;_IL$r~MA6;7DS;&JxUJbyF#pMDN?q9W4sFwZ^$Ze3mjw!`lFmNbQS`2p!kM zQt@@6!tUzyZAa5(u`FR{&>|ubOVfTd<5v1gG;Vk?Cc%5UN_y85&u0v}r1Otf~n2 z3Ydb~J-HvdN!E_>C&O#%39_yyX2n%Ho~jO6mxi3GQ_@aR@nz#&HIGe=$rka6WeuQC z{n~eT91QL)E1P^F>4>oMfq@2CXLScHM|aq;tkUH@us(a*v>$5V(>;IjQ8cb^S@dFXwOJCQ&j4*bklO7fD9RsW13#;7THteM#0PF?Yc?t ziM+nGDZwBne;pIC7DGBNsd-+r(S|&&#L+x2RScTm$pgO6%yosU$9K{9r=kV%rh*aX zQz+2J^V~=~iD|zhXv`-RP@B5xrndsjwp_uB!QNxqdZItiRl#ls58Zf^K1oa&XK(WfC7^6G zIPhVd5k_j7mF)06(_p3=NXe!q4bdsg!%eR=XZ3%uGi$r3S82E99>-<%n|d*H%8zIZ zA`M?x5)cNxZxvXe4KHUp8N@L@IU&Bs{0XSpoA~9hO$^GjMUB@MD=J+n7(%e{=fdIC z7lijq7wWwURpU*vhqo&LU&%m^$1wqxmG{oAJrM2E5AYTr9>vAyhAcE~r+Gm5wlt(8 ztxI$5_6dVL1AX>$S-A7#f$#QjU|v6X!u-KnNbM^gUE?gCHz8 zC=dmin&S`ze*d`mH^5!hgnZ3KtFfW(tldAd@5gKPrt|PiK*?5f5hL<%!4ip3 z3wa8S;hYjinn_Rr^1f+~26_-5oDax;QeKgBM$&K@@F8Rb2g5r?b`%O|*w{4Q!-h3i zBly!Wg7Akh4EPQ}iio$=eNm1vAwt-YPQ1*XKGwx}fvO^8nCkten_8n_MaC0Er*V8g znaGzF24Tqc4Ia_3ykuxX@k%evQQvF4W!Px^f~n^yVb7tBUw<#$=(3`#IfY@YKhz)x zNc8Q3`?o*F`|rMm+aJDCjdCg-}NRn1u`fEqT zT_fQVQVC!HxLhu1b=(XyRy10?l_O^n#ByoSz#_jwfd;~NmlM?*h48X6v%Wa$i$osk z4jhb>_TI6s+;_Hb>;?%n3Mm|WhoHn0GL2$zu6Y9kEOJ9TvM4@$|v%+ z?+azyNi%~+ShmfpBG09~c;(ctajqJiLm$)f$v z`$4CDSr(eO7M-Q-HJwgZGkH&Ba>hS8w z9m3)2?8(Ty0ilk*qzB@InW>`Z7TPb&;SE1Jv6$rKkYD^PgGw^0eie-+_-slSwZ^^N zg_owi@-pNF;(R}Xj^+8zFTt$2;HWMy*N0e#S;!!_t~M^_JmJ8BJcWBVjt` z+8hu~XM5cbqB5-t$jV9XeFyr;_RWrSg!V2MzUe%<6xK8VIg-v;lCd4=G`$Lj1O`de zwz0By-p}0+RvDR|27GmN8W<$Zl=GvG!y!6N*=NI!AU~&Y<##(XOZ<=bQ8tnnXuozg zYFn7~_lQuCk+|U;txHJTP)F%O?;*#htC9*JetAE~%PA<1~sYO6;KnQ^%El1B7G-cv$fVcdzrevCm-v^x#<`f^!sXVsTfl61$|fKz2M>4-=FxRI*?{z*O3FzBfs}i(d`G24H4D)n<7k49UongslII93AZp02*Ne9%RU3EXdyjQ@Mr~V+Siu zi~x~>@t*K~*A%eJ_bg0nC43fqR%q9_92tW~rnr{4!@k99f_tK9f!Xg$Of@gzo6_K0h)@Y6ybCQAuP*+ZR0UkhEvL6Xew1* zmIaF)0`WVWEKPJFIo7kIAHsiX?K$|CYb0LQ)yF|?*mp*R2jN;73}xr}Ls{9%PAg;B z+=bkpX2M_dIJyd-^B-S{~EVHdKdTa+{D$>rxhfSW&@D8aXur;c=Mib z!t&@mlk;>si54Z#l}$Gs5yX4b{^G7|cBnbost)IX;L;j{Y09kjW5>2{IIlN=#>xq1 zrxzLac9h9itu?GCW(B$&(=6kc^hMNhU4AG3-m62lOa0tLt5=CH4S31|{?`qNT8wk7 z$uD*;eYBzGDtfZ9069iYS>eHplsVV8t7v;dL}k2txB95iey>OzYc>U@qne)BVCo$it{^9!G<`yX@&_{1dmiY{W|vOw%D9gp zdUiVQnWPksJ?!Rkx``~l(}^e(Ox*L0!@jyjOu(A>G`P_P(KMVA9Qx-3`7I~p&7fYfuQY13pv>5* zqcKs)WCQI3&VjHj7|y(x=7Hfywx$ZINjIlX$AIQElDUL+y#F_UUhMA6i?#9#O|r^D z6Cyw|nGF*noj_}qAvHldb3~FGOzWsW7uzA_Z{ynH*Gj~QoaRbElPF72qy&@!qy3FU zoe;=bXnamKtjJ7I8lU9_RNVEG3^3j{mv(F67UC5p)R#a#XW}cND`A}FWNLiDn$Pfi z-p5t&4hiY$-fQd7m#HXQ5wqau_X{+9HpDKH9=z2A<%ri1oMc=oFu#}2jXmZZZU~@x zoms}5;U$6MQnY#=+}oW2gvW9emN$eQJzZQsaju*^ z!oN;GGDz`UlvEdK&=)@uCKDR<$H1+y84LzwMY*{iKEbDLFq{8sZ0!TuFVFDc&P{yy z?pwJ1@rSs2_DI6erO4o3+R30rkiTrWloB<)YJC>|h`_ERXmWDs%6SRmY*|^}dh{K~ zE@#q+kT505a_-*KyUPirGN$v?m3?bfeyn4tENe<;=$v18Zqz zO@g|9&=`<|Luc)Tv}2_bTPg)QS$2#mxH(rSSa8zb({O2;_>ix%5yGZz0G<5R<=f6I zKS#%wV~D_Z#j_%P3o8fc3;|lJ_lqe5If;}~R2_-a`33ymt6#@!-~K+n^4jaT@zST! z0W6JX*X8MBJi2=upWL~Ln;(6E+aG^`XHTAB+b-DlXY7EjOdsUv@4MElJ;SXvoYqz6 zY4f6^C%GgAZ@O#h)K1s}r-cEM%2;b}JJ(L5glB#8`$HL`W#Rto>?}AoeXpt@9Vf9~@e0qAA*S(nbF51txwLC53BAh8&p#2){aUEs{{>3l-E|AYdL zyr|omji;>kT2i}C`2)|5}-sD2$pwv9Q0y#PL9RpQuSqdg02aRLedH0qJ2ZY#E z3&Hc3XIMuBqMCr2fe9OC-|Q$+&ePG;Iy&`xmY(I9E>mSdV8M~fYKm`=NgG}^obi|G zOeEiDS_5eNjVmUY2(Lwp^c^taocn=!uZfcI&=`_e4A^$0ssZMgTBcBv`1XGwy+Fe0TAQ3soc8j$gLS} zsj((%DB82n5sl|Oha7p^r^W&4nDe48ClXDY5L^rmSL1Xau?Cc5+Ln{(fLkXmA)Nf= zTZS#Fo6bq*3aVI^E7;)Qx=Oaa?tU$cO^qznR$69E{7^?ofBDQCcfMT<7^2-TBdcZ5Lwhf?I41=r7;2I6^dD2nF45h_5dTwp_ok;4yA9+ZBOQFpX|HzIf5AWmS_uj(o58uP%2lv$#yTfPY8gyOJ zyiD%1JSnnw97i>=a-{*I=M+B2LBZtAad+263_e)2cZ`lH_c^wuu^EwI+`SJms7!o@ zY}dKap)s5#h33|K;&eJmHV{XL<`Qx^5#A3Vok2J{u z!Q7PPefsDj9zD2+Pww2p`|rGkhxhK_$>WE(Qk6sRz-41*@OilnG-kR9V;Wa> zlGxm8yl`E)7oFro{5YdZCx^imIKU*D8wP<4bAns2A3Bfp&W_0lp1s}O(K_fzrvRKF z9k<4#u8w|SF+VzsUFFiGD;jMwh|G}U$u1eO?L4R4^2kyI)zzCvBXJDPN4Jvv$a|zQ z?Lm{F!_K2@Y=e2vc_;0M&LK|mm)(3jkYTu2;M4PCfMJomFub^joqElNOB23QjExyy>~jp8(W3OKAVO>-h}5yn`)Pjp$u`#7y;>>3mOzVWG?5&_p@;t>54Lnz5vME4meD*ni7-p zzAX#dazYv>qFvrcx(w@)9CFDpWspR=MRWDD0O+FzE0zdvB{r;=bj7>>;m?!Aq%SlA zCtwC6Gr=zqG4UgjQoq}fH^A+H2r+&+1qkm+31Bm5n7DW&AQ_h!1pbd*Ic41fbK2cT zAXh`du1rHICeGPHlgz{Cx%Y&~r2)?|xjq}KLP!WkvT-b+29mh6S-FtG8Obchlv6l} zVkile!sI(fSv$YG4j-+LWL-jNyobt&_YFm6N3QUol86-Ugb>b8?=+L>b_sUg6G8IF zB#22f`dxyT>);qAu!Ar+ywgFN&>q?eMuv;`qGj(oq#Yas-1FMPdzvT^3A@e`>xmgr z&MlY4xoU|M2^`0trVZaB%BHsEnM&58}Jy~wPuK4_yU&R}5{t&Oe_BuZO%IA2bjsj1l;X$t_t7*rxNKYtE7f&XgYt)|eKUxw0`v8fl9Lie~+I!U{d#&@arP55dK2$Z=cKn2!pH<^c(U3JiN&O$5D%q$;rcs-$~m z<7j3I^Kfg4P_FrLj-(OWPO^1FPE#t#0j{6&N{la=cpMw&qTgF%kbv~Z7c!XT`3|2U zqR}aghGjVkHY2cb9ha`&8U8{@MrUvomuI^ zCCKOA#FG}8BLRz?d=v$&qBTGPOmEBa z{aJqHsHm`wMIIzAv+MZa=QjCGp(-_3G%O0#lMRv?XVNSOvrzNjHn@poznp)Q;K2n6 zAVeB3z9TCe78sFd18p!RD#{^_8Doji2=tKaB0$2<)^+DBp>pvShIvKrA*RM8+(`69 zAm=>RM}jBNx)0XoWQvJ~A|u1lf#hfZl;zZNJ%^8xQbfPr8ERb68SzSx5o}m^u*Bpz zc`B15B`|6he5mki*m?dKYw#;HP5nf80dl7FwBgld6xDF14Qpu82PY-Ld`;<5F=JAW zjJmsoL{66-Loo)KiZAtg=aTJ|5VJEUs7Xk24iJ-@etKqAKu~!TmJo={8v~#EE>O_( zn+Ci=Z_M|&R0+`j6J8}K#4CKSU?W8`Xie9QH%^d-p0mGw$CC&5@zMM5;G_3`iwF1a zRCh<0`EV~H4T@da<3-(j_id+Y4dOL|qRn*B1v}$#aYG`2+1vD)jf<`m>m;0di&Spy}fu7|3B#*5N8%NQRJ#bnUv~}Te zOI?n8iawnY+gZZv%qBZGlipdHO*z&oB29;wek6tYaqy_4l)`~|SlA17Yfi8#!GVjc zJ$K4-DnPNW>?XZ!%s7S}gajh8g#DqcSTxcO8-mF2kl{w-lt)*k*0HP$yJ4#t38LZr z!b|wv7k?k$`tJAf`uBc-^Gh#N;c0KDNmFDyT{ij$T*i-!x3NLR-KvXo$SCQ`Qn%!u`*o$G zeYspnUyHggV>E*ze(BuoJG(76?Y|`rvczy*82ku0V59&`^$1`k0Rt_{O1p;u65EBx ze1)={u#5=+P+tnsak@0PGjK(5m^ds;gN7~1l)9xKbihyRN&`w=y))T~n(~-e$O%PA zhuSz)W4`RlN{1>o)acBO&^(IeB_`QUV>V?c%b@_rrc9*_>{GC4S%@cBS2&9S9c#w> zX`@gEDNbabX~Msn4$?p+pem@4+O835;H3T0y&o)52XMKt*$xnB4cPYs=NCVP@BHXz z`2J7-310ZrXK=Y(Xut^yMgQ6gV$Lrc!$zKXLXU8H6#yIhWDGpNfilH7OHVUBcxIDd!{n-P-0A)ka znI+Do8pj&NBXpjIAr8?PG?$V!(jjJ$sP)h(qK@D*uGR>sA(mu|*J)>_HFmJ~8i@l) zACPuJ&L0hU+J>oM6%-^R9ETmsFgDl9F3e0e`6B+H{k5pzfN7;@bXgP>w4+Hn?&+X{ z9W&A>d%qA?393(&tt_GhfWoI>n+Lk8v#ba>2ibYaIZ{Ur8Y8V(X7D*pLHB699I}sV zmkk)ik)P#0>`{R?cuTfL7Uo6UBjcS8q?S!c`4021VUB69u}L4CLaPLl;A4ANoutiI zWOG$_rFst)eG4Y4WaFKaKPK8rWR?E&b5}!3&dC9S<^Vu4Nz+_Cp#TB#EuW!9W%@&M z6FP#eA=qq8+nG|;!-Ngj#`$_(wzcWcQ~+3Oir|>Uzk-ljSE{Osu##RDJ~%J=i-w74 zw)caTef_AwFFWY#9^utN5LR{>@T3Fc$@}GrN6uL=dFQYH&jh6RCgE+yf(XgV&qCeI zLEKAtIqRx%hEV2&bhC&6nkLfR+?(KMS(EZPNYb|6kZ$%@XE*9Fd0^W zgg=PCh;deBlpqkyMBJQP6zx#BNGVJ-?eR?C0^XR>BzOjD2X0}Vg>U>{-(cP}{RSTa~ zNin2^yr$Mv64V&&&weO#JUO%Gp9 z{_5AU-grTZccx#-5AjCHlrm=dcK|drMeh;Ai3YA$EVKsXv0>YGJbU~IcW!=wn;(9F zPww5tlSiN6$)iuuj~(l>Vp+M~rlUgu;C#LT_|2kE(oSHzY;~N`yw}%Vn@!J@b)@fZ z<6HDW)@P=g@&-7a)@l;7Y47g&bOZabu>{|h4w@NZ!}+m{pZ5@Viq^a7mf#mH5b&{Y zq^qt{>@H`z$>AfoC07M!9_z%<%+|UrS#vSopumdIx~kJK|E4@!8)=d3P8^c96Nli% z@y8jf}1f=jHBkwqn&{050tnG{r!kx#y z5djBr z>dj>@^c`1^ZeqJUq@yXi3w=&iym-|N(z{}U&YwV39aghQx&8JZvA zTO>m#TDp$wr6U}&8*53~Y4?iPLP7pKICi)^oG&R_@M?&6j+UZ5UZjVR!_9m3N^O(L z5JmLkEz4INrz-HwErevd7MZ>DvzAuqBC8(wt~9iBCH%wy!u9(RgJS zz1Q3OQb&a)X+r#uL0h(k6V!FX{&3~upkdC>cC{86QLr|hqu{Qw#@!`(mI22mJex+?!y{F_X`L3FCXWu)H_y8Zj z_ZB{Q_qVuv`xdUQo*-0KW#{*F zq*;N!&IomDHqK2#Y(_@1qq1+yLT9CTTuLNUXBk`3vy8bd4gE-L+eMjl85JreJtUi& z8km`GINuS8?LDc&tYAe4{73I>%+M1q%=C3<{HrA1PVj>B>5N3`d}kiPvMAGR17C?e z@mE7ED{j2}8NB|TAL6w)-o$4<_j%ICq4S|NA3df2^1b98!p2i4yAmc2NN_zvi4oQs zaWYz)&M#gXfZnlPUGT}hTlo0BcX0RCM|gPuE*?L+Px_Ms7210Wmo+Q+LYnnaMVw>E zF@-+!xxltvDkx!N!~q~S$&&G?KwPNW&}-nr)JGwirR7!;DQ8dzTDBp`)uy-XqBNv0 zQ_e4OW&|&Hdn1N3qWwf_OgSM?FeD5cJh#&s^Pq4o5;yC(Bj*6*(OVMZTd9;2g z!k0`(Qs;(krgApeIF<6BwhW*wm2~+GTN==|gXB1iM^p4tEbBmhCyqE%Q?T_@%Kq>; z8cXlzdw7>GzfV)HZ#~ff(D$dfdVB}_c520iNmVHD%wT)p_Pb zH!B>NQKr$K`q46vnKuQR6?kFLl3SosWxj+f?dX^sP7xrJPg+|+Om@ES4cq0SLqGv7 zIqTFh;!DXq%3A3($z94ZEhN>`qjN4J%iU%BYc2(6!pU?(TV&X8CEy~I!RCB3Q-N6V zpSG$%ut@3ofVKz;r=(eB88OQBC!*zCg7y_<^^hSDF1hBY3O5HgXRZ)O&>WJpXurmI zN!i2p6PnM3PTE(1X>bZqDS6e8UFGlqrg2CDvPXp<;(MM8NITLOv`Hv?QV!$vfbdN? zrGr#;c_ zM&T2@&-3o-JzL!CVnGx^W;CphWcJq)BT+1%C?N%cv&u%e4Sgy+&T%3#t;^`JY;he4 z=Q!Z6)hb}1d0JWcQ3;b&l#O)YIg_{&Iup|0`Jbr23U<*NBjz$TNe}#$YZg(4O4WsL zGkTrDEEp3ML$NzEQHIwvE8}#`ia;_g_M}6##5n7f&)I;}Qx?ivV1`x| zK+(8pAY>5PQJrxZHm-}t_^fd!VOe%)|EGDVtKo*vV%|1( zbq<$k29>Y(0|%5%l2e^XoX@9G4w`I27NN6|A)}%k2BMGmrf7%F(M)eF-=sof7O_`n zWzxr>O`*U9j(tDS7B;7f7&0@}?bI}}Z5!7k#~5<%Eql2(-rJKUunjCFPL1pU*YNsxe}J#O{svzD^efD= z+riR20Xm=v2GY{7aexF0uD}Krvo_`zNpX&*2W_O0_63pf-|SC7*-6>nkl42kS5F?| z!M$6!ck3o@ef$yb-MNXYr;kO}bapa>$BikdQbyt&bZZPiIcVznLi(v`e{=60ZDj>Q zQ}^xE*~&f+tEq~kwWdy{G=2#aM(TMS)u2UazNuqzI4yNbUnT=V!I+Tad-Q`1;gYm; z>@2%qmW8F^GA3+zv_@y1W!fDPJTKW~!@c&bIB+G|zLR%}*Al0Q)^J)@9R0v{xzJc_ zqfGqRebk8PT(IK*sFPP!3uI8)ri$DEu@%1;pht1U_6P+M2aD^;d)(wL< z1y4x4nH+4L1Q#ys5`dDjDP6t58*-jd1L0dxfK08>P_BmFvvFl<;~Iq6w$i(th=?`pKgo5-8euY-o2%;WnQ1l1nm5(&gTX%uC% zdaeGiWqFInYS3iXm!RdkQ}h_+&v7kG=zxvl%!bS?b7)SqYpmCovWXC^VjIXg+8%z`! zM4X@Dg23lRL}`P8Vr&emMWujA0vJwy-8d#OK2A)?HHjI*jtjL7X+f^CVxoS3^z>Gx2nONPmP_ z=y;-|YfqPg5auIk07T<`ma zzz9gX1T$)XGJ{K~ZqhPxvZEi=t5*E$%6)9x#xWtRw8hQKaZDjqxH?iS9V;uAl*3IOoVLasDkn@&IlKdA^jvA8>PjYxnXU|ws58i(ED@`X2%btv*YquPV+_2QE5@wD2)XnP$EUS^pYz#5Lgzz9DD7)bBVK5H!iodo~MHELDMibNpgcylOL zL%EWZHlc!O4iuCf%4Zzds>}AcK^()8qf^$gu4kUVD_+r^YT%?wCJej23&*W7>&pM9 z+~FKOKIh((TLfY%J9bc*vQYq;=_@2J_kAKY@&kmc7E9SGhx+*}!L#WxIgM$%*qMIW z)(AG1OgV#rKu-n&xy|eZDBDS!OES#C2tCWORrXu7Vp|KMUCP<&4f7oSy_J*;mmC+Y zX>k$$RmNPjPMd_}s>%wGZ_HmPT6N}DHkR}(^&d;VAzFm=3nWiCp6zK)!E(UzJ`yNJ z8j6gnIcgolD*HS_)q4ZErRRY_9y_|T|C3EXkv5npKzT`A8#Km^U|<*v=AOTgnwl#Z z9OUIAthfvLjSxOcK0AmDCH34Rz|W{6&$P$M3l8u6)xTjc08NdCFd1~dQh?1QOlyG2 zSxFgI1{8s2=UG=!A@uCkIEdquSc()m2?B{wLW1R!yxF&mcMw2ANJO0nNd-;7S($kr zkTV!drX@JUcaf0gr>|5=uZYaCLd248)J;#@4xOe*&K6v+SeE81WxIBHx zXk&yQMF_T;z_wk8FO@Q<6l8$=zfS9l(`hXOicvTk3H!FOtnKL7b<7J5W}koPGGQ7C zjs`C-9Ie&9XF4=PbU@DO5(_$LWb?fNfDT00c%~(m>2l5c zv18xH8I!5Xg7b-y`J=PZ8=yVX3uV(w zpZW~G{;lue+u#2QkCQsxV1+>rCxfsBxz4ZRQm^xx-)>nkB;2^41M!xS#*lIEC4|e} zf-;=EO4^-eX3SY^Wp2nT%rc{QY|ozJlY6&u`=bwV=jMmF`Oyb>aQ80vPU=dqY1Fbb ztaWy_jm43rK6C_Hs!D>5>d041IjC+hMp`}j_%xLiw`Bo(VmsWKRQwUt7>fgok{g)` zG@z-HiM-Zv96GkHH3WF{T5AoP0zNKVZ0h#wN(X@NveRjOU3>o(7^KlbL;le5Xx{t2 ztZN0>=p3jExA!ZWCMCbP5@p$w22E3DptXi|Rpwp+f!3I1-nN~zNFTrGpSqYl*%rzz z%Wli_o@&UaCVQc353d)ZJD4N`0O%Yr5Mt*!vF#ha_?55W5C7!9!K<%*3kTTXkQ_1V zY0?dBWIh+vPQ5Hpdr!y|hyZi2Qpsp03cuIS*NXxqw=t^*t( zafe}5U!;ydlFo9Up8#+q|eWoKj(hQ@JCI~5ctYA_o4R~-JLcASmi%U7tBo;ZEkdrxa zBVi(CU|}!7kU55;;yT9r0}||FtYW4K7U}uVV*tXRG3akJs_sX%?UVi1Xc6c=tEI#3%ReV!vGBvRx@?LLEci<33|L#EMEO zGX#}OXO6RfQ|Ei1v=t#*!@6)@Ex5Oi!44S~6F5o3q|<0L?v7(eL*tQ1Q?-FXpy}OR)LZy57KdfAll__!obI7eDoBT=uKFzhpsOq&drd>U`;OQuQQ1{KPQ*fT<#@ z_+NIDhZoF4>&E;Z5ZLA&)EsS#_+6S~8S|cucwBmQIL-rq?hI}m`hB$^epx!rNRLQt zPwwOLdUmn@rEsB z`k$ScOxXlwqxv&-WTImUcQdw*w7xLCdaH-{>DnA+<4yr7PHyOjkP{S*)m@zH^(`R) z9inJzidRkv2F@%O!~+O+63?pU(rFJf{c82;Otk~<8qYLkUSI{D9P5$;)Dx8NvNHn+ zOpTzVU(Ev^V_D9KU}pW;c~qpwVKVIcMShs=CDR^{8}i&ERCPDeQ^8!e6!D?$YJ!Ay zKk1GBO{)_KMu5My>AYsktnAo#I`T|5Y1@IEIpfi=0Jx7nWk2Yfnv0(>Oaa;G3gDQ3 z0@O*Q*W|u>odCfHS>ZsMAg@KpU@Lq~Hh}SCTGGh`&eYSsqR!`fcpqWv=QTI!Ol~Jq z<&5ZK(3jy)8!n(KT=|Kt${E36NJsVAUrE;L5axq!=k-QwfNa~KzyCVPf@|<^{}^HDjV||fsQ}j^ zhcusy>ReloVd-E2-^uSr1}wc-51h;Ob)pEq{;lZ?e!gG-X6N3f&mZC z#B^G?A7oH#F}wri7^DP8a|wuFFDH#IKk?)W}kx7RT zlOiz%5$(>#6TU8WHns4c&v-E=K&#IxR7&0$C+7+)4HIo z4S6s~z`2Y)(zOQRlLDE-oHR^Wad{0pyEDCYgx{?y8{hYx`#a?@Ac50KQFk%Haddp<^IyW7Kl~ZK`Q{Jsna_S68Ph&= z8wVhtn)kR)atjKhoTI-PJWoZ%Z^u7rr7y9V7!@)cyAOpbqtG&fYtXA|?bVynhGx@7%)858lP?n>X?J;e9-L@__i14BNeZA$Xp&Y3Xux z1=!$F125~jRaqO)CzJ18SMK2rm6ihx3~_FPZ9w~#M}{cCJ^EZ5v$mOhp3WXK;QO&7 zkKweM7ihHEnc+TW0Mc)Sr+?r3f$W^eoJo#g<%EM$ZKV$EqJJAZ=ARVrlCNyMaGhM& zXml2mBN9awtngNY-VdIStw0PbT?p5%U^FxE+|%Ft!F440xJicWJGbnZj~5+>v+KAy z!hmCb?n__6&;Izo!q>n3UF=;BLgqyGBCnt`H5zzvjp_f2CY&V`YB&ym7~}Xy(G0(q z{Ij6c@A?t29p(AdJ^UgxKkGndc=ZmiCHW7Bl|B#Y&q+_ixYD|Yp7p$aQ{9e+#O29d zTs^)6LFJlb%~ug_Gi(P81}u2ZaJ7BNu@Et3E48=3GG;+oIuoYGmNox+A7M&e;6Tif zNfpp=rd)IMTr8KO9G9&9)^=PBpqf9fx;1Ey=l6V z?QQsrGl<9agaG7->AoCW5}NpTz)uw$rGI4GyAOiwFgselsIfierp>Iq8yPgbSVV)>Z|wCiscwQo)$8uOb}u&V)$L z+Y$79YsjBz##C=LuK)w}_EWg+J=4snO`jq?(q#Sn6nfznB(a9Fu zP^1&_Ow>QTr|f3oaFRtGIu0^{WA{;l;IkE=Bg|}1GDsyqZ<14{kofIi{;TAq8U;~# z;?P2TCke+d9T7GPPJ;uJKqJkOct~_A9Rmp=(-8wH{GbEmm{&cQc_x&CAA@ z=fht)-aw$EKt`fI$#@eH$~hpO^taa3s$@*#Nr+qd51~M4LP8}Wr6gpVa2$*f1(y&M zMqwfem+pXYE|{3mID5TMOrZCWzU}<^v(`S%rC=d_ zsUw+DxQ@Ng6fRY!q;WJ^8Lc$@IX7Sl;Ofa^eDwZr@$PSah5NT};@Pt&fcLffISmsr zvZ(+wPhH)3NlLzeukb_kM)ezVjwt{>qUmnDsjp=#w)gS-~J9=R{r>KUzLPG{r$Vwj;plS(v`(5y! zc;UsD@Wz`z#`k~r$9Uo8SFrD!Kxn$+e5E0t(}zRGB!(U&TxY!|7;67D@xn4l(>yTi z3prTj3~(-0ohFzOGJHNQhwGi@6&t+{WQ;MyEEPRmb=X>`ICG&#m@t0<2V0X_>lY=IO4a=mP;l|M-i%hu8s)Ims;nYOjnILq6oQkl;dO^xAXUpgV9@@Hs^O$(lu z(tXN+#zzEl+Un+YCPuYVYm2H*I@ouB(=VvGQEfR z)HaaikpnZtpSeV|q@+Nmw#P0j0u9HpX|I4(2#FufZewi0ZVlx4XnP`3xDkG)FyJbwbQnV^IK9vw3)A0jyd6lW=Bl{0YUju!6b zmwGuFnDFG3c-EkhFs;q&@%_=2%t{Mk~?M!Qiu?OQ`jbVhOlq7{7@6rEDAn<7mRFBF3Xk-ceWm$mU@$mi~y!+O# z@X@=!#e;iykbURgNo8xo=f@(sk1lC@#qSi0F%{>V8B*W%~)oT5HtWVfO(5@={{ zZ4v#bF%t1igS@K&S5!I8kQvZBEp+Ha(UGGp5e_kzb@D&`IJmC1;?P0%&=EoDGmDD{ zIL_&G;yK0c{#^Sxb-%DtCAG?Sg$*)y|5YQDwos9nS0YHCl~I;KzAo9O;-o!%#Kw~5Q7kuYnG?$=>~r9Yp>zE zKl(Yo{;lue{K8AvkDcjn@i^(<@EXR62xt0!&@aE9WR-}9%7%RB8^=4wc;pWUNHr#z zLDea8ki)+ZX%thLoU3%|9TpxMk%o%&2*43)O=Pkj|w8|m`$>?s~UxR2W(e~4Qj zy^l}s+{V+Vk8t($DV{!kLY_omT?ZYQ$Ck#e0NcLfEc*1;1&9{e45#C?@c65v^LV(y zlgbq9oMQ8jmq8Sbwb|(m(~y4gchw_ArP`?@m;4;JJ&Yin`I*lun2W@I=x8nRJT#AG zSty^x?+NK!?U`N{@>)OGK<#`!(?GQ$jpTL#Zw2wVjYd~@R#lmVNM^^oO0pf4N+gb* z{DGW;W5=QH;S8n{_s;CFA=wwc{3?F-NB<08`Nr$ma|*D_?jcC2kHn&azUZlFO5YV_ z$)FBCMcHCoMzET65#&^R|EXn+FM^%#3YTpQ`n@umm ztaus7i(q*u?ON&0S;uOArgwvnv*A03*;kjb>MI22qc|g2D!y6QyDnKgT^#@ zN&QU(TiZrm0s72w^c$u<{Die2DL!!ERX5i&O~K)#`hEWQ_0x~wbG@@KONe$6EJcq{kU;q03e1nMD*yl}P@rSo z02J++U!o~^C04NP@cM~N&VhUjt&ei>+IUD-a41+nSdt$d0I(kN_5{;G@9bvJNxGMW z@x*yu^Bwi%%uDh1U;LRe&Sctpu@p9xDMoqTBE=l-;W_jmqNarxfo2lUzPC>$dXPYJ zsY9ZOh(gMH{%9-^!8)F065?i@fRD5g!+Wv9)|gL#drTQUDkijZMQoXECAdxe{Lb@- zmA<1Gvp_54^SuBh{Am1=StesI>K{!)%q0AzBK;Qf*Dj4^2AY}61~)&`F!Os&IO)07 zUe7e&@g9GAj_LS@@j1mL02m>gjOk(uIR=EG=pxM!7NUc;>Mav-d%eV|=Onm-nGL<9 z1il9`OuYx+G-1U~h0677QB@-B%kVdDh^t{}j`<0_*Mt+{v9yM(r;l*w;}7uRyKm#e z_kN43CyxN_eTk4Vi_ozyYA~ev_2gdMwl3)Vfy>nvCq-lN+5Rw&P&qp2fT({*%*bKK6S}FUE;L%j}!!vZA1Ll)B2d%~Oo-vs`ls zXyWb=K3h=Ht|yjh?fYngHD~76b-~gWo@IO-ED;SLl6-9AzEyz10i|Hq-(_VZqH^{v z&IWJ`>q&d5FPr$o%9U_)Q@h%W-bbLTckWdWm9_2r zjsqPpe(KYB^=se6cYpLVy!zT3SWai`o63|-w_tX|pNob?o4hPzh*bw!NzeQxWY8Jc z36<`P#7sXkAE)I)%#|{_qvXP%pwRbxpJj#Ro5rkY@iaM4U!x_4tRq1ZcASIfubIfu zF?_LaxLnbZzJKR7K7RileEiV|c=G5Wo;`hvZMzKG0RT1J+U0ohta0fS$;eelUug<} z@MvSBNrtT}w^X#IEI>II3NTCtiW`%4b+lbi=TWi>%OjS32gW|_bO28-+Ea`o~#SAA1-YlhNERJ9o%)*e(HXpIY1da0?vUi2G&$L z6AT`8>ES*F8#70_B21KZ;}*q%MmJNTRR0Lyk+0$TG0m(0HYiEaY*$H-e6lx1vpBL3kawQOT<2OxSBo!&~++*QOa*Z%Mv$eA2$l zjh##yV_Ke;Fda54DF~2_w=C?a-dzqJf`ul~<%l=QjiNb?5gMl-8zKU2VfBWoqI;jG zws~QasivVZ#%K`b`L%GVvgCmR_ok25yp6@H(z|uP_loB@mC?HsYaI)>mXZUZJ z7eRRn$wZPA-&*`?Icu8}m;xV~qpE6BdVL~O%AeXIGhhhV2AIa3*-gQXfN5*IuUnpo zUsG$M?a(WW%)e;^wX(SkN6x`K6tX!Y*q5eskj9DN(U|rLJ?G-Os$3v^Vy551iQt{Q zC!MtLJUH8_v6-->XN_k#I3d1F%oRGOHhxIk?Ur}-J#ow~|6DI+tL+TJvTJ*m#f@ct zy!99VA_IgencjQ+TxP}c3{Nyl$a9kABzw|6H&23)f=h^(;2BHfU~SGgtLt)rf`vlI zgdwBTQTSkuI^Qkx%0wto6Bhh#2tGw-^&}ZqNSKwDg^S7`%*wpDUJWzBL>tMkn=bGLt*9h8`mf2J=}46rA;>Af-*f`OXYEM^m*tI^uwuNANMj-jsuRy%of{F8l`b+aMTO zoW%7O_+F#o*~l#Q8*yh`4w&#`&^ge3es7@T>EnmE`N8{m_c#B5TOYoQ?xsRZ18p2p zl;7w5!^#9#W?Aqpf`4FFgR#YX zbY<+4e>o4AHMZ6mU=yrNc1&q4x&`%Z_LG6#rg%s}pvp~~19@5kmWKX7DX%fw5c`ZT`z?Kknp4}O9#eEBPA z>kTSFpFdgh&&c39%sB+8*5)jbV1bf>nvkA znX9WrvSU4;(4yh8U3fGMUrGHbU2mCWx+fk8*Zttx@2ArV7t4fc-z3N3+);YO++irY z@+4TQ*%Rx^(sUm!RZeaTcB)s}3-ri)$1XYZ^c>HWYV{oW;C z5WwCOpZd(_@$*0V=lI4OZvrYUw}Dv{ZllUN!iY^M5g^=we&*w6eUjGAUxM?E5IBxY1x>H7nNXx`;X6vMk>PavWEmuw zEYTo*C(AzJiZx$;XtG-X&YjoDwq+^^DEN>*IYxr~?5AAzTEjTIEZQWk6W-aSN9CMx zKw7uey_+wgGaW%^RPQsb+4!HGA&7qrcaKBIQ1?}G%(f+@-(`c_Aq-Yudd#DUA3dYhe z5o+=#Sim%#bokqR^r&EXs5E-fmL1rJsNOcJ##Om+S5Jm$+&`QS^oTfm>b!1kEAf@? zPg9wG@-_8>EBHtc0nwN7Q2e5KJCIZB;QBLd2k~#@2-whWRDz96RgSZUR?D_XGaFMc4M@xA0Feowmg-=s6IKboo#JU~st%7k)W_%0CGV87kA*Y5DqiK@N{*a;S9=*v- z;e|z&muN4p5e8we1on6j01~2~Zvu#rG#;0*eJ-1#U$t_*)IXTe>@*lbT8``YEr2Xg|2@yw?WKsESN=suCDxymCG4X=&>LX4f65G`kKDqNT-h2Dk`1ph0 z;>p7YJcb~^`_u@BLRNw^fz?ON$arN2R7y<y6`Bes1jO!hs-+KXIUE7#;S+qv;vWIkfQNS^JrMtwYD2X3!K&yPNx%k zKXBPDn)`rj*S=#0btrRvd{bN4bS^W=TW!J7y%mJc5>!1Bf?+pYK^e0y+S|NqtHrwF z=pEbTf^}K3oR;E$m#3djCoId#v(I(L^`gvk1cj~V@9Z-5&RAEI;pHGZgKsiWh06|> ziyr`9{l**k#h?Dy_}rJjQfEwC&)L}4awi=k(l99%t>{s1CaxGu7VNA`?$^$pJ+ z-9m1U#ZNL|4M&xb+VKHcnhfF4c0p~!(1gg({wxQxoE@!a3ZUA~;oeBXFl|Q&Hc$yK z<(y>XHG1OvD8F2`3Lra=GZIeR@Tn&&`%-h^e+x2#m|?(oM9}Wmk_Zn@L9}O>I|wYN zK^a=2l0gjMdZ}>lAwzoC>fKYd-93!sFMs>8P`a=Evf@KB)=>p=gJWWQ*#cO zKt518*#jTOO;^A&nyV(g49eCHW{@fO33UdGd2V<5cf(LsPf)YChT-EPcr;aO3;MqE z8SyvCNj|CUf&lnT3&dN0@n;$843sudEMUYy%^Tl;$C=2=N>K1mf?d&mpl}VC(H%h* zg2E11Cu5hGsSk3FL-_$MhCm%gmj>h$BK|T)0u=%A9L?11P--A~vRqrJdneH@LlI+9 zo(nwxV$w;$DmzYEw38{JF)2?IogG@AUuZ#Ba}!*>t`zP9La^4Zn=7I~o?#Q0xtpYk z6UaK~+zQBVnsFJBLq|vuo)8XnMLD&G)BjK8UIIIc#%6fPu~liJW;$?O`=YpR-Y=s^ zf>u8k{6b}U372lu7qlftDz2D%szxvO( zf9GRtmuD>DRf8Usvpn{K7^mbL3X((J6hp=--#yL@rctYMMxD_NpQ#8MrlD-{vuDrH z+QM^d3%@itV`|*B=xCj;ea`T_^&XdcS~gHA94dp-`~{-~o$;XzX~?7i=u9>Rwj=LD z76GHgl%2mh-ztxmqDVQJ%VZFlPu$>XHr$fLSU69YscQ*mJ2rvJ<}<6rg)MT7A$|l% zs$LpW?P?zO`SI;TN^gqHW(~vSQ^o;pS{LC>O{2Ke{C~w9)XP75EH@{N`4kuT* zJ*=cXo&$N6CDG~xXUm@`XF{;3nV9rFZy8KnEJv6sqRN2all7TvA-x8G%mdri1<#&7 z!TtMpar@Rsc=qrCZhi0`9zM9srzDHHje>@GH1YuCv{`1g7C0?5>KH`uKe*|*ENCSP&>3cEriAvoTBg)3gnus~I5SHRNSr=z~CD(>}LVGV^NNu$*7On?L?Je(@uCcCLuyh(`e+j|(o3?_j??8gdqL&TQ++-W%}9 zS@c#Dq=>N{FC?$K8+A>xgp;*R#Qhp>24int44Q*G@m_p3dG; zN91I7v_`#3`Xt=ECC+~r#57l-M+C><*!*3<4@}hJ@BJHztrAG}lLpE>@Rs6kIlEJh|{n{FR<7k0+`!D~c-ogJc3c5 z3c?aKyk0UmOVw$(r0`=Vt=9*g2^X+K`0T};V|b#TOg-bd7XcZ6nxCHH`uHwhgt_-C zWYUNp2Ecr;<~T=tg-`vccLl7y%bQTmue&tyx#c?o2%Vz-ryJ2&0rlY7j=T*FO^wimfJTEo7yAMkVD!P|_MD9ay$mDs`IFBLi=oI$ChH!;+=l&-*)}{d$uQGkYElA2@wwqX=4RyaEcjZB=In7P%V-pG zk)zKAYOQ>s4Fc%h^&Y{0;6jd=`Ns6&@<=-<1zXN<9?^2;#`y*U2R?lJm-vhS`_FOr z_DyzY@7&M7Z>%_J;*Hbk2B332s~4GvWK_mKWz_I{;iDuax8Ww0u+9UY%~m-X*^%y z#fSjCZi*8dVnt=hkTx(uh;NEF#VaG=AUd9ovYPoORccFo+c4Kr_L0A%Tp99( zzngwFZ>`bmCOr>hn(&nV4@j1o;Pu^p!R5&v9NQDLNU(y+rJO_IMzBH$s!l1J&N8rU zBQ=W5xzybDU6f4VbE=%;Y_x`<6NodyeaKYvi}=ZQ6(2t4CjFt!VaKw;5mk6m37kOfQmc_f0C5uZMo0y2VCfIL6t0&DT8(x)G+UN zw1wL{hRufxoMi9xxm+GC-uJpo4@jRgb3E+82>oNC#dTYidrKL(A9x`=x=w+xlQbMC zuATTB`~(n(x?#uz-P=?i?Vt{ClZ|yP`4xbgk~wI2JqW>Sz1}xvjaS;3oXvQ@Xp%H6 ze(pyl5`;AAZ9Yv$dg##v9sTU=&pe5D4H_~HmD0VQIu zYl_-sMbRfiGbD^C1#8Sq#S%hF%1Fx5ec7|JkQf45MqN|Z$@NAW%k#5SKh{P;NCh>r z?s<*NQ^o(I)S8~81r=| zOTUy}mmqUQ(9bE;02?P$X0CUMI9q>bepr?T+vS2gw?4w#zy1f@ z`tUtGd-6!*(qV62Lv{Y6VLA*VmdP-~$u)ayHs%sf7*|L{u$=qYSu)J1sd#1TFlgMn zgb1N5nfCMS+g3u)nV@yyS%HM-h%|O2j-BI>Pbefa*>yL>U#rf=J9@`inXrD)fun;? zCwVmc8jd!%Gic%<(wVJ%IOSQU&9dwpsaeVCw%q`U%|J( z_d|U3o8QKbmtICrnSi3InKl)$0*1^JvvcH`wx8oo@)BWY9RTq82+Knoa=OPn$Qwn| zTq8%ffg_#|T-)x#XJvSyS0~hZfJ2bt%tu;d!Ao->F8Mq6hZ6qOEt)n5AewiXK72MQC1obW}x7kEzn zh8yP_lr!q)-;;s#Z~($pHsaqyy&2l+2EOsFH}U2l{1d$R@~71of|sBp_IOTLiRQc( zQUiat9l%ROn!F2kM#DU>jD=+!xb0=;pCr*f_io!MCN7qzmX69S9bH$^CdaDvRCb=T zcA(BQOZfBZI(A75;M*I^XHtIy0QP6NdVCw`7x9OizK*NPXxoBRPY61@gNA4R2MCLt z0`8fDsXm)t9X#?{5AXX7t}SeTdVh0{03{t(>xQE;Vx?ORbvqf7&F1~mw`~O)@ytD} zOpFjcw2wpqvf-$wx=LXQ9O-u1RK`cZ0c7HlV~R#K56-V?jighQ)Lf^YZs%TVxIHpJLJJll9SGRX&Fnvz}9Vj0^rqv_e%-ksJqv6k^8MqvO( zRBu`%eDuO3-;)LaC>Ub80+`?^JO>jbWs8NAV?%#Iva87<1yI!uOLIqc!m;qA`wUNr zo}hUc-gfv1GiF>lKLs{G+A^*wYda9^!9u&&nPxk)3OdS(_EwlMtWtJlfm&5G`W>>P z%t!E=#6RW(4j`!#e3p(X*J_=~--o8=saptQ`kwCLC?k z@4d6+`_TSg8+NPC0Ni(WeU8FK@CdBV&fWmKYctDA8PRowS1Q}g#%IdVfpZ~Vj4tyb z`5BNQ0+)zJ?R{j>i}H%{0FnT9p# z{NhV0S?17#FJvsxODvwS0mx~L`9SYCKWf<%A5O9d8h^r%`6{6dfu3#^Z1rfSF~3@h zaWYs<58NJOBRgrtPJ1248m+|jy_^9~OaV>vmglceSqsp&*{kf*0PXL`zTw@seucmLo4>;S+aG)Jq-*H`SCn)xQ>&pxvx&vdZUk19=!ODP`h;zO0VTmPcd- z!OYJ&f#V8|($7HGnZtgK;Q+}T+m~LG$Ag?{2Ffjat3I+|m*pJHv?g3l-xZx%!gBG2 z>jY(dluVq)Ds&GUkAVR-{S2Um_v4cPpnb%axegql@IVcoS+l-1g z1N=O_o4TtJte~yI3YihCaRA19i0SYVW}b6!BKpPwE&nxiBVnjyNcCS)LQF?iHI3Ih z%k3e(sE3P>B7(FDpsLEmZ?kP?yU24PY4IELjDw_Hv)3_ruH=|-;dz?oBiw^Pzq)ij z2_LSV_jbXy>H7qLi3jVF?U<@WgKYidVAI;rR;bKAZFlse@Up+H$H4^4pSsY&ppjP`Oe^_HfI*+s51DKdnOauuttE~tcL4>W>$+c%D zGJr*AdRDY%f-p12!jA~bcVto!1}wPzyNJ-xkkGZ@2sQR0$YQ|4f*mA8M3%`c( zIEySr0Ku30aSzx77h zZ?Syr+M^}@D8dpi8$!H)RHvlhS4P|5$uSqcbB1lVK3W=1OT*opALH-;_OI~HZ-0g3 z>H?kMMm*`i5!_P3$_Hivc;p;|JKM%UmEX_68lNZLI3F4!4E|C6jm$8&Yj9t8QU8oRO#WpZGuf}%7k~GuO6y$VQD#KEt^`-Sz-pC z6WqhRtS6-88ncOn;kNBOe#P|DI|Ga+yd@BA#jD?V1Khdw+_)u*dXZxd3t+ELVHMa#yc>@q?Qv{)1bAiYvv zn8vw>*k|Tar*@UfIx#|>DLEngFEQDlqnU2B}&s>VmC4Axo1jC8u`E-kF%F2RNqJVz>oZHs9J>!2_6%wJwgqVN;E=ydJ_u zC=nHc+*&kE({R8d_2mAvq20-956 zY(m;qp^+euHYj-@ya41$W`E^7bneY29rl%{9o${+Hg~2G^4M{ zVXyBf4?_FIjVF)kGmmMr(JmbPNcx53LGg^8TloMbQ+0>s5lNf&mkDB50M&BDPknkHhL=PP(ah7re~ek-L$hA5a$ zbdfd&=gUGq8TlccMpTn99naLgdKMrTxIBY2F=%N5YR##+OgKB~PtS1fO~wi5<8s8O zk3PZ2AH0Y6-+2qSKmGt$PalJzHH^Im34o+>up(dOL}pjiD>(1anp}42Qq4f%vTbO_ zojNyCh!F1~*jdqWK8^dRjpMGe%ih(Y+Re#WB;pcLPKWyfp{#nESK6Y6dWv#NNU}4u zDjp;Y1`Mczj>AVQsCk5<(4msGCY(E?%Ke9<^CM|a`*mbwq%v8jbyaqp%CpXR)kTFO zo=C!QY20r~xyF)i?>j$KzU>m~^|WGLPcoz=z*QD#92I>_N%!QLnVzH9O6YiA#AyKN z=vbCBzV!QF$G6}7F~0oOZ(upUplG(A7-dr8^5Pp`{KVfJxIoY-pLv?kO!rMs^Y`)G z?9}#Dx7N9oy7<|D^DPmBMpJwg+V{t2Y!D23@zVhtz5dSEBp<8`K)UOddD8PqnM*mY zb!#2Tbe%HR%Ho$SBc6-yUBYR8vHaCL*JVNO8-Dfo{}KP`KmHA#K77FA&(v9()fVhn z%?2xs!=BE}0&n|)ZQDrON5@`9w(+Eui?>z>)`EJ2^HW1Y9T~z#VAS8eb4vhpzH}xt z$Zcg$11JE6+SRXs0uS1A{z8q11a}r}!ET z&mY;zJjE-dYu_@O1Pz%iT9Q!@$%K@?vb?6u=9(jHbC99|CJ8bb)aanxDw>E8O^01E zAjjA#_}NY}?Q-Pn^D!5By%Pj<@i)F3I4+NHd3*~wWr+G@D}{7mjVoX!fK;fInL3QNoq`EkLx*$)pd*6@ zj{ww>v!h8YfRbEYlUrMW#;s63#>mbaL`wkme|GO-=UfaAz0IhuIaLy7K(BC5IWIiQ z(KSq?4d>amJ&&tn9|s4+94PC_s)4S+L`d(qvPboP(^HGeWFNDf&jd<;DWeGPvpzEG zWDLmXAl;IODEHJnP9@^71Kf1xG=O8@Y7f1Zel6(%>$B1mqSb?YfVnk+u!90HEf&N= zJ!QTK*)XIb?U$|~J#pZ_84%z_x+hO2KpIiTy6`9*vr17uR4gV@-evg#*$?Vd!yY|+ z4`l=&qHH33E4ZS2O}FMR(+St_vTyOA*5RvgFE~Quq5L*Mvu+#uqX9+; zYhw$4UA$5OiBy#ffW2=lX~(c%xDM(jC@2H~u`IN26|hV-3?91P^CeHTXo%nZJ03>` z8I(EgH6h$=yl}|@3vLm!Bk~E?40*VMlNASZG!P!JxkL@5kVO=Lm-F7N@(#(!A60>y$2)`zRiM!1|7#5 zYvFy@*P*EAHRw*y8KyQ;*)r5@2jujJ3ssxzV!RwKwD2dMrcxIwP+bUSvZjsXFlLZK4UsoW)UeR zHW*L*DFa=~W*#e3@+G3U4`nD)Ir0G;wK9r5PS^pE5wAH9{`B~Kt7qjgD9cch@fo(9 z_oCCmj!$$u9MK9S<@LtD1}E9jYR!EVFp{d^&Lphqi`vD2VI%7-y2f zOP#4s2ft4^T9=iXsLsY@1gEM?meD||Cz~L-RIw|QNXL1Cv0!mJpKww(oczi6RuDja zNup6l+pM4yL!IAL?Xe&3;LGy(>@*(tqvH!-`5J!wi$B4azWPn<2WeZ25FAVyyv$FA zk?$-0J!RKZj&o)J*Jy_S4arD^{uT_l1O;gPcF~_Wg?}sH0C2vfPqB^XYd(koG-iU% zN9tz7ovY=X{5&gLIX3yiI=5b|xtj;{TmXH0gsVrl5nx~jNh#18n<7Sx3@zl0vPy{W z$n11f(hayIoA`!$k3S3X1q&Ose2=odnm5;&NF7_VtZeE?9pJ1aJ1JKaBW1WKr#N4L z{BB!lT}~kX5jW{T8*e17G}5r04bqLaVb#5Yv78d(X~<^FN7E~54wcns$*yb|;g)Yv z1>GFvw5KIG6`C9M0pSh@1p9&GVDEy`_YMT4`j;p$#&Tw89h@8QBU~aBXaLB$rnTPD z^T4v46tw5qC_Q4>@c!Zl(VyW|6=#+|%L&VJQs4x@)S27(?MZpVc?cO4>HruRFw>>* z!|@5PrmC31BcjvRh!Zu3qi*2UyXCF}7kqXOFnQgk7vASY2c5srwj8Kzm^db_xC+f{ zgX}HaE(it}T5DLAvuq>wmq>^n1r9*`GX)hWi%g3ns8Ekg_Iz3{)i~ofu2ovo{+AL8 zw4WvKK^rfCLFdws9-I1(qlT(R4)qnFPMKwioMiE$>HF9>baiq^1p2-SPKz3KD%c{K z+1i3-D%B=Y58~zjxN!0MqyUT%&Yd8Elnh@+!Z1VgYa2l}P(}yp-ko_; zM^r7F!)r__0i=+rNKTnf;YB`&#BvgeIDi?3pah`c5?T{nFA4(VpE5u~5z(4fP^cwp zU#yMgm^2Kqilo>G}W#9hYN>Y~))8tI>@>;-vgp7PMBi|)L_E_n2w56<* zL~mHy!SOSE)ll?|96|dgxZWBY5-bvoDJQ`5FdY@fg+mG&3N)|B_nCPRoM&@ck&M37 zyrYDb@KA<&s#)9wOTD+~OU%5q1^c$)&dm?;_OE}5n;*Q7?b%aA6rhSOm(~cC0FI4E z%mjdKXGSGtfN4UcNo9!6I;T0)#)<@|FuJY`d~E7W%cee~v@vx=l-K8LKXexAvXo)$ z>=GSfW~OLhaBovN1u`zVOe)U8zar02ckC7v=5&gwJeuIYA2d>Epp)iZE*Uz@*akC# zjxUvgcp`aZkLfsd{8Ue1RnR7tmFk_P*{J}89n?dm*HucYHPczP#8AK{0?R_<-A>jb z8jjHZLJ;1|vQ!q!fe7g6s1U!lV25ne=5QwX`GuEW!ta0WoA~B;e~2%9e z$rJy}NnE3UW5oeH8U{81Z2-CC&COLj0giFVhzT3rb}%I0DEr3>(#EiC2}7|&Wuopj z&-LwsU;X{x;XnPyzs2K+pQwVs4ycZQ>QdSYU^UT8PiA#k>Q5tr&oZOCL}*Kg^iD+0 z#c&82`&nCuIC)8L(K`1edp$JVxV}ckDVPvfZ7Nqw>Cp&gi`ycZ;eefvtFr+IFndBY z)8J#^%ydJ(tG)=X*ii7y4q;meuNKN`A?xU;))qukmUWfQdY^kn!|UJsF~0w^e~R^m zm)IE1?+zl4^vBc1p+^Ra5aB0;SikgMTeS>rVIRxF01-fEq-66X(@MwtY0Diu=emb-trwiqJXH+tX%PZt zGeuzZ^5B~F#B{GI!Zx|&emPg>Z#o{SMqB2+*?FGV)BEWCvZxa4(0SH{U(gO-mKFWj z%TXtulJ|Naso)J)s>wbu9{~)E1MILKttl&QJ8E`Lq+^o$Co|D^RmkZ50o$9RXHjh` zxI-Dp|5f>WjAWH@UAzX_^TE>(S~O%*UM~tvbY+-HU&LF=F(oY+H(Cd%1A(!{Z&}z& zsmm5a%KkF{RpVcMmc8NtkYP@oDM+Bekp-9G4nUo|2i`?8UUA(=US*JKzLg(jiwj4z z2Tf13Jvl$opsJ?EKpd$BZP5yBc)sNuNX}vSAk9)$_8+SW5>OBV<#TckZCO~Qpn!F2 z0SMP&dnEbBwlu`A|Ly;geL^BifC~wefTFWN;j+jm(h4Qu%#_y&6r5Wvm`ff%o3Tro z7b6UWm1wZRq&N1^tW-_vvrRi!wrIHwsfkPQg z8o8X+x~|IJB+jQBoSSHaQSyP803)S6DOi`*)BuRE?PB_X4LJ%`8m1g7C0JeBv$Pel z@J!T9mXSi)Xd1&RaXI)-3K;I+T9%XE%{lG+&aT|*wp&{gTskj9+}W`7kx%Pz3JIIU z?hLk_%ti+^suZ0hW<0tPQudB*<9ZkGA6D6R3a;~aKGudt44unmRiLN#W;1Bf0Ia98 zx+^lP2GHn>08Zx{Xwk4A+t>@LY>3}ij!iTyrxU*N)o#%X>xl3tl&V1|i=7^F32r%;pNPf(`Kkm2jj9}cy87Rs+&z@ssS2>~9OSpR4 z9RIW~xb@+C_}hQ~Z}HLl@6j1vsGpo==K9p|4-Ax$mPuFp&I%9G#TYvU*j0hpnLZsv zNl-pC(eR;e+N67S0X>e6P3N5>m}R7s!hNGP4o!**co3&{ikI3iwyr$$o%FzYrpnY= z%`wKSeUg&PHs&(i6JbR^4wci&kT1HR;dwrvX_!_}LA@D3M+3^h^sa2!ddBzv;E(aG zAN~y47VO(aL2KzD7|~;`kdgg7-Q@HS;Ww&0Se;Qf@P6A+(qne87!;&FhF~#Y173!w z{w@Q{`pFz1dJUA`Cu3wBpFf?RNtadVhB8hf3Y2fI-hu$wk--WeB8?!;-8sPOanm;qK=W zcsim=0MD(V4_5#?8)q7 z%|7PJNdQ-BSYLU*HghI)#Fw@d_Ku_PXd*iDIL8ga1ENLaKblUhabvr(N2xpP tYnWGQw{v^NdO9O~(n+VDN^mQ~{|6JF+(Dc*VkrOs002ovPDHLkV1khMni>EA literal 171793 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 U(uC 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%0A3@rOq{mPkCa3JvF|0sx*ST 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�x>A~9I*t%jKG$jm-W}D$GUT(7b)ZpX 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 xZRD-(lJFnH5 Date: Tue, 14 Apr 2026 03:51:30 +0300 Subject: [PATCH 50/65] refactor: center readme preview --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b31e10..3dfc471 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -![Превью](images/preview.png) +

+ GoodRoad banner +

## GoodRoad (Android client) From 28673feeec8bba73565b42bc6a676e709f4f36ea Mon Sep 17 00:00:00 2001 From: SanriaArgos Date: Tue, 14 Apr 2026 11:57:21 +0300 Subject: [PATCH 51/65] refactor: scheme of client --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3dfc471..e240602 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ cp docker/.env.backend.example docker/.env.backend Из корня `GoodRoad-Client`: ```bash -docker compose --env-file docker/.env.backend -f docker-compose.backend.yml up --build +docker-compose up --build ``` После запуска поднимутся PostgreSQL + PostGIS, а также backend GoodRoad на Spring Boot. @@ -142,6 +142,4 @@ app/src/main/java/com/example/goodroad/ ## Схема клиентского приложения -Ниже приведена схема экранов и основных пользовательских переходов, которая используется как ориентир для клиентской части проекта. Зеленым обозначены блоки, реализованные к MVP. - -![Схема клиентского приложения](images/android_scheme.png) +**надо добавить (а может и не надо)** From 1a95f158949e6dd7af2598b0a270a3e1277fb927 Mon Sep 17 00:00:00 2001 From: KseniaG8 Date: Tue, 14 Apr 2026 18:38:13 +0300 Subject: [PATCH 52/65] fOBix: fix map ui and map logic A A  --- .../java/com/example/goodroad/MapActivity.kt | 220 ++++++++++++++++++ .../features/network/api/GoodRoadApi.kt | 25 ++ .../example/goodroad/model/RouteRequest.kt | 36 +++ .../goodroad/obstacle/ObstacleModels.kt | 26 +++ app/src/main/res/drawable/rounded_button.xml | 6 + app/src/main/res/layout/activity_map.xml | 51 ++++ 6 files changed, 364 insertions(+) create mode 100644 app/src/main/java/com/example/goodroad/MapActivity.kt create mode 100644 app/src/main/java/com/example/goodroad/features/network/api/GoodRoadApi.kt create mode 100644 app/src/main/java/com/example/goodroad/model/RouteRequest.kt create mode 100644 app/src/main/java/com/example/goodroad/obstacle/ObstacleModels.kt create mode 100644 app/src/main/res/drawable/rounded_button.xml create mode 100644 app/src/main/res/layout/activity_map.xml diff --git a/app/src/main/java/com/example/goodroad/MapActivity.kt b/app/src/main/java/com/example/goodroad/MapActivity.kt new file mode 100644 index 0000000..cbefd49 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/MapActivity.kt @@ -0,0 +1,220 @@ +package com.example.goodroad + +import android.location.Address +import org.maplibre.android.camera.CameraUpdateFactory +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.lifecycleScope +import com.example.goodroad.features.location.LocationTracker +import kotlinx.coroutines.launch +import org.maplibre.android.MapLibre +import org.maplibre.android.maps.MapView +import org.maplibre.android.maps.Style +import android.location.Geocoder +import android.widget.Button +import android.widget.EditText +import android.widget.Toast +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.util.Locale +import com.example.goodroad.model.RouteRequest +import com.example.goodroad.model.RouteResponse +import com.example.goodroad.features.network.api.GoodRoadApi +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import com.example.goodroad.features.network.utils.decodePoints +import com.example.goodroad.model.PathResponse +import org.maplibre.android.geometry.LatLng +import com.google.maps.android.PolyUtil +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.style.layers.LineLayer +import org.maplibre.android.style.sources.GeoJsonSource +import org.maplibre.android.style.layers.PropertyFactory + +class MapActivity : AppCompatActivity() { + private lateinit var mapView: MapView + private lateinit var locationTracker: LocationTracker + private lateinit var addressEditText: EditText + private lateinit var setDestinationButton: Button + + private var startLat: Double = 0.0 + private var startLon: Double = 0.0 + + private val api: GoodRoadApi by lazy { + Retrofit.Builder() + //.baseUrl("http://10.0.2.2:8080/") // для эмулятора + .baseUrl("http://10.0.0.2:8080/") // для моего телефона + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(GoodRoadApi::class.java) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + supportActionBar?.hide() + MapLibre.getInstance(this) + setContentView(R.layout.activity_map) + + mapView = findViewById(R.id.mapView) + mapView.onCreate(savedInstanceState) + + addressEditText = findViewById(R.id.addressEditText) + setDestinationButton = findViewById(R.id.setDestinationButton) + + locationTracker = LocationTracker(this) + + mapView.getMapAsync { map -> + map.setStyle(Style.Builder().fromUri("https://tiles.stadiamaps.com/styles/alidade_smooth.json?api_key=a5972731-a9e9-4ebb-943b-2965bc3f9dca")) { + getUserLocation() + } + } + + setDestinationButton.setOnClickListener { + val destinationAddress = addressEditText.text.toString() + if (destinationAddress.isNotBlank()) { + getCoordinatesFromAddress(destinationAddress) + } else { + Toast.makeText(this, "Введите адрес назначения", Toast.LENGTH_SHORT).show() + } + } + } + + private fun getUserLocation() { + lifecycleScope.launch { + val location = locationTracker.getCurrentLocation() + if (location != null) { + startLat = location.latitude + startLon = location.longitude + Toast.makeText( + this@MapActivity, + "Ваше местоположение: $startLat, $startLon", + Toast.LENGTH_SHORT + ).show() + } else { + Toast.makeText(this@MapActivity, "Не удалось определить местоположение", Toast.LENGTH_SHORT).show() + } + } + } + + private fun getCoordinatesFromAddress(address: String) { + lifecycleScope.launch { + Toast.makeText(this@MapActivity, "Поиск адреса...", Toast.LENGTH_SHORT).show() + + val addresses = withContext(Dispatchers.IO) { + try { + val geocoder = Geocoder(this@MapActivity, Locale.getDefault()) + geocoder.getFromLocationName(address, 1) + } catch (e: Exception) { + null + } + } + + if (!addresses.isNullOrEmpty()) { + val destination = addresses[0] + val endLat = destination.latitude + val endLon = destination.longitude + + Toast.makeText( + this@MapActivity, + "Маршрут от \$startLat,\$startLon до \$endLat,\$endLon", + Toast.LENGTH_LONG + ).show() + + buildRoute(endLat, endLon) + } else { + Toast.makeText(this@MapActivity, "Адрес не найден. Попробуйте точнее.", Toast.LENGTH_SHORT).show() + } + } + } + + private fun buildRoute(endLat: Double, endLon: Double) { + lifecycleScope.launch { + //взять реальный Id пользователя + if (startLat == 0.0 || startLon == 0.0) { + Toast.makeText(this@MapActivity, "Стартовая точка не определена", Toast.LENGTH_SHORT).show() + return@launch + } + + val policies = api.getUserObstacles("") // надо узнать где токен + val allowedTypes = setOf("SAND", "GRAVEL") + + val request = RouteRequest( + start = "$startLat,$startLon", + end = "$endLat,$endLon", + avoidStairs = policies.find { it.obstacleType == "STAIRS" }?.selected == true, + maxCurbHeight = policies.find { it.obstacleType == "CURB" }?.maxAllowedSeverity?.toInt(), + maxSlopeAngle = policies.find { it.obstacleType == "ROAD_SLOPE" }?.maxAllowedSeverity?.toDouble(), + avoidBadRoad = policies.find { it.obstacleType == "POTHOLES" }?.selected == true, + avoidSurfaceTypes = policies.filter { it.selected && it.obstacleType in allowedTypes}.map{ it.obstacleType } + ) + + //drawRoute(RouteResponse(id = "test", paths = emptyList())) + + try { + val response = api.getRoute(request) + drawRoute(response) + } catch (e: Exception) { + Toast.makeText(this@MapActivity, "Ошибка: ${e.message}", Toast.LENGTH_SHORT).show() + e.printStackTrace() + } + } + } + + private fun drawRoute(response: RouteResponse) { + val path = response.paths.firstOrNull() ?: return + val points = decodePoints(path.points) + + mapView.getMapAsync { map -> + map.getStyle { style -> + style.removeLayer("route-layer") + style.removeSource("route-source") + + val coordinates = points.joinToString(", ") { + "[${it.longitude}, ${it.latitude}]" + } + val geojson = """ + { + "type": "FeatureCollection", + "features": [{ + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [$coordinates] + } + }] + } + """.trimIndent() + + val source = GeoJsonSource("route-source", geojson) + style.addSource(source) + + val lineLayer = LineLayer("route-layer", "route-source").apply { + setProperties( + PropertyFactory.lineColor("#8B7AC6"), + PropertyFactory.lineWidth(5f), + PropertyFactory.lineOpacity(0.8f) + ) + } + style.addLayer(lineLayer) + + map.animateCamera( + CameraUpdateFactory.newLatLngZoom(LatLng(startLat, startLon), 15.0), + 1000 + ) + } + } + } + + override fun onStart() { super.onStart(); mapView.onStart() } + override fun onResume() { super.onResume(); mapView.onResume() } + override fun onPause() { super.onPause(); mapView.onPause() } + override fun onStop() { super.onStop(); mapView.onStop() } + override fun onDestroy() { super.onDestroy(); mapView.onDestroy() } + override fun onLowMemory() { super.onLowMemory(); mapView.onLowMemory() } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + mapView.onSaveInstanceState(outState) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/features/network/api/GoodRoadApi.kt b/app/src/main/java/com/example/goodroad/features/network/api/GoodRoadApi.kt new file mode 100644 index 0000000..de64362 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/features/network/api/GoodRoadApi.kt @@ -0,0 +1,25 @@ +package com.example.goodroad.features.network.api + +import com.example.goodroad.model.RouteRequest +import com.example.goodroad.model.RouteResponse +import retrofit2.http.Body +import retrofit2.http.POST +import retrofit2.http.Query +import retrofit2.http.GET +import retrofit2.http.PUT +import retrofit2.http.Header +import com.example.goodroad.obstacle.ObstacleMapItemResp +import com.example.goodroad.obstacle.PolicyItem +import com.example.goodroad.obstacle.ReplacePolicyReq +interface GoodRoadApi { + @POST("/api/v1/routes") + suspend fun getRoute(@Body request: RouteRequest): RouteResponse + + @GET("obstacles") + suspend fun getObstaclesInBox( + @Query("minLat") minLat: Double, + @Query("maxLat") maxLat: Double, + @Query("minLon") minLon: Double, + @Query("maxLon") maxLon: Double + ): List +} \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/model/RouteRequest.kt b/app/src/main/java/com/example/goodroad/model/RouteRequest.kt new file mode 100644 index 0000000..03f9165 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/model/RouteRequest.kt @@ -0,0 +1,36 @@ +package com.example.goodroad.model + +import com.fasterxml.jackson.annotation.JsonProperty + +data class RouteRequest( + val start: String, // (lat,lon) + val end: String, // (lat,lon) + @JsonProperty("user_id") + val userId: String? = null, + + @JsonProperty("max_stairs") + val maxStairsCount: Int? = null, // сколько ступенек максимум + @JsonProperty("max_slope") + val maxSlopeAngle: Double? = null, // макс угол уклона + @JsonProperty("max_curb_height") + val maxCurbHeight: Int? = null, // макс высота бордюра + @JsonProperty("min_path_width") + val minPathWidth: Int? = null, // мин ширина прохода + + @JsonProperty("avoid_stairs") + val avoidStairs: Boolean = false, // избегать лестниц + @JsonProperty("need_ramp") + val needRamp: Boolean = false, // нужен пандус + @JsonProperty("avoid_bad_road") + val avoidBadRoad: Boolean = false, // избегать плохих дорог + + // какие поверхности избегать + @JsonProperty("avoid_surfaces") + val avoidSurfaceTypes: List = emptyList(), // "SAND", "GRAVEL" + + val locale: String = "ru", // язык инструкций + @JsonProperty("alternatives") + val needAlternatives: Boolean = true, // нужны ли альтернативы + @JsonProperty("points_encoded") + val pointsEncoded: Boolean = true +) \ No newline at end of file diff --git a/app/src/main/java/com/example/goodroad/obstacle/ObstacleModels.kt b/app/src/main/java/com/example/goodroad/obstacle/ObstacleModels.kt new file mode 100644 index 0000000..3d2a594 --- /dev/null +++ b/app/src/main/java/com/example/goodroad/obstacle/ObstacleModels.kt @@ -0,0 +1,26 @@ +package com.example.goodroad.obstacle + +// data/obstacles/ObstacleModels.kt + +import java.time.Instant + +data class ObstacleMapItemResp( + val id: String, + val type: String, + val latitude: Double, + val longitude: Double, + val address: AddressResp?, + val severityEstimate: Short?, + val reviewsCount: Int, + val lastReviewedAt: Instant? +) + +data class AddressResp( + val country: String?, + val region: String?, + val localityType: String?, + val city: String?, + val street: String?, + val house: String?, + val placeName: String? +) \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_button.xml b/app/src/main/res/drawable/rounded_button.xml new file mode 100644 index 0000000..7882a40 --- /dev/null +++ b/app/src/main/res/drawable/rounded_button.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_map.xml b/app/src/main/res/layout/activity_map.xml new file mode 100644 index 0000000..f04026f --- /dev/null +++ b/app/src/main/res/layout/activity_map.xml @@ -0,0 +1,51 @@ + + + + + + + + + +