Updated on 2026-08-14
This commit is contained in:
parent
8d16f06c08
commit
7d9f1f0cf4
10 changed files with 105 additions and 20 deletions
|
|
@ -120,4 +120,10 @@ internal object SettingsDomainModule {
|
|||
): ShouldShowSwapPromoTokenUseCase {
|
||||
return ShouldShowSwapPromoTokenUseCase(swapPromoRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideDeleteDeprecatedLogsUseCase(settingsRepository: SettingsRepository): DeleteDeprecatedLogsUseCase {
|
||||
return DeleteDeprecatedLogsUseCase(settingsRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,11 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.email.EmailSender
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.core.ui.theme.AppThemeModeHolder
|
||||
import com.tangem.domain.feedback.FeedbackManagerFeatureToggles
|
||||
import com.tangem.domain.feedback.GetSupportFeedbackEmailUseCase
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.tap.common.analytics.events.Settings
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
|
|
@ -24,11 +27,26 @@ internal class DetailsFragment : ComposeFragment(), StoreSubscriber<DetailsState
|
|||
@Inject
|
||||
lateinit var walletsRepository: WalletsRepository
|
||||
|
||||
@Inject
|
||||
lateinit var feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles
|
||||
|
||||
@Inject
|
||||
lateinit var getSupportFeedbackEmailUseCase: GetSupportFeedbackEmailUseCase
|
||||
|
||||
@Inject
|
||||
lateinit var emailSender: EmailSender
|
||||
|
||||
private lateinit var detailsViewModel: DetailsViewModel
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
detailsViewModel = DetailsViewModel(store, walletsRepository)
|
||||
detailsViewModel = DetailsViewModel(
|
||||
store = store,
|
||||
walletsRepository = walletsRepository,
|
||||
feedbackManagerFeatureToggles = feedbackManagerFeatureToggles,
|
||||
getSupportFeedbackEmailUseCase = getSupportFeedbackEmailUseCase,
|
||||
emailSender = emailSender,
|
||||
)
|
||||
Analytics.send(Settings.ScreenOpened())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,14 @@ import com.tangem.common.extensions.guard
|
|||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.email.EmailSender
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.event.triggeredEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.feedback.FeedbackManagerFeatureToggles
|
||||
import com.tangem.domain.feedback.GetSupportFeedbackEmailUseCase
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.tap.common.analytics.events.Settings
|
||||
import com.tangem.tap.common.extensions.addContext
|
||||
|
|
@ -24,6 +27,7 @@ import com.tangem.tap.features.details.redux.DetailsState
|
|||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
||||
import com.tangem.tap.features.home.LocaleRegionProvider
|
||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.wallet.BuildConfig
|
||||
|
|
@ -35,6 +39,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
|
|||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Store
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
@ -42,6 +47,9 @@ import timber.log.Timber
|
|||
internal class DetailsViewModel(
|
||||
private val store: Store<AppState>,
|
||||
private val walletsRepository: WalletsRepository,
|
||||
private val feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles,
|
||||
private val getSupportFeedbackEmailUseCase: GetSupportFeedbackEmailUseCase,
|
||||
private val emailSender: EmailSender,
|
||||
) {
|
||||
|
||||
var detailsScreenState: MutableState<DetailsScreenState> = mutableStateOf(updateState(store.state.detailsState))
|
||||
|
|
@ -138,8 +146,22 @@ internal class DetailsViewModel(
|
|||
|
||||
private fun sendFeedback() {
|
||||
Analytics.send(Settings.ButtonSendFeedback())
|
||||
if (feedbackManagerFeatureToggles.isLocalLogsEnabled) {
|
||||
mainScope.launch {
|
||||
val email = getSupportFeedbackEmailUseCase()
|
||||
emailSender.send(
|
||||
email = EmailSender.Email(
|
||||
address = email.address,
|
||||
subject = email.subject,
|
||||
message = email.message,
|
||||
attachment = email.file,
|
||||
),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
store.dispatchOnMain(GlobalAction.SendEmail(FeedbackEmail()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToAppSettings() {
|
||||
Analytics.send(Settings.ButtonAppSettings())
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.balancehiding.BalanceHidingSettings
|
|||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
|
||||
import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.settings.DeleteDeprecatedLogsUseCase
|
||||
import com.tangem.tap.features.main.model.MainScreenState
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -17,12 +18,14 @@ import kotlinx.coroutines.flow.*
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@HiltViewModel
|
||||
internal class MainViewModel @Inject constructor(
|
||||
private val updateBalanceHidingSettingsUseCase: UpdateBalanceHidingSettingsUseCase,
|
||||
private val listenToFlipsUseCase: ListenToFlipsUseCase,
|
||||
private val reduxNavController: ReduxNavController,
|
||||
private val fetchAppCurrenciesUseCase: FetchAppCurrenciesUseCase,
|
||||
private val deleteDeprecatedLogsUseCase: DeleteDeprecatedLogsUseCase,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
) : ViewModel(), MainIntents {
|
||||
|
|
@ -41,6 +44,10 @@ internal class MainViewModel @Inject constructor(
|
|||
observeFlips()
|
||||
displayBalancesHidingStatusToast()
|
||||
displayHiddenBalancesModalNotification()
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
deleteDeprecatedLogsUseCase()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAppCurrencies() {
|
||||
|
|
|
|||
|
|
@ -59,9 +59,10 @@ internal class DefaultFeedbackRepository(
|
|||
override suspend fun getAppLogs(): List<AppLogModel> {
|
||||
return appPreferencesStore.getObjectMap<String>(key = PreferencesKeys.APP_LOGS_KEY)
|
||||
.map { AppLogModel(timestamp = it.key.toLong(), message = it.value) }
|
||||
.sortedBy(AppLogModel::timestamp)
|
||||
}
|
||||
|
||||
override suspend fun createLogFile(logs: List<String>): File? {
|
||||
override suspend fun createLogFile(logs: String): File? {
|
||||
return try {
|
||||
val file = File(context.filesDir, LOGS_FILE)
|
||||
file.delete()
|
||||
|
|
@ -69,7 +70,7 @@ internal class DefaultFeedbackRepository(
|
|||
|
||||
val stringWriter = StringWriter()
|
||||
|
||||
logs.forEach(stringWriter::append)
|
||||
stringWriter.append(logs)
|
||||
|
||||
val fileWriter = FileWriter(file)
|
||||
fileWriter.write(stringWriter.toString())
|
||||
|
|
|
|||
|
|
@ -44,22 +44,21 @@ internal class DefaultSettingsRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteDeprecatedLogs() {
|
||||
val threeDaysAgo = getThreeDaysAgoTimestamp()
|
||||
|
||||
override suspend fun deleteDeprecatedLogs(maxSize: Int) {
|
||||
appPreferencesStore.editData { preferences ->
|
||||
val savedLogs = preferences.getObjectMap<String>(PreferencesKeys.APP_LOGS_KEY)
|
||||
|
||||
var sum = 0
|
||||
preferences.setObjectMap(
|
||||
key = PreferencesKeys.APP_LOGS_KEY,
|
||||
value = savedLogs.filterKeys { it.toLong() > threeDaysAgo },
|
||||
value = savedLogs.entries
|
||||
.sortedBy(Map.Entry<String, String>::key)
|
||||
.takeLastWhile {
|
||||
sum += it.value.length
|
||||
sum < maxSize
|
||||
}
|
||||
.associate { it.key to it.value },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getThreeDaysAgoTimestamp(): Long = DateTime.now().minusDays(THREE_DAYS).millis
|
||||
|
||||
private companion object {
|
||||
const val THREE_DAYS = 3
|
||||
}
|
||||
}
|
||||
|
|
@ -81,15 +81,31 @@ class GetSupportFeedbackEmailUseCase(
|
|||
}.toString()
|
||||
}
|
||||
|
||||
private suspend fun getLogs(): List<String> {
|
||||
return feedbackRepository.getAppLogs().map {
|
||||
val date = dateFormatter.print(DateTime(it.timestamp))
|
||||
"$date: ${it.message}\n"
|
||||
private suspend fun getLogs(): String {
|
||||
val builder = StringBuilder()
|
||||
|
||||
var sum = 0
|
||||
val appLogs = feedbackRepository.getAppLogs()
|
||||
for (i in appLogs.lastIndex downTo 0) {
|
||||
val log = appLogs[i]
|
||||
val date = dateFormatter.print(DateTime(log.timestamp))
|
||||
|
||||
val formattedLog = "$date: ${log.message}\n"
|
||||
|
||||
sum += formattedLog.length
|
||||
if (sum < GMAIL_MAX_FILE_SIZE) {
|
||||
builder.insert(0, formattedLog)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val START2COIN_SUPPORT_EMAIL = "cardsupport@start2coin.com"
|
||||
const val TANGEM_SUPPORT_EMAIL = "support@tangem.com"
|
||||
const val GMAIL_MAX_FILE_SIZE = 24_900_000 // ≈ 25 MB
|
||||
}
|
||||
}
|
||||
|
|
@ -16,5 +16,5 @@ interface FeedbackRepository {
|
|||
|
||||
suspend fun getAppLogs(): List<AppLogModel>
|
||||
|
||||
suspend fun createLogFile(logs: List<String>): File?
|
||||
suspend fun createLogFile(logs: String): File?
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
|
||||
class DeleteDeprecatedLogsUseCase(
|
||||
private val settingsRepository: SettingsRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke() {
|
||||
settingsRepository.deleteDeprecatedLogs(maxSize = MAX_LOGS_SIZE)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val MAX_LOGS_SIZE = 24_900_000 // ≈ 25 MB
|
||||
}
|
||||
}
|
||||
|
|
@ -12,5 +12,5 @@ interface SettingsRepository {
|
|||
suspend fun updateAppLogs(message: String)
|
||||
|
||||
@Throws
|
||||
suspend fun deleteDeprecatedLogs()
|
||||
suspend fun deleteDeprecatedLogs(maxSize: Int)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue