Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ fun DoubleButtonAlertDialog(
onClickPrimaryButton: () -> Unit,
onClickGrayButton: () -> Unit,
modifier: Modifier = Modifier,
properties: DialogProperties = DialogProperties(),
properties: DialogProperties = DialogProperties(
usePlatformDefaultWidth = false,
),
) {
Dialog(
onDismissRequest = onDismissRequest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ fun SingleButtonAlertDialog(
onClick: () -> Unit,
enabled: Boolean = true,
properties: DialogProperties = DialogProperties(
usePlatformDefaultWidth = false,
dismissOnBackPress = false,
dismissOnClickOutside = false,
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package com.neki.android.core.designsystem.dialog

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.R
import com.neki.android.core.designsystem.button.CTAButtonPrimary
import com.neki.android.core.designsystem.modifier.clickableSingle
import com.neki.android.core.designsystem.ui.theme.NekiTheme

@Composable
fun SingleButtonWithTextButtonAlertDialog(
title: String,
content: String,
buttonText: String,
textButtonText: String,
onDismissRequest: () -> Unit,
onButtonClick: () -> Unit,
onTextButtonClick: () -> Unit,
enabled: Boolean = true,
properties: DialogProperties = DialogProperties(
usePlatformDefaultWidth = false,
dismissOnBackPress = false,
dismissOnClickOutside = false,
),
) {
Dialog(
onDismissRequest = onDismissRequest,
properties = properties,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp)
.widthIn(max = 400.dp)
.clip(RoundedCornerShape(20.dp))
.background(NekiTheme.colorScheme.white)
.padding(top = 20.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.icon_dialog_alert),
tint = Color.Unspecified,
contentDescription = null,
)
Column(
modifier = Modifier.padding(horizontal = 24.dp),
verticalArrangement = Arrangement.spacedBy(2.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = title,
style = NekiTheme.typography.title18Bold,
color = NekiTheme.colorScheme.gray900,
textAlign = TextAlign.Center,
)
Text(
text = content,
style = NekiTheme.typography.body14Regular,
color = NekiTheme.colorScheme.gray500,
textAlign = TextAlign.Center,
)
}
Column(
modifier = Modifier.padding(vertical = 12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
CTAButtonPrimary(
text = buttonText,
onClick = onButtonClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp),
Comment thread
Ojongseok marked this conversation as resolved.
enabled = enabled,
)
Text(
modifier = Modifier
.padding(
vertical = 4.dp,
horizontal = 56.dp,
)
.clickableSingle(onClick = onTextButtonClick),
text = textButtonText,
style = NekiTheme.typography.body14Regular,
color = NekiTheme.colorScheme.primary600,
textDecoration = TextDecoration.Underline,
)
}
}
}
}

@ComponentPreview
@Composable
private fun SingleButtonWithTextButtonAlertDialogPreview() {
NekiTheme {
SingleButtonWithTextButtonAlertDialog(
title = "메인 텍스트가 들어가는 곳",
content = "보조 설명 텍스트가 들어가는 공간입니다",
buttonText = "텍스트",
textButtonText = "텍스트 공간",
onDismissRequest = {},
onButtonClick = {},
onTextButtonClick = {},
)
}
}
2 changes: 2 additions & 0 deletions detekt-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ exceptions:
active: false

style:
FunctionOnlyReturningConstant:
active: false
Comment thread
ikseong00 marked this conversation as resolved.
UnusedParameter:
active: false
MagicNumber:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.neki.android.feature.archive.impl.main.ArchiveMainViewModel
import com.neki.android.feature.archive.impl.photo.AllPhotoRoute
import com.neki.android.feature.archive.impl.photo_detail.PhotoDetailRoute
import com.neki.android.feature.archive.impl.photo_detail.PhotoDetailViewModel
import com.neki.android.feature.photo_upload.api.QRScanResult
import com.neki.android.feature.photo_upload.api.navigateToQRScan
import com.neki.android.feature.photo_upload.api.navigateToUploadAlbum
import dagger.Module
Expand All @@ -44,8 +45,16 @@ private fun EntryProviderScope<NavKey>.archiveEntry(navigator: Navigator) {
entry<ArchiveNavKey.Archive> {
val resultBus = LocalResultEventBus.current
val viewModel = hiltViewModel<ArchiveMainViewModel>()
ResultEffect<String>(resultBus) { imageUrl ->
viewModel.store.onIntent(ArchiveMainIntent.QRCodeScanned(imageUrl))
ResultEffect<QRScanResult>(resultBus) { result ->
when (result) {
is QRScanResult.QRCodeScanned -> {
viewModel.store.onIntent(ArchiveMainIntent.QRCodeScanned(result.imageUrl))
}

QRScanResult.OpenGallery -> {
viewModel.store.onIntent(ArchiveMainIntent.ClickGalleryUploadRow)
}
}
}

ArchiveMainRoute(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.neki.android.feature.photo_upload.api

sealed interface QRScanResult {
data class QRCodeScanned(val imageUrl: String) : QRScanResult
data object OpenGallery : QRScanResult
}
1 change: 1 addition & 0 deletions feature/photo-upload/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ android {
}

defaultConfig {
buildConfigField("String", "BRAND_PROPOSAL_URL", properties["BRAND_PROPOSAL_URL"].toString())
buildConfigField("String", "PHOTOISM_URL", properties["PHOTOISM_URL"].toString())
buildConfigField("String", "PHOTOISM_IMAGE_URL", properties["PHOTOISM_IMAGE_URL"].toString())
buildConfigField("String", "PHOTOISM_IMG_URL_MIME_TYPE", properties["PHOTOISM_IMG_URL_MIME_TYPE"].toString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import com.neki.android.core.navigation.EntryProviderInstaller
import com.neki.android.core.navigation.Navigator
import com.neki.android.core.navigation.result.LocalResultEventBus
import com.neki.android.feature.archive.api.navigateToAlbumDetail
import com.neki.android.feature.archive.api.navigateToArchive
import com.neki.android.feature.photo_upload.api.PhotoUploadNavKey
import com.neki.android.feature.photo_upload.api.QRScanResult
import com.neki.android.feature.photo_upload.impl.album.UploadAlbumRoute
import com.neki.android.feature.photo_upload.impl.album.UploadAlbumViewModel
import com.neki.android.feature.photo_upload.impl.qrscan.QRScanRoute
Expand All @@ -35,10 +35,7 @@ private fun EntryProviderScope<NavKey>.photoUploadEntry(navigator: Navigator) {

QRScanRoute(
navigateBack = navigator::goBack,
navigateToHome = {
resultBus.sendResult<String>(result = it)
navigator.navigateToArchive()
},
setQRResult = { resultBus.sendResult<QRScanResult>(result = it) },
)
}
entry<PhotoUploadNavKey.UploadAlbum> { key ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ data class QRScanState(
val viewType: QRScanViewType = QRScanViewType.QR_SCAN,
val scannedUrl: String? = null,
val detectedImageUrl: String? = null,
val isShowInfoDialog: Boolean = false,
val isShowShouldDownloadDialog: Boolean = false,
val isShowUnSupportedBrandDialog: Boolean = false,
val isTorchEnabled: Boolean = false,
)

Expand All @@ -15,12 +16,19 @@ sealed interface QRScanIntent {
data class ScanQRCode(val scannedUrl: String) : QRScanIntent
data class SetViewType(val viewType: QRScanViewType) : QRScanIntent
data class DetectImageUrl(val imageUrl: String) : QRScanIntent
data object DismissShouldDownloadDialog : QRScanIntent
data object ClickGoDownload : QRScanIntent
data object DismissUnSupportedBrandDialog : QRScanIntent
data object ClickUploadGallery : QRScanIntent
data object ClickProposeBrand : QRScanIntent
}

sealed interface QRScanSideEffect {
data object NavigateBack : QRScanSideEffect
data class NavigateToHome(val imageUrl: String) : QRScanSideEffect
data class SetQRScannedResult(val imageUrl: String) : QRScanSideEffect
Comment thread
Ojongseok marked this conversation as resolved.
data class ShowToast(val message: String) : QRScanSideEffect
data object OpenBrandProposalUrl : QRScanSideEffect
data object SetOpenGalleryResult : QRScanSideEffect
}

enum class QRScanViewType {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,54 @@
package com.neki.android.feature.photo_upload.impl.qrscan

import android.content.Intent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.core.net.toUri
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.neki.android.core.designsystem.dialog.SingleButtonAlertDialog
import com.neki.android.core.designsystem.dialog.SingleButtonWithTextButtonAlertDialog
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.ui.compose.collectWithLifecycle
import com.neki.android.core.ui.toast.NekiToast
import com.neki.android.feature.photo_upload.api.QRScanResult
import com.neki.android.feature.photo_upload.impl.BuildConfig
import com.neki.android.feature.photo_upload.impl.qrscan.component.PhotoWebViewContent
import com.neki.android.feature.photo_upload.impl.qrscan.component.QRScannerContent

@Composable
internal fun QRScanRoute(
viewModel: QRScanViewModel = hiltViewModel(),
navigateBack: () -> Unit,
navigateToHome: (String) -> Unit,
setQRResult: (QRScanResult) -> Unit = {},
) {
val uiState by viewModel.store.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
val nekiToast = remember { NekiToast(context) }

viewModel.store.sideEffects.collectWithLifecycle { sideEffect ->
when (sideEffect) {
QRScanSideEffect.NavigateBack -> navigateBack()
is QRScanSideEffect.NavigateToHome -> navigateToHome(sideEffect.imageUrl)
is QRScanSideEffect.ShowToast -> {}
is QRScanSideEffect.SetQRScannedResult -> {
setQRResult(QRScanResult.QRCodeScanned(sideEffect.imageUrl))
navigateBack()
}
is QRScanSideEffect.ShowToast -> nekiToast.showToast(sideEffect.message)
QRScanSideEffect.OpenBrandProposalUrl -> {
val intent = Intent(Intent.ACTION_VIEW, BuildConfig.BRAND_PROPOSAL_URL.toUri())
context.startActivity(intent)
}
Comment thread
ikseong00 marked this conversation as resolved.

QRScanSideEffect.SetOpenGalleryResult -> {
setQRResult(QRScanResult.OpenGallery)
navigateBack()
}
}
}
QRScanScreen(
Expand Down Expand Up @@ -63,6 +86,28 @@ internal fun QRScanScreen(
}
}
}

if (uiState.isShowShouldDownloadDialog) {
SingleButtonAlertDialog(
title = "갤러리에 사진을 먼저 다운받아주세요",
content = "해당 브랜드는 웹사이트에서 사진을 저장해야\n네키에 자동으로 저장돼요",
buttonText = "사진 다운로드하러가기",
onDismissRequest = { onIntent(QRScanIntent.DismissShouldDownloadDialog) },
onClick = { onIntent(QRScanIntent.ClickGoDownload) },
)
}

if (uiState.isShowUnSupportedBrandDialog) {
SingleButtonWithTextButtonAlertDialog(
title = "지원하지 않는 브랜드예요",
content = "갤러리에서 사진을 추가해 바로 저장할 수 있어요\n원하는 브랜드가 있다면 제안해주세요!",
buttonText = "갤러리에서 추가하기",
textButtonText = "브랜드 제안하기",
onDismissRequest = { onIntent(QRScanIntent.DismissUnSupportedBrandDialog) },
onButtonClick = { onIntent(QRScanIntent.ClickUploadGallery) },
onTextButtonClick = { onIntent(QRScanIntent.ClickProposeBrand) },
)
}
}

@Preview(showBackground = true)
Expand Down
Loading