diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/webView/WebView.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/webView/WebView.kt
index 2962afdf..34ee2734 100644
--- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/webView/WebView.kt
+++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/webView/WebView.kt
@@ -2,9 +2,13 @@ package com.easyhz.noffice.core.design_system.component.webView
import android.annotation.SuppressLint
import android.view.ViewGroup
+import android.webkit.WebChromeClient
+import android.webkit.WebSettings
import android.webkit.WebView
+import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.easyhz.noffice.core.design_system.util.webView.nofficeWebViewClient
@@ -12,23 +16,37 @@ import com.easyhz.noffice.core.design_system.util.webView.nofficeWebViewClient
@Composable
fun NofficeWebView(
modifier: Modifier = Modifier,
+ webView: WebView,
url: String,
+ onGoBack: (Boolean) -> Unit,
onLoading: (Boolean) -> Unit
) {
AndroidView(
- modifier = modifier,
- factory = { WebView(it).apply {
- layoutParams = ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
- )
- settings.apply {
- javaScriptEnabled = true
- allowFileAccess = true
- allowContentAccess = true
+ modifier = modifier.padding(bottom = 48.dp),
+ factory = { view ->
+ webView.apply {
+ layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ settings.apply {
+ javaScriptEnabled = true
+ allowFileAccess = true
+ allowContentAccess = true
+ domStorageEnabled = true
+ mediaPlaybackRequiresUserGesture = false
+ cacheMode = WebSettings.LOAD_DEFAULT
+ textZoom = 100
+ }
+ webChromeClient = WebChromeClient()
+ webViewClient = nofficeWebViewClient(
+ canGoBack = { onGoBack(it) },
+ onLoading = onLoading
+ )
}
- webViewClient = nofficeWebViewClient(onLoading)
- } },
- update = { it.loadUrl(url)}
+ },
+ update = {
+ it.loadUrl(url)
+ }
)
}
\ No newline at end of file
diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/webView/WebViewClient.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/webView/WebViewClient.kt
index 3382acec..e81efb2f 100644
--- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/webView/WebViewClient.kt
+++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/webView/WebViewClient.kt
@@ -7,29 +7,21 @@ import android.graphics.Bitmap
import android.net.Uri
import android.webkit.URLUtil
import android.webkit.WebResourceRequest
-import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
-import androidx.webkit.WebViewAssetLoader
import java.net.URISyntaxException
fun nofficeWebViewClient(
+ canGoBack: (Boolean) -> Unit,
onLoading: (Boolean) -> Unit
) = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
return view?.context?.handleUrl(request?.url.toString()) ?: false
}
- override fun shouldInterceptRequest(
- view: WebView?,
- request: WebResourceRequest?
- ): WebResourceResponse? {
- val a = WebViewAssetLoader.Builder().setHttpAllowed(true).build()
- return super.shouldInterceptRequest(view, request)
- }
-
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
+ canGoBack(view?.canGoBack() ?: false)
onLoading(true)
}
@@ -45,7 +37,6 @@ private fun Context.handleUrl(url: String): Boolean {
} catch (e: Exception) {
return false
}
-
return when (uri.scheme) {
"intent" -> {
startSchemeIntent(url)
diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml
index 20b0abbc..067d003c 100644
--- a/core/design-system/src/main/res/values/strings.xml
+++ b/core/design-system/src/main/res/values/strings.xml
@@ -115,4 +115,5 @@
이벤트 일시
이벤트 장소
TO DO LIST
+ 기본 브라우저로 연결
\ No newline at end of file
diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/component/detail/PlaceBottomSheet.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/component/detail/PlaceBottomSheet.kt
new file mode 100644
index 00000000..e847642e
--- /dev/null
+++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/component/detail/PlaceBottomSheet.kt
@@ -0,0 +1,78 @@
+package com.easyhz.noffice.feature.announcement.component.detail
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import com.easyhz.noffice.core.design_system.R
+import com.easyhz.noffice.core.design_system.extension.borderBottom
+import com.easyhz.noffice.core.design_system.extension.noRippleClickable
+import com.easyhz.noffice.core.design_system.extension.screenHorizonPadding
+import com.easyhz.noffice.core.design_system.theme.Grey400
+import com.easyhz.noffice.core.design_system.theme.Grey50
+import com.easyhz.noffice.core.design_system.theme.Grey700
+import com.easyhz.noffice.core.design_system.theme.SemiBold14
+import com.easyhz.noffice.core.design_system.theme.White
+
+@Stable
+@Composable
+internal fun PlaceBottomSheetTopBar(
+ modifier: Modifier = Modifier,
+ placeUrl: String,
+ onClickBack: () -> Unit,
+) {
+ Box(
+ modifier = modifier
+ .padding(top = 8.dp)
+ .screenHorizonPadding()
+ .fillMaxWidth()
+ .background(White)
+ .height(44.dp)
+ .borderBottom(Grey50, 1.dp)
+ ) {
+
+ Box(
+ modifier = Modifier
+ .align(Alignment.CenterStart)
+ .sizeIn(minHeight = 32.dp, minWidth = 32.dp)
+ .noRippleClickable { onClickBack() },
+ contentAlignment = Alignment.CenterStart
+ ) {
+ Icon(
+ modifier = Modifier.size(24.dp),
+ painter = painterResource(id = R.drawable.ic_chevron_left),
+ contentDescription = "left",
+ tint = Grey400
+ )
+ }
+
+
+ Box(
+ modifier = Modifier
+ .align(Alignment.Center)
+ .widthIn(max = 300.dp)
+ ) {
+ Text(
+ text = placeUrl,
+ style = SemiBold14,
+ color = Grey700,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+ }
+}
+
diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/component/detail/PlaceWebView.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/component/detail/PlaceWebView.kt
index 3b9dbc86..32c4d9d0 100644
--- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/component/detail/PlaceWebView.kt
+++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/component/detail/PlaceWebView.kt
@@ -1,5 +1,6 @@
package com.easyhz.noffice.feature.announcement.component.detail
+import android.webkit.WebView
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.rememberScrollState
@@ -12,7 +13,9 @@ import com.easyhz.noffice.core.design_system.component.webView.NofficeWebView
@Composable
internal fun PlaceWebView(
modifier: Modifier = Modifier,
+ webView: WebView,
url: String,
+ onGoBack: (Boolean) -> Unit,
onLoading: (Boolean) -> Unit
) {
val state = rememberScrollState()
@@ -24,8 +27,9 @@ internal fun PlaceWebView(
) {
NofficeWebView(
url = url,
- onLoading = onLoading
+ webView = webView,
+ onGoBack = onGoBack,
+ onLoading = onLoading,
)
}
-
}
\ No newline at end of file
diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailIntent.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailIntent.kt
index 91882659..4514ff8d 100644
--- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailIntent.kt
+++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailIntent.kt
@@ -4,7 +4,11 @@ import com.easyhz.noffice.core.common.base.UiIntent
sealed class DetailIntent: UiIntent() {
data class InitScreen(val id: Int, val title: String): DetailIntent()
+ data object NavigateToUp: DetailIntent()
data object ClickPlace: DetailIntent()
data object HideBottomSheet: DetailIntent()
data class LoadWebView(val isLoading: Boolean): DetailIntent()
+ data object ClickOpenBrowser: DetailIntent()
+ data object ClickWebViewBack: DetailIntent()
+ data class UpdateCanGoBack(val canGoBack: Boolean): DetailIntent()
}
\ No newline at end of file
diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailSideEffect.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailSideEffect.kt
index c7d5f009..933334e3 100644
--- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailSideEffect.kt
+++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailSideEffect.kt
@@ -3,4 +3,7 @@ package com.easyhz.noffice.feature.announcement.contract.detail
import com.easyhz.noffice.core.common.base.UiSideEffect
sealed class DetailSideEffect: UiSideEffect() {
+ data object NavigateToUp: DetailSideEffect()
+ data class OpenBrowser(val url: String): DetailSideEffect()
+ data object NavigateToUpInWebView: DetailSideEffect()
}
\ No newline at end of file
diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailState.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailState.kt
index d1eb2a48..f33c9641 100644
--- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailState.kt
+++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/contract/detail/DetailState.kt
@@ -8,6 +8,7 @@ data class DetailState(
val isLoading: Boolean,
val isShowBottomSheet: Boolean,
val isWebViewLoading: Boolean,
+ val canGoBack: Boolean,
val detail: AnnouncementDetail
): UiState() {
companion object {
@@ -15,6 +16,7 @@ data class DetailState(
isLoading = true,
isShowBottomSheet = false,
isWebViewLoading = true,
+ canGoBack = false,
detail = AnnouncementDetail(
title = "",
creationDate = "",
@@ -46,7 +48,7 @@ internal val DUMMY = AnnouncementDetail(
organizationProfileImage = "",
date = "2024.07.27(토) 14:02",
place = "서울 창업 허브 : 장소 이름이름이름이름..",
- placeUrl = "www.naver.com",
+ placeUrl = "https://naver.me/IGJAhyjN",
content = """
15기 챌린저 전원이 함께 모여 작업할 수 있는 모각작 세션과 UT(User Test)가 진행됩니다.
User Test는 실제 사용자가 서비스를 테스트하며 피드백하는 중요한 과정입니다. 사용자가 주어진 작업을 완료하는 데 걸리는 시간을 관찰하는 등의 방법을 통해 사용성을 평가하고, 피드백을 받아 서비스를 더욱 더 발전시킬 수 있습니다.
diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/detail/AnnouncementDetailScreen.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/detail/AnnouncementDetailScreen.kt
index 9c9876b3..a713bb3d 100644
--- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/detail/AnnouncementDetailScreen.kt
+++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/detail/AnnouncementDetailScreen.kt
@@ -1,15 +1,20 @@
package com.easyhz.noffice.feature.announcement.screen.detail
+import android.content.Intent
+import android.net.Uri
+import android.webkit.WebView
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material3.Divider
@@ -18,16 +23,25 @@ import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
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.hilt.navigation.compose.hiltViewModel
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.bottomSheet.BottomSheet
+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.Blue600
import com.easyhz.noffice.core.design_system.theme.Green500
import com.easyhz.noffice.core.design_system.theme.Grey200
import com.easyhz.noffice.core.design_system.theme.Grey400
@@ -38,9 +52,11 @@ import com.easyhz.noffice.feature.announcement.component.detail.ContentField
import com.easyhz.noffice.feature.announcement.component.detail.DetailField
import com.easyhz.noffice.feature.announcement.component.detail.DetailTitle
import com.easyhz.noffice.feature.announcement.component.detail.OrganizationField
+import com.easyhz.noffice.feature.announcement.component.detail.PlaceBottomSheetTopBar
import com.easyhz.noffice.feature.announcement.component.detail.PlaceWebView
import com.easyhz.noffice.feature.announcement.component.detail.TaskListField
import com.easyhz.noffice.feature.announcement.contract.detail.DetailIntent
+import com.easyhz.noffice.feature.announcement.contract.detail.DetailSideEffect
import com.easyhz.noffice.feature.announcement.util.detail.DetailType
@OptIn(ExperimentalMaterial3Api::class)
@@ -54,7 +70,9 @@ fun AnnouncementDetailScreen(
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val scrollState = rememberScrollState()
-
+ val context = LocalContext.current
+ var backEnabled by remember { mutableStateOf(false) }
+ val webView = remember { WebView(context) }
LaunchedEffect(Unit) {
viewModel.postIntent(DetailIntent.InitScreen(id, title))
}
@@ -74,15 +92,15 @@ fun AnnouncementDetailScreen(
tint = Grey400
)
},
- onClick = { }
+ onClick = { viewModel.postIntent(DetailIntent.NavigateToUp) }
),
)
}
- ) {
+ ) { paddingValues ->
Column(
modifier = modifier
.verticalScroll(scrollState)
- .padding(it)
+ .padding(paddingValues)
.padding(horizontal = 24.dp)
.padding(bottom = 16.dp)
) {
@@ -136,26 +154,65 @@ fun AnnouncementDetailScreen(
}
if (uiState.isShowBottomSheet) {
BottomSheet(
+ shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp),
containerColor = White,
- onDismissRequest = { viewModel.postIntent(DetailIntent.HideBottomSheet) }
+ onDismissRequest = {
+ viewModel.postIntent(DetailIntent.HideBottomSheet)
+ },
+ dragHandle = {
+ PlaceBottomSheetTopBar(
+ placeUrl = uiState.detail.placeUrl,
+ ) {
+ viewModel.postIntent(DetailIntent.ClickWebViewBack)
+ }
+ }
) {
Box(
modifier = Modifier.fillMaxHeight(0.9f),
contentAlignment = Alignment.Center
) {
PlaceWebView(
- url = uiState.detail.placeUrl
+ modifier = Modifier.fillMaxSize(),
+ url = uiState.detail.placeUrl,
+ webView = webView,
+ onGoBack = {
+ viewModel.postIntent(DetailIntent.UpdateCanGoBack(it))
+ }
) {
viewModel.postIntent(DetailIntent.LoadWebView(it))
}
+ MediumButton(
+ modifier = Modifier
+ .screenHorizonPadding()
+ .fillMaxWidth()
+ .align(Alignment.BottomCenter)
+ .padding(bottom = 32.dp),
+ text = stringResource(id = R.string.announcement_detail_web_view_browser_button),
+ containerColor = Blue600,
+ contentColor = White,
+ onClick = { viewModel.postIntent(DetailIntent.ClickOpenBrowser) }
+ )
if (uiState.isWebViewLoading) {
- CircularProgressIndicator(
- strokeWidth = 3.dp,
- color = Green500
- )
+ CircularProgressIndicator(
+ strokeWidth = 3.dp,
+ color = Green500
+ )
}
}
}
}
}
+
+ viewModel.sideEffect.collectInSideEffectWithLifecycle { sideEffect ->
+ when (sideEffect) {
+ is DetailSideEffect.NavigateToUp -> { navigateToUp() }
+ is DetailSideEffect.OpenBrowser -> {
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse(sideEffect.url))
+ context.startActivity(intent)
+ }
+ is DetailSideEffect.NavigateToUpInWebView -> {
+ webView.goBack()
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/detail/AnnouncementDetailViewModel.kt b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/detail/AnnouncementDetailViewModel.kt
index d84ca598..15140847 100644
--- a/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/detail/AnnouncementDetailViewModel.kt
+++ b/feature/announcement/src/main/java/com/easyhz/noffice/feature/announcement/screen/detail/AnnouncementDetailViewModel.kt
@@ -20,10 +20,14 @@ class AnnouncementDetailViewModel @Inject constructor(
) {
override fun handleIntent(intent: DetailIntent) {
when(intent) {
- is DetailIntent.InitScreen -> { initScreen(intent.id, intent.title)}
+ is DetailIntent.InitScreen -> { initScreen(intent.id, intent.title) }
+ is DetailIntent.NavigateToUp -> { navigateToUp() }
is DetailIntent.ClickPlace -> { showBottomSheet() }
is DetailIntent.HideBottomSheet -> { hideBottomSheet() }
is DetailIntent.LoadWebView -> { onLoadWebView(intent.isLoading) }
+ is DetailIntent.ClickOpenBrowser -> { onClickOpenBrowser() }
+ is DetailIntent.ClickWebViewBack -> { onClickWebViewBack() }
+ is DetailIntent.UpdateCanGoBack -> { updateCanGoBack(intent.canGoBack) }
}
}
@@ -34,10 +38,14 @@ class AnnouncementDetailViewModel @Inject constructor(
private fun fetchData(id: Int) = viewModelScope.launch {
// FIXME
- delay(5000)
+ delay(1000)
reduce { copy(detail = DUMMY, isLoading = false) }
}
+ private fun navigateToUp() {
+ postSideEffect { DetailSideEffect.NavigateToUp }
+ }
+
private fun showBottomSheet() {
reduce { copy(isShowBottomSheet = true) }
}
@@ -49,4 +57,20 @@ class AnnouncementDetailViewModel @Inject constructor(
private fun onLoadWebView(isLoading: Boolean) {
reduce { copy(isWebViewLoading = isLoading) }
}
+
+ private fun onClickOpenBrowser() {
+ postSideEffect { DetailSideEffect.OpenBrowser(currentState.detail.placeUrl) }
+ }
+
+ private fun onClickWebViewBack() {
+ if (currentState.canGoBack) {
+ postSideEffect { DetailSideEffect.NavigateToUpInWebView }
+ } else {
+ hideBottomSheet()
+ }
+ }
+
+ private fun updateCanGoBack(canGoBack: Boolean) {
+ reduce { copy(canGoBack = canGoBack) }
+ }
}
\ No newline at end of file