Skip to content
Closed
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
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@ dependencies {
implementation(projects.feature.course)
implementation(projects.feature.collection)
implementation(projects.feature.maps)
implementation(projects.feature.serach)
implementation(libs.kakao.login)
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ fun SolplyPlaceCard(
else -> SolplyTheme.colors.gray400
}
val iconBackGroundColor = when (placeType) {
PlaceType.CAFE -> SolplyTheme.colors.red200
PlaceType.FOOD -> SolplyTheme.colors.yellow100
PlaceType.WALKING, PlaceType.UNIQUE_SPACE -> SolplyTheme.colors.green200
PlaceType.SHOPPING, PlaceType.BOOKSTORE -> SolplyTheme.colors.purple100
else -> SolplyTheme.colors.gray200
PlaceType.CAFE -> SolplyTheme.colors.red500
PlaceType.FOOD -> SolplyTheme.colors.yellow400
PlaceType.WALKING, PlaceType.UNIQUE_SPACE -> SolplyTheme.colors.green500
PlaceType.SHOPPING, PlaceType.BOOKSTORE -> SolplyTheme.colors.purple200
else -> SolplyTheme.colors.purple500
}

Column(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.teamsolply.solply.designsystem.component.searchbar

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.teamsolply.solply.designsystem.R
import com.teamsolply.solply.designsystem.theme.SolplyTheme

@Composable
fun SolplySearchbar(
modifier: Modifier,
query: String,
onQueryChange: (String) -> Unit,
onImageClick: () -> Unit
) {
val keyboardController = LocalSoftwareKeyboardController.current
val focusRequester = remember { FocusRequester() }

Row(
modifier = modifier
.fillMaxWidth()
.border(
width = 1.dp,
color = SolplyTheme.colors.gray300,
shape = RoundedCornerShape(20.dp)
)
.background(color = SolplyTheme.colors.white)
.padding(vertical = 14.dp, horizontal = 20.dp)
.clickable {
focusRequester.requestFocus()
keyboardController?.show()
},
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
BasicTextField(
value = query,
onValueChange = onQueryChange,
modifier = modifier
.weight(1f)
.fillMaxWidth()
.focusRequester(focusRequester),
Comment on lines +54 to +60
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue | ๐Ÿ”ด Critical

weight import ๋ˆ„๋ฝ์œผ๋กœ ์ปดํŒŒ์ผ์ด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

Line 58์—์„œ .weight(1f)๋ฅผ ํ˜ธ์ถœํ•˜์ง€๋งŒ androidx.compose.foundation.layout.weight import๊ฐ€ ์—†์–ด์„œ ๋นŒ๋“œ๊ฐ€ ๋ฉˆ์ถฅ๋‹ˆ๋‹ค. ์•„๋ž˜์ฒ˜๋Ÿผ import๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”.

+import androidx.compose.foundation.layout.weight
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
BasicTextField(
value = query,
onValueChange = onQueryChange,
modifier = modifier
.weight(1f)
.fillMaxWidth()
.focusRequester(focusRequester),
// Add at the top of SolplySearchBar.kt alongside the other Compose imports
import androidx.compose.foundation.layout.weight
๐Ÿค– Prompt for AI Agents
In
core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/searchbar/SolplySearchBar.kt
around lines 54 to 60, the call to .weight(1f) causes a compile error because
the androidx.compose.foundation.layout.weight import is missing; add the import
statement for androidx.compose.foundation.layout.weight at the top of the file
(with the other imports) so Modifier.weight is resolved and the file compiles.

textStyle = SolplyTheme.typography.body16R.copy(color = SolplyTheme.colors.black),
decorationBox = { innerTextField ->
if (query.isEmpty()) {
Text(
text = "์ฐพ๋Š” ๊ณต๊ฐ„์„ ์ž…๋ ฅํ•˜์„ธ์š”",
style = SolplyTheme.typography.body16R,
color = SolplyTheme.colors.gray500
)
}
innerTextField()
},
maxLines = 1,
singleLine = true
)
Image(
painter = painterResource(id = R.drawable.ic_setting), // ic_search 134 ๋ธŒ๋žœ์น˜ ๋“ค์–ด์˜ค๊ณ  ๋ฐ”๊ฟ”์•ผํ•จ
contentDescription = "Search Icon",
modifier = Modifier
.clickable {
onImageClick()
keyboardController?.hide()
}
)
}
}

@Preview
@Composable
fun SearchBoxPreview() {
SolplyTheme {
SolplySearchbar(
modifier = Modifier.fillMaxWidth(),
query = "",
onQueryChange = {},
onImageClick = {}
)
}
}
1 change: 1 addition & 0 deletions domain/search/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
8 changes: 8 additions & 0 deletions domain/search/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
plugins {
alias(libs.plugins.solply.java.library)
}

dependencies {
implementation(projects.core.model)
implementation(libs.bundles.coroutine)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.teamsolply.solply.search

class MyClass
1 change: 1 addition & 0 deletions feature/main/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dependencies {
implementation(projects.feature.oauth)
implementation(projects.feature.onboarding)
implementation(projects.feature.place)
implementation(projects.feature.serach)
implementation(projects.feature.course)
implementation(projects.feature.maps)
implementation(projects.feature.collection)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class MainActivity : ComponentActivity() {
setContent {
SolplyTheme {
MainScreen(
modifier = Modifier.fillMaxSize()
modifier = Modifier.fillMaxSize(),
isFreshLaunch = savedInstanceState == null
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.teamsolply.solply.maps.navigation.navigateMaps
import com.teamsolply.solply.oauth.navigation.navigateOauth
import com.teamsolply.solply.onboarding.navigation.navigateOnBoarding
import com.teamsolply.solply.place.navigation.navigatePlace
import com.teamsolply.solply.search.navigation.navigateSearch

internal class MainNavigator(
val navController: NavHostController
Expand Down Expand Up @@ -85,6 +86,10 @@ internal class MainNavigator(
navController.navigatePlace(navOptions)
}

fun navigateToSearch(navOptions: NavOptions) {
navController.navigateSearch(navOptions)
}

fun navigateToCourse(navOptions: NavOptions) {
navController.navigateCourse(navOptions)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.dp
import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.compose.NavHost
import androidx.navigation.navOptions
import com.teamsolply.solply.collection.collection.course.courseCollectionNavGraph
Expand All @@ -38,24 +41,51 @@ import com.teamsolply.solply.main.component.MainBottomBar
import com.teamsolply.solply.main.model.SolplySnackBarData
import com.teamsolply.solply.main.splash.splashNavGraph
import com.teamsolply.solply.maps.navigation.mapsNavGraph
import com.teamsolply.solply.model.MapsType
import com.teamsolply.solply.model.SnackBarType
import com.teamsolply.solply.oauth.navigation.oauthNavGraph
import com.teamsolply.solply.onboarding.navigation.onBoardingNavGraph
import com.teamsolply.solply.place.navigation.placeNavGraph
import com.teamsolply.solply.search.navigation.Search
import com.teamsolply.solply.search.navigation.searchNavGraph
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch

@Composable
internal fun MainScreen(
modifier: Modifier = Modifier,
navigator: MainNavigator = rememberMainNavigator()
navigator: MainNavigator = rememberMainNavigator(),
isFreshLaunch: Boolean = false
) {
val coroutineScope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
val currentSnackbarJob = remember { mutableStateOf<Job?>(null) }
val currentSnackbarState = remember { mutableStateOf(SolplySnackBarData()) }

val navController = navigator.navController

if (isFreshLaunch) {
LaunchedEffect(navController) {
val initialDestination = snapshotFlow { navController.currentBackStackEntry }
.filterNotNull()
.first()
.destination

if (!initialDestination.hasRoute(Search::class)) {
val initialNavOptions = navOptions {
popUpTo(0) {
inclusive = true
}
launchSingleTop = true
}
navigator.navigateToSearch(initialNavOptions)
}
}
}
Comment on lines +71 to +87
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue | ๐Ÿ”ด Critical

์ดˆ๊ธฐ ๋ฐฑ์Šคํƒ ์กฐํšŒ๊ฐ€ ์˜์›ํžˆ ์™„๋ฃŒ๋˜์ง€ ์•Š๋Š” ๋ฒ„๊ทธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค

navController.currentBackStackEntry๋Š” Compose ์Šค๋ƒ…์ƒท ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ์–ด์„œ snapshotFlow๊ฐ€ ์ดํ›„ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ์ฒซ ์ฝ๊ธฐ๊ฐ€ null์ด๋ฉด filterNotNull().first()๊ฐ€ ๋๊นŒ์ง€ ๋Œ€๊ธฐํ•˜์—ฌ ๊ฒ€์ƒ‰ ํ™”๋ฉด์œผ๋กœ์˜ ์ดˆ๊ธฐ ์ง„์ž…์ด ์ ˆ๋Œ€ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฝœ๋“œ ์Šคํƒ€ํŠธ์—์„œ ์‹ค์ œ๋กœ ์ด๋Ÿฐ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•„์ˆ˜ ์ˆ˜์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

         LaunchedEffect(navController) {
-            val initialDestination = snapshotFlow { navController.currentBackStackEntry }
-                .filterNotNull()
-                .first()
-                .destination
+            val initialDestination = navController.currentBackStackEntryFlow
+                .filterNotNull()
+                .first()
+                .destination

import androidx.navigation.currentBackStackEntryFlow ์ถ”๊ฐ€๋„ ํ•จ๊ป˜ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿค– Prompt for AI Agents
In feature/main/src/main/java/com/teamsolply/solply/main/MainScreen.kt around
lines 71-87, the use of snapshotFlow { navController.currentBackStackEntry }
never observes later changes because currentBackStackEntry is not a Compose
snapshot state; replace it with navController.currentBackStackEntryFlow and
change the await to use filterNotNull().first() on that flow to obtain the
initial destination without hanging, and add the import
androidx.navigation.currentBackStackEntryFlow at the top of the file.


suspend fun showTextSnackBar(message: String) {
currentSnackbarJob.value?.join()
currentSnackbarJob.value = coroutineScope.launch {
Expand Down Expand Up @@ -128,6 +158,29 @@ internal fun MainScreen(
.background(color = SolplyTheme.colors.gray100)
.fillMaxSize()
) {
searchNavGraph(
paddingValues = innerPadding,
onBack = navigator::navigateToBack,
navigateToPlaceDetail = { townId, placeId ->
val navOptions = navOptions {}
navigator.navigateToMaps(
mapsType = MapsType.PLACE_DETAIL.name,
townId = townId,
placeId = placeId,
courseId = null,
navOptions = navOptions
)
},
onNoPlaceClick = {
val navOptions = navOptions {
popUpTo(0) {
inclusive = true
}
launchSingleTop = true
}
navigator.navigateToPlace(navOptions = navOptions)
}
)
splashNavGraph(
navigateToOauth = {
val navOptions = navOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.teamsolply.solply.maps.component.bottomsheet
import ClickableAnnotatedText
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.teamsolply.solply.place

import android.util.Log
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
Expand Down
1 change: 1 addition & 0 deletions feature/serach/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
15 changes: 15 additions & 0 deletions feature/serach/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
plugins {
alias(libs.plugins.solply.feature)
}

android {
namespace = "com.teamsolply.solply.search"
}

dependencies {
implementation(projects.core.designsystem)
implementation(projects.core.model)
implementation(projects.core.ui)
implementation(projects.domain.place)
implementation(projects.domain.search)
}
Empty file.
21 changes: 21 additions & 0 deletions feature/serach/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
4 changes: 4 additions & 0 deletions feature/serach/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.teamsolply.solply.search

import com.teamsolply.solply.model.PlaceType
import com.teamsolply.solply.ui.base.SideEffect
import com.teamsolply.solply.ui.base.UiIntent
import com.teamsolply.solply.ui.base.UiState
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf

data class SearchState(
val query: String = "",
val isLoading: Boolean = false,
val results: PersistentList<SearchItemUi> = persistentListOf(),
val selectedTownId: Long? = null
) : UiState {
val resultCount: Int get() = results.size
val hasQuery: Boolean get() = query.isNotBlank()
}

data class SearchItemUi(
val id: Long,
val name: String,
val tag: PlaceType,
val address: String,
val imageUrl: String
)

sealed interface SearchIntent : UiIntent {
data class QueryChanged(val value: String) : SearchIntent
data object ClearQuery : SearchIntent
data object Submit : SearchIntent
data class ClickItem(val id: Long) : SearchIntent
data object ClickNoResult : SearchIntent
data object Retry : SearchIntent
}

sealed interface SearchSideEffect : SideEffect {
data class NavigateToPlaceDetail(val townId: Long, val placeId: Long) : SearchSideEffect
data object NavigateToNoResult : SearchSideEffect
}
Loading