diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..8e817bd Binary files /dev/null and b/.DS_Store differ diff --git a/Nabi/.gitignore b/Nabi/.gitignore index aa724b7..951e883 100644 --- a/Nabi/.gitignore +++ b/Nabi/.gitignore @@ -13,3 +13,4 @@ .externalNativeBuild .cxx local.properties + diff --git a/Nabi/data/src/main/java/com/nabi/data/datasource/DiaryRemoteDataSource.kt b/Nabi/data/src/main/java/com/nabi/data/datasource/DiaryRemoteDataSource.kt index 3983bd1..c617c3c 100644 --- a/Nabi/data/src/main/java/com/nabi/data/datasource/DiaryRemoteDataSource.kt +++ b/Nabi/data/src/main/java/com/nabi/data/datasource/DiaryRemoteDataSource.kt @@ -2,9 +2,13 @@ package com.nabi.data.datasource import com.nabi.data.model.BaseResponse import com.nabi.data.model.PageableResponse +import com.nabi.data.model.diary.AddDiaryRequestDTO +import com.nabi.data.model.diary.AddDiaryResponseDTO import com.nabi.data.model.diary.DiaryDetailResponseDTO import com.nabi.data.model.diary.ResponseMonthDiaryDTO import com.nabi.data.model.diary.SearchDiaryResponseDTO +import com.nabi.data.model.diary.UpdateDiaryRequestDTO +import com.nabi.data.model.diary.UpdateDiaryResponseDTO interface DiaryRemoteDataSource { suspend fun getMonthlyDiary( @@ -19,5 +23,19 @@ interface DiaryRemoteDataSource { sort: String ): Result>> - suspend fun getDiaryDetail(accessToken: String, diaryId: Int): Result> + suspend fun getDiaryDetail( + accessToken: String, + diaryId: Int + ): Result> + + suspend fun addDiary( + accessToken: String, + body: AddDiaryRequestDTO + ): Result> + + suspend fun updateDiary( + accessToken: String, + id: Int, + body: UpdateDiaryRequestDTO + ): Result> } \ No newline at end of file diff --git a/Nabi/data/src/main/java/com/nabi/data/datasourceImpl/DiaryRemoteDataSourceImpl.kt b/Nabi/data/src/main/java/com/nabi/data/datasourceImpl/DiaryRemoteDataSourceImpl.kt index cd9cae9..40145d2 100644 --- a/Nabi/data/src/main/java/com/nabi/data/datasourceImpl/DiaryRemoteDataSourceImpl.kt +++ b/Nabi/data/src/main/java/com/nabi/data/datasourceImpl/DiaryRemoteDataSourceImpl.kt @@ -3,9 +3,13 @@ package com.nabi.data.datasourceImpl import com.nabi.data.datasource.DiaryRemoteDataSource import com.nabi.data.model.BaseResponse import com.nabi.data.model.PageableResponse +import com.nabi.data.model.diary.AddDiaryRequestDTO +import com.nabi.data.model.diary.AddDiaryResponseDTO import com.nabi.data.model.diary.DiaryDetailResponseDTO import com.nabi.data.model.diary.ResponseMonthDiaryDTO import com.nabi.data.model.diary.SearchDiaryResponseDTO +import com.nabi.data.model.diary.UpdateDiaryRequestDTO +import com.nabi.data.model.diary.UpdateDiaryResponseDTO import com.nabi.data.service.DiaryService import javax.inject.Inject @@ -80,4 +84,49 @@ class DiaryRemoteDataSourceImpl @Inject constructor( Result.failure(e) } } + + override suspend fun addDiary( + accessToken: String, + body: AddDiaryRequestDTO + ): Result> { + return try { + val response = diaryService.addDiary(accessToken, body) + + if (response.isSuccessful) { + val diaryResponse = response.body() + if (diaryResponse != null) { + Result.success(diaryResponse) + } else { + Result.failure(Exception("Add Diary Fail: response body is null")) + } + } else { + Result.failure(Exception("Add Diary fail: ${response.message()}")) + } + } catch (e: Exception) { + Result.failure(e) + } + } + + override suspend fun updateDiary( + accessToken: String, + id: Int, + body: UpdateDiaryRequestDTO + ): Result> { + return try { + val response = diaryService.updateDiary(accessToken, id, body) + + if (response.isSuccessful) { + val diaryResponse = response.body() + if (diaryResponse != null) { + Result.success(diaryResponse) + } else { + Result.failure(Exception("Update Diary Fail: response body is null")) + } + } else { + Result.failure(Exception("Update Diary Fail: ${response.message()}")) + } + } catch (e: Exception) { + Result.failure(e) + } + } } \ No newline at end of file diff --git a/Nabi/data/src/main/java/com/nabi/data/mapper/HomeMapper.kt b/Nabi/data/src/main/java/com/nabi/data/mapper/HomeMapper.kt index c1eb134..902d568 100644 --- a/Nabi/data/src/main/java/com/nabi/data/mapper/HomeMapper.kt +++ b/Nabi/data/src/main/java/com/nabi/data/mapper/HomeMapper.kt @@ -15,7 +15,7 @@ object HomeMapper { RecentFiveDiary( content = diary.content, diaryEntryDate = diary.diaryEntryDate, - emotion = diary.emotion + emotion = diary.emotion ?: "" ) } ) diff --git a/Nabi/data/src/main/java/com/nabi/data/model/diary/AddDiaryRequestDTO.kt b/Nabi/data/src/main/java/com/nabi/data/model/diary/AddDiaryRequestDTO.kt new file mode 100644 index 0000000..0debbc1 --- /dev/null +++ b/Nabi/data/src/main/java/com/nabi/data/model/diary/AddDiaryRequestDTO.kt @@ -0,0 +1,6 @@ +package com.nabi.data.model.diary + +data class AddDiaryRequestDTO( + val content: String, + val diaryEntryDate: String +) diff --git a/Nabi/data/src/main/java/com/nabi/data/model/diary/AddDiaryResponseDTO.kt b/Nabi/data/src/main/java/com/nabi/data/model/diary/AddDiaryResponseDTO.kt new file mode 100644 index 0000000..9ad6b35 --- /dev/null +++ b/Nabi/data/src/main/java/com/nabi/data/model/diary/AddDiaryResponseDTO.kt @@ -0,0 +1,13 @@ +package com.nabi.data.model.diary + + +import com.google.gson.annotations.SerializedName + +data class AddDiaryResponseDTO( + @SerializedName("content") + val content: String, + @SerializedName("diaryEntryDate") + val diaryEntryDate: String, + @SerializedName("id") + val id: Int +) \ No newline at end of file diff --git a/Nabi/data/src/main/java/com/nabi/data/model/diary/UpdateDiaryRequestDTO.kt b/Nabi/data/src/main/java/com/nabi/data/model/diary/UpdateDiaryRequestDTO.kt new file mode 100644 index 0000000..2839663 --- /dev/null +++ b/Nabi/data/src/main/java/com/nabi/data/model/diary/UpdateDiaryRequestDTO.kt @@ -0,0 +1,5 @@ +package com.nabi.data.model.diary + +data class UpdateDiaryRequestDTO ( + val content: String +) \ No newline at end of file diff --git a/Nabi/data/src/main/java/com/nabi/data/model/diary/UpdateDiaryResponseDTO.kt b/Nabi/data/src/main/java/com/nabi/data/model/diary/UpdateDiaryResponseDTO.kt new file mode 100644 index 0000000..53c1d83 --- /dev/null +++ b/Nabi/data/src/main/java/com/nabi/data/model/diary/UpdateDiaryResponseDTO.kt @@ -0,0 +1,15 @@ +package com.nabi.data.model.diary + + +import com.google.gson.annotations.SerializedName + +data class UpdateDiaryResponseDTO( + @SerializedName("content") + val content: String, + @SerializedName("diaryEntryDate") + val diaryEntryDate: String, + @SerializedName("DiaryId") + val diaryId: Int, + @SerializedName("userId") + val userId: Int +) \ No newline at end of file diff --git a/Nabi/data/src/main/java/com/nabi/data/repository/DiaryRepositoryImpl.kt b/Nabi/data/src/main/java/com/nabi/data/repository/DiaryRepositoryImpl.kt index d210eea..ef0aafe 100644 --- a/Nabi/data/src/main/java/com/nabi/data/repository/DiaryRepositoryImpl.kt +++ b/Nabi/data/src/main/java/com/nabi/data/repository/DiaryRepositoryImpl.kt @@ -2,9 +2,13 @@ package com.nabi.data.repository import com.nabi.data.datasource.DiaryRemoteDataSource import com.nabi.data.mapper.DiaryMapper +import com.nabi.data.model.diary.AddDiaryRequestDTO +import com.nabi.data.model.diary.UpdateDiaryRequestDTO import com.nabi.domain.model.PageableInfo +import com.nabi.domain.model.diary.AddDiaryInfo import com.nabi.domain.model.diary.DiaryInfo import com.nabi.domain.model.diary.SearchDiary +import com.nabi.domain.model.diary.UpdateDiaryInfo import com.nabi.domain.repository.DiaryRepository import javax.inject.Inject @@ -50,13 +54,24 @@ class DiaryRepositoryImpl @Inject constructor( if (res != null) { val data = res.data if (data != null) { - val pageableInfo = PageableInfo(totalPages = data.totalPages, totalElements = data.totalElements, elementSize = data.size, currentPageNumber = data.number, isLastPage = data.last) + val pageableInfo = PageableInfo( + totalPages = data.totalPages, + totalElements = data.totalElements, + elementSize = data.size, + currentPageNumber = data.number, + isLastPage = data.last + ) - if(data.size == 0) { + if (data.size == 0) { Result.success(Pair(pageableInfo, emptyList())) - } - else { - val searchDiaryList = data.content.map { SearchDiary(it.previewContent, it.diaryEntryDate, it.diaryId) } + } else { + val searchDiaryList = data.content.map { + SearchDiary( + it.previewContent, + it.diaryEntryDate, + it.diaryId + ) + } Result.success(Pair(pageableInfo, searchDiaryList)) } } else { @@ -78,14 +93,16 @@ class DiaryRepositoryImpl @Inject constructor( if (res != null) { val data = res.data if (data != null) { - Result.success(DiaryInfo( - content = data.content, - diaryId = data.diaryId, - nickname = data.nickname, - emotion = data.emotion, - diaryEntryDate = data.diaryEntryDate, - isBookmarked = data.isBookmarked - )) + Result.success( + DiaryInfo( + content = data.content, + diaryId = data.diaryId, + nickname = data.nickname, + emotion = data.emotion, + diaryEntryDate = data.diaryEntryDate, + isBookmarked = data.isBookmarked + ) + ) } else { Result.failure(Exception("Month diary List data is null")) } @@ -96,4 +113,64 @@ class DiaryRepositoryImpl @Inject constructor( Result.failure(result.exceptionOrNull() ?: Exception("Unknown error")) } } + + override suspend fun addDiary( + accessToken: String, + content: String, + diaryEntryDate: String + ): Result { + val result = + diaryRemoteDataSource.addDiary(accessToken, AddDiaryRequestDTO(content, diaryEntryDate)) + + return if (result.isSuccess) { + val res = result.getOrNull() + if (res != null) { + val data = res.data + if (data != null) { + val addDiaryInfo = AddDiaryInfo( + content = data.content, + diaryEntryDate = data.diaryEntryDate + ) + Result.success(addDiaryInfo) + } else { + Result.failure(Exception("Add Diary Failed: data is null")) + } + } else { + Result.failure(Exception("Add Diary Failed: response body is null")) + } + } else { + Result.failure(result.exceptionOrNull() ?: Exception("Unknown error")) + } + } + + override suspend fun updateDiary( + accessToken: String, + id: Int, + content: String + ): Result { + val result = + diaryRemoteDataSource.updateDiary(accessToken, id, UpdateDiaryRequestDTO(content)) + + return if (result.isSuccess) { + val res = result.getOrNull() + if (res != null) { + val data = res.data + if (data != null) { + val updateDiaryInfo = UpdateDiaryInfo( + diaryId = data.diaryId, + content = data.content, + diaryEntryDate = data.diaryEntryDate + ) + Result.success(updateDiaryInfo) + } else { + Result.failure(Exception("Update Diary Failed: data is null")) + } + } else { + Result.failure(Exception("Update Diary Failed: response body is null")) + } + } else { + Result.failure(result.exceptionOrNull() ?: Exception("Unknown error")) + } + } + } \ No newline at end of file diff --git a/Nabi/data/src/main/java/com/nabi/data/service/DiaryService.kt b/Nabi/data/src/main/java/com/nabi/data/service/DiaryService.kt index b308809..18ade9d 100644 --- a/Nabi/data/src/main/java/com/nabi/data/service/DiaryService.kt +++ b/Nabi/data/src/main/java/com/nabi/data/service/DiaryService.kt @@ -1,13 +1,22 @@ package com.nabi.data.service import com.nabi.data.model.BaseResponse +import com.nabi.data.model.MessageResponseDTO import com.nabi.data.model.PageableResponse +import com.nabi.data.model.auth.SignInRequestDTO +import com.nabi.data.model.diary.AddDiaryRequestDTO +import com.nabi.data.model.diary.AddDiaryResponseDTO import com.nabi.data.model.diary.DiaryDetailResponseDTO import com.nabi.data.model.diary.ResponseMonthDiaryDTO import com.nabi.data.model.diary.SearchDiaryResponseDTO +import com.nabi.data.model.diary.UpdateDiaryRequestDTO +import com.nabi.data.model.diary.UpdateDiaryResponseDTO import retrofit2.Response +import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.Header +import retrofit2.http.PATCH +import retrofit2.http.POST import retrofit2.http.Path import retrofit2.http.Query @@ -34,4 +43,17 @@ interface DiaryService { @Header("Authorization") accessToken: String, @Path("diaryId") diaryId: Int ): Response> + + @POST("/diarys") + suspend fun addDiary( + @Header("Authorization") accessToken: String, + @Body body: AddDiaryRequestDTO + ): Response> + + @PATCH("/diarys/{id}") + suspend fun updateDiary( + @Header("Authorization") accessToken: String, + @Path("id") id: Int, + @Body body: UpdateDiaryRequestDTO + ): Response> } \ No newline at end of file diff --git a/Nabi/domain/src/main/java/com/nabi/domain/model/diary/AddDiaryInfo.kt b/Nabi/domain/src/main/java/com/nabi/domain/model/diary/AddDiaryInfo.kt new file mode 100644 index 0000000..1131cc6 --- /dev/null +++ b/Nabi/domain/src/main/java/com/nabi/domain/model/diary/AddDiaryInfo.kt @@ -0,0 +1,6 @@ +package com.nabi.domain.model.diary + +data class AddDiaryInfo( + val content: String, + val diaryEntryDate: String +) diff --git a/Nabi/domain/src/main/java/com/nabi/domain/model/diary/UpdateDiaryInfo.kt b/Nabi/domain/src/main/java/com/nabi/domain/model/diary/UpdateDiaryInfo.kt new file mode 100644 index 0000000..7427b82 --- /dev/null +++ b/Nabi/domain/src/main/java/com/nabi/domain/model/diary/UpdateDiaryInfo.kt @@ -0,0 +1,7 @@ +package com.nabi.domain.model.diary + +data class UpdateDiaryInfo( + val diaryId: Int, + val content: String, + val diaryEntryDate: String +) diff --git a/Nabi/domain/src/main/java/com/nabi/domain/repository/DiaryRepository.kt b/Nabi/domain/src/main/java/com/nabi/domain/repository/DiaryRepository.kt index 57d8d61..6da625e 100644 --- a/Nabi/domain/src/main/java/com/nabi/domain/repository/DiaryRepository.kt +++ b/Nabi/domain/src/main/java/com/nabi/domain/repository/DiaryRepository.kt @@ -1,8 +1,10 @@ package com.nabi.domain.repository import com.nabi.domain.model.PageableInfo +import com.nabi.domain.model.diary.AddDiaryInfo import com.nabi.domain.model.diary.DiaryInfo import com.nabi.domain.model.diary.SearchDiary +import com.nabi.domain.model.diary.UpdateDiaryInfo interface DiaryRepository { suspend fun getMonthlyDiary(accessToken: String, year: Int, month: Int): Result> @@ -16,4 +18,16 @@ interface DiaryRepository { ): Result>> suspend fun getDiaryDetail(accessToken: String, diaryId: Int): Result + + suspend fun addDiary( + accessToken: String, + content: String, + diaryEntryDate: String + ): Result + + suspend fun updateDiary( + accessToken: String, + id: Int, + content: String + ): Result } \ No newline at end of file diff --git a/Nabi/domain/src/main/java/com/nabi/domain/usecase/diary/AddDiaryUseCase.kt b/Nabi/domain/src/main/java/com/nabi/domain/usecase/diary/AddDiaryUseCase.kt new file mode 100644 index 0000000..1572682 --- /dev/null +++ b/Nabi/domain/src/main/java/com/nabi/domain/usecase/diary/AddDiaryUseCase.kt @@ -0,0 +1,14 @@ +package com.nabi.domain.usecase.diary + +import com.nabi.domain.model.diary.AddDiaryInfo +import com.nabi.domain.repository.DiaryRepository + +class AddDiaryUseCase(private val repository: DiaryRepository) { + suspend operator fun invoke( + accessToken: String, + content: String, + diaryEntryDate: String + ): Result { + return repository.addDiary("Bearer $accessToken", content, diaryEntryDate) + } +} \ No newline at end of file diff --git a/Nabi/domain/src/main/java/com/nabi/domain/usecase/diary/UpdateDiaryUseCase.kt b/Nabi/domain/src/main/java/com/nabi/domain/usecase/diary/UpdateDiaryUseCase.kt new file mode 100644 index 0000000..07e6a4e --- /dev/null +++ b/Nabi/domain/src/main/java/com/nabi/domain/usecase/diary/UpdateDiaryUseCase.kt @@ -0,0 +1,14 @@ +package com.nabi.domain.usecase.diary + +import com.nabi.domain.model.diary.UpdateDiaryInfo +import com.nabi.domain.repository.DiaryRepository + +class UpdateDiaryUseCase(private val repository: DiaryRepository) { + suspend operator fun invoke( + accessToken: String, + id: Int, + content: String + ): Result { + return repository.updateDiary("Bearer $accessToken", id, content) + } +} \ No newline at end of file diff --git a/Nabi/gradle/libs.versions.toml b/Nabi/gradle/libs.versions.toml index 941f86a..35c649d 100644 --- a/Nabi/gradle/libs.versions.toml +++ b/Nabi/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] agp = "8.3.1" +balloon = "1.4.6" datastorePreferences = "1.1.1" glide = "4.16.0" hiltAndroid = "2.48" @@ -22,6 +23,7 @@ retrofit = "2.11.0" v2User = "2.20.1" googleGmsGoogleServices = "4.4.2" firebaseMessaging = "24.0.0" +viewpager2 = "1.1.0" workRuntimeKtx = "2.9.0" [libraries] @@ -30,6 +32,8 @@ androidx-datastore-preferences = { module = "androidx.datastore:datastore-prefer androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" } androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycleViewmodelKtx" } androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" } +androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "viewpager2" } +balloon = { module = "com.github.skydoves:balloon", version.ref = "balloon" } dagger-hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hiltAndroid" } glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroidVersion" } diff --git a/Nabi/presentation/build.gradle.kts b/Nabi/presentation/build.gradle.kts index b7bb5a6..3a42f9d 100644 --- a/Nabi/presentation/build.gradle.kts +++ b/Nabi/presentation/build.gradle.kts @@ -22,14 +22,36 @@ android { versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - buildConfigField("String", "KAKAO_NATIVE_KEY", "\"${properties.getProperty("KAKAO_NATIVE_KEY")}\"") + buildConfigField( + "String", + "KAKAO_NATIVE_KEY", + "\"${properties.getProperty("KAKAO_NATIVE_KEY")}\"" + ) manifestPlaceholders["KAKAO_NATIVE_KEY"] = properties.getProperty("KAKAO_NATIVE_KEY") buildConfigField("String", "BASE_URL", "\"${properties.getProperty("BASE_URL")}\"") } + applicationVariants.all { + outputs.all { + val outputImpl = this as com.android.build.gradle.internal.api.ApkVariantOutputImpl + val newFileName = "Nabi-${name}.apk" + outputImpl.outputFileName = newFileName + } + } + + signingConfigs { + create("release") { + keyAlias = properties["SIGNED_KEY_ALIAS"] as String? + keyPassword = properties["SIGNED_KEY_PASSWORD"] as String? + storeFile = properties["SIGNED_STORE_FILE"]?.let { file(it) } + storePassword = properties["SIGNED_STORE_PASSWORD"] as String? + } + } + buildTypes { - release { + getByName("release") { + signingConfig = signingConfigs.getByName("release") isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), @@ -49,15 +71,6 @@ android { dataBinding = true buildConfig = true } - - signingConfigs { - getByName("debug") { - keyAlias = properties["SIGNED_KEY_ALIAS"] as String? - keyPassword = properties["SIGNED_KEY_PASSWORD"] as String? - storeFile = properties["SIGNED_STORE_FILE"]?.let { file(it) } - storePassword = properties["SIGNED_STORE_PASSWORD"] as String? - } - } } dependencies { @@ -107,7 +120,7 @@ dependencies { implementation(libs.androidx.work.runtime.ktx) // ViewPager2 - implementation("androidx.viewpager2:viewpager2:1.1.0") + implementation(libs.androidx.viewpager2) // Tooltip - Balloon implementation("com.github.skydoves:balloon:1.4.6") diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/di/DiaryUseCaseModule.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/di/DiaryUseCaseModule.kt index 9475013..9b7e9ce 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/di/DiaryUseCaseModule.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/di/DiaryUseCaseModule.kt @@ -1,9 +1,11 @@ package com.nabi.nabi.di import com.nabi.domain.repository.DiaryRepository +import com.nabi.domain.usecase.diary.AddDiaryUseCase import com.nabi.domain.usecase.diary.GetDiaryDetailUseCase import com.nabi.domain.usecase.diary.GetMonthlyDiaryUseCase import com.nabi.domain.usecase.diary.SearchDiaryUseCase +import com.nabi.domain.usecase.diary.UpdateDiaryUseCase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -37,4 +39,20 @@ object DiaryUseCaseModule { ): GetDiaryDetailUseCase { return GetDiaryDetailUseCase(repository = repository) } + + @Provides + @Singleton + fun provideAddDiaryUseCase( + repository: DiaryRepository + ): AddDiaryUseCase { + return AddDiaryUseCase(repository = repository) + } + + @Provides + @Singleton + fun provideUpdateDiaryUseCase( + repository: DiaryRepository + ): UpdateDiaryUseCase { + return UpdateDiaryUseCase(repository = repository) + } } \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryDayAdapter.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryDayAdapter.kt index 56ce16b..f14be8d 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryDayAdapter.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryDayAdapter.kt @@ -4,6 +4,7 @@ import android.graphics.Color import android.graphics.PorterDuff import android.view.LayoutInflater import android.view.ViewGroup +import android.widget.Toast import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.nabi.nabi.R @@ -33,15 +34,11 @@ class AddDiaryDayAdapter( override fun onBindViewHolder(holder: DayView, position: Int) { with(holder.binding) { - itemDayLayout.setOnClickListener { - val selectedDate = dayList[position] - onDateSelected(selectedDate) - } - val calendar = Calendar.getInstance() calendar.time = dayList[position] val dayMonth = calendar.get(Calendar.MONTH) + 1 - tvDay.text = calendar.get(Calendar.DAY_OF_MONTH).toString() + val dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH) + tvDay.text = dayOfMonth.toString() val today = Calendar.getInstance() if (currentMonth != dayMonth) { @@ -52,13 +49,19 @@ class AddDiaryDayAdapter( } else { tvDay.alpha = 1.0f - // 해당 일(day)이 diaryDates에 포함되어 있는지 확인 - val dateFormat = SimpleDateFormat("dd", Locale.KOREAN) - val formattedDay = dateFormat.format(calendar.time) - val isDateInDiary = diaryDates.contains(formattedDay) + // 해당 일에 이미 일기가 존재 + val isDateInDiary = diaryDates.any { dateString -> + try { + val date = SimpleDateFormat("yyyy-MM-dd", Locale.KOREAN).parse(dateString) + val diaryCalendar = Calendar.getInstance() + diaryCalendar.time = date!! + diaryCalendar.get(Calendar.DAY_OF_MONTH) == dayOfMonth + } catch (e: Exception) { + false + } + } val isSelectedDate = isSameDate(dayList[position], selectedDate) - // 선택된 날짜에 일기가 존재하면 ivDiaryCheck.alpha = if (isDateInDiary) 1.0f else 0.0f ivDiaryCheck.background.setColorFilter( if (isSelectedDate && isDateInDiary) Color.WHITE else Color.TRANSPARENT, @@ -69,7 +72,15 @@ class AddDiaryDayAdapter( ivSelectDate.alpha = if (isSelectedDate) 1.0f else 0.0f itemDayLayout.setOnClickListener { - onDateSelected(calendar.time) + if (calendar.after(today)) { + Toast.makeText( + holder.itemView.context, + "오늘 이후의 날짜는 선택할 수 없습니다.", + Toast.LENGTH_SHORT + ).show() + } else { + onDateSelected(calendar.time) + } } // 오늘 이후 날짜는 회색으로 처리 diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryFragment.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryFragment.kt new file mode 100644 index 0000000..88a351e --- /dev/null +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryFragment.kt @@ -0,0 +1,81 @@ +package com.nabi.nabi.views.diary.add + +import androidx.fragment.app.activityViewModels +import com.nabi.nabi.R +import com.nabi.nabi.base.BaseFragment +import com.nabi.nabi.databinding.FragmentAddDiaryBinding +import com.nabi.nabi.utils.LoggerUtils +import com.nabi.nabi.utils.UiState +import com.nabi.nabi.views.MainActivity +import dagger.hilt.android.AndroidEntryPoint +import java.time.LocalDate +import java.time.format.DateTimeFormatter + +@AndroidEntryPoint +class AddDiaryFragment : BaseFragment(R.layout.fragment_add_diary) { + private val viewModel: AddDiaryViewModel by activityViewModels() + override fun initView() { + viewModel.selectedDate.observe(viewLifecycleOwner) { date -> + binding.tvDiaryDate.text = date.toString() + } + } + + override fun initListener() { + super.initListener() + + binding.ibBack.setOnClickListener { + (requireActivity() as MainActivity).replaceFragment(AddDiarySelectDateFragment(), false) + } + + binding.ibMic.setOnClickListener { + // mic 버튼 눌렀을 때 로직 + } + + binding.btnAddDiary.setOnClickListener { + val content = binding.etDiary.text.toString().trim() + val selectedDate = viewModel.selectedDate.value.orEmpty() + + if (content.isNotEmpty()) { + val inputFormatter = DateTimeFormatter.ofPattern("yyyy년 M월 d일") + val outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + + val date = LocalDate.parse(selectedDate, inputFormatter) + val diaryEntryDate = date.format(outputFormatter) + LoggerUtils.d(diaryEntryDate) + viewModel.addDiary(content, diaryEntryDate) + } else { + showToast("일기 내용을 입력해주세요") + } + } + } + + override fun setObserver() { + super.setObserver() + + viewModel.addState.observe(viewLifecycleOwner) { + when (it) { + is UiState.Loading -> {} + is UiState.Failure -> { + showToast("일기 추가 실패: ${it.message}") + } + + is UiState.Success -> { + showToast("일기 추가 성공") + binding.etDiary.text.clear() + requireActivity().supportFragmentManager.popBackStack() + } + } + } + + viewModel.updateState.observe(viewLifecycleOwner) { + when (it) { + is UiState.Loading -> {} + is UiState.Failure -> { + showToast("일기 수정 실패") + } + + is UiState.Success -> {} + } + } + } +} \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiarySelectDateFragment.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiarySelectDateFragment.kt index d3e8af5..d6baf39 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiarySelectDateFragment.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiarySelectDateFragment.kt @@ -1,13 +1,14 @@ package com.nabi.nabi.views.diary.add import android.annotation.SuppressLint -import androidx.fragment.app.viewModels +import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.LinearLayoutManager import com.nabi.nabi.utils.LoggerUtils import com.nabi.nabi.R import com.nabi.nabi.base.BaseFragment import com.nabi.nabi.databinding.FragmentSelectDateBinding import com.nabi.nabi.utils.UiState +import com.nabi.nabi.views.MainActivity import dagger.hilt.android.AndroidEntryPoint import java.text.SimpleDateFormat import java.util.Calendar @@ -15,12 +16,14 @@ import java.util.Date import java.util.Locale @AndroidEntryPoint -class AddDiarySelectDateFragment : BaseFragment(R.layout.fragment_select_date), +class AddDiarySelectDateFragment : + BaseFragment(R.layout.fragment_select_date), AddDiaryMonthAdapter.OnDateSelectedListener { private lateinit var monthAdapter: AddDiaryMonthAdapter private lateinit var monthListManager: LinearLayoutManager - private val viewModel: AddDiaryViewModel by viewModels() + private val viewModel: AddDiaryViewModel by activityViewModels() private val calendar = Calendar.getInstance() + private var diaryDates: Set = emptySet() @SuppressLint("NotifyDataSetChanged") override fun initView() { @@ -31,13 +34,23 @@ class AddDiarySelectDateFragment : BaseFragment(R.lay binding.ivLeftMonth.setOnClickListener { updateMonth(-1) monthAdapter.updateCurrentMonth(-1) - } binding.ivRightMonth.setOnClickListener { updateMonth(1) monthAdapter.updateCurrentMonth(1) } + binding.btnDone.setOnClickListener { + val selectedDate = changeDateFormat(binding.tvSelectDate.text.toString()) + + if (diaryDates.contains(selectedDate)) { + showToast("이미 일기를 쓴 날이에요!") + } else { + viewModel.selectDate(selectedDate) + (requireActivity() as MainActivity).replaceFragment(AddDiaryFragment(), true) + } + } + setTodayDate() updateMonthDisplay() } @@ -70,11 +83,12 @@ class AddDiarySelectDateFragment : BaseFragment(R.lay binding.tvSelectDate.text = formattedDate } + @SuppressLint("ClickableViewAccessibility") override fun initListener() { val today = Calendar.getInstance().time monthAdapter = AddDiaryMonthAdapter(0, today) monthAdapter.setOnDateSelectedListener(this) - + monthListManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false) @@ -104,10 +118,17 @@ class AddDiarySelectDateFragment : BaseFragment(R.lay is UiState.Loading -> {} is UiState.Success -> { val dates = state.data.map { it.diaryEntryDate } + diaryDates = dates.toSet() monthAdapter.updateDiaryDates(dates) } } } + } + private fun changeDateFormat(selectedDate: String): String { + val inputFormat = SimpleDateFormat("yyyy년 M월 d일", Locale.KOREAN) + val outputFormat = SimpleDateFormat("yyyy-MM-dd", Locale.KOREAN) + val date = inputFormat.parse(selectedDate) + return outputFormat.format(date!!) } } \ No newline at end of file diff --git a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryViewModel.kt b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryViewModel.kt index 8cfc4bf..8569892 100644 --- a/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryViewModel.kt +++ b/Nabi/presentation/src/main/java/com/nabi/nabi/views/diary/add/AddDiaryViewModel.kt @@ -6,7 +6,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.nabi.domain.model.diary.DiaryInfo import com.nabi.domain.repository.DataStoreRepository +import com.nabi.domain.usecase.diary.AddDiaryUseCase import com.nabi.domain.usecase.diary.GetMonthlyDiaryUseCase +import com.nabi.domain.usecase.diary.UpdateDiaryUseCase import com.nabi.nabi.utils.UiState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -15,11 +17,27 @@ import javax.inject.Inject @HiltViewModel class AddDiaryViewModel @Inject constructor( private val getMonthlyDiaryUseCase: GetMonthlyDiaryUseCase, + private val addDiaryUseCase: AddDiaryUseCase, + private val updateDiaryUseCase: UpdateDiaryUseCase, private val dataStoreRepository: DataStoreRepository ) : ViewModel() { + + private val _selectedDate = MutableLiveData() + val selectedDate: LiveData get() = _selectedDate + private val _diaryState = MutableLiveData>>(UiState.Loading) val diaryState: LiveData>> get() = _diaryState + private val _addState = MutableLiveData>(UiState.Loading) + val addState: LiveData> get() = _addState + + private val _updateState = MutableLiveData>(UiState.Loading) + val updateState: LiveData> get() = _updateState + + fun selectDate(date: String) { + _selectedDate.value = date + } + fun checkMonthDiary(year: Int, month: Int) { _diaryState.value = UiState.Loading @@ -38,4 +56,34 @@ class AddDiaryViewModel @Inject constructor( } } } + + fun addDiary(content: String, diaryEntryDate: String) { + _addState.value = UiState.Loading + + viewModelScope.launch { + val accessToken = dataStoreRepository.getAccessToken().getOrNull().orEmpty() + + addDiaryUseCase(accessToken, content, diaryEntryDate) + .onSuccess { + _addState.value = UiState.Success(Unit) + }.onFailure { e -> + _addState.value = UiState.Failure(message = e.message.toString()) + } + } + } + + fun updateDiary(id: Int, content: String) { + _updateState.value = UiState.Loading + + viewModelScope.launch { + val accessToken = dataStoreRepository.getAccessToken().getOrNull().orEmpty() + + updateDiaryUseCase(accessToken, id, content) + .onSuccess { + _addState.value = UiState.Success(Unit) + }.onFailure { e -> + _addState.value = UiState.Failure(message = e.message.toString()) + } + } + } } \ No newline at end of file diff --git a/Nabi/presentation/src/main/res/drawable/ic_mic.xml b/Nabi/presentation/src/main/res/drawable/ic_mic.xml new file mode 100644 index 0000000..2635d44 --- /dev/null +++ b/Nabi/presentation/src/main/res/drawable/ic_mic.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/Nabi/presentation/src/main/res/layout/fragment_add_diary.xml b/Nabi/presentation/src/main/res/layout/fragment_add_diary.xml new file mode 100644 index 0000000..695fffa --- /dev/null +++ b/Nabi/presentation/src/main/res/layout/fragment_add_diary.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Nabi/presentation/src/main/res/layout/item_day.xml b/Nabi/presentation/src/main/res/layout/item_day.xml index 19ddf85..1455c49 100644 --- a/Nabi/presentation/src/main/res/layout/item_day.xml +++ b/Nabi/presentation/src/main/res/layout/item_day.xml @@ -1,6 +1,7 @@ + app:layout_constraintTop_toTopOf="parent" + tools:ignore="ContentDescription" /> + app:layout_constraintStart_toStartOf="parent" + tools:ignore="ContentDescription" /> + app:layout_constraintTop_toTopOf="parent" + tools:text="1" /> \ No newline at end of file diff --git a/Nabi/presentation/src/main/res/values/strings.xml b/Nabi/presentation/src/main/res/values/strings.xml index 8221ec3..609719a 100644 --- a/Nabi/presentation/src/main/res/values/strings.xml +++ b/Nabi/presentation/src/main/res/values/strings.xml @@ -22,4 +22,8 @@ 내 감정 통계 보러가기 일기 보기 + + 오늘은 어떤 일이 있었나요? + 일기 작성 + \ No newline at end of file