Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.woocommerce.android.ui.woopos.util.datastore

import com.woocommerce.android.ui.woopos.common.util.WooPosLogWrapper
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.TimeZone
import javax.inject.Inject

class WooPosSyncTimestampManager @Inject constructor(
private val timestampRepository: WooPosSyncTimestampRepository,
private val logger: WooPosLogWrapper
) {
private val gmtDateFormat = SimpleDateFormat(GMT_DATE_FORMAT, Locale.US).apply {
timeZone = TimeZone.getTimeZone("GMT")
isLenient = false
}

suspend fun storeProductsLastSyncTimestamp(timestamp: Date) {
val timestampGmt: String = gmtDateFormat.format(timestamp)
timestampRepository.storeProductsLastSyncTimestamp(timestampGmt)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 I think storing formatted dates has some drawbacks, the main one is that we can't safely change the format without clearing the storage. I'd suggest storing just timestamps - milliseconds since 1970.

Considering Samuel is AFK, @kidinov could you double check my train of thoughts and share whether you agree with this suggestion before I make the change? Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Also timestamp just feels cleaner as we don't need to format anything, And btw, gmtDateFormat is not thread safe

Not related to this, but I just realized that even if the date is retrieved from the backend, it’s not something we control. Therefore, there’s no guarantee that the date will be accurate and can change in some cases

}

suspend fun getProductsLastSyncTimestamp(): Date? {
val timestampString = timestampRepository.getProductsLastSyncTimestamp()
return timestampString?.let { parseGmtTimestamp(it) }
}

suspend fun clearProductsLastSyncTimestamp() {
timestampRepository.clearProductsLastSyncTimestamp()
}

suspend fun storeVariationsLastSyncTimestamp(timestamp: Date) {
val timestampGmt: String = gmtDateFormat.format(timestamp)
timestampRepository.storeVariationsLastSyncTimestamp(timestampGmt)
}

suspend fun getVariationsLastSyncTimestamp(): Date? {
val timestampString = timestampRepository.getVariationsLastSyncTimestamp()
return timestampString?.let { parseGmtTimestamp(it) }
}

suspend fun clearVariationsLastSyncTimestamp() {
timestampRepository.clearVariationsLastSyncTimestamp()
}

suspend fun clearAllSyncTimestamps() {
timestampRepository.clearAllSyncTimestamps()
}

fun formatTimestampForApi(timestamp: Date): String {
return gmtDateFormat.format(timestamp)
}

fun parseTimestampFromApi(timestampString: String): Date? {
return parseGmtTimestamp(timestampString)
}

private fun parseGmtTimestamp(timestampString: String): Date? {
return try {
gmtDateFormat.parse(timestampString)
} catch (e: ParseException) {
logger.e("Failed to parse GMT timestamp: '$timestampString'", e)
null
}
}

private companion object {
const val GMT_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.woocommerce.android.ui.woopos.util.datastore

import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.ui.woopos.common.util.WooPosLogWrapper
import kotlinx.coroutines.flow.first
import javax.inject.Inject

class WooPosSyncTimestampRepository @Inject constructor(
private val selectedSite: SelectedSite,
private val dataStore: DataStore<Preferences>,
private val logger: WooPosLogWrapper
) {

suspend fun storeProductsLastSyncTimestamp(timestampGmt: String) {
val key = buildSiteSpecificKey(PRODUCTS_TIMESTAMP_KEY)
if (key != null) {
dataStore.edit { preferences ->
preferences[key] = timestampGmt
}
}
}

suspend fun getProductsLastSyncTimestamp(): String? {
val key = buildSiteSpecificKey(PRODUCTS_TIMESTAMP_KEY)
return if (key != null) {
dataStore.data.first()[key]
} else {
null
}
}

suspend fun clearProductsLastSyncTimestamp() {
val key = buildSiteSpecificKey(PRODUCTS_TIMESTAMP_KEY)
if (key != null) {
dataStore.edit { preferences ->
preferences.remove(key)
}
}
}

suspend fun storeVariationsLastSyncTimestamp(timestampGmt: String) {
val key = buildSiteSpecificKey(VARIATIONS_TIMESTAMP_KEY)
if (key != null) {
dataStore.edit { preferences ->
preferences[key] = timestampGmt
}
}
}

suspend fun getVariationsLastSyncTimestamp(): String? {
val key = buildSiteSpecificKey(VARIATIONS_TIMESTAMP_KEY)
return if (key != null) {
dataStore.data.first()[key]
} else {
null
}
}

suspend fun clearVariationsLastSyncTimestamp() {
val key = buildSiteSpecificKey(VARIATIONS_TIMESTAMP_KEY)
if (key != null) {
dataStore.edit { preferences ->
preferences.remove(key)
}
}
}

suspend fun clearAllSyncTimestamps() {
val productsKey = buildSiteSpecificKey(PRODUCTS_TIMESTAMP_KEY)
val variationsKey = buildSiteSpecificKey(VARIATIONS_TIMESTAMP_KEY)

if (productsKey != null && variationsKey != null) {
dataStore.edit { preferences ->
preferences.remove(productsKey)
preferences.remove(variationsKey)
}
}
}

private fun buildSiteSpecificKey(key: String): Preferences.Key<String>? {
val site = selectedSite.getOrNull()
return if (site != null) {
stringPreferencesKey("${site.siteId}-$key")
} else {
logger.e("Cannot build site-specific key '$key': no site selected")
null
}
}

private companion object {
const val PRODUCTS_TIMESTAMP_KEY = "pos_products_sync_timestamp"
const val VARIATIONS_TIMESTAMP_KEY = "pos_variations_sync_timestamp"
}
}
Loading