Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.woocommerce.android.WooException
import com.woocommerce.android.tools.SelectedSite
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingUpdatePayload
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsFilterOption
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsOrderOption
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsStore
Expand Down Expand Up @@ -62,6 +63,13 @@ class BookingsRepository @Inject constructor(
bookingId = bookingId
)

suspend fun getBooking(bookingId: Long): Booking? {
return bookingsStore.getBooking(
site = selectedSite.get(),
bookingId = bookingId
)
}

suspend fun fetchBooking(
bookingId: Long
): Result<Booking> {
Expand Down Expand Up @@ -105,10 +113,26 @@ class BookingsRepository @Inject constructor(
bookingId: Long,
attendanceStatus: BookingEntity.AttendanceStatus,
): Result<Unit> {
val result = bookingsStore.updateAttendanceStatus(
val result = bookingsStore.updateBooking(
site = selectedSite.get(),
bookingId = bookingId,
bookingUpdatePayload = BookingUpdatePayload(attendanceStatus = attendanceStatus)
)
return if (result.isError) {
Result.failure(WooException(result.error))
} else {
Result.success(Unit)
}
}

suspend fun updateNote(
bookingId: Long,
note: String,
): Result<Unit> {
val result = bookingsStore.updateBooking(
site = selectedSite.get(),
bookingId = bookingId,
attendanceStatus = attendanceStatus
bookingUpdatePayload = BookingUpdatePayload(note = note)
)
return if (result.isError) {
Result.failure(WooException(result.error))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.woocommerce.android.ui.base.BaseFragment
import com.woocommerce.android.ui.base.UIMessageResolver
import com.woocommerce.android.ui.compose.composeView
Expand All @@ -21,6 +22,7 @@ class BookingDetailsFragment : BaseFragment() {
lateinit var uiMessageResolver: UIMessageResolver

private val viewModel: BookingDetailsViewModel by viewModels()
private val args: BookingDetailsFragmentArgs by navArgs()

override val activityAppBarStatus: AppBarStatus
get() = AppBarStatus.Hidden
Expand All @@ -35,6 +37,12 @@ class BookingDetailsFragment : BaseFragment() {
BookingDetailsFragmentDirections
.actionBookingDetailsFragmentToOrderDetailFragment(orderId)
)
},
onViewNotes = {
findNavController().navigate(
BookingDetailsFragmentDirections
.actionBookingDetailsFragmentToBookingNoteFragment(args.bookingId)
)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground
fun BookingDetailsScreen(
viewModel: BookingDetailsViewModel,
onBack: () -> Unit,
onViewOrder: (Long) -> Unit
onViewOrder: (Long) -> Unit,
onViewNotes: () -> Unit,
) {
val viewState by viewModel.state.observeAsState()

Expand All @@ -62,6 +63,7 @@ fun BookingDetailsScreen(
viewState = it,
onBack = onBack,
onViewOrder = onViewOrder,
onViewNotes = onViewNotes,
)
}
}
Expand All @@ -72,6 +74,7 @@ fun BookingDetailsScreen(
viewState: BookingDetailsViewState,
onBack: () -> Unit,
onViewOrder: (Long) -> Unit,
onViewNotes: () -> Unit,
) {
val showAttendanceSheet = remember { mutableStateOf(false) }
Scaffold(
Expand Down Expand Up @@ -103,6 +106,7 @@ fun BookingDetailsScreen(
onCancelBooking = viewState.onCancelBooking,
onViewOrder = onViewOrder,
onAttendanceStatusClicked = { showAttendanceSheet.value = true },
onViewNotes = onViewNotes,
)
}
}
Expand All @@ -126,6 +130,7 @@ private fun BookingDetailsContent(
onCancelBooking: () -> Unit,
onViewOrder: (Long) -> Unit,
onAttendanceStatusClicked: () -> Unit,
onViewNotes: () -> Unit,
) {
BookingSummary(
model = booking.bookingSummary,
Expand Down Expand Up @@ -158,7 +163,7 @@ private fun BookingDetailsContent(
}
BookingNoteSection(
note = booking.note,
onClick = {},
onClick = onViewNotes,
modifier = Modifier.fillMaxWidth()
)
}
Expand Down Expand Up @@ -262,7 +267,8 @@ private fun BookingDetailsPreview() {
),
),
onBack = {},
onViewOrder = {}
onViewOrder = {},
onViewNotes = {},
)
}
}
Expand All @@ -278,6 +284,7 @@ private fun BookingDetailsLoadingPreview() {
),
onBack = {},
onViewOrder = {},
onViewNotes = {},
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.woocommerce.android.ui.bookings.note

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.woocommerce.android.ui.base.BaseFragment
import com.woocommerce.android.ui.base.UIMessageResolver
import com.woocommerce.android.ui.compose.composeView
import com.woocommerce.android.ui.main.AppBarStatus
import com.woocommerce.android.viewmodel.MultiLiveEvent
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class BookingNoteFragment : BaseFragment() {

@Inject
lateinit var uiMessageResolver: UIMessageResolver

private val viewModel: BookingNoteViewModel by viewModels()

override val activityAppBarStatus: AppBarStatus
get() = AppBarStatus.Hidden

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return composeView {
BookingNoteScreen(
viewModel = viewModel,
onBack = { findNavController().popBackStack() },
)
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
handleEvents()
}

private fun handleEvents() {
viewModel.event.observe(viewLifecycleOwner) { event ->
when (event) {
is MultiLiveEvent.Event.ShowSnackbar -> {
uiMessageResolver.showSnack(event.message)
}
is MultiLiveEvent.Event.Exit -> {
findNavController().navigateUp()
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package com.woocommerce.android.ui.bookings.note

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import com.woocommerce.android.R
import com.woocommerce.android.ui.compose.component.Toolbar
import com.woocommerce.android.ui.compose.component.WCTextButton

@Composable
fun BookingNoteScreen(
viewModel: BookingNoteViewModel,
onBack: () -> Unit,
) {
val viewState by viewModel.state.observeAsState()
viewState?.let {
BookingNoteScreen(
viewState = it,
onBack = onBack,
)
}
}

@Composable
fun BookingNoteScreen(
viewState: BookingNoteViewState,
onBack: () -> Unit,
) {
val focusRequester = remember { FocusRequester() }

// Request focus only after the note is available in the composition
LaunchedEffect(viewState.editedNote != null) {
focusRequester.requestFocus()
}

Scaffold(
topBar = {
Toolbar(
title = stringResource(R.string.booking_note_screen_title),
onNavigationButtonClick = onBack,
actions = {
if (viewState.isSaveVisible) {
WCTextButton(
onClick = viewState.onSaveClicked,
enabled = viewState.isSaveEnabled,
) {
when (viewState.noteSaveStatus) {
is NoteSaveStatus.Idle -> {
Text(stringResource(R.string.booking_note_screen_done))
}

is NoteSaveStatus.InProgress -> {
CircularProgressIndicator(
modifier = Modifier.size(24.dp)
)
}
}
}
}
},
modifier = Modifier.shadow(4.dp)
)
}
) { innerPadding ->
Box(
modifier = Modifier
.background(MaterialTheme.colorScheme.surface)
.padding(top = innerPadding.calculateTopPadding())
) {
viewState.editedNote?.let {
NoteTextField(
value = it,
onValueChange = viewState.onNoteChange,
enabled = viewState.noteEditable,
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 16.dp, vertical = 12.dp)
.focusRequester(focusRequester)
)
}
}
}
}

@Composable
private fun NoteTextField(
value: String,
onValueChange: (String) -> Unit,
enabled: Boolean,
modifier: Modifier = Modifier,
) {
var textFieldValueState by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(
TextFieldValue(
text = value,
selection = TextRange(value.length)
)
)
}

val textFieldValue = textFieldValueState.copy(text = value)

val lastValue by rememberUpdatedState(value)

BasicTextField(
value = textFieldValue,
onValueChange = {
textFieldValueState = it
if (it.text != lastValue) {
// Update external value when changed
onValueChange(it.text)
}
},
enabled = enabled,
modifier = modifier,
textStyle = MaterialTheme.typography.bodyLarge.copy(
color = MaterialTheme.colorScheme.onSurface
),
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary)
)
}
Loading