Setup: Auto Scan Interval을 별도 화면(AUTO_SCAN_SETUP)으로 분리
사내 피드백: PiezoPersonalization 한 화면에 카드 3개를 모은 게 아니라 MaxCapacity 다음 단계로 Auto Scan Interval 화면을 독립적으로 띄우길 원함. 흐름 변경: before: DeviceScan → PiezoPersonalization (MaxCapacity + Catheter + AutoScan) → Placement after: DeviceScan → PiezoPersonalization (MaxCapacity + Catheter) → AutoScanSetup → Placement 변경: - PiezoPersonalizationView 원복: 2카드 / verticalScroll 제거 / spacing 64dp 중앙 정렬 - AutoScanIntervalSetupView 신규 — Bladdy hero + 큰 카드 + Continue 버튼 - AppScreen.AUTO_SCAN_SETUP enum 추가 - personalizationComplete() → AUTO_SCAN_SETUP으로 이동 - autoScanSetupComplete() 신설 — PlacementGuide로 진행, piezoSettings persist - backFromPlacementGuide() — setup 흐름이면 AUTO_SCAN_SETUP으로 돌아감 - MainActivity 라우팅 + Back handler 추가 (AUTO_SCAN_SETUP → PiezoPersonalization) PiezoMonitoring의 in-monitoring settings 슬라이더는 그대로 유지 — 측정 중에도 같은 PiezoSettings.autoScanIntervalMs 값을 조정 가능. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,8 @@ import com.example.medilightv2android.services.*
|
|||||||
|
|
||||||
enum class AppScreen {
|
enum class AppScreen {
|
||||||
ONBOARDING, CONSENT, REGISTRATION, HOME, SENSOR_SELECT, DEVICE_SCAN,
|
ONBOARDING, CONSENT, REGISTRATION, HOME, SENSOR_SELECT, DEVICE_SCAN,
|
||||||
NIRS_CALIBRATION, PIEZO_PERSONALIZATION, PLACEMENT_GUIDE, PIN_SETUP, PIN_ENTRY,
|
NIRS_CALIBRATION, PIEZO_PERSONALIZATION, AUTO_SCAN_SETUP, // ★ MaxCapacity 다음 단계 — Auto Scan Interval 별도 화면
|
||||||
|
PLACEMENT_GUIDE, PIN_SETUP, PIN_ENTRY,
|
||||||
VIVAMYO_MONITORING, PIEZO_MONITORING, MEASUREMENT_HISTORY,
|
VIVAMYO_MONITORING, PIEZO_MONITORING, MEASUREMENT_HISTORY,
|
||||||
VOIDING_DIARY, URINE_CAMERA, REMINDER_SETTINGS,
|
VOIDING_DIARY, URINE_CAMERA, REMINDER_SETTINGS,
|
||||||
CLINICAL_HOME, // ★ Dev 모드 한정 — 임상 R&D 측정 세션 화면 (입력 폼)
|
CLINICAL_HOME, // ★ Dev 모드 한정 — 임상 R&D 측정 세션 화면 (입력 폼)
|
||||||
@@ -135,6 +136,13 @@ class AppState(private val context: Context) {
|
|||||||
fun personalizationComplete() {
|
fun personalizationComplete() {
|
||||||
storage.piezoSettings = piezoSettings
|
storage.piezoSettings = piezoSettings
|
||||||
placementFromMonitoring = false
|
placementFromMonitoring = false
|
||||||
|
// MaxCapacity 화면 다음에 AutoScan Interval 설정 화면을 거쳐 Placement로 진행
|
||||||
|
currentScreen = AppScreen.AUTO_SCAN_SETUP
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Auto Scan Interval 설정 화면 → Placement Guide */
|
||||||
|
fun autoScanSetupComplete() {
|
||||||
|
storage.piezoSettings = piezoSettings
|
||||||
currentScreen = AppScreen.PLACEMENT_GUIDE
|
currentScreen = AppScreen.PLACEMENT_GUIDE
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +169,7 @@ class AppState(private val context: Context) {
|
|||||||
|
|
||||||
fun backFromPlacementGuide() {
|
fun backFromPlacementGuide() {
|
||||||
currentScreen = if (placementFromMonitoring) AppScreen.PIEZO_MONITORING
|
currentScreen = if (placementFromMonitoring) AppScreen.PIEZO_MONITORING
|
||||||
else AppScreen.PIEZO_PERSONALIZATION
|
else AppScreen.AUTO_SCAN_SETUP // setup 흐름: Placement ← AutoScan ← MaxCapacity
|
||||||
placementFromMonitoring = false
|
placementFromMonitoring = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.example.medilightv2android.ui.views.monitoring.VivamyoMonitoringView
|
|||||||
import com.example.medilightv2android.ui.views.monitoring.UrineCameraScreen
|
import com.example.medilightv2android.ui.views.monitoring.UrineCameraScreen
|
||||||
import com.example.medilightv2android.ui.views.monitoring.VoidingDiaryView
|
import com.example.medilightv2android.ui.views.monitoring.VoidingDiaryView
|
||||||
import com.example.medilightv2android.ui.views.onboarding.OnboardingView
|
import com.example.medilightv2android.ui.views.onboarding.OnboardingView
|
||||||
|
import com.example.medilightv2android.ui.views.personalization.AutoScanIntervalSetupView
|
||||||
import com.example.medilightv2android.ui.views.personalization.PiezoPersonalizationView
|
import com.example.medilightv2android.ui.views.personalization.PiezoPersonalizationView
|
||||||
import com.example.medilightv2android.ui.views.pin.PinMode
|
import com.example.medilightv2android.ui.views.pin.PinMode
|
||||||
import com.example.medilightv2android.ui.views.pin.PinView
|
import com.example.medilightv2android.ui.views.pin.PinView
|
||||||
@@ -103,6 +104,7 @@ fun MedilightApp(modifier: Modifier = Modifier) {
|
|||||||
AppScreen.CLINICAL_HOME
|
AppScreen.CLINICAL_HOME
|
||||||
else AppScreen.HOME
|
else AppScreen.HOME
|
||||||
AppScreen.PIEZO_PERSONALIZATION -> appState.currentScreen = AppScreen.DEVICE_SCAN
|
AppScreen.PIEZO_PERSONALIZATION -> appState.currentScreen = AppScreen.DEVICE_SCAN
|
||||||
|
AppScreen.AUTO_SCAN_SETUP -> appState.currentScreen = AppScreen.PIEZO_PERSONALIZATION
|
||||||
// 임상 모드 측정 중이면 endMeasurement → ClinicalHome 복귀
|
// 임상 모드 측정 중이면 endMeasurement → ClinicalHome 복귀
|
||||||
AppScreen.PLACEMENT_GUIDE ->
|
AppScreen.PLACEMENT_GUIDE ->
|
||||||
if (com.example.medilightv2android.services.ClinicalSessionStore.currentSession != null) {
|
if (com.example.medilightv2android.services.ClinicalSessionStore.currentSession != null) {
|
||||||
@@ -152,6 +154,7 @@ fun MedilightApp(modifier: Modifier = Modifier) {
|
|||||||
AppScreen.DEVICE_SCAN -> DeviceScanView(appState)
|
AppScreen.DEVICE_SCAN -> DeviceScanView(appState)
|
||||||
AppScreen.NIRS_CALIBRATION -> CalibrationView(appState)
|
AppScreen.NIRS_CALIBRATION -> CalibrationView(appState)
|
||||||
AppScreen.PIEZO_PERSONALIZATION -> PiezoPersonalizationView(appState)
|
AppScreen.PIEZO_PERSONALIZATION -> PiezoPersonalizationView(appState)
|
||||||
|
AppScreen.AUTO_SCAN_SETUP -> AutoScanIntervalSetupView(appState)
|
||||||
AppScreen.PLACEMENT_GUIDE -> PlacementGuideView(appState)
|
AppScreen.PLACEMENT_GUIDE -> PlacementGuideView(appState)
|
||||||
AppScreen.PIN_SETUP -> PinView(appState, PinMode.SETUP, context)
|
AppScreen.PIN_SETUP -> PinView(appState, PinMode.SETUP, context)
|
||||||
AppScreen.PIN_ENTRY -> PinView(appState, PinMode.ENTER, context)
|
AppScreen.PIN_ENTRY -> PinView(appState, PinMode.ENTER, context)
|
||||||
|
|||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
package com.example.medilightv2android.ui.views.personalization
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Remove
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.min
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.example.medilightv2android.AppScreen
|
||||||
|
import com.example.medilightv2android.AppState
|
||||||
|
import com.example.medilightv2android.ui.components.BladdyIdleRiveView
|
||||||
|
import com.example.medilightv2android.ui.theme.*
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto Scan Interval 설정 화면.
|
||||||
|
*
|
||||||
|
* 흐름: PiezoPersonalization(MaxCapacity + CatheterThreshold)
|
||||||
|
* → 본 화면 (Auto Scan Interval)
|
||||||
|
* → PlacementGuide → PiezoMonitoring
|
||||||
|
*
|
||||||
|
* 600~5000ms 범위, 200ms 단위 snap. PiezoMonitoring의 in-monitoring settings에서
|
||||||
|
* 같은 슬라이더로 측정 중에도 조정 가능.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun AutoScanIntervalSetupView(appState: AppState) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.White)
|
||||||
|
) {
|
||||||
|
// Header
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 24.dp, vertical = 16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
IconButton(onClick = { appState.currentScreen = AppScreen.PIEZO_PERSONALIZATION }) {
|
||||||
|
Icon(Icons.AutoMirrored.Filled.ArrowBack, "Back", tint = MlPrimary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bladdy hero — Personalization과 동일 톤
|
||||||
|
BoxWithConstraints(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 8.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
val bladdySize = min(maxWidth, 220.dp)
|
||||||
|
BladdyIdleRiveView(size = bladdySize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 카드 영역 — 중앙 정렬
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 24.dp),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
AutoScanIntervalCard(appState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue button
|
||||||
|
Button(
|
||||||
|
onClick = { appState.autoScanSetupComplete() },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 32.dp)
|
||||||
|
.padding(bottom = 32.dp)
|
||||||
|
.height(56.dp),
|
||||||
|
shape = RoundedCornerShape(16.dp),
|
||||||
|
colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent),
|
||||||
|
contentPadding = PaddingValues()
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize().background(
|
||||||
|
Brush.horizontalGradient(listOf(MlTeal, MlPrimary)),
|
||||||
|
RoundedCornerShape(16.dp)
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text("Continue", fontSize = 18.sp, fontWeight = FontWeight.SemiBold, color = Color.White)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AutoScanIntervalCard(appState: AppState) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
colors = CardDefaults.cardColors(containerColor = MlPrimary.copy(alpha = 0.06f))
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(horizontal = 20.dp, vertical = 24.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text("Auto Scan Interval", fontSize = 34.sp, fontWeight = FontWeight.ExtraBold)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Text("Auto scan 사이클 간격 — 짧을수록 빠른 갱신, 길수록 안정",
|
||||||
|
fontSize = 13.sp, color = MlSecondaryText)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
Row(verticalAlignment = Alignment.Bottom) {
|
||||||
|
Text("${appState.piezoSettings.autoScanIntervalMs}", fontSize = 64.sp,
|
||||||
|
fontWeight = FontWeight.ExtraBold, color = MlPrimary)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text("ms", fontSize = 24.sp, fontWeight = FontWeight.SemiBold, color = MlSecondaryText,
|
||||||
|
modifier = Modifier.padding(bottom = 12.dp))
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) {
|
||||||
|
StepperButton(icon = Icons.Default.Remove) {
|
||||||
|
val next = (appState.piezoSettings.autoScanIntervalMs - 200).coerceIn(600, 5000)
|
||||||
|
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = next)
|
||||||
|
}
|
||||||
|
StepperButton(icon = Icons.Default.Add) {
|
||||||
|
val next = (appState.piezoSettings.autoScanIntervalMs + 200).coerceIn(600, 5000)
|
||||||
|
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
Slider(
|
||||||
|
value = appState.piezoSettings.autoScanIntervalMs.toFloat(),
|
||||||
|
onValueChange = {
|
||||||
|
val snapped = ((it / 200f).roundToInt() * 200).coerceIn(600, 5000)
|
||||||
|
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = snapped)
|
||||||
|
},
|
||||||
|
valueRange = 600f..5000f,
|
||||||
|
steps = 21,
|
||||||
|
modifier = Modifier.padding(horizontal = 24.dp),
|
||||||
|
colors = SliderDefaults.colors(thumbColor = MlPrimary, activeTrackColor = MlPrimary)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun StepperButton(icon: androidx.compose.ui.graphics.vector.ImageVector, onClick: () -> Unit) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(56.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(MlPrimary)
|
||||||
|
.clickable(onClick = onClick),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Icon(icon, null, tint = Color.White, modifier = Modifier.size(28.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-58
@@ -6,8 +6,6 @@ import androidx.compose.foundation.background
|
|||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@@ -68,21 +66,17 @@ fun PiezoPersonalizationView(appState: AppState) {
|
|||||||
BladdyIdleRiveView(size = bladdySize)
|
BladdyIdleRiveView(size = bladdySize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 카드 영역 — 3카드 스크롤 가능
|
// 카드 영역 — 수직 중앙 정렬
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.verticalScroll(rememberScrollState())
|
|
||||||
.padding(horizontal = 24.dp),
|
.padding(horizontal = 24.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(24.dp),
|
verticalArrangement = Arrangement.spacedBy(64.dp, Alignment.CenterVertically),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
MaxVolumeCard(appState)
|
MaxVolumeCard(appState)
|
||||||
CatheterThresholdCard(appState)
|
CatheterThresholdCard(appState)
|
||||||
AutoScanIntervalCard(appState)
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue button
|
// Continue button
|
||||||
@@ -219,56 +213,6 @@ private fun CatheterThresholdCard(appState: AppState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun AutoScanIntervalCard(appState: AppState) {
|
|
||||||
Card(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
shape = RoundedCornerShape(20.dp),
|
|
||||||
colors = CardDefaults.cardColors(containerColor = MlPrimary.copy(alpha = 0.04f))
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(horizontal = 20.dp, vertical = 18.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
Text("Auto Scan Interval", fontSize = 28.sp, fontWeight = FontWeight.ExtraBold)
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
Text("Auto scan 사이클 간격 (ms). 짧을수록 빠른 갱신, 길수록 안정.",
|
|
||||||
fontSize = 12.sp, color = MlSecondaryText)
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
|
||||||
Row(verticalAlignment = Alignment.Bottom) {
|
|
||||||
Text("${appState.piezoSettings.autoScanIntervalMs}", fontSize = 48.sp,
|
|
||||||
fontWeight = FontWeight.ExtraBold, color = MlPrimary)
|
|
||||||
Spacer(modifier = Modifier.width(6.dp))
|
|
||||||
Text("ms", fontSize = 20.sp, fontWeight = FontWeight.SemiBold, color = MlSecondaryText,
|
|
||||||
modifier = Modifier.padding(bottom = 8.dp))
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) {
|
|
||||||
StepperButton(icon = Icons.Default.Remove) {
|
|
||||||
val next = (appState.piezoSettings.autoScanIntervalMs - 200).coerceIn(600, 5000)
|
|
||||||
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = next)
|
|
||||||
}
|
|
||||||
StepperButton(icon = Icons.Default.Add) {
|
|
||||||
val next = (appState.piezoSettings.autoScanIntervalMs + 200).coerceIn(600, 5000)
|
|
||||||
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
Slider(
|
|
||||||
value = appState.piezoSettings.autoScanIntervalMs.toFloat(),
|
|
||||||
onValueChange = {
|
|
||||||
val snapped = ((it / 200f).roundToInt() * 200).coerceIn(600, 5000)
|
|
||||||
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = snapped)
|
|
||||||
},
|
|
||||||
valueRange = 600f..5000f,
|
|
||||||
steps = 21,
|
|
||||||
modifier = Modifier.padding(horizontal = 24.dp),
|
|
||||||
colors = SliderDefaults.colors(thumbColor = MlPrimary, activeTrackColor = MlPrimary)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun StepperButton(icon: androidx.compose.ui.graphics.vector.ImageVector, onClick: () -> Unit) {
|
private fun StepperButton(icon: androidx.compose.ui.graphics.vector.ImageVector, onClick: () -> Unit) {
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
Reference in New Issue
Block a user