Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UI/YAF-74] 알람 설정 시 알람 미루기를 설정할 수 있다 #46

Merged
merged 10 commits into from
Jan 19, 2025
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import com.yapp.common.navigation.destination.OnboardingDestination
import com.yapp.common.navigation.destination.HomeDestination
import com.yapp.common.navigation.destination.TopLevelDestination

class OrbitNavigator(
val navController: NavHostController,
) {
val startDestination = OnboardingDestination.Route.route
val startDestination = HomeDestination.Route.route

private val currentDestination: NavDestination?
@Composable get() = navController
Expand Down
13 changes: 13 additions & 0 deletions core/designsystem/src/main/res/drawable/ic_arrow_right.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M9,18L15,12L9,6"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#A5B2C5"
android:strokeLineCap="round"/>
</vector>
52 changes: 49 additions & 3 deletions core/ui/src/main/java/com/yapp/ui/component/OrbitBottomSheet.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package com.yapp.ui.component

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.SheetState
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.tooling.preview.Preview
Expand Down Expand Up @@ -54,12 +61,51 @@ fun OrbitBottomSheet(
@Preview
@Composable
fun OrbitBottomSheetPreview() {
var isSheetOpen by rememberSaveable { mutableStateOf(false) }
var isSheetOpen by rememberSaveable { mutableStateOf(true) }
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val scope = rememberCoroutineScope()

OrbitTheme {
Button(
onClick = {
scope.launch {
sheetState.show()
}.invokeOnCompletion {
if (!isSheetOpen) {
isSheetOpen = true
}
}
},
) {
Text("Toggle Bottom Sheet")
}

OrbitBottomSheet(
isSheetOpen = true,
isSheetOpen = isSheetOpen,
sheetState = sheetState,
onDismissRequest = { isSheetOpen = !isSheetOpen },
content = {},
content = {
Box(
modifier = Modifier
.fillMaxWidth()
.height(600.dp),
contentAlignment = Alignment.Center,
) {
Button(
onClick = {
scope.launch {
sheetState.hide()
}.invokeOnCompletion {
if (isSheetOpen) {
isSheetOpen = false
}
}
},
) {
Text("Toggle Bottom Sheet")
}
}
},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.yapp.designsystem.theme.OrbitTheme
@Composable
fun OrbitRadioButton(
isSelected: Boolean,
isEnabled: Boolean = true,
onClick: (Boolean) -> Unit,
) {
val backgroundColor = if (isSelected) {
Expand Down Expand Up @@ -50,7 +51,9 @@ fun OrbitRadioButton(
)
.clip(CircleShape)
.clickable {
onClick(!isSelected)
if (isEnabled) {
onClick(!isSelected)
}
},
contentAlignment = Alignment.Center,
) {
Expand Down
117 changes: 83 additions & 34 deletions core/ui/src/main/java/com/yapp/ui/component/switch/Switch.kt
Original file line number Diff line number Diff line change
@@ -1,54 +1,87 @@
package com.yapp.ui.component.switch

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.yapp.designsystem.theme.OrbitTheme
import kotlin.math.roundToInt

@Composable
fun OrbitSwitch(
isSelected: Boolean,
onClick: (Boolean) -> Unit,
modifier: Modifier = Modifier,
isChecked: Boolean,
isEnabled: Boolean = true,
onClick: () -> Unit,
) {
val thumbColor = if (isSelected) {
OrbitTheme.colors.gray_900
val (thumbColor, backgroundColor) = if (isChecked && isEnabled) {
Pair(OrbitTheme.colors.gray_900, OrbitTheme.colors.main)
} else if (!isEnabled) {
Pair(OrbitTheme.colors.gray_500, OrbitTheme.colors.gray_700)
} else {
OrbitTheme.colors.gray_300
Pair(OrbitTheme.colors.gray_300, OrbitTheme.colors.gray_600)
}

Switch(
checked = isSelected,
onCheckedChange = onClick,
thumbContent = {
Box(
modifier = Modifier
.size(20.dp)
.background(
color = thumbColor,
shape = CircleShape,
),
)
},
colors = SwitchDefaults.colors(
checkedThumbColor = Color.Transparent,
checkedTrackColor = OrbitTheme.colors.main,
uncheckedThumbColor = Color.Transparent,
uncheckedTrackColor = OrbitTheme.colors.gray_600,
uncheckedBorderColor = Color.Transparent,
),
val density = LocalDensity.current
val minBound = with(density) { 0.dp.toPx() }
val maxBound = with(density) { 20.dp.toPx() }
val state by animateFloatAsState(
targetValue = if (isChecked && isEnabled) maxBound else minBound,
animationSpec = tween(durationMillis = 200),
label = "custom_switch",
)

Box(
modifier = modifier
.size(width = 46.dp, height = 26.dp)
.background(
color = backgroundColor,
shape = CircleShape,
)
.clip(CircleShape)
.padding(3.dp)
.pointerInput(isEnabled) {
detectTapGestures(
onTap = {
if (isEnabled) {
onClick()
}
},
)
},
) {
Box(
modifier = Modifier
.offset { IntOffset(state.roundToInt(), 0) }
.size(20.dp)
.background(
color = thumbColor,
shape = CircleShape,
),
)
}
}

@Preview
Expand All @@ -57,11 +90,27 @@ fun OrbitTogglePreview() {
OrbitTheme {
var isSelected by remember { mutableStateOf(false) }

OrbitSwitch(
isSelected = isSelected,
onClick = {
isSelected = it
},
)
var isEnabled by remember { mutableStateOf(true) }

Column {
OrbitSwitch(
isChecked = isSelected,
isEnabled = isEnabled,
onClick = {
isSelected = !isSelected
},
)
Spacer(modifier = Modifier.height(20.dp))
Button(
onClick = {
isEnabled = !isEnabled
},
) {
Text(
text = "스위치 활성화 여부 반영",
style = OrbitTheme.typography.title2Medium,
)
}
}
}
}
34 changes: 32 additions & 2 deletions feature/home/src/main/java/com/yapp/alarm/AlarmAddEditContract.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,52 @@ import com.yapp.ui.base.UiState
sealed class AlarmAddEditContract {

data class State(
val timeState: AlarmTimeState = AlarmTimeState(),
val daySelectionState: AlarmDaySelectionState = AlarmDaySelectionState(),
val holidayState: AlarmHolidayState = AlarmHolidayState(),
val snoozeState: AlarmSnoozeState = AlarmSnoozeState(),
) : UiState

data class AlarmTimeState(
val currentAmPm: String = "오전",
val currentHour: Int = 6,
val currentHour: Int = 1,
val currentMinute: Int = 0,
val alarmMessage: String = "",
)

data class AlarmDaySelectionState(
val days: Set<AlarmDay> = enumValues<AlarmDay>().toSet(),
val isWeekdaysChecked: Boolean = false,
val isWeekendsChecked: Boolean = false,
val selectedDays: Set<AlarmDay> = setOf(),
)

data class AlarmHolidayState(
val isDisableHolidayEnabled: Boolean = false,
val isDisableHolidayChecked: Boolean = false,
) : UiState
)

data class AlarmSnoozeState(
val isSnoozeEnabled: Boolean = true,
val snoozeIntervalIndex: Int = 2,
val snoozeCountIndex: Int = 1,
val isBottomSheetOpen: Boolean = false,
val snoozeIntervals: List<String> = listOf("1분", "3분", "5분", "10분", "15분"),
val snoozeCounts: List<String> = listOf("1회", "3회", "5회", "10회", "무한"),
)

sealed class Action {
data object ClickBack : Action()
data object ClickSave : Action()
data class UpdateAlarmTime(val amPm: String, val hour: Int, val minute: Int) : Action()
data object ToggleWeekdaysChecked : Action()
data object ToggleWeekendsChecked : Action()
data class ToggleDaySelection(val day: AlarmDay) : Action()
data object ToggleDisableHolidayChecked : Action()
data object ToggleSnoozeSettingBottomSheetOpen : Action()
data object ToggleSnoozeEnabled : Action()
data class UpdateSnoozeInterval(val index: Int) : Action()
data class UpdateSnoozeCount(val index: Int) : Action()
}

sealed class SideEffect : com.yapp.ui.base.SideEffect {
Expand Down
Loading
Loading