diff --git a/app/src/main/java/com/cornellappdev/resell/android/model/api/AvailabilityApiService.kt b/app/src/main/java/com/cornellappdev/resell/android/model/api/AvailabilityApiService.kt index 632d9cb..75f7d18 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/model/api/AvailabilityApiService.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/model/api/AvailabilityApiService.kt @@ -3,12 +3,16 @@ package com.cornellappdev.resell.android.model.api import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST +import retrofit2.http.Path interface AvailabilityApiService { @GET("availability/") suspend fun getMyAvailability(): AvailabilityResponse + @GET("availability/user/{userId}") + suspend fun getUserAvailability(@Path("userId") userId: String): AvailabilityResponse + @POST("availability/update/") suspend fun updateAvailability( @Body request: UpdateAvailabilityRequest diff --git a/app/src/main/java/com/cornellappdev/resell/android/model/profile/AvailabilityRepository.kt b/app/src/main/java/com/cornellappdev/resell/android/model/profile/AvailabilityRepository.kt index 1ccf827..11602df 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/model/profile/AvailabilityRepository.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/model/profile/AvailabilityRepository.kt @@ -5,6 +5,7 @@ import com.cornellappdev.resell.android.model.api.RetrofitInstance import com.cornellappdev.resell.android.model.api.UpdateAvailabilityRequest import com.cornellappdev.resell.android.model.api.UserAvailability import com.cornellappdev.resell.android.ui.components.availability.helper.SLOT_DURATION_MINUTES +import java.time.Instant import java.time.LocalDateTime import java.time.ZoneId import javax.inject.Inject @@ -14,8 +15,12 @@ import javax.inject.Singleton class AvailabilityRepository @Inject constructor( private val retrofitInstance: RetrofitInstance ) { - suspend fun getMyAvailability(): UserAvailability { - return retrofitInstance.availabilityApi.getMyAvailability().availability + suspend fun getMyAvailability(): Set { + return retrofitInstance.availabilityApi.getMyAvailability().availability.toLocalDateTimes() + } + + suspend fun getUserAvailability(userId: String): Set { + return retrofitInstance.availabilityApi.getUserAvailability(userId).availability.toLocalDateTimes() } suspend fun updateAvailability(slots: List): UserAvailability { @@ -40,4 +45,12 @@ class AvailabilityRepository @Inject constructor( // The backend stores/returns dates as UTC instants (e.g. "2026-01-23T16:00:00.000Z"), so // device-local wall-clock times must be converted to an instant before sending. private fun LocalDateTime.toUtcInstantString(): String = - atZone(ZoneId.systemDefault()).toInstant().toString() \ No newline at end of file + atZone(ZoneId.systemDefault()).toInstant().toString() + +// The backend sends startDate as a UTC instant (e.g. "2026-01-23T16:00:00.000Z"), so it's +// parsed as an Instant and converted to the device's local wall-clock time. +private fun UserAvailability.toLocalDateTimes(): Set { + return schedule.values.flatten().map { slot -> + Instant.parse(slot.startDate).atZone(ZoneId.systemDefault()).toLocalDateTime() + }.toSet() +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/AvailabilitySheet.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/AvailabilitySheet.kt index ec634d3..a07174c 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/AvailabilitySheet.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/AvailabilitySheet.kt @@ -28,6 +28,8 @@ fun AvailabilitySheet( title = uiState.title, subtitle = uiState.subtitle, gridSelectionType = uiState.gridSelectionType, + availableAvailabilities = uiState.overlapTimes, + onEditAvailabilityClicked = uiState.onEditAvailability, setProposalTime = availabilitySheetViewModel::setProposalTime ) diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/AvailabilitySheetViewModel.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/AvailabilitySheetViewModel.kt index d05bb76..ccd071a 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/AvailabilitySheetViewModel.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/AvailabilitySheetViewModel.kt @@ -37,7 +37,9 @@ class AvailabilitySheetViewModel @Inject constructor( val initialAvailabilities: List, val textButtonState: ResellTextButtonState = ResellTextButtonState.ENABLED, val gridSelectionType: GridSelectionType, - val proposedTime: LocalDateTime? = null + val proposedTime: LocalDateTime? = null, + val overlapTimes: List? = null, + val onEditAvailability: (() -> Unit)? = null, ) fun onAvailabilityChanged(availability: List) { @@ -80,7 +82,9 @@ class AvailabilitySheetViewModel @Inject constructor( callback = uiEvent.payload.callback, initialAvailabilities = uiEvent.payload.initialTimes, textButtonState = uiEvent.payload.initialButtonState, - gridSelectionType = uiEvent.payload.gridSelectionType + gridSelectionType = uiEvent.payload.gridSelectionType, + overlapTimes = uiEvent.payload.overlapTimes, + onEditAvailability = uiEvent.payload.onEditAvailability ) } } diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/SelectableAvailabilityPager.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/SelectableAvailabilityPager.kt index 8c338ba..115cce1 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/SelectableAvailabilityPager.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/SelectableAvailabilityPager.kt @@ -23,9 +23,11 @@ fun SelectableAvailabilityPager( title: String, subtitle: String, initialSelectedAvailabilities: List = emptyList(), + availableAvailabilities: List? = null, scrollRange: Pair = 0 to 6, modifier: Modifier = Modifier, gridSelectionType: GridSelectionType, + onEditAvailabilityClicked: (() -> Unit)? = null, setProposalTime: (LocalDateTime) -> Unit, setSelectedAvailabilities: (List) -> Unit, ) { @@ -65,6 +67,7 @@ fun SelectableAvailabilityPager( modifier = modifier, title = title, subtitle = subtitle, + onEditAvailabilityClicked = onEditAvailabilityClicked, ) { dates, page -> SelectableAvailabilityGrid( dates = dates, @@ -81,6 +84,7 @@ fun SelectableAvailabilityPager( setSelectedAvailabilities(updatedDates.values.flatten()) }, gridSelectionType = gridSelectionType, + availableAvailabilities = availableAvailabilities, onProposalSelected = setProposalTime ) } diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/AvailabilityPagerContainer.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/AvailabilityPagerContainer.kt index 5bc0175..205c376 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/AvailabilityPagerContainer.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/AvailabilityPagerContainer.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.cornellappdev.resell.android.R +import com.cornellappdev.resell.android.ui.theme.ResellPurple import com.cornellappdev.resell.android.ui.theme.Secondary import com.cornellappdev.resell.android.ui.theme.Style import com.cornellappdev.resell.android.util.clickableNoIndication @@ -45,6 +46,7 @@ fun AvailabilityPagerContainer( startDate: LocalDate, scrollRange: Pair, modifier: Modifier = Modifier, + onEditAvailabilityClicked: (() -> Unit)? = null, availabilityGrid: @Composable (dates: List, page: Int) -> Unit, ) { val state = @@ -85,11 +87,26 @@ fun AvailabilityPagerContainer( horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = title, style = Style.heading3) - Text( - text = subtitle, - style = Style.body2, - color = Secondary - ) + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = subtitle, + style = Style.body2, + color = Secondary + ) + if (onEditAvailabilityClicked != null) { + Text( + text = " | ", + style = Style.body2, + color = Secondary + ) + Text( + text = "Edit Availability", + style = Style.body2, + color = ResellPurple, + modifier = Modifier.clickableNoIndication { onEditAvailabilityClicked() } + ) + } + } } Icon( painter = painterResource(R.drawable.ic_chevron_right), diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/AvailabilityUtil.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/AvailabilityUtil.kt index def4913..5edf94c 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/AvailabilityUtil.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/AvailabilityUtil.kt @@ -9,6 +9,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.cornellappdev.resell.android.ui.theme.ResellPurple import com.cornellappdev.resell.android.ui.theme.Stroke +import com.cornellappdev.resell.android.ui.theme.Wash import com.cornellappdev.resell.android.util.day import java.time.LocalDate import java.time.LocalDateTime @@ -89,6 +90,27 @@ fun List.mapToGrid(dates: List): List { return grid } +/** + * Greys out every cell where [unavailableGrid] is true, so only cells left white/normal + * represent times available to both parties. + */ +fun DrawScope.drawUnavailableCells(unavailableGrid: List, rectWidth: Float, rectHeight: Float) { + for (row in unavailableGrid.indices) { + for (col in unavailableGrid[row].indices) { + if (unavailableGrid[row][col]) { + val position = Offset(rectWidth * col, rectHeight * row) + + drawRect( + size = Size(rectWidth, rectHeight), + topLeft = position, + color = Wash, + style = Fill + ) + } + } + } +} + fun DrawScope.drawBorder(grid: List, rectWidth: Float, rectHeight: Float) { for (row in grid.indices.filter { it % 2 == 0 }) { for (col in grid[row].indices) { diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/SelectableAvailabilityGrid.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/SelectableAvailabilityGrid.kt index ca941d2..603cd29 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/SelectableAvailabilityGrid.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/components/availability/helper/SelectableAvailabilityGrid.kt @@ -44,6 +44,7 @@ private fun SelectableGrid( updateGrid: ((List) -> List) -> Unit, gridSelectionType: GridSelectionType, modifier: Modifier = Modifier, + unavailableGrid: List? = null, onProposalSelected: (Pair) -> Unit ) { var isRemoving by remember { mutableStateOf(false) } @@ -160,6 +161,9 @@ private fun SelectableGrid( * in. */ + // Grey out cells not available to both parties, before the border/selection layers. + unavailableGrid?.let { drawUnavailableCells(it, rectWidth, rectHeight) } + // Draw border drawBorder(grid, rectWidth, rectHeight) @@ -216,9 +220,18 @@ fun SelectableAvailabilityGrid( setSelectedAvailabilities: (List) -> Unit, gridSelectionType: GridSelectionType, modifier: Modifier = Modifier, + /** + * When non-null, cells NOT in this list are greyed out — e.g. the intersection of two + * people's saved availability, so only times that work for both are shown as normal/white. + * Null means the greying feature isn't used for this grid. + */ + availableAvailabilities: List? = null, onProposalSelected: (LocalDateTime) -> Unit, ) { val grid = selectedAvailabilities.mapToGrid(dates) + val unavailableGrid = availableAvailabilities?.mapToGrid(dates)?.map { row -> + BooleanArray(row.size) { col -> !row[col] } + } AvailabilityGridContainer(dates, modifier) { SelectableGrid( @@ -228,6 +241,7 @@ fun SelectableAvailabilityGrid( setSelectedAvailabilities(newGrid.toAvailabilities(dates)) }, gridSelectionType = gridSelectionType, + unavailableGrid = unavailableGrid, onProposalSelected = { val (row, col) = it onProposalSelected(rowColToLocalDateTime(row, col, dates)) diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/screens/main/AvailabilityScreen.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/screens/main/AvailabilityScreen.kt index d6481e1..1900eb5 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/ui/screens/main/AvailabilityScreen.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/screens/main/AvailabilityScreen.kt @@ -77,6 +77,7 @@ fun AvailabilityScreen( onSetCurrentMonth = { availabilityViewModel.setCurrentMonth(it) }, onSetVisibleDates = { availabilityViewModel.setVisibleDates(it) }, onSave = { availabilityViewModel.saveAvailability() }, + onBackPressed = { availabilityViewModel.onBackPressed() }, ) } @@ -87,6 +88,7 @@ fun AvailabilityScreenContent( onSetCurrentMonth: (YearMonth) -> Unit, onSetVisibleDates: (List) -> Unit, onSave: () -> Unit, + onBackPressed: () -> Unit = {}, ) { // just some UI logic to allow for smooth transitions between panels expanding on the screen. var activePanel by remember { mutableStateOf(AvailabilityPanel.NONE) } @@ -110,6 +112,7 @@ fun AvailabilityScreenContent( ResellHeader( title = "Availability", leftPainter = R.drawable.ic_chevron_left, + onLeftClick = onBackPressed, ) Column( modifier = Modifier @@ -253,5 +256,6 @@ fun AvailabilityScreenPreview() { onSetCurrentMonth = {}, onSetVisibleDates = {}, onSave = {}, + onBackPressed = {}, ) } diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/screens/root/RootNavigation.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/screens/root/RootNavigation.kt index 6907644..72c9850 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/ui/screens/root/RootNavigation.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/screens/root/RootNavigation.kt @@ -25,6 +25,7 @@ import com.cornellappdev.resell.android.ui.components.submitted.ConfettiOverlay import com.cornellappdev.resell.android.ui.screens.externalprofile.ExternalProfileNavigation import com.cornellappdev.resell.android.ui.screens.feedback.FeedbackNavigation import com.cornellappdev.resell.android.ui.screens.main.AllSearchScreen +import com.cornellappdev.resell.android.ui.screens.main.AvailabilityScreen import com.cornellappdev.resell.android.ui.screens.main.ChatScreen import com.cornellappdev.resell.android.ui.screens.main.MainTabNavigation import com.cornellappdev.resell.android.ui.screens.main.NotificationsHubScreen @@ -179,6 +180,10 @@ fun RootNavigation( composable { NotificationsHubScreen() } + + composable { + AvailabilityScreen() + } } RootConfirmationOverlay() @@ -281,4 +286,7 @@ sealed class ResellRootRoute { @Serializable data object NOTIFS : ResellRootRoute() + + @Serializable + data object AVAILABILITY : ResellRootRoute() } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/AvailabilityViewModel.kt b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/AvailabilityViewModel.kt index 4996243..00b7b47 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/AvailabilityViewModel.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/AvailabilityViewModel.kt @@ -2,21 +2,20 @@ package com.cornellappdev.resell.android.viewmodel.main import androidx.lifecycle.viewModelScope import com.cornellappdev.resell.android.model.profile.AvailabilityRepository -import com.cornellappdev.resell.android.model.api.UserAvailability import com.cornellappdev.resell.android.ui.components.availability.helper.dayGroupContaining import com.cornellappdev.resell.android.viewmodel.ResellViewModel +import com.cornellappdev.resell.android.viewmodel.navigation.RootNavigationRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch -import java.time.Instant import java.time.LocalDate import java.time.LocalDateTime import java.time.YearMonth -import java.time.ZoneId import javax.inject.Inject @HiltViewModel class AvailabilityViewModel @Inject constructor( - private val availabilityRepository: AvailabilityRepository + private val availabilityRepository: AvailabilityRepository, + private val rootNavigationRepository: RootNavigationRepository ) : ResellViewModel( initialUiState = AvailabilityUiState() ) { @@ -24,7 +23,7 @@ class AvailabilityViewModel @Inject constructor( data class AvailabilityUiState( val selectedAvailabilities: Set = emptySet(), val currentMonth: YearMonth = YearMonth.now(), - val visibleDates: List = dayGroupContaining(YearMonth.now().atDay(1)), + val visibleDates: List = dayGroupContaining(LocalDate.now()), // TODO: googleCalendarEnabled and availabilitySharingEnabled are not yet wired in. // Need to check how/where it is in the backend @@ -44,6 +43,10 @@ class AvailabilityViewModel @Inject constructor( loadAvailability() } + fun onBackPressed() { + rootNavigationRepository.popBackStack() + } + // grid interactions /** @@ -91,7 +94,7 @@ class AvailabilityViewModel @Inject constructor( val availability = availabilityRepository.getMyAvailability() applyMutation { copy( - selectedAvailabilities = availability.toLocalDateTimes(), + selectedAvailabilities = availability, isLoading = false, errorMessage = null ) @@ -113,18 +116,4 @@ class AvailabilityViewModel @Inject constructor( } } } -} - -/** - * Converts the backend schedule (Map>) back into - * a flat list of LocalDateTimes for the grid to consume. - * Each slot's startDate is used as the representative time for a cell. - * - * The backend sends startDate as a UTC instant (e.g. "2026-01-23T16:00:00.000Z"), so it's - * parsed as an [Instant] and converted to the device's local wall-clock time. - */ -private fun UserAvailability.toLocalDateTimes(): Set { - return schedule.values.flatten().map { slot -> - Instant.parse(slot.startDate).atZone(ZoneId.systemDefault()).toLocalDateTime() - }.toSet() } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/ChatViewModel.kt b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/ChatViewModel.kt index 71fb8ae..6d7add7 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/ChatViewModel.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/ChatViewModel.kt @@ -23,7 +23,6 @@ import com.cornellappdev.resell.android.model.Chat import com.cornellappdev.resell.android.model.ChatMessageData import com.cornellappdev.resell.android.model.api.ChatRepository import com.cornellappdev.resell.android.model.api.Post -import com.cornellappdev.resell.android.model.chats.AvailabilityBlock import com.cornellappdev.resell.android.model.chats.AvailabilityDocument import com.cornellappdev.resell.android.model.chats.MeetingInfo import com.cornellappdev.resell.android.model.chats.TransactionInfo @@ -33,6 +32,7 @@ import com.cornellappdev.resell.android.model.classes.ResellApiResponse import com.cornellappdev.resell.android.model.core.UserInfoRepository import com.cornellappdev.resell.android.model.login.FireStoreRepository import com.cornellappdev.resell.android.model.login.FirebaseMessagingRepository +import com.cornellappdev.resell.android.model.profile.AvailabilityRepository import com.cornellappdev.resell.android.model.ptf.PostTransactionRatingRepository import com.cornellappdev.resell.android.ui.components.availability.helper.GridSelectionType import com.cornellappdev.resell.android.ui.components.global.ResellTextButtonContainer @@ -41,7 +41,6 @@ import com.cornellappdev.resell.android.ui.screens.root.ResellRootRoute import com.cornellappdev.resell.android.ui.theme.Style import com.cornellappdev.resell.android.ui.theme.Style.heading3 import com.cornellappdev.resell.android.util.UIEvent -import com.cornellappdev.resell.android.util.convertToFirestoreTimestamp import com.cornellappdev.resell.android.util.loadBitmapFromUri import com.cornellappdev.resell.android.util.toNetworkingString import com.cornellappdev.resell.android.viewmodel.ResellViewModel @@ -74,6 +73,7 @@ class ChatViewModel @Inject constructor( private val rootDialogRepository: RootDialogRepository, private val rootNavigationRepository: RootNavigationRepository, private val postTransactionRatingRepository: PostTransactionRatingRepository, + private val availabilityRepository: AvailabilityRepository, @ApplicationContext private val context: Context ) : ResellViewModel( @@ -247,48 +247,49 @@ class ChatViewModel @Inject constructor( } fun onSendAvailabilityPressed() { - rootNavigationSheetRepository.showBottomSheet( - sheet = RootSheet.Availability( - title = "When are you free to meet?", - buttonString = "Continue", - description = "Drag across the grid to add/remove availability", - callback = ::availabilityCallback, - gridSelectionType = GridSelectionType.AVAILABILITY - ) - ) - } + val canPropose = mostRecentMeetingStateIs("confirmed") == null - private fun availabilityCallback(availability: List) { viewModelScope.launch { - try { - val myInfo = userInfoRepository.getUserInfo() - - val asTimeStamp = availability.map { - it.convertToFirestoreTimestamp() + // Grey out everything but the overlap between both people's saved availability, so + // the proposer only sees times that could actually work for both of them. If either + // side hasn't saved availability (or the fetch fails), fall back to an ungreyed grid. + val overlapTimes = try { + val myId = userInfoRepository.getUserId() + if (myId == null) { + null + } else { + val mine = availabilityRepository.getMyAvailability() + val theirs = availabilityRepository.getUserAvailability(navArgs.otherUserId) + mine.intersect(theirs).toList() } + } catch (e: Exception) { + Log.e("ChatViewModel", "Error loading combined availability: ", e) + null + } - chatRepository.sendAvailability( - selfIsBuyer = navArgs.isBuyer, - listingId = listing.id, - myId = myInfo.id, - otherId = navArgs.otherUserId, - availability = AvailabilityDocument( - asTimeStamp.mapIndexed { index, it -> - AvailabilityBlock( - startDate = it, - id = index + rootNavigationSheetRepository.showBottomSheet( + sheet = RootSheet.Availability( + title = "When are you free to meet?", + buttonString = "Propose", + description = "Select a 30 minute block", + initialButtonState = ResellTextButtonState.DISABLED, + callback = { + if (canPropose && it.isNotEmpty()) { + onMeetingProposal(it.first()) + } else { + rootConfirmationRepository.showError( + "Please select a 30-minute block to propose a meeting, and ensure there is no current meeting." ) } - ), - chatId = navArgs.chatId - ) - rootNavigationSheetRepository.hideSheet() - } catch (e: Exception) { - Log.e("ChatViewModel", "Error sending availability: ", e) - rootConfirmationRepository.showError( - "Something went wrong while sending your availability. Please try again later." + }, + gridSelectionType = if (canPropose) GridSelectionType.PROPOSAL else GridSelectionType.NONE, + overlapTimes = overlapTimes, + onEditAvailability = { + rootNavigationSheetRepository.hideSheet() + rootNavigationRepository.navigate(ResellRootRoute.AVAILABILITY) + } ) - } + ) } } diff --git a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/ProfileViewModel.kt b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/ProfileViewModel.kt index ce0ebf1..e8da8d6 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/ProfileViewModel.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/ProfileViewModel.kt @@ -10,7 +10,6 @@ import com.cornellappdev.resell.android.model.core.UserInfoRepository import com.cornellappdev.resell.android.model.login.GoogleAuthRepository import com.cornellappdev.resell.android.model.profile.ProfileRepository import com.cornellappdev.resell.android.model.settings.BlockedUsersRepository -import com.cornellappdev.resell.android.ui.components.availability.helper.GridSelectionType import com.cornellappdev.resell.android.ui.components.global.ResellTextButtonContainer import com.cornellappdev.resell.android.ui.components.global.ResellTextButtonState import com.cornellappdev.resell.android.ui.screens.root.ResellRootRoute @@ -19,9 +18,7 @@ import com.cornellappdev.resell.android.viewmodel.navigation.RootNavigationRepos import com.cornellappdev.resell.android.viewmodel.root.RootConfirmationRepository import com.cornellappdev.resell.android.viewmodel.root.RootDialogContent import com.cornellappdev.resell.android.viewmodel.root.RootDialogRepository -import com.cornellappdev.resell.android.viewmodel.root.RootNavigationSheetRepository import com.cornellappdev.resell.android.viewmodel.root.RootOptionsMenuRepository -import com.cornellappdev.resell.android.viewmodel.root.RootSheet import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import kotlinx.serialization.encodeToString @@ -35,7 +32,6 @@ class ProfileViewModel @Inject constructor( private val rootConfirmationRepository: RootConfirmationRepository, private val profileRepository: ProfileRepository, private val userInfoRepository: UserInfoRepository, - private val rootNavigationSheetRepository: RootNavigationSheetRepository // add this ): ResellViewModel( initialUiState = ProfileUiState( profileTab = ProfileTab.SHOP, @@ -91,16 +87,7 @@ class ProfileViewModel @Inject constructor( rootNavigationRepository.navigate(ResellRootRoute.SETTINGS) } fun onCalendarPressed() { - rootNavigationSheetRepository.showBottomSheet( - RootSheet.Availability( - buttonString = "Propose", - title = "Availability", - description = "Propose a time to meet", - initialTimes = listOf(), - gridSelectionType = GridSelectionType.PROPOSAL, - callback = { /* TODO: handle selected times */ } - ) - ) + rootNavigationRepository.navigate(ResellRootRoute.AVAILABILITY) } fun onRequestPressed(request: RequestListing) { diff --git a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/root/RootSheetRepository.kt b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/root/RootSheetRepository.kt index b9b591d..75529b8 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/root/RootSheetRepository.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/root/RootSheetRepository.kt @@ -72,7 +72,15 @@ sealed class RootSheet { val title: String, val description: String, val callback: (List) -> Unit, - val gridSelectionType: GridSelectionType + val gridSelectionType: GridSelectionType, + /** + * When non-null, cells NOT in this list are greyed out on the grid — e.g. the + * intersection of both chat participants' saved availability, so only times that + * work for both show as normal/white. Null means this feature isn't used. + */ + val overlapTimes: List? = null, + /** When non-null, shows an "Edit Availability" link next to the description. */ + val onEditAvailability: (() -> Unit)? = null, ) : RootSheet() data class MeetingCancel(