From 28f7b3cb70db6fba57e65315f91f94965a049274 Mon Sep 17 00:00:00 2001 From: easyhz Date: Wed, 21 Aug 2024 00:18:14 +0900 Subject: [PATCH 1/4] =?UTF-8?q?chore:=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20#111?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- domain/my-page/build.gradle.kts | 4 +++- feature/announcement/build.gradle.kts | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/domain/my-page/build.gradle.kts b/domain/my-page/build.gradle.kts index b0076c0a..366de3b5 100644 --- a/domain/my-page/build.gradle.kts +++ b/domain/my-page/build.gradle.kts @@ -9,5 +9,7 @@ android { } dependencies { - + api(projects.core.model) + implementation(projects.core.common) + implementation(projects.data.member) } \ No newline at end of file diff --git a/feature/announcement/build.gradle.kts b/feature/announcement/build.gradle.kts index 67e36624..d91c60c3 100644 --- a/feature/announcement/build.gradle.kts +++ b/feature/announcement/build.gradle.kts @@ -12,6 +12,7 @@ dependencies { implementation(projects.core.designSystem) implementation(projects.core.common) implementation(projects.domain.announcement) + implementation(projects.domain.myPage) implementation(projects.domain.organization) // paging From 32d08ce935a335218882a1855ff5681dda30516e Mon Sep 17 00:00:00 2001 From: easyhz Date: Wed, 21 Aug 2024 00:18:29 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20dateFormat=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../noffice/core/common/util/DateFormat.kt | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/core/common/src/main/java/com/easyhz/noffice/core/common/util/DateFormat.kt b/core/common/src/main/java/com/easyhz/noffice/core/common/util/DateFormat.kt index aa89751b..7ef70a63 100644 --- a/core/common/src/main/java/com/easyhz/noffice/core/common/util/DateFormat.kt +++ b/core/common/src/main/java/com/easyhz/noffice/core/common/util/DateFormat.kt @@ -3,7 +3,6 @@ package com.easyhz.noffice.core.common.util import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime -import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.format.DateTimeParseException import java.time.format.TextStyle @@ -19,8 +18,9 @@ object DateFormat { CUSTOM_REMIND("MM월 dd일 E a h:mm"), DAY("MM/dd"), TIME("HH:mm"), - SERVER("yyyy-MM-dd'T'HH:mm:ss") + REQUEST("yyyy-MM-dd'T'HH:mm:ss") } + fun fullText(date: LocalDate): String = DateTimeFormatter.ofPattern(PATTERN.DATE_TEXT.value).format(date) @@ -51,10 +51,9 @@ object DateFormat { } fun formatDateTime(date: String, pattern: PATTERN = PATTERN.FULL): String { - val dateTime = ZonedDateTime.parse(date) + val dateTime = LocalDateTime.parse(date) val formatter = DateTimeFormatter.ofPattern(pattern.value) - .withLocale(Locale.KOREAN) val formattedDate = dateTime.format(formatter) return formattedDate @@ -62,10 +61,9 @@ object DateFormat { fun formatDateTimeNullable(date: String?, pattern: PATTERN = PATTERN.FULL): String? { if (date.isNullOrBlank()) return null - val dateTime = ZonedDateTime.parse(date) + val dateTime = LocalDateTime.parse(date) val formatter = DateTimeFormatter.ofPattern(pattern.value) - .withLocale(Locale.KOREAN) val formattedDate = dateTime.format(formatter) return formattedDate @@ -83,9 +81,12 @@ object DateFormat { val dayOfWeek = currentDate.dayOfWeek return dayOfWeek.getDisplayName(TextStyle.FULL, Locale.getDefault()) } - fun localDateTimeToString(dateTime: LocalDateTime, pattern: PATTERN = PATTERN.FULL): String { - val formatter = DateTimeFormatter.ofPattern(pattern.value) - return dateTime.format(formatter) + + fun localDateTimeToString(dateTime: String, pattern: PATTERN = PATTERN.CUSTOM_REMIND): String { + val parsDate = LocalDateTime.parse(dateTime, DateTimeFormatter.ISO_LOCAL_DATE_TIME) + val outputFormatter = DateTimeFormatter.ofPattern(pattern.value, Locale.KOREAN) + val formattedDate = parsDate.format(outputFormatter) + return formattedDate } fun dateTimeToRequestStringNullable(date: LocalDate?, time: LocalTime?): String? { @@ -94,4 +95,12 @@ object DateFormat { val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME return dateTime.format(formatter) } + + fun localDateTimeToRequest(dateTime: LocalDateTime): String { + val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME + return dateTime.format(formatter) + } + + fun parseLocalDateTime(input: String): LocalDateTime? = + LocalDateTime.parse(input, DateTimeFormatter.ISO_LOCAL_DATE_TIME) } \ No newline at end of file From 1be049fc91e186b005c7d71a58ae64431d3e4483 Mon Sep 17 00:00:00 2001 From: easyhz Date: Wed, 21 Aug 2024 00:18:59 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20resourceUri=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20#111?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/image/ImageRepository.kt | 2 ++ .../repository/image/ImageRepositoryImpl.kt | 28 ++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/data/organization/src/main/java/com/easyhz/noffice/data/organization/repository/image/ImageRepository.kt b/data/organization/src/main/java/com/easyhz/noffice/data/organization/repository/image/ImageRepository.kt index 02e4f22e..81b989f6 100644 --- a/data/organization/src/main/java/com/easyhz/noffice/data/organization/repository/image/ImageRepository.kt +++ b/data/organization/src/main/java/com/easyhz/noffice/data/organization/repository/image/ImageRepository.kt @@ -1,5 +1,6 @@ package com.easyhz.noffice.data.organization.repository.image +import android.content.Context import android.net.Uri import com.easyhz.noffice.core.model.image.ImagePurpose import com.easyhz.noffice.core.model.image.ImageUrl @@ -10,4 +11,5 @@ interface ImageRepository { suspend fun getMimeType(uri: Uri): Result suspend fun uploadImage(url: String, fileType: String, uri: Uri): Result suspend fun completeImageUpload(fileName: String): Result + suspend fun getDrawableUri(drawableId: Int): Result } \ No newline at end of file diff --git a/data/organization/src/main/java/com/easyhz/noffice/data/organization/repository/image/ImageRepositoryImpl.kt b/data/organization/src/main/java/com/easyhz/noffice/data/organization/repository/image/ImageRepositoryImpl.kt index 2723bf96..4fa62c13 100644 --- a/data/organization/src/main/java/com/easyhz/noffice/data/organization/repository/image/ImageRepositoryImpl.kt +++ b/data/organization/src/main/java/com/easyhz/noffice/data/organization/repository/image/ImageRepositoryImpl.kt @@ -1,5 +1,6 @@ package com.easyhz.noffice.data.organization.repository.image +import android.content.ContentResolver import android.content.Context import android.net.Uri import com.easyhz.noffice.core.common.di.Dispatcher @@ -41,11 +42,30 @@ class ImageRepositoryImpl @Inject constructor( return NofficeFileProvider.getMimeType(context, uri) } - override suspend fun uploadImage(url: String, fileType: String, uri: Uri): Result = withContext(dispatcher) { - return@withContext imageUploader.uploadImage(context, url, fileType, uri) + override suspend fun uploadImage(url: String, fileType: String, uri: Uri): Result = + withContext(dispatcher) { + return@withContext imageUploader.uploadImage(context, url, fileType, uri) + } + + override suspend fun completeImageUpload(fileName: String): Result = + withContext(dispatcher) { + return@withContext imageService.completeImageUpload(ImageRequest(fileName)) + } + + override suspend fun getDrawableUri(drawableId: Int): Result { + return kotlin.runCatching { + drawableId.getResourceUri(context) + } } - override suspend fun completeImageUpload(fileName: String): Result = withContext(dispatcher) { - return@withContext imageService.completeImageUpload(ImageRequest(fileName)) + private fun Int.getResourceUri(context: Context): Uri { + return context.resources.let { + Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(it.getResourcePackageName(this)) + .appendPath(it.getResourceTypeName(this)) + .appendPath(it.getResourceEntryName(this)) + .build() + } } } \ No newline at end of file From d2abd62762891a569c9d11b2dd41672ca10a2ff7 Mon Sep 17 00:00:00 2001 From: easyhz Date: Wed, 21 Aug 2024 00:23:12 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20announcement=20creation=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80=20#111?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 네비게이션 처리 - 업로드 완료 화면 처리 --- .../java/com/easyhz/noffice/NofficeApp.kt | 1 + .../announcement/AnnouncementNavigation.kt | 70 +++++- .../screen/AnnouncementCreation.kt | 4 +- .../screen/AnnouncementSuccess.kt | 9 + .../src/main/res/drawable/ic_success_bell.xml | 163 +++++++++++++ .../src/main/res/values/strings.xml | 5 + .../announcement/param/AnnouncementParam.kt | 2 +- .../announcement/AnnouncementRequest.kt | 2 +- .../my_page/usecase/GetMemberIdUseCase.kt | 14 ++ .../usecase/image/GetDrawableUriUseCase.kt | 14 ++ .../usecase/image/UploadImageUseCase.kt | 3 +- .../contract/creation/CreationIntent.kt | 1 + .../contract/creation/CreationState.kt | 4 + .../creation/promotion/PromotionSideEffect.kt | 3 + .../creation/promotion/PromotionState.kt | 6 +- .../contract/creation/remind/RemindState.kt | 3 +- .../creation/selection/SelectionSideEffect.kt | 2 +- .../screen/creation/ContentScreen.kt | 4 + .../screen/creation/CreationViewModel.kt | 38 +++- .../creation/promotion/PromotionScreen.kt | 214 ++++++++++-------- .../creation/promotion/PromotionViewModel.kt | 57 ++++- .../creation/remind/CustomRemindViewModel.kt | 5 +- .../selection/NofficeSelectionScreen.kt | 126 ++++++----- .../creation/selection/SelectionViewModel.kt | 2 +- .../screen/success/SuccessScreen.kt | 115 ++++++++++ .../announcement/util/creation/RemindUtil.kt | 27 +++ 26 files changed, 712 insertions(+), 182 deletions(-) create mode 100644 app/src/main/java/com/easyhz/noffice/navigation/announcement/screen/AnnouncementSuccess.kt create mode 100644 core/design-system/src/main/res/drawable/ic_success_bell.xml create mode 100644 domain/my-page/src/main/java/com/easyhz/noffice/domain/my_page/usecase/GetMemberIdUseCase.kt create mode 100644 domain/organization/src/main/java/com/easyhz/noffice/domain/organization/usecase/image/GetDrawableUriUseCase.kt create mode 100644 feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/success/SuccessScreen.kt create mode 100644 feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/util/creation/RemindUtil.kt diff --git a/app/src/main/java/com/easyhz/noffice/NofficeApp.kt b/app/src/main/java/com/easyhz/noffice/NofficeApp.kt index 68ed7c16..58a6a695 100644 --- a/app/src/main/java/com/easyhz/noffice/NofficeApp.kt +++ b/app/src/main/java/com/easyhz/noffice/NofficeApp.kt @@ -138,6 +138,7 @@ internal fun NofficeApp( navController = navController ) announcementGraph( + snackBarHostState = snackBarHostState, navController = navController, ) myPageGraph( diff --git a/app/src/main/java/com/easyhz/noffice/navigation/announcement/AnnouncementNavigation.kt b/app/src/main/java/com/easyhz/noffice/navigation/announcement/AnnouncementNavigation.kt index 302a7768..3db669ce 100644 --- a/app/src/main/java/com/easyhz/noffice/navigation/announcement/AnnouncementNavigation.kt +++ b/app/src/main/java/com/easyhz/noffice/navigation/announcement/AnnouncementNavigation.kt @@ -1,9 +1,12 @@ package com.easyhz.noffice.navigation.announcement +import androidx.compose.material3.SnackbarHostState import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions import androidx.navigation.compose.composable import androidx.navigation.compose.navigation +import androidx.navigation.navOptions import androidx.navigation.toRoute import com.easyhz.noffice.core.model.announcement.param.AnnouncementParam import com.easyhz.noffice.feature.announcement.screen.creation.ContentScreen @@ -17,13 +20,18 @@ import com.easyhz.noffice.feature.announcement.screen.creation.remind.RemindView import com.easyhz.noffice.feature.announcement.screen.creation.selection.NofficeSelectionScreen import com.easyhz.noffice.feature.announcement.screen.creation.task.TaskScreen import com.easyhz.noffice.feature.announcement.screen.detail.AnnouncementDetailScreen +import com.easyhz.noffice.feature.announcement.screen.success.SuccessScreen import com.easyhz.noffice.navigation.announcement.screen.AnnouncementCreation import com.easyhz.noffice.navigation.announcement.screen.AnnouncementCreation.Promotion.Companion.decode import com.easyhz.noffice.navigation.announcement.screen.AnnouncementCreation.Promotion.Companion.encode import com.easyhz.noffice.navigation.announcement.screen.AnnouncementDetail +import com.easyhz.noffice.navigation.announcement.screen.AnnouncementSuccess +import com.easyhz.noffice.navigation.home.navigateToHome +import com.easyhz.noffice.navigation.home.screen.Home import com.easyhz.noffice.navigation.util.sharedViewModel internal fun NavGraphBuilder.announcementGraph( + snackBarHostState: SnackbarHostState, navController: NavController, ) { composable { @@ -46,8 +54,10 @@ internal fun NavGraphBuilder.announcementGraph( composable { val viewModel = it.sharedViewModel(navController = navController) + val args = it.toRoute() ContentScreen( viewModel = viewModel, + organizationId = args.organizationId, navigateToUp = navController::navigateUp, navigateToDateTime = navController::navigateToDateTime, navigateToPlace = navController::navigateToPlace, @@ -93,7 +103,8 @@ internal fun NavGraphBuilder.announcementGraph( composable { val viewModel = it.sharedViewModel(navController = navController) - val creationViewModel = it.sharedViewModel(navController = navController) + val creationViewModel = + it.sharedViewModel(navController = navController) val args = it.toRoute() RemindScreen( viewModel = viewModel, @@ -115,19 +126,48 @@ internal fun NavGraphBuilder.announcementGraph( composable( typeMap = AnnouncementCreation.Promotion.typeMap ) { + val navOptions = navOptions { + popUpTo(Home) { + inclusive = false + } + } val args = it.toRoute() PromotionScreen( - param = args.announcementParam.decode() + param = args.announcementParam.decode(), + snackBarHostState = snackBarHostState, + navigateToUp = navController::navigateUp, + navigateToSuccess = { id, title -> + navController.navigateToSuccess( + id, + title, + navOptions + ) + } ) } } + composable { + val args = it.toRoute() + val navOptions = navOptions { + popUpTo(Home) { + inclusive = false + } + } + SuccessScreen( + id = args.announcementId, + title = args.title, + navigateToHome = { navController.navigateToHome(navOptions) }, + navigateToAnnouncementDetail = { id, title -> navController.navigateToAnnouncementDetail(id, title, navOptions)} + ) + } } internal fun NavController.navigateToAnnouncementDetail( id: Int, - title: String + title: String, + navOptions: NavOptions? = null ) { - navigate(route = AnnouncementDetail(id, title)) + navigate(route = AnnouncementDetail(id, title), navOptions) } internal fun NavController.navigateToAnnouncementNofficeSelection() { @@ -136,9 +176,9 @@ internal fun NavController.navigateToAnnouncementNofficeSelection() { ) } -internal fun NavController.navigateToAnnouncementCreationContent() { +internal fun NavController.navigateToAnnouncementCreationContent(organizationId: Int) { navigate( - route = AnnouncementCreation.Content + route = AnnouncementCreation.Content(organizationId) ) } @@ -168,9 +208,15 @@ internal fun NavController.navigateToTask(taskList: List?) { ) } -internal fun NavController.navigateToRemind(remindList: List?, isSelectedDateTime: Boolean) { +internal fun NavController.navigateToRemind( + remindList: List?, + isSelectedDateTime: Boolean +) { navigate( - route = AnnouncementCreation.Remind(remindList = remindList, isSelectedDateTime = isSelectedDateTime) + route = AnnouncementCreation.Remind( + remindList = remindList, + isSelectedDateTime = isSelectedDateTime + ) ) } @@ -182,4 +228,12 @@ internal fun NavController.navigateToCustomRemind() { internal fun NavController.navigateToPromotion(param: AnnouncementParam) { navigate(route = AnnouncementCreation.Promotion(param.encode())) +} + +internal fun NavController.navigateToSuccess( + id: Int, + title: String, + navOptions: NavOptions? = null +) { + navigate(route = AnnouncementSuccess(id, title), navOptions) } \ No newline at end of file diff --git a/app/src/main/java/com/easyhz/noffice/navigation/announcement/screen/AnnouncementCreation.kt b/app/src/main/java/com/easyhz/noffice/navigation/announcement/screen/AnnouncementCreation.kt index 52482464..735c6c8e 100644 --- a/app/src/main/java/com/easyhz/noffice/navigation/announcement/screen/AnnouncementCreation.kt +++ b/app/src/main/java/com/easyhz/noffice/navigation/announcement/screen/AnnouncementCreation.kt @@ -17,7 +17,9 @@ internal object AnnouncementCreation: Parcelable { data object NofficeSelection @Serializable - data object Content + data class Content( + val organizationId: Int + ) @Serializable data class DateTime( diff --git a/app/src/main/java/com/easyhz/noffice/navigation/announcement/screen/AnnouncementSuccess.kt b/app/src/main/java/com/easyhz/noffice/navigation/announcement/screen/AnnouncementSuccess.kt new file mode 100644 index 00000000..dc8461fe --- /dev/null +++ b/app/src/main/java/com/easyhz/noffice/navigation/announcement/screen/AnnouncementSuccess.kt @@ -0,0 +1,9 @@ +package com.easyhz.noffice.navigation.announcement.screen + +import kotlinx.serialization.Serializable + +@Serializable +data class AnnouncementSuccess( + val announcementId: Int, + val title: String +) diff --git a/core/design-system/src/main/res/drawable/ic_success_bell.xml b/core/design-system/src/main/res/drawable/ic_success_bell.xml new file mode 100644 index 00000000..61f5cff6 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_success_bell.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 4491a05a..28b1722b 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -133,6 +133,11 @@ 프로모션 참여하러 가기 선택 완료 + 노티 홈으로 + 확인하러 가기 + 노티가 등록되었어요 + 등록된 노티를 확인하러 갈까요? + 삭제 이벤트 일시 diff --git a/core/model/src/main/java/com/easyhz/noffice/core/model/announcement/param/AnnouncementParam.kt b/core/model/src/main/java/com/easyhz/noffice/core/model/announcement/param/AnnouncementParam.kt index c119c986..b7740fe1 100644 --- a/core/model/src/main/java/com/easyhz/noffice/core/model/announcement/param/AnnouncementParam.kt +++ b/core/model/src/main/java/com/easyhz/noffice/core/model/announcement/param/AnnouncementParam.kt @@ -10,7 +10,7 @@ data class AnnouncementParam( val isFaceToFace: Boolean?, val memberId: Int, val noticeBefore: List?, - val noticeDate: String?, + val noticeDate: List?, val organizationId: Int, val placeLinkName: String?, val placeLinkUrl: String?, diff --git a/core/network/src/main/java/com/easyhz/noffice/core/network/model/request/announcement/AnnouncementRequest.kt b/core/network/src/main/java/com/easyhz/noffice/core/network/model/request/announcement/AnnouncementRequest.kt index d55caa99..6a131062 100644 --- a/core/network/src/main/java/com/easyhz/noffice/core/network/model/request/announcement/AnnouncementRequest.kt +++ b/core/network/src/main/java/com/easyhz/noffice/core/network/model/request/announcement/AnnouncementRequest.kt @@ -6,7 +6,7 @@ data class AnnouncementRequest( val isFaceToFace: Boolean?, val memberId: Int, val noticeBefore: List?, - val noticeDate: String?, + val noticeDate: List?, val organizationId: Int, val placeLinkName: String?, val placeLinkUrl: String?, diff --git a/domain/my-page/src/main/java/com/easyhz/noffice/domain/my_page/usecase/GetMemberIdUseCase.kt b/domain/my-page/src/main/java/com/easyhz/noffice/domain/my_page/usecase/GetMemberIdUseCase.kt new file mode 100644 index 00000000..9986e6a8 --- /dev/null +++ b/domain/my-page/src/main/java/com/easyhz/noffice/domain/my_page/usecase/GetMemberIdUseCase.kt @@ -0,0 +1,14 @@ +package com.easyhz.noffice.domain.my_page.usecase + +import com.easyhz.noffice.core.common.base.BaseUseCase +import com.easyhz.noffice.data.member.repository.user.UserRepository +import javax.inject.Inject + +class GetMemberIdUseCase @Inject constructor( + private val userRepository: UserRepository +): BaseUseCase() { + + override suspend fun invoke(param: Unit): Result { + return userRepository.getMemberId() + } +} \ No newline at end of file diff --git a/domain/organization/src/main/java/com/easyhz/noffice/domain/organization/usecase/image/GetDrawableUriUseCase.kt b/domain/organization/src/main/java/com/easyhz/noffice/domain/organization/usecase/image/GetDrawableUriUseCase.kt new file mode 100644 index 00000000..515798b9 --- /dev/null +++ b/domain/organization/src/main/java/com/easyhz/noffice/domain/organization/usecase/image/GetDrawableUriUseCase.kt @@ -0,0 +1,14 @@ +package com.easyhz.noffice.domain.organization.usecase.image + +import android.net.Uri +import com.easyhz.noffice.core.common.base.BaseUseCase +import com.easyhz.noffice.data.organization.repository.image.ImageRepository +import javax.inject.Inject + +class GetDrawableUriUseCase @Inject constructor( + private val imageRepository: ImageRepository +): BaseUseCase() { + override suspend fun invoke(param: Int): Result { + return imageRepository.getDrawableUri(param) + } +} \ No newline at end of file diff --git a/domain/organization/src/main/java/com/easyhz/noffice/domain/organization/usecase/image/UploadImageUseCase.kt b/domain/organization/src/main/java/com/easyhz/noffice/domain/organization/usecase/image/UploadImageUseCase.kt index ca1b99a2..75df10a4 100644 --- a/domain/organization/src/main/java/com/easyhz/noffice/domain/organization/usecase/image/UploadImageUseCase.kt +++ b/domain/organization/src/main/java/com/easyhz/noffice/domain/organization/usecase/image/UploadImageUseCase.kt @@ -18,7 +18,8 @@ class UploadImageUseCase @Inject constructor( ) : BaseUseCase() { override suspend fun invoke(param: ImageParam): Result = withContext(dispatcher) { runCatching { - val fileType = getFileType(param.uri) + val fileType = if(param.purpose == ImagePurpose.ANNOUNCEMENT_PROFILE) "png" + else getFileType(param.uri) val fileName = param.uri.path + param.uri.port val imageUrl = fetchImageUrl(fileType, fileName, param.purpose) uploadImage(url = imageUrl.url, type = fileType, uri = param.uri) diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/CreationIntent.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/CreationIntent.kt index e3d58528..3d449334 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/CreationIntent.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/CreationIntent.kt @@ -7,6 +7,7 @@ import com.easyhz.noffice.core.common.base.UiIntent import com.easyhz.noffice.feature.announcement.util.creation.OptionData sealed class CreationIntent: UiIntent() { + data class InitScreen(val organizationId: Int) :CreationIntent() data object ClickBackButton: CreationIntent() data object ClickNextButton: CreationIntent() data class ChangeTitleTextValue(val newText: String): CreationIntent() diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/CreationState.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/CreationState.kt index f37a624e..7ddabf90 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/CreationState.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/CreationState.kt @@ -9,6 +9,8 @@ import com.easyhz.noffice.core.design_system.R import com.easyhz.noffice.feature.announcement.util.creation.OptionData data class CreationState( + val organizationId: Int, + val memberId: Int, val title: String, val content: TextFieldValue, val taskList: List, @@ -22,6 +24,8 @@ data class CreationState( ) : UiState() { companion object { fun init() = CreationState( + organizationId = -1, + memberId = -1, title = "", content = TextFieldValue(""), taskList = emptyList(), diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/promotion/PromotionSideEffect.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/promotion/PromotionSideEffect.kt index a8904b88..b12649db 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/promotion/PromotionSideEffect.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/promotion/PromotionSideEffect.kt @@ -1,9 +1,12 @@ package com.easyhz.noffice.feature.announcement.contract.creation.promotion +import androidx.annotation.StringRes import com.easyhz.noffice.core.common.base.UiSideEffect sealed class PromotionSideEffect: UiSideEffect() { data object NavigateToUp: PromotionSideEffect() data object HidePromotionBottomSheet: PromotionSideEffect() data class ScrollToItem(val index: Int): PromotionSideEffect() + data class ShowSnackBar(@StringRes val stringId: Int): PromotionSideEffect() + data class NavigateToSuccess(val id: Int, val title: String): PromotionSideEffect() } \ No newline at end of file diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/promotion/PromotionState.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/promotion/PromotionState.kt index 02e38a57..40a05b2d 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/promotion/PromotionState.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/promotion/PromotionState.kt @@ -10,7 +10,8 @@ data class PromotionState( val selectCard: CardImage, val bottomSheetSelectCard: CardImage, val isShowPromotionBottomSheet: Boolean, - val hasPromotion: Boolean + val hasPromotion: Boolean, + val isLoading: Boolean ): UiState() { companion object { fun init() = PromotionState( @@ -18,7 +19,8 @@ data class PromotionState( selectCard = CardImage.CARD1, bottomSheetSelectCard = CardImage.CARD1, isShowPromotionBottomSheet = false, - hasPromotion = true + hasPromotion = false, + isLoading = false ) } } diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/remind/RemindState.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/remind/RemindState.kt index 177a38d7..5ac1dec8 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/remind/RemindState.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/remind/RemindState.kt @@ -1,6 +1,7 @@ package com.easyhz.noffice.feature.announcement.contract.creation.remind import com.easyhz.noffice.core.common.base.UiState +import com.easyhz.noffice.core.common.util.DateFormat.localDateTimeToString data class RemindState( val remindMap: LinkedHashMap, @@ -41,7 +42,7 @@ internal val timeList = listOf( ) internal fun secondsToString(s: String): String { - val seconds = s.toIntOrNull() ?: return s + val seconds = s.toIntOrNull() ?: return localDateTimeToString(s) val weeks = seconds / (60 * 60 * 24 * 7) val days = (seconds % (60 * 60 * 24 * 7)) / (60 * 60 * 24) diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/selection/SelectionSideEffect.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/selection/SelectionSideEffect.kt index f1632801..0081a32b 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/selection/SelectionSideEffect.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/creation/selection/SelectionSideEffect.kt @@ -4,5 +4,5 @@ import com.easyhz.noffice.core.common.base.UiSideEffect sealed class SelectionSideEffect : UiSideEffect() { data object NavigateToUp: SelectionSideEffect() - data object NavigateToNext: SelectionSideEffect() + data class NavigateToNext(val id: Int): SelectionSideEffect() } \ No newline at end of file diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/ContentScreen.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/ContentScreen.kt index e78af2d9..c8129ea7 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/ContentScreen.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/ContentScreen.kt @@ -60,6 +60,7 @@ import kotlinx.coroutines.launch fun ContentScreen( modifier: Modifier = Modifier, viewModel: CreationViewModel = hiltViewModel(), + organizationId: Int, navigateToUp: () -> Unit, navigateToDateTime: (String?, String?) -> Unit, navigateToPlace: (String?, String?, String?) -> Unit, @@ -77,6 +78,9 @@ fun ContentScreen( val paddingHeight = remember(density) { with(density) { 32.dp.toPx().toInt() } } + LaunchedEffect(key1 = Unit) { + viewModel.postIntent(CreationIntent.InitScreen(organizationId)) + } LaunchedEffect(key1 = uiState.absoluteCursorY, key2 = uiState.isFocused) { val targetScroll = uiState.absoluteCursorY - halfHeight - paddingHeight diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/CreationViewModel.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/CreationViewModel.kt index 0ff2db04..576f4b64 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/CreationViewModel.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/CreationViewModel.kt @@ -4,10 +4,12 @@ import android.view.View import androidx.compose.ui.geometry.Offset import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.text.input.TextFieldValue +import androidx.lifecycle.viewModelScope import com.easyhz.noffice.core.common.base.BaseViewModel import com.easyhz.noffice.core.common.util.DateFormat import com.easyhz.noffice.core.model.announcement.param.AnnouncementParam import com.easyhz.noffice.core.model.task.Task +import com.easyhz.noffice.domain.my_page.usecase.GetMemberIdUseCase import com.easyhz.noffice.feature.announcement.contract.creation.CreationIntent import com.easyhz.noffice.feature.announcement.contract.creation.CreationSideEffect import com.easyhz.noffice.feature.announcement.contract.creation.CreationState @@ -16,17 +18,20 @@ import com.easyhz.noffice.feature.announcement.contract.creation.datetime.Select import com.easyhz.noffice.feature.announcement.contract.creation.place.ContactState import com.easyhz.noffice.feature.announcement.contract.creation.place.ContactType import com.easyhz.noffice.feature.announcement.util.creation.OptionData +import com.easyhz.noffice.feature.announcement.util.creation.calculateRemind import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class CreationViewModel @Inject constructor( - + private val getMemberIdUseCase: GetMemberIdUseCase ) : BaseViewModel( initialState = CreationState.init() ) { override fun handleIntent(intent: CreationIntent) { when(intent) { + is CreationIntent.InitScreen -> { initScreen(intent.organizationId) } is CreationIntent.ClickBackButton -> { onClickBackButton() } is CreationIntent.ClickNextButton -> { onClickNextButton() } is CreationIntent.ChangeTitleTextValue -> { onChangeTitleTextValue(intent.newText) } @@ -39,21 +44,42 @@ class CreationViewModel @Inject constructor( } } + init { + getMemberId() + } + + private fun getMemberId() = viewModelScope.launch { + getMemberIdUseCase.invoke(Unit).onSuccess { + reduce { copy(memberId = it) } + }.onFailure { + onClickBackButton() + } + } + + private fun initScreen(organizationId: Int) { + reduce { copy(organizationId = organizationId) } + } + private fun onClickBackButton() { postSideEffect { CreationSideEffect.NavigateToUp } } private fun onClickNextButton() { val dateTimeState = currentState.getOptionValue(Options.DATE_TIME) + val endAt = DateFormat.dateTimeToRequestStringNullable(dateTimeState?.date, dateTimeState?.time) val contactState = currentState.getOptionValue(Options.PLACE) val taskListState = currentState.getOptionValue>(Options.TASK) val remindListState = currentState.getOptionValue>(Options.REMIND) + val (timeList, noticeBefore, noticeDate) = remindListState?.let { + calculateRemind(it, endAt) + } ?: Triple(null, null, null) + val param = AnnouncementParam( - organizationId = 2, // FIXME + organizationId = currentState.organizationId, title = currentState.title, content = currentState.content.text, - memberId = 2, // FIXME - endAt = DateFormat.dateTimeToRequestStringNullable(dateTimeState?.date, dateTimeState?.time), + memberId = currentState.memberId, + endAt = endAt, isFaceToFace = contactState?.contactType == ContactType.CONTACT, placeLinkName = contactState?.title, placeLinkUrl = contactState?.url, @@ -64,8 +90,8 @@ class CreationViewModel @Inject constructor( isDone = true ) }, - noticeDate = null, // FIXME - noticeBefore = null // FIXME + noticeDate = noticeDate, + noticeBefore = noticeBefore ) postSideEffect { CreationSideEffect.NavigateToNext(param) } } diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/promotion/PromotionScreen.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/promotion/PromotionScreen.kt index 8d9a67a3..817d6c27 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/promotion/PromotionScreen.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/promotion/PromotionScreen.kt @@ -19,6 +19,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -27,6 +28,7 @@ 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.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -35,6 +37,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.easyhz.noffice.core.common.util.collectInSideEffectWithLifecycle import com.easyhz.noffice.core.design_system.R import com.easyhz.noffice.core.design_system.component.button.MediumButton +import com.easyhz.noffice.core.design_system.component.loading.LoadingScreenProvider import com.easyhz.noffice.core.design_system.component.scaffold.NofficeBasicScaffold import com.easyhz.noffice.core.design_system.component.topBar.DetailTopBar import com.easyhz.noffice.core.design_system.extension.noRippleClickable @@ -57,122 +60,136 @@ fun PromotionScreen( modifier: Modifier = Modifier, viewModel: PromotionViewModel = hiltViewModel(), param: AnnouncementParam, + snackBarHostState: SnackbarHostState, + navigateToUp: () -> Unit, + navigateToSuccess: (Int, String) -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val scrollState = rememberLazyListState() val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val context = LocalContext.current LaunchedEffect(key1 = Unit) { viewModel.postIntent(PromotionIntent.InitScreen(param)) } - NofficeBasicScaffold( - modifier = Modifier.noRippleClickable { }, - bottomBar = { - MediumButton( - modifier = Modifier - .screenHorizonPadding() - .fillMaxWidth() - .padding(bottom = 16.dp), - text = stringResource(id = R.string.announcement_creation_option_promotion_button), - enabled = true - ) { - viewModel.postIntent(PromotionIntent.ClickBackButton) - } - }, - topBar = { - DetailTopBar( - leadingItem = DetailTopBarMenu( - content = { - Icon( - modifier = Modifier.size(24.dp), - painter = painterResource(id = R.drawable.ic_chevron_left), - contentDescription = "left", - tint = Grey400 - ) - }, - onClick = { } - ), - title = stringResource(id = R.string.announcement_creation_title), - ) - } - ) { paddingValues -> - Column( - modifier = modifier - .fillMaxSize() - .padding(paddingValues) - ) { - CreationTitle( - modifier = Modifier.screenHorizonPadding(), - title = stringResource(id = R.string.announcement_creation_option_promotion_title) - ) - Box( - modifier = Modifier.fillMaxWidth(), + LoadingScreenProvider( + isLoading = uiState.isLoading + ) { + NofficeBasicScaffold( + modifier = Modifier.noRippleClickable { }, + topBar = { + DetailTopBar( + leadingItem = DetailTopBarMenu( + content = { + Icon( + modifier = Modifier.size(24.dp), + painter = painterResource(id = R.drawable.ic_chevron_left), + contentDescription = "left", + tint = Grey400 + ) + }, + onClick = { viewModel.postIntent(PromotionIntent.ClickBackButton) } + ), + title = stringResource(id = R.string.announcement_creation_title), + ) + }, + bottomBar = { + MediumButton( + modifier = Modifier + .screenHorizonPadding() + .fillMaxWidth() + .padding(bottom = 16.dp), + text = stringResource(id = R.string.announcement_creation_option_promotion_button), + enabled = true + ) { + viewModel.postIntent(PromotionIntent.ClickSaveButton) + } + }, + ) { paddingValues -> + Column( + modifier = modifier + .fillMaxSize() + .padding(paddingValues) ) { - LazyRow( - modifier = Modifier.padding(vertical = 12.dp), - state = scrollState, - horizontalArrangement = Arrangement.spacedBy(8.dp), - contentPadding = PaddingValues(start = 16.dp, end = 44.dp) + CreationTitle( + modifier = Modifier.screenHorizonPadding(), + title = stringResource(id = R.string.announcement_creation_option_promotion_title) + ) + Box( + modifier = Modifier.fillMaxWidth(), ) { - items(CardImage.entries) { - PromotionCard( - isSelected = uiState.selectCard == it, - hasPromotion = uiState.hasPromotion, - cardImage = it - ) { - viewModel.postIntent(PromotionIntent.ClickPromotionCard(it)) + LazyRow( + modifier = Modifier.padding(vertical = 12.dp), + state = scrollState, + horizontalArrangement = Arrangement.spacedBy(8.dp), + contentPadding = PaddingValues(start = 16.dp, end = 44.dp) + ) { + items(CardImage.entries) { + PromotionCard( + isSelected = uiState.selectCard == it, + hasPromotion = uiState.hasPromotion, + cardImage = it + ) { + viewModel.postIntent(PromotionIntent.ClickPromotionCard(it)) + } } } + Box( + modifier = Modifier + .height(52.dp) + .background(White) + .align(Alignment.CenterEnd) + .padding(horizontal = 4.dp) + .clickable { + viewModel.postIntent( + PromotionIntent.SetPromotionBottomSheet( + true + ) + ) + } + .padding(horizontal = 4.dp), + contentAlignment = Alignment.Center + ) { + Icon( + modifier = Modifier + .size(24.dp), + painter = painterResource(id = R.drawable.ic_chevron_right), + contentDescription = "right", + tint = Grey300 + ) + } } - Box( - modifier = Modifier - .height(52.dp) - .background(White) - .align(Alignment.CenterEnd) - .padding(horizontal = 4.dp) - .clickable { viewModel.postIntent(PromotionIntent.SetPromotionBottomSheet(true)) } - .padding(horizontal = 4.dp), - contentAlignment = Alignment.Center - ) { - Icon( + Crossfade( + modifier = Modifier.padding(vertical = 12.dp), + targetState = uiState.selectCard, + label = "Check" + ) { selectedCard -> + Image( modifier = Modifier - .size(24.dp), - painter = painterResource(id = R.drawable.ic_chevron_right), - contentDescription = "right", - tint = Grey300 + .screenHorizonPadding() + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)), + painter = painterResource(id = selectedCard.imageId), + contentDescription = selectedCard.imageId.toString(), + contentScale = ContentScale.Crop, ) } } - Crossfade( - modifier = Modifier.padding(vertical = 12.dp), - targetState = uiState.selectCard, - label = "Check" - ) { selectedCard -> - Image( - modifier = Modifier - .screenHorizonPadding() - .fillMaxWidth() - .clip(RoundedCornerShape(16.dp)), - painter = painterResource(id = selectedCard.imageId), - contentDescription = selectedCard.imageId.toString(), - contentScale = ContentScale.Crop, - ) + if(uiState.isShowPromotionBottomSheet) { + PromotionBottomSheet( + sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), + selectedCard = uiState.bottomSheetSelectCard, + hasPromotion = uiState.hasPromotion, + onDismissRequest = { viewModel.postIntent(PromotionIntent.HideUserNameBottomSheet) }, + onClickJoinPromotion = { /* FIXME */ }, + onClickItem = { viewModel.postIntent(PromotionIntent.ClickBottomSheetCard(it)) } + ) { viewModel.postIntent(PromotionIntent.ClickBottomSheetSelectButton) } } } - if(uiState.isShowPromotionBottomSheet) { - PromotionBottomSheet( - sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), - selectedCard = uiState.bottomSheetSelectCard, - hasPromotion = uiState.hasPromotion, - onDismissRequest = { viewModel.postIntent(PromotionIntent.HideUserNameBottomSheet) }, - onClickJoinPromotion = { /* FIXME */ }, - onClickItem = { viewModel.postIntent(PromotionIntent.ClickBottomSheetCard(it)) } - ) { viewModel.postIntent(PromotionIntent.ClickBottomSheetSelectButton) } - } } viewModel.sideEffect.collectInSideEffectWithLifecycle { sideEffect -> when(sideEffect) { - is PromotionSideEffect.NavigateToUp -> { } + is PromotionSideEffect.NavigateToUp -> { navigateToUp() } is PromotionSideEffect.HidePromotionBottomSheet -> { sheetState.hide() viewModel.postIntent(PromotionIntent.SetPromotionBottomSheet(false)) @@ -180,6 +197,15 @@ fun PromotionScreen( is PromotionSideEffect.ScrollToItem -> { scrollState.animateScrollToItem(index = sideEffect.index) } + is PromotionSideEffect.ShowSnackBar -> { + snackBarHostState.showSnackbar( + message = context.getString(sideEffect.stringId), + withDismissAction = true + ) + } + is PromotionSideEffect.NavigateToSuccess -> { + navigateToSuccess(sideEffect.id, sideEffect.title) + } } } } \ No newline at end of file diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/promotion/PromotionViewModel.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/promotion/PromotionViewModel.kt index a4bca3ed..2720db10 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/promotion/PromotionViewModel.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/promotion/PromotionViewModel.kt @@ -1,17 +1,30 @@ package com.easyhz.noffice.feature.announcement.screen.creation.promotion +import android.net.Uri +import android.util.Log +import androidx.annotation.StringRes +import androidx.lifecycle.viewModelScope import com.easyhz.noffice.core.common.base.BaseViewModel +import com.easyhz.noffice.core.common.error.handleError import com.easyhz.noffice.core.model.announcement.param.AnnouncementParam +import com.easyhz.noffice.core.model.image.ImageParam +import com.easyhz.noffice.core.model.image.ImagePurpose +import com.easyhz.noffice.domain.announcement.usecase.announcement.CreateAnnouncementUseCase +import com.easyhz.noffice.domain.organization.usecase.image.GetDrawableUriUseCase +import com.easyhz.noffice.domain.organization.usecase.image.UploadImageUseCase import com.easyhz.noffice.feature.announcement.contract.creation.promotion.CardImage import com.easyhz.noffice.feature.announcement.contract.creation.promotion.PromotionIntent import com.easyhz.noffice.feature.announcement.contract.creation.promotion.PromotionSideEffect import com.easyhz.noffice.feature.announcement.contract.creation.promotion.PromotionState import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class PromotionViewModel @Inject constructor( - + private val uploadImageUseCase: UploadImageUseCase, + private val getDrawableUriUseCase: GetDrawableUriUseCase, + private val createAnnouncementUseCase: CreateAnnouncementUseCase ): BaseViewModel( initialState = PromotionState.init() ) { @@ -70,7 +83,47 @@ class PromotionViewModel @Inject constructor( postSideEffect { PromotionSideEffect.ScrollToItem(index) } } - private fun saveButton() { + private fun saveButton() = viewModelScope.launch { + setIsLoading(true) + val imageUri = getDrawableUri() ?: return@launch + val imageUrl = uploadImage(imageUri) ?: return@launch + val param = currentState.announcementParam?.copy( + profileImageUrl = imageUrl + ) ?: return@launch + createAnnouncementUseCase.invoke(param).onSuccess { + postSideEffect { PromotionSideEffect.NavigateToSuccess(it.organizationId, it.title) } + }.onFailure { + showSnackBar(it.handleError()) + }.also { + setIsLoading(false) + } + } + private suspend fun uploadImage(imageUri: Uri): String? { + val param = ImageParam(uri = imageUri, purpose = ImagePurpose.ANNOUNCEMENT_PROFILE) + return uploadImageUseCase.invoke(param).getOrElse { + Log.d(this.javaClass.name, "uploadImage - ${it.message}") + setIsLoading(false) + showSnackBar(it.handleError()) + null + } + } + + private suspend fun getDrawableUri(): Uri? { + return getDrawableUriUseCase.invoke(currentState.selectCard.imageId).getOrElse { + Log.d(this.javaClass.name, "getDrawableUri - ${it.message}") + setIsLoading(false) + showSnackBar(it.handleError()) + null + } + } + + private fun setIsLoading(value: Boolean) { + reduce { copy(isLoading = value) } + } + private fun showSnackBar(@StringRes stringId: Int) { + postSideEffect { + PromotionSideEffect.ShowSnackBar(stringId) + } } } \ No newline at end of file diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/remind/CustomRemindViewModel.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/remind/CustomRemindViewModel.kt index 4e43f82c..7e741256 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/remind/CustomRemindViewModel.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/remind/CustomRemindViewModel.kt @@ -1,7 +1,7 @@ package com.easyhz.noffice.feature.announcement.screen.creation.remind import com.easyhz.noffice.core.common.base.BaseViewModel -import com.easyhz.noffice.core.common.util.DateFormat +import com.easyhz.noffice.core.common.util.DateFormat.localDateTimeToRequest import com.easyhz.noffice.core.common.util.TimeFormat import com.easyhz.noffice.feature.announcement.contract.creation.remind.custom.CustomRemindIntent import com.easyhz.noffice.feature.announcement.contract.creation.remind.custom.CustomRemindSideEffect @@ -33,8 +33,7 @@ class CustomRemindViewModel @Inject constructor( private fun onClickSaveButton() { reduce { copy(selectionTime = TimeFormat.convertToLocalTime(hour, minute, isAm)) } val dateTime = LocalDateTime.of(currentState.selectionDate, currentState.selectionTime) - - val data = DateFormat.localDateTimeToString(dateTime, pattern = DateFormat.PATTERN.CUSTOM_REMIND) + val data = localDateTimeToRequest(dateTime) postSideEffect { CustomRemindSideEffect.SaveToDateTime(data) } } diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/selection/NofficeSelectionScreen.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/selection/NofficeSelectionScreen.kt index 49aa4781..1f8745bf 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/selection/NofficeSelectionScreen.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/selection/NofficeSelectionScreen.kt @@ -18,12 +18,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.easyhz.noffice.core.common.util.collectInSideEffectWithLifecycle import com.easyhz.noffice.core.design_system.R import com.easyhz.noffice.core.design_system.component.button.CheckButton import com.easyhz.noffice.core.design_system.component.button.CheckButtonDefaults import com.easyhz.noffice.core.design_system.component.button.MediumButton +import com.easyhz.noffice.core.design_system.component.loading.LoadingScreenProvider import com.easyhz.noffice.core.design_system.component.scaffold.NofficeBasicScaffold import com.easyhz.noffice.core.design_system.component.topBar.DetailTopBar import com.easyhz.noffice.core.design_system.extension.screenHorizonPadding @@ -43,71 +45,75 @@ fun NofficeSelectionScreen( modifier: Modifier = Modifier, viewModel: SelectionViewModel = hiltViewModel(), navigateToUp: () -> Unit, - navigateToAnnouncementCreationContent: () -> Unit + navigateToAnnouncementCreationContent: (Int) -> Unit ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val organizationList = viewModel.organizationState.collectAsLazyPagingItems() - NofficeBasicScaffold( - topBar = { - DetailTopBar( - leadingItem = DetailTopBarMenu( - content = { - Icon( - modifier = Modifier.size(24.dp), - painter = painterResource(id = R.drawable.ic_chevron_left), - contentDescription = "left", - tint = Grey400 - ) - }, - onClick = { viewModel.postIntent(SelectionIntent.ClickBackButton) } - ), - title = stringResource(id = R.string.announcement_creation_title), - ) - } - ) { paddingValues -> - Column( - modifier = modifier - .padding(paddingValues) - .fillMaxSize() - .screenHorizonPadding(), - ) { - CreationTitle( - title = stringResource(id = R.string.announcement_creation_noffice_selection_title) - ) - - LazyColumn( - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(12.dp) + LoadingScreenProvider( + isLoading = organizationList.loadState.refresh == LoadState.Loading + ) { + NofficeBasicScaffold( + topBar = { + DetailTopBar( + leadingItem = DetailTopBarMenu( + content = { + Icon( + modifier = Modifier.size(24.dp), + painter = painterResource(id = R.drawable.ic_chevron_left), + contentDescription = "left", + tint = Grey400 + ) + }, + onClick = { viewModel.postIntent(SelectionIntent.ClickBackButton) } + ), + title = stringResource(id = R.string.announcement_creation_title), + ) + } + ) { paddingValues -> + Column( + modifier = modifier + .padding(paddingValues) + .fillMaxSize() + .screenHorizonPadding(), ) { - item { - Spacer(modifier = Modifier.width(4.dp)) - } - items(organizationList.itemCount, key = { organizationList[it]?.id ?: -1}) { index -> - CheckButton( - modifier = Modifier.fillMaxWidth(), - text = organizationList[index]?.name!!, - isComplete = uiState.selectedOrganization == organizationList[index]?.id, - color = CheckButtonDefaults( - completeContainerColor = Green100, - completeContentColor = Green700, - completeIconColor = Green700, - incompleteContainerColor = Grey50, - incompleteContentColor = Grey600, - incompleteIconColor = Grey300 - ) - ) { - viewModel.postIntent(SelectionIntent.SelectedOrganization(organizationList[index]?.id ?: -1)) + CreationTitle( + title = stringResource(id = R.string.announcement_creation_noffice_selection_title) + ) + + LazyColumn( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + item { + Spacer(modifier = Modifier.width(4.dp)) + } + items(organizationList.itemCount, key = { organizationList[it]?.id ?: -1}) { index -> + CheckButton( + modifier = Modifier.fillMaxWidth(), + text = organizationList[index]?.name!!, + isComplete = uiState.selectedOrganization == organizationList[index]?.id, + color = CheckButtonDefaults( + completeContainerColor = Green100, + completeContentColor = Green700, + completeIconColor = Green700, + incompleteContainerColor = Grey50, + incompleteContentColor = Grey600, + incompleteIconColor = Grey300 + ) + ) { + viewModel.postIntent(SelectionIntent.SelectedOrganization(organizationList[index]?.id ?: -1)) + } } } - } - MediumButton( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 16.dp), - text = stringResource(id = R.string.next_button), - enabled = uiState.enabledButton - ) { - viewModel.postIntent(SelectionIntent.ClickNextButton) + MediumButton( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp), + text = stringResource(id = R.string.next_button), + enabled = uiState.enabledButton + ) { + viewModel.postIntent(SelectionIntent.ClickNextButton) + } } } } @@ -115,7 +121,7 @@ fun NofficeSelectionScreen( viewModel.sideEffect.collectInSideEffectWithLifecycle {sideEffect -> when(sideEffect) { is SelectionSideEffect.NavigateToUp -> { navigateToUp() } - is SelectionSideEffect.NavigateToNext -> { navigateToAnnouncementCreationContent() } + is SelectionSideEffect.NavigateToNext -> { navigateToAnnouncementCreationContent(sideEffect.id) } } } } \ No newline at end of file diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/selection/SelectionViewModel.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/selection/SelectionViewModel.kt index 44f71110..5f843ae4 100644 --- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/selection/SelectionViewModel.kt +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/creation/selection/SelectionViewModel.kt @@ -51,7 +51,7 @@ class SelectionViewModel @Inject constructor( } private fun onClickNextButton() { - postSideEffect { SelectionSideEffect.NavigateToNext } + postSideEffect { SelectionSideEffect.NavigateToNext(currentState.selectedOrganization) } } private fun onSelectedOrganization(organizationId: Int) { diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/success/SuccessScreen.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/success/SuccessScreen.kt new file mode 100644 index 00000000..af271525 --- /dev/null +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/success/SuccessScreen.kt @@ -0,0 +1,115 @@ +package com.easyhz.noffice.feature.announcement.screen.success + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +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.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.easyhz.noffice.core.design_system.R +import com.easyhz.noffice.core.design_system.component.button.MediumButton +import com.easyhz.noffice.core.design_system.component.scaffold.NofficeBasicScaffold +import com.easyhz.noffice.core.design_system.component.topBar.DetailTopBar +import com.easyhz.noffice.core.design_system.extension.screenHorizonPadding +import com.easyhz.noffice.core.design_system.theme.Grey100 +import com.easyhz.noffice.core.design_system.theme.Grey400 +import com.easyhz.noffice.core.design_system.theme.Grey600 +import com.easyhz.noffice.core.design_system.theme.SubTitle1 +import com.easyhz.noffice.core.design_system.theme.Title4 +import com.easyhz.noffice.core.design_system.theme.White +import com.easyhz.noffice.core.design_system.util.topBar.DetailTopBarMenu + +@Composable +fun SuccessScreen( + modifier: Modifier = Modifier, + id: Int, + title: String, + navigateToHome: () -> Unit, + navigateToAnnouncementDetail: (Int, String) -> Unit, +) { + NofficeBasicScaffold( + statusBarColor = White, + navigationBarColor = White, + topBar = { + DetailTopBar( + leadingItem = DetailTopBarMenu( + content = { + Icon( + modifier = Modifier.size(24.dp), + painter = painterResource(id = R.drawable.ic_chevron_left), + contentDescription = "left", + tint = Grey400 + ) + }, + onClick = { navigateToHome() } + ), + ) + }, + bottomBar = { + Row( + modifier = Modifier + .padding(bottom = 16.dp) + .screenHorizonPadding(), + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + MediumButton( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.announcement_creation_success_to_home), + contentColor = Grey600, + containerColor = Grey100 + ) { + navigateToHome() + } + MediumButton( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.announcement_creation_success_to_detail) + ) { + navigateToAnnouncementDetail(id, title) + } + } + } + ) { + Column( + modifier = modifier + .screenHorizonPadding() + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Column( + modifier = Modifier.weight(1f), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Image( + painter = painterResource(id = R.drawable.ic_success_bell), + contentDescription = "success" + ) + Spacer(modifier = Modifier.height(18.dp)) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = stringResource(id = R.string.announcement_creation_success_title), + style = Title4 + ) + Text( + text = stringResource(id = R.string.announcement_creation_success_content), + style = SubTitle1 + ) + } + } + } + } +} \ No newline at end of file diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/util/creation/RemindUtil.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/util/creation/RemindUtil.kt new file mode 100644 index 00000000..07c7980b --- /dev/null +++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/util/creation/RemindUtil.kt @@ -0,0 +1,27 @@ +package com.easyhz.noffice.feature.announcement.util.creation + +import com.easyhz.noffice.core.common.util.DateFormat.localDateTimeToRequest +import com.easyhz.noffice.core.common.util.DateFormat.parseLocalDateTime +import com.easyhz.noffice.feature.announcement.contract.creation.remind.timeList + +internal fun separateRemind(remindList: List): Pair, List> { + return remindList.partition { it in timeList } +} + +internal fun calculateRemind(remindList: List, endAt: String?): Triple, List, List> { + val (time, custom) = separateRemind(remindList) + val secondList = mutableListOf() + val timeList = mutableListOf() + + endAt?.let { endAtStr -> + val endDateTime = parseLocalDateTime(endAtStr) ?: return Triple(secondList, timeList, custom) + time.forEach { timeString -> + timeString.toLongOrNull()?.let { sec -> + secondList.add(sec) + timeList.add(localDateTimeToRequest(endDateTime.minusSeconds(sec))) + } + } + } + + return Triple(secondList, timeList, custom) +} \ No newline at end of file