Skip to content

Commit 4ae74f9

Browse files
authored
Merge branch 'trunk' into woomob-1244-woo-poslocal-catalog-use-new-pos-specific-variation-model-in
2 parents 2dc7ce9 + 08931bc commit 4ae74f9

File tree

46 files changed

+1727
-532
lines changed

Some content is hidden

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

46 files changed

+1727
-532
lines changed

.github/dependabot.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ updates:
1515
- "com.google.devtools.ksp*"
1616
exclude-patterns:
1717
- "org.jetbrains.kotlinx*"
18+
cooldown:
19+
semver-major-days: 30
20+
semver-minor-days: 30
1821
ignore:
1922
# Bumping 2.26.3 to 2.27.2 will break the mocks. For more details, see
2023
# https://github.com/wiremock/wiremock/issues/1345#issuecomment-656060968

.github/workflows/dependabot_automerge.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,16 @@ jobs:
2828
if: steps.metadata.outputs.update-type == 'version-update:semver-patch'
2929
run: |
3030
set -e
31-
number=$(gh api repos/$GITHUB_REPOSITORY/milestones \
31+
title=$(gh api repos/$GITHUB_REPOSITORY/milestones \
3232
--jq '[.[]
3333
| select(.state=="open" and .due_on!=null and (.due_on | fromdateiso8601) >= now)
3434
]
3535
| sort_by(.due_on)
36-
| .[0].number')
36+
| .[0].title')
3737
38-
if [ -n "$number" ]; then
39-
echo "Assigning milestone #$number to PR #${{ github.event.pull_request.number }}"
40-
gh pr edit "${{ github.event.pull_request.number }}" --milestone "$number"
38+
if [ -n "$title" ]; then
39+
echo "Assigning milestone '$title' to PR #${{ github.event.pull_request.number }}"
40+
gh pr edit "${{ github.event.pull_request.number }}" --milestone "$title"
4141
else
4242
echo "No future open milestones found."
4343
fi

WooCommerce/build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ fladle {
1919
variant = "vanillaDebug"
2020
serviceAccountCredentials = rootProject.file(".configure-files/firebase.secrets.json")
2121
testTargets = [
22-
"notPackage com.woocommerce.android.e2e.tests.screenshot"
22+
"notPackage com.woocommerce.android.e2e.tests.screenshot",
23+
"notClass com.woocommerce.android.e2e.tests.ui.OrdersRealAPI",
24+
"notClass com.woocommerce.android.e2e.tests.ui.ProductsRealAPI"
2325
]
2426
devices = [
2527
["model": "MediumPhone.arm", "version": "35"]

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/WooShippingEditAddressScreen.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,6 @@ fun WooShippingEditAddressScreen(
309309
error = editableAddress.postalCode.error,
310310
isRequired = editableAddress.postalCode.isRequired,
311311
keyboardOptions = KeyboardOptions(
312-
keyboardType = KeyboardType.Number,
313312
imeAction = ImeAction.Next
314313
),
315314
keyboardActions = KeyboardActions(

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersDataSource.kt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package com.woocommerce.android.ui.woopos.orders
33
import com.woocommerce.android.model.Order
44
import com.woocommerce.android.model.OrderMapper
55
import com.woocommerce.android.tools.SelectedSite
6+
import kotlinx.coroutines.flow.Flow
7+
import kotlinx.coroutines.flow.flow
68
import org.wordpress.android.fluxc.network.rest.wpcom.wc.order.OrderRestClient
79
import org.wordpress.android.fluxc.network.rest.wpcom.wc.order.OrderRestClient.OrderBy
810
import org.wordpress.android.fluxc.persistence.entity.OrderEntity
@@ -17,26 +19,35 @@ class WooPosOrdersDataSource @Inject constructor(
1719
private val restClient: OrderRestClient,
1820
private val selectedSite: SelectedSite,
1921
private val orderMapper: OrderMapper,
22+
private val ordersCache: WooPosOrdersInMemoryCache
2023
) {
21-
suspend fun loadOrders(): LoadOrdersResult {
24+
companion object {
25+
const val POS_ORDERS_PAGE_SIZE = 25
26+
}
27+
fun loadOrders(): Flow<LoadOrdersResult> = flow {
28+
val cached = ordersCache.getAll()
29+
emit(LoadOrdersResult.Success(cached))
30+
2231
val result = restClient.fetchOrders(
2332
site = selectedSite.get(),
24-
count = 25,
33+
count = POS_ORDERS_PAGE_SIZE,
2534
page = 1,
2635
orderBy = OrderBy.DATE,
2736
sortOrder = OrderRestClient.SortOrder.DESCENDING,
2837
statusFilter = null,
2938
createdVia = "pos-rest-api"
3039
)
3140

32-
return if (result.isError) {
33-
LoadOrdersResult.Error(result.error.message)
41+
if (result.isError) {
42+
emit(LoadOrdersResult.Error(result.error.message))
3443
} else {
35-
LoadOrdersResult.Success(result.orders.toAppModels())
44+
val mapped = result.orders.toAppModels()
45+
ordersCache.setAll(mapped)
46+
emit(LoadOrdersResult.Success(result.orders.toAppModels()))
3647
}
3748
}
3849

3950
private suspend fun List<OrderEntity>.toAppModels(): List<Order> = map {
4051
orderMapper.toAppModel(it)
41-
}
52+
} ?: emptyList()
4253
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.woocommerce.android.ui.woopos.orders
2+
3+
import com.woocommerce.android.model.Order
4+
import java.util.concurrent.atomic.AtomicReference
5+
import javax.inject.Inject
6+
import javax.inject.Singleton
7+
8+
@Singleton
9+
class WooPosOrdersInMemoryCache @Inject constructor() {
10+
private val ordersCache = AtomicReference<List<Order>>(emptyList())
11+
12+
fun setAll(orders: List<Order>) {
13+
ordersCache.set(orders.toList())
14+
}
15+
16+
fun getAll(): List<Order> = ordersCache.get()
17+
18+
fun clear() {
19+
ordersCache.set(emptyList())
20+
}
21+
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/orders/WooPosOrdersViewModel.kt

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,28 @@ class WooPosOrdersViewModel @Inject constructor(
3030
viewModelScope.launch {
3131
_state.update { it.copy(isLoading = true, error = null) }
3232

33-
when (val result = ordersDataSource.loadOrders()) {
34-
is LoadOrdersResult.Error -> {
35-
_state.update {
36-
it.copy(
37-
isLoading = false,
38-
error = result.message ?: "Unknown error"
39-
)
33+
ordersDataSource.loadOrders().collect { result ->
34+
when (result) {
35+
is LoadOrdersResult.Error -> {
36+
_state.update {
37+
it.copy(
38+
isLoading = false,
39+
error = result.message
40+
)
41+
}
4042
}
41-
}
42-
is LoadOrdersResult.Success -> {
43-
val list = result.orders
44-
_state.update { prev ->
45-
prev.copy(
46-
isLoading = false,
47-
orders = list,
48-
selectedOrderId = prev.selectedOrderId?.takeIf { id ->
49-
list.any { o -> o.id == id }
50-
} ?: list.firstOrNull()?.id
51-
)
43+
44+
is LoadOrdersResult.Success -> {
45+
val list = result.orders
46+
_state.update { prev ->
47+
prev.copy(
48+
isLoading = false,
49+
orders = list,
50+
selectedOrderId = prev.selectedOrderId?.takeIf { id ->
51+
list.any { o -> o.id == id }
52+
} ?: list.firstOrNull()?.id
53+
)
54+
}
5255
}
5356
}
5457
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/splash/WooPosSplashViewModel.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
44
import androidx.lifecycle.viewModelScope
55
import com.woocommerce.android.ui.woopos.common.data.WooPosPopularProductsProvider
66
import com.woocommerce.android.ui.woopos.home.items.products.WooPosProductsDataSource
7+
import com.woocommerce.android.ui.woopos.orders.WooPosOrdersInMemoryCache
78
import com.woocommerce.android.ui.woopos.tab.WooPosCanBeLaunchedInTab
89
import com.woocommerce.android.ui.woopos.tab.WooPosLaunchability
910
import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsEvent.Event.Loaded
@@ -22,6 +23,7 @@ class WooPosSplashViewModel @Inject constructor(
2223
private val popularProductsProvider: WooPosPopularProductsProvider,
2324
private val analyticsTracker: WooPosAnalyticsTracker,
2425
private val posCanBeLaunchedInTab: WooPosCanBeLaunchedInTab,
26+
private val ordersCache: WooPosOrdersInMemoryCache
2527
) : ViewModel() {
2628
private val _state = MutableStateFlow<WooPosSplashState>(WooPosSplashState.Loading)
2729
val state: StateFlow<WooPosSplashState> = _state
@@ -38,7 +40,8 @@ class WooPosSplashViewModel @Inject constructor(
3840

3941
joinAll(
4042
launch { productsDataSource.prepopulateProductsCache() },
41-
launch { popularProductsProvider.fetchAndCachePopularProducts() }
43+
launch { popularProductsProvider.fetchAndCachePopularProducts() },
44+
launch { ordersCache.clear() }
4245
)
4346
_state.value = WooPosSplashState.Loaded
4447
trackPosLoaded(splashScreenStartTime)
Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,40 @@
11
package com.woocommerce.android.util
22

3+
import java.util.IllformedLocaleException
34
import java.util.Locale
45

56
object AddressUtils {
67
/**
7-
* Translates a two-character country code into a human
8-
* readable label.
8+
* Returns a human-readable country label for the given input.
99
*
10-
* Example: US -> United States
10+
* Behavior:
11+
* - If input is blank -> returns empty string.
12+
* - If input looks like a 2-letter ISO country code (e.g., "US", "gb") ->
13+
* returns the localized display name (e.g., "United States"). Falls back
14+
* to the original input if it cannot be resolved.
15+
* - Otherwise (e.g., full country name like "India") -> returns the trimmed input.
1116
*/
1217
fun getCountryLabelByCountryCode(countryCode: String): String {
13-
val locale = Locale.Builder()
14-
.setLanguage(Locale.getDefault().language)
15-
.setRegion(countryCode)
16-
.build()
17-
return locale.displayCountry
18+
val value = countryCode.trim()
19+
if (value.isEmpty() || !isIsoLikeCountryCode(value)) return value
20+
21+
val region = value.uppercase(Locale.ROOT)
22+
return resolveDisplayCountry(region, fallback = value)
1823
}
24+
25+
private fun isIsoLikeCountryCode(input: String): Boolean =
26+
input.length == 2 && input.all { it.isLetter() }
27+
28+
private fun resolveDisplayCountry(region: String, fallback: String): String =
29+
try {
30+
val locale = Locale.Builder()
31+
.setLanguage(Locale.getDefault().language)
32+
.setRegion(region)
33+
.build()
34+
val display = locale.displayCountry
35+
display.ifBlank { fallback }
36+
} catch (_: IllformedLocaleException) {
37+
// If the region is ill-formed, return the safest fallback (original input)
38+
fallback
39+
}
1940
}

WooCommerce/src/main/res/values-ar/strings.xml

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,68 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
3-
Translation-Revision-Date: 2025-08-11 15:54:04+0000
3+
Translation-Revision-Date: 2025-08-25 14:54:04+0000
44
Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;
55
Generator: GlotPress/2.4.0-alpha
66
Language: ar
77
-->
88
<resources xmlns:tools="http://schemas.android.com/tools">
9+
<string name="woo_shipping_split_shipment_purchased_message_desc">لا يمكنك نقل المنتجات إليه أو الخروج منه.</string>
10+
<string name="woo_shipping_split_shipment_purchased_message_title">لقد اشتريت ملصقًا لهذه الشحنة.</string>
11+
<string name="woo_shipping_labels_customs_origin_country_info">البلد الذي تم تصنيع المنتج أو تجميعه فيه.</string>
12+
<string name="woo_shipping_labels_customs_origin_country_info_button">المزيد من المعلومات حول بلد منشأ المنتج</string>
13+
<string name="woo_shipping_labels_customs_description_info">عند الشحن إلى البلدان التي تتبع القواعد الجمركية المعمول بها في الاتحاد الأوروبي، يجب عليك تقديم وصف صريح ومحدد حول كل عنصر. على سبيل المثال، إذا كنت ترسل ملابس، فيجب عليك تحديد نوع الملابس (مثل: قمصان الرجال وسترات النساء التحتية وسترات الأولاد) لكي يصبح الوصف مقبولاً. وإلا، فإن الشحنات قد تتأجل أو تُعتَرض في الجمارك.</string>
14+
<string name="woo_shipping_labels_customs_description_info_button">المزيد من المعلومات حول وصف المنتج</string>
15+
<string name="woo_shipping_labels_customs_hs_tariff_info_button">المزيد من المعلومات حول تعريف النظام المنسق</string>
16+
<string name="woo_shipping_labels_customs_itn_info_button">المزيد من المعلومات حول رقم المعاملة</string>
17+
<string name="woo_shipping_labels_package_creation_shipping_rates_empty_with_hazmat">تعذر علينا العثور على خدمة الشحن لمجموعة من تصنيفات المواد الخطرة المحددة، والطرود المحددة، وإجمالي وزن الشحنة. يرجى ضبط إدخالك والمحاولة مجددًا.</string>
18+
<string name="woo_shipping_labels_package_creation_shipping_rates_empty">تعذر علينا العثور على خدمة الشحن لمجموعة من الطرود المحددة وإجمالي وزن الشحنة. يرجى ضبط إدخالك والمحاولة مجددًا.</string>
19+
<string name="woopos_settings_store_not_set">لم يتم التعيين</string>
20+
<string name="woopos_settings_refund_policy_label">سياسة الاسترداد والإرجاع</string>
21+
<string name="woopos_settings_store_email_label">البريد الإلكتروني</string>
22+
<string name="woopos_settings_store_phone_label">الهاتف</string>
23+
<string name="woopos_settings_store_address_label">العنوان</string>
24+
<string name="woopos_settings_store_name_label">اسم المتجر</string>
25+
<string name="woopos_settings_receipt_information_title">معلومات الإيصال</string>
26+
<string name="woopos_settings_store_information_title">معلومات الدفع المخزنة</string>
27+
<string name="woopos_settings_help_get_support_subtitle">اتصل بفريق الدعم لدينا</string>
28+
<string name="woopos_settings_help_documentation_subtitle">عرض الأدلة والبرامج التعليمية</string>
29+
<string name="woopos_settings_help_product_limitations_subtitle">تعرف على المنتجات المدعومة في نقطة البيع</string>
30+
<string name="woopos_settings_help_category_subtitle">احصل على المساعدة والدعم</string>
31+
<string name="woopos_settings_card_reader_connected_reader">تم توصيل القارئ</string>
32+
<string name="woopos_settings_card_reader_documentation_subtitle">تعرف على المزيد حول قبول المدفوعات باستخدام الهاتف المحمول</string>
33+
<string name="woopos_settings_card_reader_documentation_title">توثيق</string>
34+
<string name="woopos_settings_card_reader_update_available">• يوجد تحديث متوفر</string>
35+
<string name="woopos_settings_card_reader_update_button">تحديث</string>
36+
<string name="woopos_settings_card_reader_unknown_firmware">غير معروف</string>
37+
<string name="woopos_settings_card_reader_unknown_reader">قارئ غير معروف</string>
38+
<string name="woopos_settings_card_reader_firmware_title">البرنامج الثابت</string>
39+
<string name="woopos_settings_card_reader_battery_title">البطارية</string>
40+
<string name="woopos_settings_card_reader_detail_title">إعدادات قارئ البطاقات</string>
41+
<string name="woopos_settings_barcode_scanner_documentation_subtitle">تعرف على المزيد حول فحص الرمز الشريطي في نقطة البيع</string>
42+
<string name="woopos_settings_barcode_scanner_documentation_title">توثيق</string>
43+
<string name="woopos_settings_barcode_scanner_setup_subtitle">قم بتكوين الماسح الضوئي للرمز الشريطي الخاص بك واختباره</string>
44+
<string name="woopos_settings_barcode_scanner_setup_title">إعداد الماسح الضوئي</string>
45+
<string name="woopos_settings_barcode_scanner_detail_title">إعدادات الماسح الضوئي للرمز الشريطي</string>
46+
<string name="woopos_settings_hardware_card_readers_subtitle">إدارة عمليات ربط قارئ البطاقات</string>
47+
<string name="woopos_settings_hardware_card_readers">أدوات قراءة البطاقات</string>
48+
<string name="woopos_settings_hardware_barcode_scanners_subtitle">تكوين إعدادات الماسح الضوئي للرمز الشريطي</string>
49+
<string name="woopos_settings_hardware_barcode_scanners">الماسحات الضوئية للرمز الشريطي</string>
50+
<string name="woopos_settings_store_category_subtitle">تكوين المتجر وإعداداته</string>
51+
<string name="woopos_settings_store_category">المتجر</string>
52+
<string name="woopos_settings_hardware_category_subtitle">إدارة عمليات ربط الأجهزة</string>
53+
<string name="woopos_settings_hardware_category">الأجهزة</string>
54+
<string name="woopos_settings_title">الإعدادات</string>
55+
<string name="error_wordpress_com_connectivity">نواجه مشكلة في الوصول إلى WordPress.com. يرجى التحقق من إعدادات الاتصال بالإنترنت أو محاولة تبديل الشبكات.</string>
56+
<string name="logviewer_current_log_file">الحالي</string>
57+
<string name="logviewer_log_files_list_footer">يتم تخزين سجلات لمدّة تصل إلى سبعة أيام.</string>
58+
<string name="logviewer_log_files_list_header">تسجيل الملفات حسب تاريخ إنشائها</string>
59+
<string name="order_refunds_credit_card_refund">بطاقة ائتمان</string>
60+
<string name="orderdetail_shipping_label_refunded">أرسلتَ طلب استرداد الأموال بنجاح. يمكنك شراء ملصق جديد.</string>
61+
<string name="orderdetail_shipping_label_shipment_tracking_number">رقم التتبع</string>
62+
<string name="orderdetail_shipping_label_shipment_items_multiple">⁦%1$d⁩ من العناصر</string>
63+
<string name="orderdetail_shipping_label_shipment_items_one">⁦%1$d⁩ من العناصر</string>
64+
<string name="orderdetail_shipping_label_shipment_header">الشحنة ⁦%1$s⁩</string>
65+
<string name="user_role_access_error_user_roles_null">يدعم هذا التطبيق رُتب أعضاء المسؤول ومدير المتجر فقط. تعذر علينا إحضار رُتب الأعضاء لحسابك. يرجى الاتصال بالدعم.</string>
966
<string name="woopos_scanning_setup_software_keyboard_bullet_three">لا يعمل حتى الآن؟ اتصل بدعم عملاء الشركة المصنعة لجهازك.</string>
1067
<string name="woopos_scanning_setup_software_keyboard_bullet_two">انتقل إلى إعدادات الجهاز، وابحث عن \"لوحة المفاتيح\"، ثم فعِّل إعداد \"إظهار لوحة المفاتيح على الشاشة عند اتصال لوحة المفاتيح الفعلية\". قد يكون هذا الإعداد ضمن إعدادات تطبيق لوحة المفاتيح.</string>
1168
<string name="woopos_scanning_setup_software_keyboard_bullet_one">ابحث عن أيقونة لوحة المفاتيح أو شريط الأدوات على الشاشة.</string>

0 commit comments

Comments
 (0)