From beb48ab41e1059cc9ef5952f0d578cfd2a752003 Mon Sep 17 00:00:00 2001 From: Eresperto Date: Mon, 1 Dec 2025 21:45:54 +0600 Subject: [PATCH] With avatar --- .kotlin/errors/errors-1764603791320.log | 76 +++++++++++++++++++ .../java/ru/myitschool/work/core/DateUtils.kt | 5 +- .../work/data/repo/AuthRepository.kt | 6 +- .../work/data/repo/UserRepository.kt | 13 +--- .../work/data/source/NetworkDataSource.kt | 23 ++---- .../work/data/source/dto/UserDto.kt | 34 +-------- .../work/domain/booking/BookPlaceUseCase.kt | 7 +- .../work/ui/screen/NavigationGraph.kt | 2 +- .../work/ui/screen/auth/AuthViewModel.kt | 3 +- .../work/ui/screen/book/BookState.kt | 1 - 10 files changed, 95 insertions(+), 75 deletions(-) create mode 100644 .kotlin/errors/errors-1764603791320.log diff --git a/.kotlin/errors/errors-1764603791320.log b/.kotlin/errors/errors-1764603791320.log new file mode 100644 index 0000000..ef56a3f --- /dev/null +++ b/.kotlin/errors/errors-1764603791320.log @@ -0,0 +1,76 @@ +kotlin version: 2.0.21 +error message: org.jetbrains.kotlin.util.FileAnalysisException: While analysing C:/Users/Admin/AndroidStudioProjects/NTO-2025-Android-TeamTask/app/src/main/java/ru/myitschool/work/App.kt:7:5: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.util.AnalysisExceptionsKt.wrapIntoFileAnalysisExceptionIfNeeded(AnalysisExceptions.kt:57) + at org.jetbrains.kotlin.fir.FirCliExceptionHandler.handleExceptionOnFileAnalysis(Utils.kt:249) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:46) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.resolveAndCheckFir(firUtils.kt:77) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.buildResolveAndCheckFirViaLightTree(firUtils.kt:88) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModuleToAnalyzedFir(jvmCompilerPipeline.kt:319) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModulesUsingFrontendIrAndLightTree(jvmCompilerPipeline.kt:118) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:148) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:43) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:103) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:49) + at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:464) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:73) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) +Caused by: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.requireNotNull(KtDiagnosticReportHelpers.kt:68) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn(KtDiagnosticReportHelpers.kt:39) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn$default(KtDiagnosticReportHelpers.kt:31) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkSourceElement(FirIncompatibleClassExpressionChecker.kt:50) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkType$checkers(FirIncompatibleClassExpressionChecker.kt:42) + at org.jetbrains.kotlin.fir.analysis.checkers.type.FirIncompatibleClassTypeChecker.check(FirIncompatibleClassTypeChecker.kt:17) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.check(TypeCheckersDiagnosticComponent.kt:81) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:53) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:19) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor.checkElement(CheckerRunningDiagnosticCollectorVisitor.kt:24) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:248) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl.acceptChildren(FirSimpleFunctionImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:118) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirSimpleFunction.accept(FirSimpleFunction.kt:51) + at org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl.acceptChildren(FirRegularClassImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitClassAndChildren(AbstractDiagnosticCollectorVisitor.kt:87) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:92) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:48) + at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.acceptChildren(FirFileImpl.kt:57) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:1151) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirFile.accept(FirFile.kt:42) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector.collectDiagnostics(AbstractDiagnosticCollector.kt:36) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:34) + ... 33 more + + diff --git a/app/src/main/java/ru/myitschool/work/core/DateUtils.kt b/app/src/main/java/ru/myitschool/work/core/DateUtils.kt index 3bc85da..455bbd0 100644 --- a/app/src/main/java/ru/myitschool/work/core/DateUtils.kt +++ b/app/src/main/java/ru/myitschool/work/core/DateUtils.kt @@ -11,7 +11,6 @@ object DateUtils { private val bookFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM") - /** Пытаемся распарсить дату в нескольких форматах */ fun parseDate(raw: String): LocalDate? { return runCatching { LocalDate.parse(raw) }.getOrElse { runCatching { OffsetDateTime.parse(raw).toLocalDate() }.getOrElse { @@ -22,13 +21,13 @@ object DateUtils { } } - /** Формат для главного экрана: dd.MM.yyyy */ + // Формат для главного экрана: dd.MM.yyyy fun formatForMain(raw: String): String { val date = parseDate(raw) ?: return raw return date.format(mainFormatter) } - /** Формат для экрана бронирования: dd.MM */ + // Формат для экрана бронирования: dd.MM fun formatForBook(raw: String): String { val date = parseDate(raw) ?: return raw return date.format(bookFormatter) diff --git a/app/src/main/java/ru/myitschool/work/data/repo/AuthRepository.kt b/app/src/main/java/ru/myitschool/work/data/repo/AuthRepository.kt index 413749a..c6a2195 100644 --- a/app/src/main/java/ru/myitschool/work/data/repo/AuthRepository.kt +++ b/app/src/main/java/ru/myitschool/work/data/repo/AuthRepository.kt @@ -13,7 +13,7 @@ object AuthRepository { private val KEY_CODE = stringPreferencesKey("auth_code") private var codeCache: String? = null - // Проверка кода на сервере и сохранение при успехе + suspend fun checkAndSave(text: String): Result { return NetworkDataSource.checkAuth(text).onSuccess { success -> if (success) { @@ -25,7 +25,7 @@ object AuthRepository { } } - /** Сохранённый код (из кэша или DataStore) */ + suspend fun getSavedCode(): String? { codeCache?.let { return it } @@ -37,7 +37,7 @@ object AuthRepository { return code } - /** Полная очистка данных авторизации (для logout) */ + suspend fun clear() { codeCache = null dataStore.edit { prefs -> diff --git a/app/src/main/java/ru/myitschool/work/data/repo/UserRepository.kt b/app/src/main/java/ru/myitschool/work/data/repo/UserRepository.kt index f933d7c..9c9b649 100644 --- a/app/src/main/java/ru/myitschool/work/data/repo/UserRepository.kt +++ b/app/src/main/java/ru/myitschool/work/data/repo/UserRepository.kt @@ -11,9 +11,7 @@ import ru.myitschool.work.domain.entities.UserEntity object UserRepository { - /** - * Получение информации о пользователе через GET /api//info - */ + suspend fun getUserInfo(): Result { val code = AuthRepository.getSavedCode() ?: return Result.failure(IllegalStateException("Auth code is not saved")) @@ -23,9 +21,7 @@ object UserRepository { .map { dto -> dto.toDomainUser() } } - /** - * Доступные слоты бронирования через GET /api//booking - */ + suspend fun getAvailableBookings(): Result> { val code = AuthRepository.getSavedCode() ?: return Result.failure(IllegalStateException("Auth code is not saved")) @@ -35,9 +31,7 @@ object UserRepository { .map { map -> map.toDomainBookings() } } - /** - * Создание нового бронирования через POST /api//book - */ + suspend fun book(date: String, placeId: Int): Result { val code = AuthRepository.getSavedCode() ?: return Result.failure(IllegalStateException("Auth code is not saved")) @@ -49,7 +43,6 @@ object UserRepository { ) } - // -------------------- Маппинг DTO -> domain -------------------- private fun UserDto.toDomainUser(): UserEntity { val bookings = booking diff --git a/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt index 12fcee3..ff7ede2 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt @@ -21,10 +21,7 @@ import ru.myitschool.work.data.source.dto.UserDto object NetworkDataSource { - /** - * Поставь false, когда поднимешь настоящий бэкенд. - * При true используются локальные заглушки. - */ + private const val USE_STUB: Boolean = false private val client by lazy { @@ -73,9 +70,7 @@ object NetworkDataSource { // ----------------- Публичные методы ----------------- - /** - * Проверка кода авторизации через GET /api//auth - */ + suspend fun checkAuth(code: String): Result { if (USE_STUB) { // Примитивная проверка заглушки: 4+ символа → ок @@ -95,9 +90,7 @@ object NetworkDataSource { } } - /** - * Получение информации о пользователе через GET /api//info - */ + suspend fun getUserInfo(code: String): Result { if (USE_STUB) { return Result.success(stubUser) @@ -116,9 +109,7 @@ object NetworkDataSource { } } - /** - * Получение доступных слотов бронирования через GET /api//booking - */ + suspend fun getAvailableBookings( code: String, ): Result>> { @@ -140,11 +131,7 @@ object NetworkDataSource { } } - /** - * Создание нового бронирования через POST /api//book - * - * Тело: { "date": "2025-01-05", "placeID": 1 } - */ + suspend fun book( code: String, date: String, diff --git a/app/src/main/java/ru/myitschool/work/data/source/dto/UserDto.kt b/app/src/main/java/ru/myitschool/work/data/source/dto/UserDto.kt index acc1537..eb43db2 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/dto/UserDto.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/dto/UserDto.kt @@ -3,19 +3,6 @@ package ru.myitschool.work.data.source.dto import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -/** - * DTO для ответа GET /api//info - * - * Пример: - * { - * "name":"Иванов Петр Федорович", - * "photoUrl":"https://...", - * "booking":{ - * "2025-01-05": {"id":1,"place":"102"}, - * "2025-01-06": {"id":2,"place":"209.13"} - * } - * } - */ @Serializable data class UserDto( @SerialName("name") @@ -26,9 +13,7 @@ data class UserDto( val booking: Map = emptyMap(), ) -/** - * Элемент бронирования в ответе /info и /booking. - */ + @Serializable data class BookedPlaceDto( @SerialName("id") @@ -37,13 +22,7 @@ data class BookedPlaceDto( val place: String, ) -/** - * DTO для доступных мест в ответе GET /api//booking: - * - * { - * "2025-01-05": [{"id": 1, "place": "102"}, ...] - * } - */ + @Serializable data class AvailablePlaceDto( @SerialName("id") @@ -52,14 +31,7 @@ data class AvailablePlaceDto( val place: String, ) -/** - * Тело запроса POST /api//book: - * - * { - * "date": "2025-01-05", - * "placeID": 1 - * } - */ + @Serializable data class BookRequestDto( @SerialName("date") diff --git a/app/src/main/java/ru/myitschool/work/domain/booking/BookPlaceUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/booking/BookPlaceUseCase.kt index 02a9829..a729fed 100644 --- a/app/src/main/java/ru/myitschool/work/domain/booking/BookPlaceUseCase.kt +++ b/app/src/main/java/ru/myitschool/work/domain/booking/BookPlaceUseCase.kt @@ -2,12 +2,7 @@ package ru.myitschool.work.domain.booking import ru.myitschool.work.data.repo.UserRepository -/** - * Юзкейс для создания бронирования. - * - * @param date дата бронирования в формате yyyy-MM-dd - * @param placeId идентификатор места (placeID из бэкенда) - */ + class BookPlaceUseCase( private val repository: UserRepository, ) { diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt b/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt index ad03e0c..1d489f8 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt @@ -40,7 +40,7 @@ fun AppNavHost( } } - // Пока не знаем стартовый экран — ничего не рисуем (можно сюда потом воткнуть Splash) + val destination = startDestination ?: return NavHost( diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt index 706b9b7..3a1a889 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt @@ -27,12 +27,11 @@ class AuthViewModel : ViewModel() { private val _uiState = MutableStateFlow(AuthState.Data()) val uiState: StateFlow = _uiState.asStateFlow() - // единичное событие навигации на главный экран + private val _actionFlow: MutableSharedFlow = MutableSharedFlow() val actionFlow: SharedFlow = _actionFlow init { - // Если код уже сохранён — сразу уходим на главный экран viewModelScope.launch(Dispatchers.Default) { val saved = getSavedAuthCodeUseCase() if (saved != null) { diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt index ccc83df..41c7f4a 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt @@ -20,6 +20,5 @@ sealed interface BookState { val message: String, ) : BookState - /** Нет доступных дат — показываем "Всё забронировано" */ object Empty : BookState }