...
This commit is contained in:
parent
9fefdc22f2
commit
3c7f652f38
@ -197,7 +197,7 @@ class OverseasFinancialService(
|
|||||||
suspend fun fetchKeyMetrics(symbol: String): Result<KeyMetrics> {
|
suspend fun fetchKeyMetrics(symbol: String): Result<KeyMetrics> {
|
||||||
return try {
|
return try {
|
||||||
val response: List<KeyMetrics> = client.get("$baseUrl/key-metrics-ttm/$symbol") {
|
val response: List<KeyMetrics> = client.get("$baseUrl/key-metrics-ttm/$symbol") {
|
||||||
parameter("apikey", apiKey)
|
parameter("apikey", "FzvO_2P679KGRDUVx3u7rkMvCvcqiynu")
|
||||||
}.body()
|
}.body()
|
||||||
|
|
||||||
if (response.isNotEmpty()) {
|
if (response.isNotEmpty()) {
|
||||||
|
|||||||
@ -3,12 +3,10 @@ package ui
|
|||||||
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.ScrollState
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.gestures.awaitEachGesture
|
|
||||||
import androidx.compose.foundation.gestures.awaitFirstDown
|
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.gestures.waitForUpOrCancellation
|
|
||||||
import androidx.compose.foundation.horizontalScroll
|
import androidx.compose.foundation.horizontalScroll
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
@ -32,9 +30,12 @@ import androidx.compose.ui.ExperimentalComposeUiApi
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.pointer.PointerType
|
import androidx.compose.ui.input.pointer.PointerEventType
|
||||||
import androidx.compose.ui.input.pointer.isSecondaryPressed
|
import androidx.compose.ui.input.pointer.isSecondaryPressed
|
||||||
|
import androidx.compose.ui.input.pointer.onPointerEvent
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
|
import androidx.compose.ui.layout.positionInParent
|
||||||
import androidx.compose.ui.onExternalDrag
|
import androidx.compose.ui.onExternalDrag
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
@ -79,6 +80,7 @@ fun TradingDecisionLog() {
|
|||||||
webSocketConnect = AutoTradingManager.webSocketConnect
|
webSocketConnect = AutoTradingManager.webSocketConnect
|
||||||
}
|
}
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
val filterScrollState = rememberScrollState()
|
||||||
|
|
||||||
// [핵심] 원본 로그에서 필터 조건에 맞는 리스트만 산출
|
// [핵심] 원본 로그에서 필터 조건에 맞는 리스트만 산출
|
||||||
val filteredLogs = TradingLogStore.decisionLogs.filter { log ->
|
val filteredLogs = TradingLogStore.decisionLogs.filter { log ->
|
||||||
@ -131,6 +133,7 @@ fun TradingDecisionLog() {
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.horizontalScroll(scrollState),
|
.horizontalScroll(scrollState),
|
||||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
filterOptions.forEach { option ->
|
filterOptions.forEach { option ->
|
||||||
val isSelected = selectedFilters.contains(option)
|
val isSelected = selectedFilters.contains(option)
|
||||||
@ -175,7 +178,8 @@ fun TradingDecisionLog() {
|
|||||||
if (newFilters.isEmpty()) newFilters.add("전체")
|
if (newFilters.isEmpty()) newFilters.add("전체")
|
||||||
selectedFilters = newFilters
|
selectedFilters = newFilters
|
||||||
},
|
},
|
||||||
label = option
|
label = option,
|
||||||
|
scrollState = scrollState
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -787,65 +791,74 @@ fun SettingSwitchField(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class, ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun FilterChipWithRightClick(
|
fun FilterChipWithRightClick(
|
||||||
label: String,
|
label: String,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onClear: () -> Unit
|
onClear: () -> Unit,
|
||||||
|
scrollState: ScrollState // 스크롤 제어를 위해 추가
|
||||||
) {
|
) {
|
||||||
var showMenu by remember { mutableStateOf(false) }
|
var showMenu by remember { mutableStateOf(false) }
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
Box {
|
var componentOffset by remember { mutableStateOf(0f) }
|
||||||
|
var componentWidth by remember { mutableStateOf(0) } // 칩 너비 추가
|
||||||
|
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||||
|
componentOffset = coordinates.positionInParent().x
|
||||||
|
componentWidth = coordinates.size.width // 칩의 실제 너비 저장
|
||||||
|
}
|
||||||
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(end = 6.dp)
|
.padding(end = 6.dp)
|
||||||
.pointerInput(label) {
|
.pointerInput(label) {
|
||||||
awaitEachGesture {
|
detectTapGestures(
|
||||||
val down = awaitFirstDown()
|
onTap = {
|
||||||
// 💡 마우스 우클릭 검사
|
onClick()
|
||||||
if (down.type == PointerType.Mouse && currentEvent.buttons.isSecondaryPressed) {
|
coroutineScope.launch {
|
||||||
down.consume()
|
// 부모(Row)의 전체 너비 가져오기
|
||||||
showMenu = true
|
val containerWidth = scrollState.maxValue + scrollState.viewportSize
|
||||||
} else {
|
val viewportWidth = scrollState.viewportSize
|
||||||
// 💡 터치/좌클릭 시 롱클릭 판별
|
|
||||||
val up = withTimeoutOrNull(viewConfiguration.longPressTimeoutMillis) {
|
// 🎯 중앙 정렬 공식:
|
||||||
waitForUpOrCancellation()
|
// (칩의 절대 위치) - (화면 절반) + (칩 너비의 절반)
|
||||||
}
|
val targetScroll = (scrollState.value + componentOffset + (componentWidth / 2)) - (viewportWidth / 2)
|
||||||
if (up == null) {
|
|
||||||
showMenu = true // 롱클릭
|
// 0과 최대 스크롤 범위 사이로 제한하여 부드럽게 이동
|
||||||
} else {
|
scrollState.animateScrollTo(
|
||||||
up.consume()
|
targetScroll.toInt().coerceIn(0, scrollState.maxValue)
|
||||||
onClick() // 일반 클릭
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
// 마우스 우클릭 별도 감지
|
||||||
|
.onPointerEvent(PointerEventType.Press) { event ->
|
||||||
|
if (event.buttons.isSecondaryPressed) showMenu = true
|
||||||
},
|
},
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(8.dp),
|
||||||
color = if (isSelected) Color(0xFF0E62CF) else Color(0xFFF0F2F5),
|
color = if (isSelected) Color(0xFF0E62CF) else Color(0xFFF0F2F5),
|
||||||
border = BorderStroke(1.dp, if (isSelected) Color(0xFF0E62CF) else Color.LightGray)
|
border = BorderStroke(1.dp, if (isSelected) Color(0xFF0E62CF) else Color.LightGray)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = label,
|
text = label,
|
||||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
|
modifier = Modifier.padding(horizontal = 6.dp, vertical = 6.dp),
|
||||||
fontSize = 11.sp,
|
fontSize = 11.sp,
|
||||||
color = if (isSelected) Color.White else Color.Black,
|
color = if (isSelected) Color.White else Color.Black
|
||||||
fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 💡 우클릭 시 나타나는 메뉴
|
DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
|
||||||
DropdownMenu(
|
|
||||||
expanded = showMenu,
|
|
||||||
onDismissRequest = { showMenu = false }
|
|
||||||
) {
|
|
||||||
DropdownMenuItem(onClick = {
|
DropdownMenuItem(onClick = {
|
||||||
onClear() // 실제 삭제 수행
|
onClear()
|
||||||
showMenu = false
|
showMenu = false
|
||||||
}) {
|
}) {
|
||||||
val menuText = if (label == "전체") "전체 로그 초기화" else "'$label' 관련 로그 삭제"
|
Text(if (label == "전체") "전체 로그 초기화" else "'$label' 삭제", color = Color.Red)
|
||||||
Text(menuText, color = Color.Red, fontSize = 12.sp)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user