Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
-----
- [*] Woo POS: Remote Tap to Pay failures now explain what went wrong - phone not eligible for Tap to Pay, NFC turned off, or a payment service error - instead of a generic message or raw error text [https://github.com/woocommerce/woocommerce-android/pull/16384]
- [*] QR login now asks you to choose a store when your WordPress.com account has multiple sites instead of preselecting one [https://github.com/woocommerce/woocommerce-android/pull/16400]
- [*] Analytics date ranges now follow the date order of the language the app is displayed in [https://github.com/woocommerce/woocommerce-android/pull/16416]
- [Internal] Close analytics gaps in the Remote Tap to Pay flow: transport on the reader-ready event, cause chains in error descriptions, no stale card reader model, and a real session ended reason [https://github.com/woocommerce/woocommerce-android/pull/16368]
- [*] Tap to Pay no longer disappears from the Payments screen shortly after it appears [https://github.com/woocommerce/woocommerce-android/pull/16370]
- [*] Fixed a crash when pressing Back on the "You're already signed in" warning shown after opening a QR login link while signed in [https://github.com/woocommerce/woocommerce-android/pull/16347]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.woocommerce.android.extensions

import android.content.Context
import android.icu.text.DateIntervalFormat
import android.icu.util.DateInterval
import android.text.format.DateFormat
import java.text.DateFormatSymbols
import java.text.SimpleDateFormat
Expand Down Expand Up @@ -161,39 +163,13 @@ fun Date.oneYearAgo(): Date =
add(Calendar.YEAR, -1)
}.time

fun Date.isInSameYearAs(other: Date, baseCalendar: Calendar): Boolean {
val calendar = baseCalendar.clone() as Calendar
calendar.time = this
val thisYear = calendar.get(Calendar.YEAR)
calendar.time = other
val otherYear = calendar.get(Calendar.YEAR)
return thisYear == otherYear
}

fun Date.isInSameMonthAs(other: Date, baseCalendar: Calendar): Boolean {
val calendar = baseCalendar.clone() as Calendar
calendar.time = this
val thisMonth = calendar.get(Calendar.MONTH)
calendar.time = other
val otherMonth = calendar.get(Calendar.MONTH)
return thisMonth == otherMonth && isInSameYearAs(other, calendar)
}

fun Date.formatAsRangeWith(other: Date, locale: Locale, calendar: Calendar): String {
val formattedStartDate = if (this.isInSameYearAs(other, calendar)) {
SimpleDateFormat("MMM d", locale).format(this)
} else {
SimpleDateFormat("MMM d, yyyy", locale).format(this)
}

val formattedEndDate = if (this.isInSameMonthAs(other, calendar)) {
SimpleDateFormat("d, yyyy", locale).format(other)
} else {
SimpleDateFormat("MMM d, yyyy", locale).format(other)
}

return "$formattedStartDate – $formattedEndDate"
}
/**
* Formats the two dates as a range in the locale's own interval format, e.g. "Jul 1 – 31, 2022" in English
* and "1.–31. Juli 2022" in German.
*/
fun Date.formatAsRangeWith(other: Date, locale: Locale): String = DateIntervalFormat
.getInstance("yMMMd", locale)
.format(DateInterval(this.time, other.time))

private const val THREE_MONTHS = 3
private const val SEVEN_DAYS = 7
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import com.woocommerce.android.aiassistant.ui.cards.AiAssistantStatsCard
import com.woocommerce.android.aiassistant.ui.cards.AiAssistantStatsCardState
import com.woocommerce.android.aiassistant.ui.cards.AssistantCard
import com.woocommerce.android.aiassistant.ui.cards.AssistantCardAction
import com.woocommerce.android.extensions.formatAsRangeWith
import com.woocommerce.android.extensions.formatToLocalizedMonthDayYear
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Date
import java.util.Locale

internal class AiAssistantStatsCardRenderer(
Expand Down Expand Up @@ -53,21 +57,16 @@ private fun formatStatsPeriod(
.filter { it.isNotBlank() }
.joinToString(ASSISTANT_STATS_PERIOD_SEPARATOR)
.ifBlank { unavailableValue }
start == end -> start.format(DateTimeFormatter.ofPattern("MMM d, yyyy", locale))
start.year == end.year -> listOf(
start.format(DateTimeFormatter.ofPattern("MMM d", locale)),
end.format(DateTimeFormatter.ofPattern("MMM d, yyyy", locale)),
).joinToString(ASSISTANT_STATS_PERIOD_SEPARATOR)
else -> listOf(
start.format(DateTimeFormatter.ofPattern("MMM d, yyyy", locale)),
end.format(DateTimeFormatter.ofPattern("MMM d, yyyy", locale)),
).joinToString(ASSISTANT_STATS_PERIOD_SEPARATOR)
start == end -> start.toDate().formatToLocalizedMonthDayYear(locale)
else -> start.toDate().formatAsRangeWith(end.toDate(), locale)
}
}

private fun String.toStatsLocalDate(): LocalDate? =
runCatching { LocalDate.parse(this, DateTimeFormatter.ISO_LOCAL_DATE) }.getOrNull()

private fun LocalDate.toDate(): Date = Date.from(atStartOfDay(ZoneId.systemDefault()).toInstant())

private fun formatStatsMoney(
value: String,
currency: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class CustomRangeData(
start = currentStart,
end = currentEnd
)
formattedCurrentRange = currentStart.formatAsRangeWith(currentEnd, locale, calendar)
formattedCurrentRange = currentStart.formatAsRangeWith(currentEnd, locale)

val dayDifference = selectedEndDate.time - selectedStartDate.time

Expand All @@ -53,6 +53,6 @@ class CustomRangeData(
start = previousStart,
end = previousEnd
)
formattedPreviousRange = previousStart.formatAsRangeWith(previousEnd, locale, calendar)
formattedPreviousRange = previousStart.formatAsRangeWith(previousEnd, locale)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class LastMonthRangeData(
start = currentStart,
end = currentEnd
)
formattedCurrentRange = currentStart.formatAsRangeWith(currentEnd, locale, calendar)
formattedCurrentRange = currentStart.formatAsRangeWith(currentEnd, locale)

val twoMonthsAgo = oneMonthAgo.oneMonthAgo()
calendar.time = twoMonthsAgo
Expand All @@ -48,6 +48,6 @@ class LastMonthRangeData(
start = previousStart,
end = previousEnd
)
formattedPreviousRange = previousStart.formatAsRangeWith(previousEnd, locale, calendar)
formattedPreviousRange = previousStart.formatAsRangeWith(previousEnd, locale)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class LastQuarterRangeData(
start = currentStart,
end = currentEnd
)
formattedCurrentRange = currentStart.formatAsRangeWith(currentEnd, locale, calendar)
formattedCurrentRange = currentStart.formatAsRangeWith(currentEnd, locale)

val twoQuartersAgo = oneQuarterAgo.oneQuarterAgo()
calendar.time = twoQuartersAgo
Expand All @@ -48,6 +48,6 @@ class LastQuarterRangeData(
start = previousStart,
end = previousEnd
)
formattedPreviousRange = previousStart.formatAsRangeWith(previousEnd, locale, calendar)
formattedPreviousRange = previousStart.formatAsRangeWith(previousEnd, locale)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class LastWeekRangeData(
start = currentStart,
end = currentEnd
)
formattedCurrentRange = currentStart.formatAsRangeWith(currentEnd, locale, calendar)
formattedCurrentRange = currentStart.formatAsRangeWith(currentEnd, locale)

val twoWeeksAgo = oneWeekAgo.oneWeekAgo()
calendar.time = twoWeeksAgo
Expand All @@ -48,6 +48,6 @@ class LastWeekRangeData(
start = previousStart,
end = previousEnd
)
formattedPreviousRange = previousStart.formatAsRangeWith(previousEnd, locale, calendar)
formattedPreviousRange = previousStart.formatAsRangeWith(previousEnd, locale)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class LastYearRangeData(
start = currentStart,
end = currentEnd
)
formattedCurrentRange = currentStart.formatAsRangeWith(currentEnd, locale, calendar)
formattedCurrentRange = currentStart.formatAsRangeWith(currentEnd, locale)

val twoYearsAgo = oneYearAgo.oneYearAgo()
calendar.time = twoYearsAgo
Expand All @@ -48,6 +48,6 @@ class LastYearRangeData(
start = previousStart,
end = previousEnd
)
formattedPreviousRange = previousStart.formatAsRangeWith(previousEnd, locale, calendar)
formattedPreviousRange = previousStart.formatAsRangeWith(previousEnd, locale)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class MonthToDateRangeData(
start = currentStart,
end = currentEnd
)
formattedCurrentRange = currentStart.formatAsRangeWith(referenceDate, locale, calendar)
formattedCurrentRange = currentStart.formatAsRangeWith(referenceDate, locale)

val oneMonthAgo = referenceDate.oneMonthAgo()
calendar.time = oneMonthAgo
Expand All @@ -46,6 +46,6 @@ class MonthToDateRangeData(
start = previousStart,
end = oneMonthAgo
)
formattedPreviousRange = previousStart.formatAsRangeWith(oneMonthAgo, locale, calendar)
formattedPreviousRange = previousStart.formatAsRangeWith(oneMonthAgo, locale)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class QuarterToDateRangeData(
start = currentStart,
end = currentEnd
)
formattedCurrentRange = currentStart.formatAsRangeWith(referenceDate, locale, calendar)
formattedCurrentRange = currentStart.formatAsRangeWith(referenceDate, locale)

val oneQuarterAgo = referenceDate.oneQuarterAgo()
calendar.time = oneQuarterAgo
Expand All @@ -46,6 +46,6 @@ class QuarterToDateRangeData(
start = previousStart,
end = oneQuarterAgo
)
formattedPreviousRange = previousStart.formatAsRangeWith(oneQuarterAgo, locale, calendar)
formattedPreviousRange = previousStart.formatAsRangeWith(oneQuarterAgo, locale)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class WeekToDateRangeData(
start = currentStart,
end = currentEnd
)
formattedCurrentRange = currentStart.formatAsRangeWith(referenceDate, locale, calendar)
formattedCurrentRange = currentStart.formatAsRangeWith(referenceDate, locale)

val oneWeekAgo = referenceDate.oneWeekAgo()
calendar.time = oneWeekAgo
Expand All @@ -46,6 +46,6 @@ class WeekToDateRangeData(
start = startOfPreviousWeek,
end = oneWeekAgo
)
formattedPreviousRange = startOfPreviousWeek.formatAsRangeWith(oneWeekAgo, locale, calendar)
formattedPreviousRange = startOfPreviousWeek.formatAsRangeWith(oneWeekAgo, locale)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class YearToDateRangeData(
start = currentStart,
end = currentEnd
)
formattedCurrentRange = currentStart.formatAsRangeWith(referenceDate, locale, calendar)
formattedCurrentRange = currentStart.formatAsRangeWith(referenceDate, locale)

val oneYearAgo = referenceDate.oneYearAgo()
calendar.time = oneYearAgo
Expand All @@ -46,6 +46,6 @@ class YearToDateRangeData(
start = previousStart,
end = oneYearAgo
)
formattedPreviousRange = previousStart.formatAsRangeWith(oneYearAgo, locale, calendar)
formattedPreviousRange = previousStart.formatAsRangeWith(oneYearAgo, locale)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection
import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType.CUSTOM
import com.woocommerce.android.ui.dashboard.data.StatsRepository
import com.woocommerce.android.util.LocalizedDatePatternsTestRule
import com.woocommerce.android.viewmodel.BaseUnitTest
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Rule
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.mock
Expand Down Expand Up @@ -37,6 +39,8 @@ import kotlin.test.Test

@OptIn(ExperimentalCoroutinesApi::class)
class BackgroundUpdateAnalyticsRepositoryTest : BaseUnitTest() {
@get:Rule
val localizedDatePatterns = LocalizedDatePatternsTestRule()

private val statsRepository: StatsRepository = mock()
private val googleAdsStore: WCGoogleStore = mock()
Expand All @@ -46,12 +50,14 @@ class BackgroundUpdateAnalyticsRepositoryTest : BaseUnitTest() {
private val testTimeZone = TimeZone.getDefault()
private val testCalendar = Calendar.getInstance(testLocale)

private val testSelectionData: StatsTimeRangeSelection = CUSTOM.generateSelectionData(
referenceStartDate = "2024-10-02".dayStartFrom(),
referenceEndDate = "2024-10-02".dayEndFrom(),
calendar = testCalendar,
locale = testLocale
)
private val testSelectionData: StatsTimeRangeSelection by lazy {
CUSTOM.generateSelectionData(
referenceStartDate = "2024-10-02".dayStartFrom(),
referenceEndDate = "2024-10-02".dayEndFrom(),
calendar = testCalendar,
locale = testLocale
)
}

private val backgroundUpdateAnalyticsRepository = BackgroundUpdateAnalyticsRepository(
statsRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package com.woocommerce.android.ui.aiassistant
import com.woocommerce.android.aiassistant.ui.cards.AiAssistantStatsCardState
import com.woocommerce.android.aiassistant.ui.cards.AssistantCard
import com.woocommerce.android.aiassistant.ui.cards.AssistantCardAction
import com.woocommerce.android.util.LocalizedDatePatternsTestRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
Expand All @@ -12,6 +14,9 @@ import org.mockito.kotlin.whenever
import java.util.Locale

class AiAssistantStatsCardRendererTest {
@get:Rule
val localizedDatePatterns = LocalizedDatePatternsTestRule()

private val currencyFormatter: AiAssistantCurrencyFormatter = mock()

@Test
Expand All @@ -30,7 +35,7 @@ class AiAssistantStatsCardRendererTest {
locale = Locale.US,
)

assertThat(model.period).isEqualTo("May 1 - May 7, 2026")
assertThat(model.period).isEqualTo("May 1, 2026 – May 7, 2026")
assertThat(model.metrics).containsExactly(
AiAssistantStatsCardState.Metric(
type = AssistantCard.Stats.MetricType.TotalSales,
Expand Down Expand Up @@ -68,7 +73,7 @@ class AiAssistantStatsCardRendererTest {

assertThat(model).isEqualTo(
AiAssistantStatsCardState(
period = "May 1 - May 7, 2026",
period = "May 1, 2026 – May 7, 2026",
metrics = listOf(
AiAssistantStatsCardState.Metric(
type = AssistantCard.Stats.MetricType.TotalSales,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import com.woocommerce.android.model.CustomerWithAnalytics
import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection
import com.woocommerce.android.ui.moremenu.customer.GetCustomerWithStats
import com.woocommerce.android.ui.products.details.ProductDetailFragment
import com.woocommerce.android.util.LocalizedDatePatternsTestRule
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
Expand All @@ -19,6 +21,9 @@ import java.util.Locale
import java.util.TimeZone

class WooAssistantCardActionNavigatorTest {
@get:Rule
val localizedDatePatterns = LocalizedDatePatternsTestRule()

private val getCustomerWithStats: GetCustomerWithStats = mock()
private val navigator = WooAssistantCardActionNavigator(getCustomerWithStats)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ class AnalyticsUpdateDataStoreTest : BaseUnitTest() {

private lateinit var sut: AnalyticsUpdateDataStore

private val defaultSelectionData = LAST_MONTH.generateSelectionData(
calendar = Calendar.getInstance(),
locale = Locale.getDefault(),
referenceStartDate = Date(),
referenceEndDate = Date()
)
private val defaultSelectionData by lazy {
LAST_MONTH.generateSelectionData(
calendar = Calendar.getInstance(),
locale = Locale.getDefault(),
referenceStartDate = Date(),
referenceEndDate = Date()
)
}

@Test
fun `given shouldUpdateAnalytics is called, when time elapsed is enough, then return true`() = testBlocking {
Expand Down
Loading
Loading