From 7d9f1f0cf4baed73dd8d6fee52a21cacb8c6cef3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 18 Mar 2024 22:00:59 +0800 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/SettingsDomainModule.kt | 6 +++++ .../details/ui/details/DetailsFragment.kt | 20 +++++++++++++++- .../details/ui/details/DetailsViewModel.kt | 24 ++++++++++++++++++- .../tangem/tap/features/main/MainViewModel.kt | 7 ++++++ .../feedback/DefaultFeedbackRepository.kt | 5 ++-- .../settings/DefaultSettingsRepository.kt | 19 +++++++-------- .../GetSupportFeedbackEmailUseCase.kt | 24 +++++++++++++++---- .../feedback/repository/FeedbackRepository.kt | 2 +- .../settings/DeleteDeprecatedLogsUseCase.kt | 16 +++++++++++++ .../repositories/SettingsRepository.kt | 2 +- 10 files changed, 105 insertions(+), 20 deletions(-) create mode 100644 domain/settings/src/main/java/com/tangem/domain/settings/DeleteDeprecatedLogsUseCase.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt index 7334227d99..2d8764fc7c 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt @@ -120,4 +120,10 @@ internal object SettingsDomainModule { ): ShouldShowSwapPromoTokenUseCase { return ShouldShowSwapPromoTokenUseCase(swapPromoRepository) } + + @Provides + @ViewModelScoped + fun provideDeleteDeprecatedLogsUseCase(settingsRepository: SettingsRepository): DeleteDeprecatedLogsUseCase { + return DeleteDeprecatedLogsUseCase(settingsRepository) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt index 57376f6c70..d050b873e5 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt @@ -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, private val walletsRepository: WalletsRepository, + private val feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles, + private val getSupportFeedbackEmailUseCase: GetSupportFeedbackEmailUseCase, + private val emailSender: EmailSender, ) { var detailsScreenState: MutableState = mutableStateOf(updateState(store.state.detailsState)) @@ -138,7 +146,21 @@ internal class DetailsViewModel( private fun sendFeedback() { Analytics.send(Settings.ButtonSendFeedback()) - store.dispatchOnMain(GlobalAction.SendEmail(FeedbackEmail())) + 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() { diff --git a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt index dc5b58c12a..6f5d289dd9 100644 --- a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt @@ -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() { diff --git a/data/feedback/src/main/java/com/tangem/data/feedback/DefaultFeedbackRepository.kt b/data/feedback/src/main/java/com/tangem/data/feedback/DefaultFeedbackRepository.kt index 580c5fb4ab..6148da1827 100644 --- a/data/feedback/src/main/java/com/tangem/data/feedback/DefaultFeedbackRepository.kt +++ b/data/feedback/src/main/java/com/tangem/data/feedback/DefaultFeedbackRepository.kt @@ -59,9 +59,10 @@ internal class DefaultFeedbackRepository( override suspend fun getAppLogs(): List { return appPreferencesStore.getObjectMap(key = PreferencesKeys.APP_LOGS_KEY) .map { AppLogModel(timestamp = it.key.toLong(), message = it.value) } + .sortedBy(AppLogModel::timestamp) } - override suspend fun createLogFile(logs: List): 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()) diff --git a/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt b/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt index 308e823b90..277d4fc31f 100644 --- a/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt +++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt @@ -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(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::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 - } } \ No newline at end of file diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/GetSupportFeedbackEmailUseCase.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/GetSupportFeedbackEmailUseCase.kt index 150c5c0b2c..25db3a0ef6 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/GetSupportFeedbackEmailUseCase.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/GetSupportFeedbackEmailUseCase.kt @@ -81,15 +81,31 @@ class GetSupportFeedbackEmailUseCase( }.toString() } - private suspend fun getLogs(): List { - 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 } } \ No newline at end of file diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/repository/FeedbackRepository.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/repository/FeedbackRepository.kt index 246afde319..5e137df28a 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/repository/FeedbackRepository.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/repository/FeedbackRepository.kt @@ -16,5 +16,5 @@ interface FeedbackRepository { suspend fun getAppLogs(): List - suspend fun createLogFile(logs: List): File? + suspend fun createLogFile(logs: String): File? } \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/DeleteDeprecatedLogsUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/DeleteDeprecatedLogsUseCase.kt new file mode 100644 index 0000000000..627d3f1124 --- /dev/null +++ b/domain/settings/src/main/java/com/tangem/domain/settings/DeleteDeprecatedLogsUseCase.kt @@ -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 + } +} \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt index 48e6bb5ca2..061ee78557 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt @@ -12,5 +12,5 @@ interface SettingsRepository { suspend fun updateAppLogs(message: String) @Throws - suspend fun deleteDeprecatedLogs() + suspend fun deleteDeprecatedLogs(maxSize: Int) } \ No newline at end of file