diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentScreen.kt index ecf7dbb1..14c8ca6f 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailContentScreen.kt @@ -2,13 +2,14 @@ package com.nexters.boolti.presentation.screen.showdetail import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.ClickableText import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -17,6 +18,7 @@ import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.BtBackAppBar import com.nexters.boolti.presentation.theme.Grey30 import com.nexters.boolti.presentation.theme.marginHorizontal +import com.nexters.boolti.presentation.util.UrlParser @Composable fun ShowDetailContentScreen( @@ -24,8 +26,11 @@ fun ShowDetailContentScreen( modifier: Modifier = Modifier, viewModel: ShowDetailViewModel = hiltViewModel(), ) { - val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val uriHandler = LocalUriHandler.current val scrollState = rememberScrollState() + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val notice = uiState.showDetail.notice + val urlParser = UrlParser(notice) Scaffold( modifier = modifier, @@ -36,14 +41,21 @@ fun ShowDetailContentScreen( ) }, ) { innerPadding -> - Text( + ClickableText( modifier = Modifier .verticalScroll(state = scrollState) .padding(innerPadding) .padding(horizontal = marginHorizontal) .padding(top = 20.dp), - text = uiState.showDetail.notice, + text = urlParser.annotatedString, style = MaterialTheme.typography.bodyLarge.copy(color = Grey30), - ) + ) { offset -> + val urlOffset = urlParser.urlOffsets.find { (start, end) -> offset in start.. + val urlOffset = urlParser.urlOffsets.find { (start, end) -> offset in start..() + val urlOffsets get() = _urlOffsets.toList() + + init { + val linkMatch = urlRegex.toPattern().matcher(url) + + while (linkMatch.find()) { + _urlOffsets.add(UrlOffset(linkMatch.start(), linkMatch.end())) + } + + annotatedString = buildAnnotatedString { + append(url) + _urlOffsets.forEach { (start, end) -> + addStyle( + SpanStyle( + textDecoration = TextDecoration.Underline, + color = Color(0xFF46A6FF) + ), + start, + end, + ) + } + } + } +} + +data class UrlOffset( + val start: Int, + val end: Int +) \ No newline at end of file diff --git a/presentation/src/test/java/com/nexters/boolti/presentation/ExampleUnitTest.kt b/presentation/src/test/java/com/nexters/boolti/presentation/ExampleUnitTest.kt deleted file mode 100644 index 8cc33356..00000000 --- a/presentation/src/test/java/com/nexters/boolti/presentation/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.nexters.boolti.presentation - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/presentation/src/test/java/com/nexters/boolti/presentation/util/UrlParserTest.kt b/presentation/src/test/java/com/nexters/boolti/presentation/util/UrlParserTest.kt new file mode 100644 index 00000000..a6cb474e --- /dev/null +++ b/presentation/src/test/java/com/nexters/boolti/presentation/util/UrlParserTest.kt @@ -0,0 +1,24 @@ +package com.nexters.boolti.presentation.util + +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.matchers.shouldBe + +class UrlParserTest : BehaviorSpec() { + init { + given("url이 포함된 문자열이 주어지고") { + val targetString = "https://www.naver.com 적당한https://www.naver.com 문자열 http://www.naver.com" + + `when`("이 문자열을 파싱하면") { + val urlParser = UrlParser(targetString) + val urlOffsets = urlParser.urlOffsets + + then("문자열 내 url의 start, end offset을 확인할 수 있다") { + urlOffsets.size shouldBe 3 + urlOffsets[0] shouldBe UrlOffset(0, 21) + urlOffsets[1] shouldBe UrlOffset(25, 46) + urlOffsets[2] shouldBe UrlOffset(51, 71) + } + } + } + } +} \ No newline at end of file