diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 96357aa7f..9a85b9266 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -7,6 +7,10 @@
+
+
+
+
+
+
diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts
index d362f9c2c..9d8529afa 100644
--- a/core/designsystem/build.gradle.kts
+++ b/core/designsystem/build.gradle.kts
@@ -24,4 +24,5 @@ dependencies {
implementation(libs.lottie)
implementation(libs.coil.compose)
implementation(libs.coil.network.okhttp)
+ implementation(libs.kakao.share)
}
\ No newline at end of file
diff --git a/core/designsystem/src/main/java/com/terning/core/designsystem/component/topappbar/TerningBasicTopAppBar.kt b/core/designsystem/src/main/java/com/terning/core/designsystem/component/topappbar/TerningBasicTopAppBar.kt
index 7d3b1bd7b..074739668 100644
--- a/core/designsystem/src/main/java/com/terning/core/designsystem/component/topappbar/TerningBasicTopAppBar.kt
+++ b/core/designsystem/src/main/java/com/terning/core/designsystem/component/topappbar/TerningBasicTopAppBar.kt
@@ -22,13 +22,14 @@ import com.terning.core.designsystem.theme.White
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TerningBasicTopAppBar(
+ modifier: Modifier = Modifier,
title: String = "",
- modifier: Modifier,
showBackButton: Boolean = false,
actions: List<@Composable () -> Unit> = emptyList(),
onBackButtonClick: () -> Unit = {},
) {
CenterAlignedTopAppBar(
+ modifier = modifier,
title = {
Text(
text = title,
@@ -56,7 +57,7 @@ fun TerningBasicTopAppBar(
}
},
actions = {
- actions.drop(1).forEach { action ->
+ actions.forEach { action ->
action()
}
},
diff --git a/core/designsystem/src/main/java/com/terning/core/designsystem/util/KakaoUtil.kt b/core/designsystem/src/main/java/com/terning/core/designsystem/util/KakaoUtil.kt
new file mode 100644
index 000000000..267575616
--- /dev/null
+++ b/core/designsystem/src/main/java/com/terning/core/designsystem/util/KakaoUtil.kt
@@ -0,0 +1,43 @@
+package com.terning.core.designsystem.util
+
+import android.content.Context
+import android.content.Intent
+import androidx.browser.customtabs.CustomTabsIntent
+import com.kakao.sdk.share.ShareClient
+import com.kakao.sdk.share.WebSharerClient
+import dagger.hilt.android.qualifiers.ApplicationContext
+import timber.log.Timber
+import javax.inject.Inject
+
+class KakaoUtil @Inject constructor(
+ @ApplicationContext private val context: Context
+) {
+ private val templateId = 118600L
+
+ fun shareToKakaoTalk(templateArgs: Map) {
+ if (ShareClient.instance.isKakaoTalkSharingAvailable(context)) {
+ ShareClient.instance.shareCustom(
+ context,
+ templateId,
+ templateArgs
+ ) { sharingResult, error ->
+ if (error != null) {
+ Timber.e("카카오톡 공유 실패: ${error.message}")
+ } else if (sharingResult != null) {
+ context.startActivity(sharingResult.intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
+ }
+ }
+ } else {
+ val sharerUrl = WebSharerClient.instance.makeCustomUrl(templateId, templateArgs)
+ try {
+ val intent = CustomTabsIntent.Builder().build().intent
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ intent.data = sharerUrl
+ context.startActivity(intent)
+ } catch (e: Exception) {
+ Timber.e("웹 공유 실패: ${e.message}")
+ }
+
+ }
+ }
+}
diff --git a/feature/intern/src/main/java/com/terning/feature/intern/InternRoute.kt b/feature/intern/src/main/java/com/terning/feature/intern/InternRoute.kt
index be433456b..7f52a79cf 100644
--- a/feature/intern/src/main/java/com/terning/feature/intern/InternRoute.kt
+++ b/feature/intern/src/main/java/com/terning/feature/intern/InternRoute.kt
@@ -10,12 +10,14 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.selection.SelectionContainer
+import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
@@ -27,16 +29,14 @@ import androidx.navigation.NavHostController
import com.terning.core.analytics.EventType
import com.terning.core.analytics.LocalTracker
import com.terning.core.designsystem.component.topappbar.BackButtonTopAppBar
-import com.terning.core.designsystem.extension.customShadow
+import com.terning.core.designsystem.extension.noRippleClickable
import com.terning.core.designsystem.extension.toast
import com.terning.core.designsystem.state.UiState
import com.terning.core.designsystem.theme.CalRed
-import com.terning.core.designsystem.theme.Grey200
import com.terning.core.designsystem.theme.Grey400
import com.terning.core.designsystem.theme.TerningTheme
import com.terning.core.designsystem.theme.White
import com.terning.domain.intern.entity.InternInfo
-import com.terning.domain.intern.entity.InternInfo.Companion.EMPTY_INTERN
import com.terning.feature.dialog.cancel.ScrapCancelDialog
import com.terning.feature.dialog.detail.ScrapDialog
import com.terning.feature.intern.component.InternBottomBar
@@ -68,42 +68,51 @@ fun InternRoute(
viewModel.sideEffect.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle)
.collect { sideEffect ->
when (sideEffect) {
- is InternViewSideEffect.ShowToast -> context.toast(sideEffect.message)
+ is InternSideEffect.Toast -> context.toast(sideEffect.message)
}
}
}
-
- val internInfo = (internState.loadState as? UiState.Success)?.data ?: EMPTY_INTERN
-
- InternScreen(
- announcementId = announcementId,
- internUiState = internState,
- internInfo = internInfo,
- navController = navController,
- onDismissCancelDialog = {
- with(viewModel) {
- updateScrapCancelDialogVisibility(false)
- getInternInfo(announcementId)
- }
- },
- onDismissScrapDialog = {
- with(viewModel) {
- updateInternDialogVisibility(false)
- getInternInfo(announcementId)
- }
- },
- onClickCancelButton = {
- viewModel.updateScrapCancelDialogVisibility(true)
- },
- onClickScrapButton = {
- amplitudeTracker.track(
- type = EventType.CLICK,
- name = "detail_scrap"
+ when (internState.loadState) {
+ UiState.Loading -> {}
+ UiState.Empty -> {}
+ is UiState.Failure -> {}
+ is UiState.Success -> {
+ InternScreen(
+ announcementId = announcementId,
+ internUiState = internState,
+ internInfo = (internState.loadState as UiState.Success).data,
+ navController = navController,
+ onClickShareButton = {
+ viewModel.onKakaoShareClicked(
+ (internState.loadState as UiState.Success).data
+ )
+ },
+ onDismissCancelDialog = {
+ with(viewModel) {
+ updateScrapCancelDialogVisibility(false)
+ getInternInfo(announcementId)
+ }
+ },
+ onDismissScrapDialog = {
+ with(viewModel) {
+ updateInternDialogVisibility(false)
+ getInternInfo(announcementId)
+ }
+ },
+ onClickCancelButton = {
+ viewModel.updateScrapCancelDialogVisibility(true)
+ },
+ onClickScrapButton = {
+ amplitudeTracker.track(
+ type = EventType.CLICK,
+ name = "detail_scrap"
+ )
+ viewModel.updateInternDialogVisibility(true)
+ }
)
- viewModel.updateInternDialogVisibility(true)
}
- )
+ }
}
@Composable
@@ -113,6 +122,7 @@ fun InternScreen(
navController: NavHostController,
internUiState: InternUiState,
internInfo: InternInfo,
+ onClickShareButton: () -> Unit,
onDismissCancelDialog: (Boolean) -> Unit,
onDismissScrapDialog: (Boolean) -> Unit,
onClickCancelButton: (InternInfo) -> Unit,
@@ -151,13 +161,20 @@ fun InternScreen(
) {
BackButtonTopAppBar(
title = stringResource(id = R.string.intern_top_app_bar_title),
- modifier = Modifier.customShadow(
- color = Grey200,
- offsetY = 2.dp
- ),
onBackButtonClick = {
navController.popBackStack()
},
+ actions = listOf {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_share_32),
+ contentDescription = null,
+ modifier = Modifier
+ .padding(end = 8.dp)
+ .noRippleClickable(
+ onClick = onClickShareButton
+ )
+ )
+ }
)
LazyColumn(
@@ -289,4 +306,4 @@ fun InternScreen(
)
}
}
-}
\ No newline at end of file
+}
diff --git a/feature/intern/src/main/java/com/terning/feature/intern/InternSideEffect.kt b/feature/intern/src/main/java/com/terning/feature/intern/InternSideEffect.kt
new file mode 100644
index 000000000..4f37bd12c
--- /dev/null
+++ b/feature/intern/src/main/java/com/terning/feature/intern/InternSideEffect.kt
@@ -0,0 +1,8 @@
+package com.terning.feature.intern
+
+import androidx.annotation.StringRes
+
+sealed class InternSideEffect {
+ data class Toast(@StringRes val message: Int) :
+ InternSideEffect()
+}
\ No newline at end of file
diff --git a/feature/intern/src/main/java/com/terning/feature/intern/InternViewModel.kt b/feature/intern/src/main/java/com/terning/feature/intern/InternViewModel.kt
index 98bd12647..05dbd5df8 100644
--- a/feature/intern/src/main/java/com/terning/feature/intern/InternViewModel.kt
+++ b/feature/intern/src/main/java/com/terning/feature/intern/InternViewModel.kt
@@ -3,6 +3,8 @@ package com.terning.feature.intern
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.terning.core.designsystem.state.UiState
+import com.terning.core.designsystem.util.KakaoUtil
+import com.terning.domain.intern.entity.InternInfo
import com.terning.domain.intern.repository.InternRepository
import com.terning.feature.intern.model.InternUiState
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -17,12 +19,13 @@ import javax.inject.Inject
@HiltViewModel
class InternViewModel @Inject constructor(
private val internRepository: InternRepository,
+ private val kakaoUtil: KakaoUtil
) : ViewModel() {
private val _internUiState = MutableStateFlow(InternUiState())
val internUiState get() = _internUiState.asStateFlow()
- private val _sideEffect: MutableSharedFlow = MutableSharedFlow()
+ private val _sideEffect: MutableSharedFlow = MutableSharedFlow()
val sideEffect = _sideEffect.asSharedFlow()
fun getInternInfo(id: Long) {
@@ -37,7 +40,7 @@ class InternViewModel @Inject constructor(
_internUiState.value = _internUiState.value.copy(
loadState = UiState.Failure(exception.toString())
)
- _sideEffect.emit(InternViewSideEffect.ShowToast(R.string.server_failure))
+ _sideEffect.emit(InternSideEffect.Toast(R.string.server_failure))
}
}
}
@@ -63,4 +66,18 @@ class InternViewModel @Inject constructor(
it.copy(showWeb = show)
}
}
+
+ fun onKakaoShareClicked(
+ internInfo: InternInfo
+ ) {
+ val templateArgs = mapOf(
+ "COMPANY_IMG" to internInfo.companyImage,
+ "TITLE" to internInfo.title,
+ "DEADLINE" to internInfo.deadline,
+ "START_DATE" to internInfo.startYearMonth,
+ "PERIOD" to internInfo.workingPeriod,
+ "REGI_WEB_DOMAIN" to internInfo.url
+ )
+ kakaoUtil.shareToKakaoTalk(templateArgs)
+ }
}
diff --git a/feature/intern/src/main/java/com/terning/feature/intern/InternViewSideEffect.kt b/feature/intern/src/main/java/com/terning/feature/intern/InternViewSideEffect.kt
index b6525a55b..9954265af 100644
--- a/feature/intern/src/main/java/com/terning/feature/intern/InternViewSideEffect.kt
+++ b/feature/intern/src/main/java/com/terning/feature/intern/InternViewSideEffect.kt
@@ -1,8 +1,7 @@
-package com.terning.feature.intern
import androidx.annotation.StringRes
sealed class InternViewSideEffect {
- data class ShowToast(@StringRes val message: Int) :
+ data class Toast(@StringRes val message: Int) :
InternViewSideEffect()
}
\ No newline at end of file
diff --git a/feature/intern/src/main/res/drawable/ic_intern_share_22.xml b/feature/intern/src/main/res/drawable/ic_intern_share_22.xml
deleted file mode 100644
index db0f653f7..000000000
--- a/feature/intern/src/main/res/drawable/ic_intern_share_22.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/feature/intern/src/main/res/drawable/ic_share_32.xml b/feature/intern/src/main/res/drawable/ic_share_32.xml
new file mode 100644
index 000000000..64f9540cd
--- /dev/null
+++ b/feature/intern/src/main/res/drawable/ic_share_32.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e64d297f4..c6d7b7741 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -203,6 +203,7 @@ timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
lottie = { group = "com.airbnb.android", name = "lottie-compose", version.ref = "lottieVersion" }
kakao-user = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakaoVersion" }
+kakao-share = { group = "com.kakao.sdk", name = "v2-share", version.ref = "kakaoVersion" }
process-phoenix = { group = "com.jakewharton", name = "process-phoenix", version.ref = "processPhoenix" }
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }