Skip to content

Commit d035e13

Browse files
authored
Merge branch 'trunk' into issue/WOOMOB-1548-filters-chip-dark-mode-color
2 parents 04c75d6 + d970832 commit d035e13

File tree

61 files changed

+703
-378
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+703
-378
lines changed

RELEASE-NOTES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
*** For entries which are touching the Android Wear app's, start entry with `[WEAR]` too.
44
23.6
55
-----
6+
- [Internal] Improve WebView experience by using Authenticated WebView for more scenarios [https://github.com/woocommerce/woocommerce-android/pull/14826]
7+
- [Internal] Update In-Person Payments setup flow to use Authenticated WebView [https://github.com/woocommerce/woocommerce-android/pull/14827]
68
- [*] Improved the Filters button colors on the Orders and Products screens [https://github.com/woocommerce/woocommerce-android/pull/14832]
79

810
23.5

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/BookingsRepository.kt

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package com.woocommerce.android.ui.bookings
22

33
import com.woocommerce.android.WooException
4+
import com.woocommerce.android.di.AppCoroutineScope
45
import com.woocommerce.android.tools.SelectedSite
6+
import kotlinx.coroutines.CoroutineScope
7+
import kotlinx.coroutines.async
58
import kotlinx.coroutines.flow.Flow
69
import kotlinx.coroutines.flow.flowOf
710
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingUpdatePayload
@@ -14,7 +17,8 @@ import javax.inject.Inject
1417

1518
class BookingsRepository @Inject constructor(
1619
private val selectedSite: SelectedSite,
17-
private val bookingsStore: BookingsStore
20+
private val bookingsStore: BookingsStore,
21+
@AppCoroutineScope private val appCoroutineScope: CoroutineScope,
1822
) {
1923
suspend fun fetchBookings(
2024
page: Int,
@@ -112,50 +116,66 @@ class BookingsRepository @Inject constructor(
112116
suspend fun updateAttendanceStatus(
113117
bookingId: Long,
114118
attendanceStatus: BookingEntity.AttendanceStatus,
115-
): Result<Unit> {
119+
): Result<Unit> = appCoroutineScope.async {
116120
val result = bookingsStore.updateBooking(
117121
site = selectedSite.get(),
118122
bookingId = bookingId,
119123
bookingUpdatePayload = BookingUpdatePayload(attendanceStatus = attendanceStatus)
120124
)
121-
return if (result.isError) {
125+
if (result.isError) {
122126
Result.failure(WooException(result.error))
123127
} else {
124128
Result.success(Unit)
125129
}
126-
}
130+
}.await()
127131

128132
suspend fun updateNote(
129133
bookingId: Long,
130134
note: String,
131-
): Result<Unit> {
135+
): Result<Unit> = appCoroutineScope.async {
132136
val result = bookingsStore.updateBooking(
133137
site = selectedSite.get(),
134138
bookingId = bookingId,
135139
bookingUpdatePayload = BookingUpdatePayload(note = note)
136140
)
137-
return if (result.isError) {
141+
if (result.isError) {
138142
Result.failure(WooException(result.error))
139143
} else {
140144
Result.success(Unit)
141145
}
142-
}
146+
}.await()
143147

144148
suspend fun cancelBooking(
145149
bookingId: Long,
146-
): Result<Unit> {
150+
): Result<Unit> = appCoroutineScope.async {
147151
val result = bookingsStore.updateBooking(
148152
site = selectedSite.get(),
149153
bookingId = bookingId,
150154
bookingUpdatePayload = BookingUpdatePayload(status = BookingEntity.Status.Cancelled),
151155
refreshOrder = true,
152156
)
153-
return if (result.isError) {
157+
if (result.isError) {
154158
Result.failure(WooException(result.error))
155159
} else {
156160
Result.success(Unit)
157161
}
158-
}
162+
}.await()
163+
164+
suspend fun markAsPaid(
165+
bookingId: Long,
166+
): Result<Unit> = appCoroutineScope.async {
167+
val result = bookingsStore.updateBooking(
168+
site = selectedSite.get(),
169+
bookingId = bookingId,
170+
bookingUpdatePayload = BookingUpdatePayload(status = BookingEntity.Status.Paid),
171+
refreshOrder = true,
172+
)
173+
if (result.isError) {
174+
Result.failure(WooException(result.error))
175+
} else {
176+
Result.success(Unit)
177+
}
178+
}.await()
159179

160180
data class FetchResult(
161181
val bookings: List<Booking>,

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/compose/BookingPaymentSection.kt

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ fun BookingPaymentSection(
3030
model: BookingPaymentDetailsModel,
3131
status: BookingStatus,
3232
onMarkAsPaid: () -> Unit,
33-
onMarkAsRefunded: () -> Unit,
3433
onViewOrder: () -> Unit,
3534
modifier: Modifier = Modifier,
35+
markAsPaidInProgress: Boolean = false,
3636
) {
3737
Column(modifier = modifier) {
3838
BookingSectionHeader(R.string.booking_payment_header)
@@ -59,28 +59,18 @@ fun BookingPaymentSection(
5959
modifier = Modifier.padding(start = 16.dp)
6060
)
6161
Spacer(modifier = Modifier.height(16.dp))
62-
// TODO Change the logic and use Order info when available
63-
if (status == BookingStatus.Paid) {
64-
WCOutlinedButton(
65-
onClick = onMarkAsRefunded,
66-
colors = ButtonDefaults.outlinedButtonColors(
67-
contentColor = MaterialTheme.colorScheme.onSurface
68-
),
69-
text = stringResource(id = R.string.orderdetail_issue_refund_button),
70-
modifier = Modifier
71-
.fillMaxWidth()
72-
.padding(horizontal = 16.dp)
73-
)
74-
} else {
62+
if (status == BookingStatus.Unpaid) {
7563
WCColoredButton(
7664
onClick = onMarkAsPaid,
7765
text = stringResource(id = R.string.booking_payment_mark_as_paid),
7866
modifier = Modifier
7967
.fillMaxWidth()
80-
.padding(horizontal = 16.dp)
68+
.padding(horizontal = 16.dp),
69+
loading = markAsPaidInProgress,
70+
enabled = !markAsPaidInProgress,
8171
)
72+
Spacer(modifier = Modifier.height(8.dp))
8273
}
83-
Spacer(modifier = Modifier.height(8.dp))
8474
WCOutlinedButton(
8575
onClick = onViewOrder,
8676
colors = ButtonDefaults.outlinedButtonColors(
@@ -145,18 +135,17 @@ private fun BookingPaymentSectionPreview() {
145135
discount = "-",
146136
total = "$59.50"
147137
),
148-
status = BookingStatus.Complete,
138+
status = BookingStatus.Unpaid,
149139
onMarkAsPaid = {},
150140
onViewOrder = {},
151-
onMarkAsRefunded = {},
152141
modifier = Modifier.fillMaxWidth()
153142
)
154143
}
155144
}
156145

157146
@LightDarkThemePreviews
158147
@Composable
159-
private fun BookingPaymentSectionWithRefundOptionPreview() {
148+
private fun BookingPaymentSectionWithPaidBookingPreview() {
160149
WooThemeWithBackground {
161150
BookingPaymentSection(
162151
model = BookingPaymentDetailsModel(
@@ -168,7 +157,6 @@ private fun BookingPaymentSectionWithRefundOptionPreview() {
168157
status = BookingStatus.Paid,
169158
onMarkAsPaid = {},
170159
onViewOrder = {},
171-
onMarkAsRefunded = {},
172160
modifier = Modifier.fillMaxWidth()
173161
)
174162
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/details/BookingDetailsScreen.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ fun BookingDetailsScreen(
108108
onViewOrder = onViewOrder,
109109
onAttendanceStatusClicked = { showAttendanceSheet.value = true },
110110
onViewNotes = onViewNotes,
111+
onMarkAsPaid = viewState.onMarkAsPaid,
112+
markAsPaidInProgress = viewState.paymentUpdateStatus == PaymentUpdateStatus.InProgress,
111113
)
112114
}
113115
}
@@ -132,6 +134,8 @@ private fun BookingDetailsContent(
132134
onViewOrder: (Long) -> Unit,
133135
onAttendanceStatusClicked: () -> Unit,
134136
onViewNotes: () -> Unit,
137+
onMarkAsPaid: () -> Unit,
138+
markAsPaidInProgress: Boolean,
135139
) {
136140
BookingSummary(
137141
model = booking.bookingSummary,
@@ -158,10 +162,10 @@ private fun BookingDetailsContent(
158162
BookingPaymentSection(
159163
model = it,
160164
status = booking.bookingSummary.status,
161-
onMarkAsPaid = { onViewOrder(booking.orderId) },
165+
onMarkAsPaid = onMarkAsPaid,
162166
onViewOrder = { onViewOrder(booking.orderId) },
163-
onMarkAsRefunded = { onViewOrder(booking.orderId) },
164-
modifier = Modifier.fillMaxWidth()
167+
modifier = Modifier.fillMaxWidth(),
168+
markAsPaidInProgress = markAsPaidInProgress,
165169
)
166170
}
167171
BookingNoteSection(

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/details/BookingDetailsViewModel.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class BookingDetailsViewModel @Inject constructor(
6060
private val cancelStatusState = MutableStateFlow<CancelStatus>(CancelStatus.Idle)
6161
private val showCancelBookingDialog = MutableStateFlow(false)
6262

63+
private val paymentUpdateStatus = MutableStateFlow<PaymentUpdateStatus>(PaymentUpdateStatus.Idle)
64+
6365
private val cancelBookingDialogState = combine(
6466
booking,
6567
showCancelBookingDialog,
@@ -102,7 +104,8 @@ class BookingDetailsViewModel @Inject constructor(
102104
bookingUiStateFlow,
103105
loadingState,
104106
cancelBookingDialogState,
105-
) { booking, bookingUiState, loadingState, cancelBookingDialog ->
107+
paymentUpdateStatus,
108+
) { booking, bookingUiState, loadingState, cancelBookingDialog, paymentUpdateStatus ->
106109
with(bookingMapper) {
107110
BookingDetailsViewState(
108111
toolbarTitle = booking?.id?.value?.let { id ->
@@ -115,6 +118,8 @@ class BookingDetailsViewModel @Inject constructor(
115118
onAttendanceStatusSelected(attendanceStatus)
116119
}
117120
},
121+
onMarkAsPaid = ::onMarkAsPaid,
122+
paymentUpdateStatus = paymentUpdateStatus,
118123
dialogState = cancelBookingDialog,
119124
loadingState = loadingState,
120125
onRefresh = ::fetchBooking,
@@ -197,6 +202,19 @@ class BookingDetailsViewModel @Inject constructor(
197202
cancelStatusState.value = CancelStatus.Idle
198203
}
199204

205+
private fun onMarkAsPaid() = launch {
206+
if (!networkStatus.isConnected()) {
207+
triggerEvent(MultiLiveEvent.Event.ShowSnackbar(R.string.offline_error))
208+
return@launch
209+
}
210+
paymentUpdateStatus.value = PaymentUpdateStatus.InProgress
211+
bookingsRepository.markAsPaid(navArgs.bookingId)
212+
.onFailure {
213+
triggerEvent(MultiLiveEvent.Event.ShowSnackbar(R.string.booking_mark_as_paid_error))
214+
}
215+
paymentUpdateStatus.value = PaymentUpdateStatus.Idle
216+
}
217+
200218
private suspend fun BookingMapper.buildBookingUiState(
201219
booking: Booking,
202220
attendanceUpdateStatus: AttendanceUpdateStatus,

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/details/BookingDetailsViewState.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ data class BookingDetailsViewState(
1313
val loadingState: BookingDetailsLoadingState = BookingDetailsLoadingState.Idle,
1414
val onCancelBooking: () -> Unit = {},
1515
val onAttendanceStatusSelected: (BookingAttendanceStatus) -> Unit = { _ -> },
16+
val onMarkAsPaid: () -> Unit = {},
17+
val paymentUpdateStatus: PaymentUpdateStatus = PaymentUpdateStatus.Idle,
1618
val dialogState: DialogState? = null,
1719
val onRefresh: () -> Unit = {},
1820
) {
@@ -44,3 +46,8 @@ sealed interface AttendanceUpdateStatus {
4446
data object Idle : AttendanceUpdateStatus
4547
data object InProgress : AttendanceUpdateStatus
4648
}
49+
50+
sealed interface PaymentUpdateStatus {
51+
data object Idle : PaymentUpdateStatus
52+
data object InProgress : PaymentUpdateStatus
53+
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/common/webview/AuthenticatedWebViewLauncher.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.woocommerce.android.ui.common.webview
22

33
import android.content.Context
4+
import androidx.fragment.app.DialogFragment
45
import androidx.fragment.app.Fragment
56
import androidx.navigation.NavController
67
import androidx.navigation.NavDirections
@@ -32,6 +33,10 @@ class AuthenticatedWebViewLauncher @Inject constructor(
3233
navController.navigate(action)
3334
} else {
3435
ChromeCustomTabUtils.launchUrl(context, event.url)
36+
if (fragment is DialogFragment) {
37+
// To align with the regular fragment behavior, we dismiss the dialog after launching the CCT.
38+
fragment.dismiss()
39+
}
3540
}
3641
}
3742

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/web/WCWebChromeClient.kt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package com.woocommerce.android.ui.compose.component.web
22

33
import android.net.Uri
4+
import android.os.Message
45
import android.webkit.ValueCallback
56
import android.webkit.WebChromeClient
67
import android.webkit.WebView
8+
import android.widget.FrameLayout
79
import androidx.activity.ComponentActivity
810
import androidx.activity.result.contract.ActivityResultContracts
11+
import androidx.core.view.isVisible
912
import com.woocommerce.android.extensions.findActivity
1013

1114
open class WCWebChromeClient : WebChromeClient() {
@@ -48,4 +51,42 @@ open class WCWebChromeClient : WebChromeClient() {
4851
override fun onProgressChanged(view: WebView?, newProgress: Int) {
4952
onProgressChanged(newProgress)
5053
}
54+
55+
override fun onCreateWindow(
56+
view: WebView,
57+
isDialog: Boolean,
58+
isUserGesture: Boolean,
59+
resultMsg: Message
60+
): Boolean {
61+
require(view.parent is FrameLayout) {
62+
"Ensure the WebView is added to a FrameLayout to support popups. Check WCWebView implementation."
63+
}
64+
65+
val popupContainer = view.parent as FrameLayout
66+
67+
val popupWebView = WebView(view.context).apply {
68+
settings.useWideViewPort = view.settings.useWideViewPort
69+
settings.loadWithOverviewMode = view.settings.loadWithOverviewMode
70+
settings.javaScriptEnabled = view.settings.javaScriptEnabled
71+
settings.domStorageEnabled = view.settings.domStorageEnabled
72+
settings.setSupportMultipleWindows(true)
73+
}
74+
75+
popupWebView.webViewClient = view.webViewClient
76+
popupWebView.webChromeClient = object : WebChromeClient() {
77+
override fun onCloseWindow(window: WebView) {
78+
popupContainer.removeView(popupWebView)
79+
view.isVisible = true
80+
}
81+
}
82+
83+
popupContainer.layoutParams = view.layoutParams
84+
popupContainer.addView(popupWebView)
85+
view.isVisible = false
86+
87+
(resultMsg.obj as WebView.WebViewTransport).webView = popupWebView
88+
resultMsg.sendToTarget()
89+
90+
return true
91+
}
5192
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/web/WCWebView.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.woocommerce.android.ui.compose.component.web
33
import android.annotation.SuppressLint
44
import android.view.ViewGroup
55
import android.webkit.WebView
6+
import android.widget.FrameLayout
67
import androidx.activity.compose.BackHandler
78
import androidx.compose.foundation.layout.Box
89
import androidx.compose.foundation.layout.Column
@@ -104,6 +105,7 @@ fun WCWebView(
104105
webView.settings.loadWithOverviewMode = settings.loadWithOverviewMode
105106
webView.settings.javaScriptEnabled = settings.isJavaScriptEnabled
106107
webView.settings.domStorageEnabled = settings.isDomStorageEnabled
108+
webView.settings.setSupportMultipleWindows(true)
107109
}
108110
}
109111

@@ -127,7 +129,7 @@ fun WCWebView(
127129

128130
AndroidView(
129131
factory = { context ->
130-
WebView(context).apply {
132+
val webView = WebView(context).apply {
131133
layoutParams = ViewGroup.LayoutParams(
132134
ViewGroup.LayoutParams.MATCH_PARENT,
133135
ViewGroup.LayoutParams.MATCH_PARENT
@@ -140,6 +142,10 @@ fun WCWebView(
140142

141143
this.settings.userAgentString = userAgent.webViewUserAgent
142144
}.also { webView = it }
145+
146+
FrameLayout(context).apply {
147+
addView(webView)
148+
}
143149
},
144150
modifier = Modifier
145151
.alpha(webViewAlpha)

0 commit comments

Comments
 (0)