Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into feature/#163-googl…
Browse files Browse the repository at this point in the history
…e-login
yuni-ju committed Jan 10, 2025
2 parents 6228244 + a73e936 commit 65ece56
Showing 15 changed files with 543 additions and 120 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ plugins {
alias(libs.plugins.ksp)
alias(libs.plugins.hilt)
alias(libs.plugins.google.services)
alias(libs.plugins.firebase.crashlytics)
}

android {
@@ -129,6 +130,7 @@ dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics)
implementation(libs.google.firebase.dynamic.module.support)
implementation(libs.firebase.crashlytics)

// Map
implementation(libs.map.sdk)
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.squirtles.musicroad.common

import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.displayCutoutPadding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.CenterAlignedTopAppBar
@@ -10,6 +12,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
@@ -23,6 +26,7 @@ fun DefaultTopAppBar(
title: String,
titleStyle: TextStyle = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold),
onBackClick: () -> Unit,
actions: @Composable RowScope.() -> Unit = {},
) {
CenterAlignedTopAppBar(
title = {
@@ -31,6 +35,7 @@ fun DefaultTopAppBar(
style = titleStyle
)
},
modifier = Modifier.displayCutoutPadding(),
navigationIcon = {
IconButton(
onClick = onBackClick
@@ -42,6 +47,7 @@ fun DefaultTopAppBar(
)
}
},
actions = actions,
colors = TopAppBarDefaults.centerAlignedTopAppBarColors().copy(
containerColor = Color.Transparent,
titleContentColor = White
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.squirtles.musicroad.pick.components
package com.squirtles.musicroad.common

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -22,18 +23,18 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.squirtles.musicroad.R
import com.squirtles.musicroad.common.VerticalSpacer
import com.squirtles.musicroad.common.HorizontalSpacer
import com.squirtles.musicroad.ui.theme.Black
import com.squirtles.musicroad.ui.theme.MusicRoadTheme
import com.squirtles.musicroad.ui.theme.Primary
import com.squirtles.musicroad.ui.theme.White

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DeletePickDialog(
internal fun MessageAlertDialog(
onDismissRequest: () -> Unit,
onDeletion: () -> Unit
title: String,
body: String,
buttons: @Composable RowScope.() -> Unit,
) {
BasicAlertDialog(
onDismissRequest = { onDismissRequest() },
@@ -48,7 +49,7 @@ fun DeletePickDialog(
verticalArrangement = Arrangement.Center
) {
Text(
text = stringResource(R.string.delete_pick_dialog_title),
text = title,
color = Black,
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.bodyLarge
@@ -57,7 +58,7 @@ fun DeletePickDialog(
VerticalSpacer(8)

Text(
text = stringResource(R.string.delete_pick_dialog_body),
text = body,
color = Black,
style = MaterialTheme.typography.bodyLarge
)
@@ -67,45 +68,59 @@ fun DeletePickDialog(
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
TextButton(
onClick = { onDismissRequest() },
colors = ButtonDefaults.buttonColors().copy(
containerColor = Color.Transparent,
contentColor = Black
)
) {
Text(stringResource(R.string.delete_pick_dialog_cancel))
}

HorizontalSpacer(8)

TextButton(
onClick = { onDeletion() },
colors = ButtonDefaults.buttonColors().copy(
containerColor = Color.Transparent,
contentColor = Primary
)
) {
Text(
text = stringResource(R.string.delete_pick_dialog_delete),
fontWeight = FontWeight.Bold
)
}
}
verticalAlignment = Alignment.CenterVertically,
content = buttons
)
}
}
}
}

@Composable
internal fun DialogTextButton(
onClick: () -> Unit,
text: String,
textColor: Color = Black,
buttonColor: Color = Color.Transparent,
fontWeight: FontWeight? = null,
) {
TextButton(
onClick = onClick,
colors = ButtonDefaults.buttonColors().copy(
containerColor = buttonColor,
contentColor = textColor
)
) {
Text(
text = text,
fontWeight = fontWeight,
)
}
}

@Preview(showBackground = true)
@Composable
fun DeletePickDialogPreview() {
private fun DeletePickDialogPreview() {
MusicRoadTheme {
DeletePickDialog(
MessageAlertDialog(
onDismissRequest = {},
onDeletion = {}
title = stringResource(R.string.delete_pick_dialog_title),
body = stringResource(R.string.delete_pick_dialog_body),
buttons = {
DialogTextButton(
onClick = {},
text = "취소"
)

HorizontalSpacer(8)

DialogTextButton(
onClick = {},
text = "삭제하기",
textColor = Primary,
fontWeight = FontWeight.Bold
)
}
)
}
}
Original file line number Diff line number Diff line change
@@ -69,9 +69,10 @@ fun CommentText(
}

@Composable
fun TotalCountText(
modifier: Modifier = Modifier,
fun CountText(
totalCount: Int,
modifier: Modifier = Modifier,
countLabel: String = stringResource(R.string.total_count_text),
defaultColor: Color = MaterialTheme.colorScheme.onSurface,
pointColor: Color = MaterialTheme.colorScheme.primary,
style: TextStyle = MaterialTheme.typography.titleMedium
@@ -84,7 +85,7 @@ fun TotalCountText(
fontWeight = FontWeight.Bold
)
) {
append("전체 ")
append("$countLabel ")
}
withStyle(
SpanStyle(
Original file line number Diff line number Diff line change
@@ -28,12 +28,12 @@ import com.squirtles.musicroad.common.AlbumImage
import com.squirtles.musicroad.common.CommentText
import com.squirtles.musicroad.common.Constants.DEFAULT_PADDING
import com.squirtles.musicroad.common.Constants.REQUEST_IMAGE_SIZE_DEFAULT
import com.squirtles.musicroad.common.CountText
import com.squirtles.musicroad.common.CreatedByOtherUserText
import com.squirtles.musicroad.common.CreatedBySelfText
import com.squirtles.musicroad.common.FavoriteCountText
import com.squirtles.musicroad.common.SongInfoText
import com.squirtles.musicroad.common.TotalCountText
import com.squirtles.musicroad.common.HorizontalSpacer
import com.squirtles.musicroad.common.SongInfoText
import com.squirtles.musicroad.common.VerticalSpacer
import kotlinx.coroutines.launch

@@ -57,11 +57,11 @@ fun ClusterBottomSheet(
containerColor = MaterialTheme.colorScheme.surface
) {
clusterPickList?.let { pickList ->
TotalCountText(
CountText(
totalCount = pickList.size,
modifier = Modifier
.fillMaxWidth()
.padding(start = DEFAULT_PADDING),
totalCount = pickList.size
.padding(start = DEFAULT_PADDING)
)

VerticalSpacer(height = 8)
35 changes: 29 additions & 6 deletions app/src/main/java/com/squirtles/musicroad/pick/DetailPickScreen.kt
Original file line number Diff line number Diff line change
@@ -41,6 +41,8 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
@@ -53,19 +55,22 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.flowWithLifecycle
import com.squirtles.domain.model.Pick
import com.squirtles.musicroad.R
import com.squirtles.musicroad.common.DialogTextButton
import com.squirtles.musicroad.common.HorizontalSpacer
import com.squirtles.musicroad.common.MessageAlertDialog
import com.squirtles.musicroad.common.VerticalSpacer
import com.squirtles.musicroad.media.PlayerServiceViewModel
import com.squirtles.musicroad.pick.PickViewModel.Companion.DEFAULT_PICK
import com.squirtles.musicroad.pick.components.CircleAlbumCover
import com.squirtles.musicroad.pick.components.CommentText
import com.squirtles.musicroad.pick.components.DeletePickDialog
import com.squirtles.musicroad.pick.components.DetailPickTopAppBar
import com.squirtles.musicroad.pick.components.MusicVideoKnob
import com.squirtles.musicroad.pick.components.PickInformation
import com.squirtles.musicroad.pick.components.SongInfo
import com.squirtles.musicroad.pick.components.music.MusicPlayer
import com.squirtles.musicroad.pick.components.music.visualizer.BaseVisualizer
import com.squirtles.musicroad.ui.theme.Black
import com.squirtles.musicroad.ui.theme.Primary
import com.squirtles.musicroad.ui.theme.White
import com.squirtles.musicroad.videoplayer.MusicVideoScreen
import kotlinx.coroutines.launch
@@ -272,14 +277,32 @@ fun DetailPickScreen(
}

if (showDeletePickDialog) {
DeletePickDialog(
MessageAlertDialog(
onDismissRequest = {
showDeletePickDialog = false
},
onDeletion = {
showDeletePickDialog = false
pickViewModel.deletePick(pickId)
}
title = stringResource(R.string.delete_pick_dialog_title),
body = stringResource(R.string.delete_pick_dialog_body),
buttons = {
DialogTextButton(
onClick = {
showDeletePickDialog = false
},
text = stringResource(R.string.delete_pick_dialog_cancel)
)

HorizontalSpacer(8)

DialogTextButton(
onClick = {
showDeletePickDialog = false
pickViewModel.deletePick(pickId)
},
text = stringResource(R.string.delete_pick_dialog_delete),
textColor = Primary,
fontWeight = FontWeight.Bold
)
},
)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.squirtles.musicroad.picklist

import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import com.squirtles.musicroad.R
import com.squirtles.musicroad.common.DialogTextButton
import com.squirtles.musicroad.common.HorizontalSpacer
import com.squirtles.musicroad.common.MessageAlertDialog
import com.squirtles.musicroad.ui.theme.Primary

@Composable
internal fun DeleteSelectedPickDialog(
selectedPickCount: Int,
pickListType: PickListType,
onDismissRequest: () -> Unit,
onDeletePickClick: () -> Unit,
) {
MessageAlertDialog(
onDismissRequest = onDismissRequest,
title = stringResource(R.string.delete_pick_dialog_title),
body = stringResource(
when (pickListType) {
PickListType.FAVORITE -> R.string.delete_selected_favorite_pick_dialog_body
PickListType.CREATED -> R.string.delete_selected_pick_dialog_body
},
selectedPickCount
),
buttons = {
DialogTextButton(
onClick = onDismissRequest,
text = stringResource(R.string.delete_pick_dialog_cancel)
)

HorizontalSpacer(8)

DialogTextButton(
onClick = onDeletePickClick,
text = stringResource(R.string.delete_pick_dialog_delete),
textColor = Primary,
fontWeight = FontWeight.Bold
)
},
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.squirtles.musicroad.picklist

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import com.squirtles.musicroad.R
import com.squirtles.musicroad.ui.theme.Gray
import com.squirtles.musicroad.ui.theme.White

@Composable
internal fun EditModeAction(
isEditMode: Boolean,
enabled: Boolean,
isSelectedEmpty: Boolean,
activateEditMode: () -> Unit,
selectAllPicks: () -> Unit,
deselectAllPicks: () -> Unit,
) {
if (isEditMode) {
if (isSelectedEmpty) {
TextButton(
onClick = selectAllPicks
) {
Text(
text = stringResource(R.string.pick_list_select_all_button_text),
color = White,
)
}
} else {
TextButton(
onClick = deselectAllPicks
) {
Text(
text = stringResource(R.string.pick_list_deselect_all_button_text),
color = White,
)
}
}
} else {
IconButton(
onClick = activateEditMode,
enabled = enabled,
colors = IconButtonDefaults.iconButtonColors().copy(
contentColor = White,
disabledContentColor = Gray,
)
) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = stringResource(R.string.pick_list_activate_edit_mode_button_description),
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.squirtles.musicroad.picklist

import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.squirtles.musicroad.R
import com.squirtles.musicroad.ui.theme.DarkGray
import com.squirtles.musicroad.ui.theme.Gray
import com.squirtles.musicroad.ui.theme.MusicRoadTheme
import com.squirtles.musicroad.ui.theme.Primary
import com.squirtles.musicroad.ui.theme.White

@Composable
internal fun EditModeBottomButton(
enabled: Boolean,
deactivateEditMode: () -> Unit,
showDeletePickDialog: () -> Unit,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(ButtonHeight)
) {
EditModeButton(
text = stringResource(R.string.pick_list_deactivate_edit_mode_button_text),
onClick = deactivateEditMode,
modifier = Modifier
.fillMaxHeight()
.weight(1f),
buttonColor = Gray
)

EditModeButton(
text = stringResource(R.string.pick_list_delete_selection_button_text),
onClick = showDeletePickDialog,
modifier = Modifier
.fillMaxHeight()
.weight(1f),
enabled = enabled
)
}
}

@Composable
private fun EditModeButton(
text: String,
onClick: () -> Unit,
modifier: Modifier,
enabled: Boolean = true,
textColor: Color = White,
buttonColor: Color = Primary,
) {
Button(
onClick = onClick,
modifier = modifier,
enabled = enabled,
shape = RectangleShape,
colors = ButtonDefaults.buttonColors().copy(
containerColor = buttonColor,
contentColor = textColor,
disabledContainerColor = DarkGray,
disabledContentColor = White
)
) {
Text(
text = text,
style = MaterialTheme.typography.bodyLarge
)
}
}

@Preview
@Composable
private fun EditModeBottomButtonPreview() {
MusicRoadTheme {
EditModeBottomButton(
enabled = true,
deactivateEditMode = {},
showDeletePickDialog = {},
)
}
}

private val ButtonHeight = 48.dp
19 changes: 19 additions & 0 deletions app/src/main/java/com/squirtles/musicroad/picklist/PickItem.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.squirtles.musicroad.picklist

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
@@ -9,6 +10,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CheckCircle
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
@@ -17,9 +21,12 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.squirtles.domain.model.Song
import com.squirtles.musicroad.R
import com.squirtles.musicroad.common.AlbumImage
import com.squirtles.musicroad.common.CommentText
import com.squirtles.musicroad.common.Constants
@@ -28,10 +35,13 @@ import com.squirtles.musicroad.common.FavoriteCountText
import com.squirtles.musicroad.common.HorizontalSpacer
import com.squirtles.musicroad.common.SongInfoText
import com.squirtles.musicroad.ui.theme.Gray
import com.squirtles.musicroad.ui.theme.Primary
import com.squirtles.musicroad.ui.theme.White

@Composable
internal fun PickItem(
isEditMode: Boolean,
isSelected: Boolean,
song: Song,
createdByOthers: Boolean,
createUserName: String,
@@ -43,6 +53,7 @@ internal fun PickItem(
Row(
modifier = Modifier
.fillMaxWidth()
.background(color = if (isSelected) White.copy(alpha = 0.2F) else Color.Transparent)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = ripple(color = White),
@@ -103,5 +114,13 @@ internal fun PickItem(
color = Gray
)
}

if (isEditMode) {
Icon(
imageVector = Icons.Outlined.CheckCircle,
contentDescription = stringResource(R.string.outlined_check_circle_icon_description),
tint = if (isSelected) Primary else Gray
)
}
}
}
221 changes: 152 additions & 69 deletions app/src/main/java/com/squirtles/musicroad/picklist/PickListScreen.kt
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@@ -36,11 +37,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.wear.compose.material.CircularProgressIndicator
import com.squirtles.domain.model.Order
import com.squirtles.domain.model.Pick
import com.squirtles.musicroad.R
import com.squirtles.musicroad.common.Constants.COLOR_STOPS
import com.squirtles.musicroad.common.Constants.DEFAULT_PADDING
import com.squirtles.musicroad.common.CountText
import com.squirtles.musicroad.common.DefaultTopAppBar
import com.squirtles.musicroad.common.TotalCountText
import com.squirtles.musicroad.common.VerticalSpacer
import com.squirtles.musicroad.ui.theme.Primary
import com.squirtles.musicroad.ui.theme.White
@@ -55,7 +57,23 @@ fun PickListScreen(
) {
val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE
val uiState by pickListViewModel.pickListUiState.collectAsStateWithLifecycle()
val selectedPicksId by pickListViewModel.selectedPicksId.collectAsStateWithLifecycle()

var isEditMode by rememberSaveable { mutableStateOf(false) }
var showOrderBottomSheet by rememberSaveable { mutableStateOf(false) }
var isDeletePickDialogVisible by rememberSaveable { mutableStateOf(false) }

val deactivateEditMode = remember {
{
isEditMode = false
pickListViewModel.deselectAllPicks()
}
}
val showDeletePickDialog = remember {
{
isDeletePickDialogVisible = true
}
}

LaunchedEffect(Unit) {
when (pickListType) {
@@ -73,7 +91,17 @@ fun PickListScreen(
PickListType.CREATED -> R.string.my_picks_top_app_bar_title
}
),
onBackClick = onBackClick
onBackClick = onBackClick,
actions = {
EditModeAction(
isEditMode = isEditMode,
enabled = uiState is PickListUiState.Success,
isSelectedEmpty = selectedPicksId.isEmpty(),
activateEditMode = { isEditMode = true },
selectAllPicks = { pickListViewModel.selectAllPicks() },
deselectAllPicks = { pickListViewModel.deselectAllPicks() },
)
}
)
},
) { innerPadding ->
@@ -101,76 +129,26 @@ fun PickListScreen(
Column(
modifier = Modifier.fillMaxSize()
) {
Row(
PickList(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = DEFAULT_PADDING),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
TotalCountText(
totalCount = pickList.size,
defaultColor = White,
)
.weight(1f),
isEditMode = isEditMode,
pickListType = pickListType,
pickList = pickList,
selectedPicksId = selectedPicksId,
order = order,
onOrderClick = { showOrderBottomSheet = true },
onItemClick = onItemClick,
onEditModeItemClick = { pickListViewModel.toggleSelectedPick(it) },
)

Box(
modifier = Modifier
.wrapContentSize()
.clip(CircleShape)
.clickable { showOrderBottomSheet = true }
) {
Text(
text = getOrderString(
pickListType = pickListType,
order = order
),
modifier = Modifier.padding(
horizontal = DEFAULT_PADDING / 2,
vertical = DEFAULT_PADDING / 4
),
color = White,
style = MaterialTheme.typography.bodyMedium
)
}
}

VerticalSpacer(8)

if (pickList.isEmpty()) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = stringResource(
when (pickListType) {
PickListType.FAVORITE -> R.string.favorite_picks_empty
PickListType.CREATED -> R.string.my_picks_empty
}
),
color = White,
style = MaterialTheme.typography.titleMedium
)
}
} else {
LazyColumn(
modifier = Modifier.weight(1f)
) {
items(
items = pickList,
key = { it.id }
) { pick ->
PickItem(
song = pick.song,
createdByOthers = pickListType == PickListType.FAVORITE,
createUserName = pick.createdBy.userName,
favoriteCount = pick.favoriteCount,
comment = pick.comment,
createdAt = pick.createdAt,
onItemClick = { onItemClick(pick.id) }
)
}
}
if (isEditMode) {
EditModeBottomButton(
enabled = selectedPicksId.isNotEmpty(),
deactivateEditMode = deactivateEditMode,
showDeletePickDialog = showDeletePickDialog,
)
}
}
}
@@ -198,6 +176,111 @@ fun PickListScreen(
},
)
}

if (isDeletePickDialogVisible) {
DeleteSelectedPickDialog(
selectedPickCount = selectedPicksId.size,
pickListType = pickListType,
onDismissRequest = { isDeletePickDialogVisible = false },
onDeletePickClick = {
isEditMode = false
isDeletePickDialogVisible = false
pickListViewModel.deleteSelectedPicks(pickListType)
},
)
}
}

@Composable
private fun PickList(
modifier: Modifier,
isEditMode: Boolean,
pickListType: PickListType,
pickList: List<Pick>,
selectedPicksId: Set<String>,
order: Order,
onOrderClick: () -> Unit,
onItemClick: (String) -> Unit,
onEditModeItemClick: (String) -> Unit,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = DEFAULT_PADDING),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
CountText(
totalCount = if (isEditMode) selectedPicksId.size else pickList.size,
countLabel = stringResource(
if (isEditMode) R.string.selected_count_text else R.string.total_count_text
),
defaultColor = White,
)

Box(
modifier = Modifier
.wrapContentSize()
.clip(CircleShape)
.clickable { onOrderClick() }
) {
Text(
text = getOrderString(
pickListType = pickListType,
order = order
),
modifier = Modifier.padding(
horizontal = DEFAULT_PADDING / 2,
vertical = DEFAULT_PADDING / 4
),
color = White,
style = MaterialTheme.typography.bodyMedium
)
}
}

VerticalSpacer(8)

if (pickList.isEmpty()) {
Box(
modifier = modifier,
contentAlignment = Alignment.Center
) {
Text(
text = stringResource(
when (pickListType) {
PickListType.FAVORITE -> R.string.favorite_picks_empty
PickListType.CREATED -> R.string.my_picks_empty
}
),
color = White,
style = MaterialTheme.typography.titleMedium
)
}
} else {
LazyColumn(
modifier = modifier
) {
items(
items = pickList,
key = { it.id }
) { pick ->
PickItem(
isEditMode = isEditMode,
isSelected = selectedPicksId.contains(pick.id),
song = pick.song,
createdByOthers = pickListType == PickListType.FAVORITE,
createUserName = pick.createdBy.userName,
favoriteCount = pick.favoriteCount,
comment = pick.comment,
createdAt = pick.createdAt,
onItemClick = {
if (isEditMode) onEditModeItemClick(pick.id) else onItemClick(pick.id)
}
)
}
}
}
}

@Composable
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
package com.squirtles.musicroad.picklist

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.squirtles.domain.model.Order
import com.squirtles.domain.model.Pick
import com.squirtles.domain.usecase.favoritepick.DeleteFavoriteUseCase
import com.squirtles.domain.usecase.favoritepick.FetchFavoritePicksUseCase
import com.squirtles.domain.usecase.local.GetCurrentUserUseCase
import com.squirtles.domain.usecase.local.GetFavoriteListOrderUseCase
import com.squirtles.domain.usecase.local.GetMyListOrderUseCase
import com.squirtles.domain.usecase.local.SaveFavoriteListOrderUseCase
import com.squirtles.domain.usecase.local.SaveMyListOrderUseCase
import com.squirtles.domain.usecase.mypick.DeletePickUseCase
import com.squirtles.domain.usecase.mypick.FetchMyPicksUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
@@ -20,17 +26,23 @@ import javax.inject.Inject
class PickListViewModel @Inject constructor(
private val fetchFavoritePicksUseCase: FetchFavoritePicksUseCase,
private val fetchMyPicksUseCase: FetchMyPicksUseCase,
private val getCurrentUserUseCase: GetCurrentUserUseCase,
private val getFavoriteListOrderUseCase: GetFavoriteListOrderUseCase,
private val getMyListOrderUseCase: GetMyListOrderUseCase,
private val saveFavoriteListOrderUseCase: SaveFavoriteListOrderUseCase,
private val saveMyListOrderUseCase: SaveMyListOrderUseCase,
private val deleteFavoriteUseCase: DeleteFavoriteUseCase,
private val deletePickUseCase: DeletePickUseCase,
) : ViewModel() {

private var defaultList: List<Pick>? = null

private val _pickListUiState = MutableStateFlow<PickListUiState>(PickListUiState.Loading)
val pickListUiState = _pickListUiState.asStateFlow()

private val _selectedPicksId = MutableStateFlow<Set<String>>(emptySet())
val selectedPicksId = _selectedPicksId.asStateFlow()

fun fetchFavoritePicks(userId: String) {
viewModelScope.launch {
fetchFavoritePicksUseCase(userId)
@@ -67,6 +79,48 @@ class PickListViewModel @Inject constructor(
}
}

fun toggleSelectedPick(pickId: String) {
val curSelectedPicksId = _selectedPicksId.value
_selectedPicksId.value =
if (curSelectedPicksId.contains(pickId)) curSelectedPicksId - pickId else curSelectedPicksId + pickId
}

fun selectAllPicks() {
defaultList?.let { pickList ->
_selectedPicksId.value = pickList.map { it.id }.toSet()
}
}

fun deselectAllPicks() {
_selectedPicksId.value = emptySet()
}

fun deleteSelectedPicks(type: PickListType) {
viewModelScope.launch {
_pickListUiState.value = PickListUiState.Loading

val userId = getUserId()
val deleteJobList = _selectedPicksId.value.map { pickId ->
when (type) {
PickListType.FAVORITE -> async { deleteFavoriteUseCase(pickId, userId) }
PickListType.CREATED -> async { deletePickUseCase(pickId, userId) }
}
}
val deleteJobResults = deleteJobList.awaitAll()

deselectAllPicks()
if (deleteJobResults.all { it.isSuccess }) {
when (type) {
PickListType.FAVORITE -> fetchFavoritePicks(userId)
PickListType.CREATED -> fetchMyPicks(userId)
}
} else {
_pickListUiState.value = PickListUiState.Error
Log.e("PickListViewModel", "[픽 목록] 다중 삭제 오류")
}
}
}

private fun setList(order: Order) {
defaultList?.let { pickList ->
val sortedList = when (order) {
@@ -84,4 +138,6 @@ class PickListViewModel @Inject constructor(
)
}
}

private fun getUserId() = getCurrentUserUseCase().userId
}
13 changes: 13 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -50,6 +50,13 @@
<string name="my_picks_empty">등록한 픽이 없습니다</string>
<string name="error_loading_pick_list">일시적인 오류가 발생했습니다.</string>

<!-- Pick List -->
<string name="pick_list_deactivate_edit_mode_button_text">취소</string>
<string name="pick_list_delete_selection_button_text">선택 삭제</string>
<string name="outlined_check_circle_icon_description">원 윤곽선이 있는 체크 아이콘</string>
<string name="total_count_text">전체</string>
<string name="selected_count_text">선택</string>

<!-- Pick List Order -->
<string name="latest_create_order">최근 등록순</string>
<string name="latest_favorite_order">최근 담은순</string>
@@ -77,8 +84,14 @@
<string name="delete_pick_dialog_cancel">취소</string>
<string name="delete_pick_dialog_delete">삭제하기</string>

<string name="delete_selected_favorite_pick_dialog_body">픽 보관함에서 %d개의 픽이 삭제됩니다.</string>
<string name="delete_selected_pick_dialog_body">선택하신 %d개의 픽이 삭제됩니다.</string>

<!-- Top App Bar -->
<string name="top_app_bar_back_description">상단 바 뒤로 가기 버튼</string>
<string name="pick_list_activate_edit_mode_button_description">픽 목록 편집 모드 활성화 버튼</string>
<string name="pick_list_select_all_button_text">전체 선택</string>
<string name="pick_list_deselect_all_button_text">선택 해제</string>
<string name="favorite_picks_top_app_bar_title">픽 보관함</string>
<string name="my_picks_top_app_bar_title">등록한 픽</string>

1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -7,4 +7,5 @@ plugins {
alias(libs.plugins.hilt) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.google.services) apply false
alias(libs.plugins.firebase.crashlytics) apply false
}
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ firebaseBom = "33.5.1"
firebaseDynamicModuleSupportVersion = "16.0.0-beta03"
firebaseFirestoreKtx = "25.1.1"
firebaseFunctionsKtx = "21.1.0"
firebaseCrashlytics = "3.0.2"
geofireAndroidCommon = "3.2.0"
googleServices = "4.4.2"
hilt = "2.51.1"
@@ -78,6 +79,7 @@ firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "fir
firebase-firestore-ktx = { group = "com.google.firebase", name = "firebase-firestore-ktx", version.ref = "firebaseFirestoreKtx" }
firebase-functions-ktx = { group = "com.google.firebase", name = "firebase-functions-ktx", version.ref = "firebaseFunctionsKtx" }
kotlinx-coroutines-play-services = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-play-services", version.ref = "kotlinxCoroutinesPlayServices" }
firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" }

# Test
geofire-android-common = { module = "com.firebase:geofire-android-common", version.ref = "geofireAndroidCommon" }
@@ -129,3 +131,4 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" }
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlytics" }

0 comments on commit 65ece56

Please sign in to comment.