Skip to content

Commit 97c8adf

Browse files
authored
CMM-886 logs sharing fix and send to he ticket (#22337)
* Adding basic UI * Renaming * Some styling * Renaming and dummy data * Using proper "new conversation icon" * Conversation details screen * Creating the reply bottomsheet * Linking to the support screen * bottomsheet fix * Mov navigation form activity to viewmodel * Adding create ticket screen * More screen adjustments * Extracting common code * Margin fix * detekt * Style * New ticket check * Creating tests * Creating repository and load conversations function * Adding createConversation function * Creating loadConversation func * Loading conversations form the viewmodel * Adding loading spinner * Pull to refresh * Proper ionitialization * Adding empty screen * Handling send new conversation * Show loading when sending * New ticket creation fix * Using snackbar for errors * Error handling * Answering conversation * Adding some test to the repository * More tests! * Compile fixes * Similarities improvements * Using snackbar in bots activity * Extracting EmptyConversationsView * Renaming * Extracting VM and UI common code * Extracting navigation common code * Renaming VMs for clarification * More refactor * Capitalise text fields * Updating rs library * Loading conversation UX * Style fix * Fixing scaffolds paddings * userID fix * Fixing the padding problem in bot chat when the keyboard is opened * Apply padding to create ticket screen when the keyboard is opened * Fixing scroll state in reply bottomsheet * Adding tests for the new common viewmodel * Fixing AIBotSupportViewModel tests * detekt * Improvements int he conversation interaction * Adding tests for HE VM * Saving draft state * Properly navigating when a ticket is selected * Error parsing improvement * accessToken suggestion improvements * General suggestions * Send message error UX improvement * Fixing tests * Converting the UI to more AndroidMaterial style * Bots screen renaming * Bots screens renaming * Make NewTicket screen more Android Material theme as well * Adding preview for EmptyConversationsView * Button fix * detekt * Ticket selection change * Supporting markdown text * detekt * Improving MarkdownUtils * Formatting text in the repository layer instead the ui * Renaming * Fixing tests * Support pagination * Triggering in the 4th element * detekt * TODO for debug purposes * Claude PR suggestions Mutex and constant * Put ConversationListView in common between bots and HE * Empty and error state * Skip site capitalization * Adding a11c labels * Adding headings labels * adding accessible labels to chat bubbles * detekt * Fixing tests * PR suggestion about bot chat bubble * Fixing tests * Updating rust * Adding attachments UI * Parsing markdown more exhaustively * New links support * Detekt * Supporting in conversation as well * Keeping the screen when select images * Add attachments to the message data class * Showing attachments in the UI * Downloading attachments * detekt * Support pagination * Triggering in the 4th element * detekt * TODO for debug purposes * Claude PR suggestions Mutex and constant * Detekt * Removing testing code * Updating RS library version * Opening images in fullscreen * Improving full screen image UX * Improving semantics * Extracting strings * Using rs PR fix * Showing attachment preview * Clearing attachments on new ticket screen close * Removing selected images limit * Unifying attachments handling inside the VM * Using a launcher instead of startActivityForResult * Remove unused parameter * Handling temp files inside the VM * Removing files * detekt * Throwing copy file error * Extracting some individual composables from HEConversation screen file * Reducing arguments * Catch file creation error * Using proper file extension * General improvements * Update RS version and some fixes * Extracting temp attachment utils * Adding new tests * Some refactoring * Removing attachments preview to open a dedicated PR * Useless changes * Useless changes * Minor refactor * Showing attachments previews * Typo * String fix * Fixing pan issue * Passing attachments directly instead of searching for then when tapped for full screen * Compile fix * Uploading logs * Uploading logs in the current way * Linking UI with the new logs upload methods * Detekt * Including logs in new conversations * updating wp-rs * Using LogFiles for logs entries * Uploading all files * Sharing the whole file * Fixing tests * detekt * Test fix * Some small fixes * Renaming and logging * Clearing temp folder oncleared * detekt * typo * Test compile fix * test fix
1 parent 59dcc08 commit 97c8adf

File tree

14 files changed

+594
-398
lines changed

14 files changed

+594
-398
lines changed

WordPress/src/main/java/org/wordpress/android/support/he/repository/HESupportRepository.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ class HESupportRepository @Inject constructor(
9797
subject: String,
9898
message: String,
9999
tags: List<String>,
100-
attachments: List<String>
100+
attachments: List<String>,
101+
encryptedLogUuids: List<String>,
101102
): CreateConversationResult = withContext(ioDispatcher) {
102103
val response = wpComApiClient.request { requestBuilder ->
103104
requestBuilder.supportTickets().createSupportTicket(
@@ -106,6 +107,7 @@ class HESupportRepository @Inject constructor(
106107
message = message,
107108
tags = tags,
108109
attachments = attachments,
110+
encryptedLogIds = encryptedLogUuids,
109111
application = APPLICATION_ID, // Only jetpack is supported
110112
)
111113
)
@@ -138,7 +140,7 @@ class HESupportRepository @Inject constructor(
138140
suspend fun addMessageToConversation(
139141
conversationId: Long,
140142
message: String,
141-
attachments: List<String>
143+
attachments: List<String>,
142144
): CreateConversationResult = withContext(ioDispatcher) {
143145
val response = wpComApiClient.request { requestBuilder ->
144146
requestBuilder.supportTickets().addMessageToSupportConversation(

WordPress/src/main/java/org/wordpress/android/support/he/ui/HENewTicketScreen.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ fun HENewTicketScreen(
7070
subject: String,
7171
messageText: String,
7272
siteAddress: String,
73+
includeAppLogs: Boolean,
7374
) -> Unit,
7475
userInfo: UserInfo,
7576
isSendingNewConversation: Boolean = false,
@@ -110,8 +111,9 @@ private fun HENewTicketScreenContent(
110111
onSubmit: (
111112
category: SupportCategory,
112113
subject: String,
113-
messageText: String,
114+
message: String,
114115
siteAddress: String,
116+
includeAppLogs: Boolean,
115117
) -> Unit,
116118
userInfo: UserInfo,
117119
isSendingNewConversation: Boolean,
@@ -143,7 +145,7 @@ private fun HENewTicketScreenContent(
143145
isLoading = isSendingNewConversation,
144146
onClick = {
145147
selectedCategory?.let { category ->
146-
onSubmit(category, subject, messageText, siteAddress)
148+
onSubmit(category, subject, messageText, siteAddress, includeAppLogs)
147149
}
148150
}
149151
)
@@ -465,7 +467,7 @@ private fun HENewTicketScreenPreviewContent(snackbarHostState: SnackbarHostState
465467
HENewTicketScreenContent(
466468
snackbarHostState = snackbarHostState,
467469
onBackClick = { },
468-
onSubmit = { _, _, _, _ -> },
470+
onSubmit = { _, _, _, _, _ -> },
469471
userInfo = UserInfo("Test user", "test.user@automattic.com", null),
470472
isSendingNewConversation = false,
471473
attachmentState = AttachmentState(),

WordPress/src/main/java/org/wordpress/android/support/he/ui/HESupportActivity.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,10 @@ class HESupportActivity : AppCompatActivity() {
189189
viewModel.clearReplyForm()
190190
viewModel.onBackClick()
191191
},
192-
onSendMessage = { message, _ ->
192+
onSendMessage = { message, includeAppLogs ->
193193
viewModel.onAddMessageToConversation(
194194
message = message,
195+
includeAppLogs = includeAppLogs,
195196
)
196197
},
197198
onClearMessageSendResult = { viewModel.clearMessageSendResult() },
@@ -240,11 +241,12 @@ class HESupportActivity : AppCompatActivity() {
240241
viewModel.clearNewTicketForm()
241242
viewModel.onBackClick()
242243
},
243-
onSubmit = { category, subject, message, _ ->
244+
onSubmit = { category, subject, message, _, includeAppLogs ->
244245
viewModel.onSendNewConversation(
245246
subject = subject,
246247
message = message,
247248
tags = listOf(category.key),
249+
includeAppLogs = includeAppLogs,
248250
)
249251
},
250252
userInfo = userInfo,

WordPress/src/main/java/org/wordpress/android/support/he/ui/HESupportViewModel.kt

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import org.wordpress.android.support.he.repository.CreateConversationResult
2424
import org.wordpress.android.support.he.repository.HESupportRepository
2525
import org.wordpress.android.support.he.util.TempAttachmentsUtil
2626
import org.wordpress.android.util.AppLog
27+
import org.wordpress.android.util.EncryptedLogging
28+
import org.wordpress.android.util.LogFileProviderWrapper
2729
import org.wordpress.android.util.NetworkUtilsWrapper
2830
import java.io.File
2931
import javax.inject.Inject
@@ -34,6 +36,8 @@ class HESupportViewModel @Inject constructor(
3436
private val heSupportRepository: HESupportRepository,
3537
@Named(IO_THREAD) private val ioDispatcher: CoroutineDispatcher,
3638
private val tempAttachmentsUtil: TempAttachmentsUtil,
39+
private val encryptedLogging: EncryptedLogging,
40+
private val logFileProvider: LogFileProviderWrapper,
3741
private val application: Application,
3842
accountStore: AccountStore,
3943
appLogWrapper: AppLogWrapper,
@@ -76,6 +80,7 @@ class HESupportViewModel @Inject constructor(
7680
subject: String,
7781
message: String,
7882
tags: List<String>,
83+
includeAppLogs : Boolean,
7984
) {
8085
viewModelScope.launch(ioDispatcher) {
8186
try {
@@ -86,14 +91,21 @@ class HESupportViewModel @Inject constructor(
8691

8792
_isSendingMessage.value = true
8893

94+
val logsIds: List<String> = if (includeAppLogs) {
95+
uploadLogs()
96+
} else {
97+
emptyList()
98+
}
99+
89100
val attachmentUris = _newTicketFormState.value.attachmentState.acceptedUris
90-
val files = tempAttachmentsUtil.createTempFilesFrom(attachmentUris)
101+
val attachments = tempAttachmentsUtil.createTempFilesFrom(attachmentUris)
91102

92103
when (val result = heSupportRepository.createConversation(
93104
subject = subject,
94105
message = message,
95106
tags = tags,
96-
attachments = files.map { it.path }
107+
attachments = attachments.map { it.path },
108+
encryptedLogUuids = logsIds
97109
)) {
98110
is CreateConversationResult.Success -> {
99111
val newConversation = result.conversation
@@ -115,7 +127,7 @@ class HESupportViewModel @Inject constructor(
115127
}
116128
}
117129

118-
tempAttachmentsUtil.removeTempFiles(files)
130+
tempAttachmentsUtil.removeTempFiles(attachments)
119131
_isSendingMessage.value = false
120132
} catch (e: Exception) {
121133
_errorMessage.value = ErrorType.GENERAL
@@ -127,11 +139,32 @@ class HESupportViewModel @Inject constructor(
127139
}
128140
}
129141

142+
@Suppress("NestedBlockDepth", "TooGenericExceptionCaught")
143+
private fun uploadLogs(): List<String> {
144+
try {
145+
val encryptedLogsUuid = mutableListOf<String>()
146+
logFileProvider.getLogFiles().forEach { logFile ->
147+
if (logFile.exists()) {
148+
encryptedLogging.encryptAndUploadLogFile(
149+
logFile = logFile,
150+
shouldStartUploadImmediately = true
151+
)?.let { uuid ->
152+
encryptedLogsUuid.add(uuid)
153+
}
154+
}
155+
}
156+
return encryptedLogsUuid
157+
} catch (throwable: Throwable) {
158+
appLogWrapper.e(AppLog.T.SUPPORT, "Error uploading logs: ${throwable.stackTraceToString()}")
159+
throw throwable
160+
}
161+
}
162+
130163
override suspend fun getConversation(conversationId: Long): SupportConversation? =
131164
heSupportRepository.loadConversation(conversationId)
132165

133166
@Suppress("TooGenericExceptionCaught")
134-
fun onAddMessageToConversation(message: String) {
167+
fun onAddMessageToConversation(message: String, includeAppLogs: Boolean) {
135168
viewModelScope.launch(ioDispatcher) {
136169
try {
137170
if (!networkUtilsWrapper.isNetworkAvailable()) {
@@ -147,13 +180,18 @@ class HESupportViewModel @Inject constructor(
147180
}
148181

149182
_isSendingMessage.value = true
183+
184+
if (includeAppLogs) {
185+
// We will add the logs when the RS layer is ready for this
186+
}
187+
150188
val attachmentUris = _replyFormState.value.attachmentState.acceptedUris
151-
val files = tempAttachmentsUtil.createTempFilesFrom(attachmentUris)
189+
val attachments = tempAttachmentsUtil.createTempFilesFrom(attachmentUris)
152190

153191
when (val result = heSupportRepository.addMessageToConversation(
154192
conversationId = selectedConversation.id,
155193
message = message,
156-
attachments = files.map { it.path }
194+
attachments = attachments.map { it.path }
157195
)) {
158196
is CreateConversationResult.Success -> {
159197
_selectedConversation.value = result.conversation
@@ -175,7 +213,7 @@ class HESupportViewModel @Inject constructor(
175213
}
176214
}
177215

178-
tempAttachmentsUtil.removeTempFiles(files)
216+
tempAttachmentsUtil.removeTempFiles(attachments)
179217
_isSendingMessage.value = false
180218
} catch (e: Exception) {
181219
_errorMessage.value = ErrorType.GENERAL

WordPress/src/main/java/org/wordpress/android/support/logs/model/LogDay.kt

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.wordpress.android.support.logs.model
2+
3+
import java.io.File
4+
5+
data class LogFile(
6+
val file: File,
7+
val fileName: String,
8+
val title: String,
9+
val subtitle: String,
10+
// Log lines could be truncated to avoid memory issues
11+
val logLines: List<String>? = null
12+
)

WordPress/src/main/java/org/wordpress/android/support/logs/ui/LogDetailScreen.kt

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,23 @@ import androidx.compose.ui.text.font.FontFamily
2525
import androidx.compose.ui.tooling.preview.Preview
2626
import androidx.compose.ui.unit.dp
2727
import androidx.compose.ui.unit.sp
28-
import androidx.core.text.HtmlCompat
2928
import org.wordpress.android.R
30-
import org.wordpress.android.support.logs.model.LogDay
29+
import org.wordpress.android.support.logs.model.LogFile
3130
import org.wordpress.android.ui.compose.components.MainTopAppBar
3231
import org.wordpress.android.ui.compose.components.NavigationIcons
3332
import org.wordpress.android.ui.compose.theme.AppThemeM3
3433

3534
@OptIn(ExperimentalMaterial3Api::class)
3635
@Composable
3736
fun LogDetailScreen(
38-
logDay: LogDay,
37+
logFile: LogFile,
3938
onBackClick: () -> Unit,
4039
onShareClick: () -> Unit
4140
) {
4241
Scaffold(
4342
topBar = {
4443
MainTopAppBar(
45-
title = logDay.displayDate,
44+
title = logFile.title,
4645
navigationIcon = NavigationIcons.BackIcon,
4746
onNavigationIconClick = onBackClick
4847
)
@@ -77,11 +76,12 @@ fun LogDetailScreen(
7776
Spacer(modifier = Modifier.height(16.dp))
7877
}
7978

79+
val logLines = logFile.logLines ?: emptyList()
8080
itemsIndexed(
81-
items = logDay.logEntries,
82-
key = { index, _ -> "${logDay.date}_$index" }
83-
) { _, logEntry ->
84-
LogEntryItem(logEntry = logEntry)
81+
items = logLines,
82+
key = { index, _ -> "${logFile.fileName}_$index" }
83+
) { _, logLine ->
84+
LogLineItem(logLine = logLine)
8585
}
8686

8787
item {
@@ -93,19 +93,13 @@ fun LogDetailScreen(
9393
}
9494

9595
@Composable
96-
private fun LogEntryItem(logEntry: String) {
96+
private fun LogLineItem(logLine: String) {
9797
Column(
9898
modifier = Modifier
9999
.fillMaxWidth()
100100
) {
101-
// Strip HTML tags for display in Compose
102-
val plainText = HtmlCompat.fromHtml(
103-
logEntry,
104-
HtmlCompat.FROM_HTML_MODE_LEGACY
105-
).toString()
106-
107101
Text(
108-
text = plainText,
102+
text = logLine,
109103
style = MaterialTheme.typography.bodySmall.copy(
110104
fontFamily = FontFamily.Monospace,
111105
fontSize = 12.sp,
@@ -119,14 +113,16 @@ private fun LogEntryItem(logEntry: String) {
119113
@Preview(showBackground = true, name = "Log Detail Screen - Light")
120114
@Composable
121115
private fun LogDetailScreenPreview() {
122-
val exampleList = getExampleLogList()
116+
val exampleLogLines = getExampleLogLines()
117+
val mockFile = java.io.File("")
123118
AppThemeM3(isDarkTheme = false) {
124119
LogDetailScreen(
125-
logDay = LogDay(
126-
date = "Oct-16",
127-
displayDate = "October 16",
128-
logEntries = exampleList,
129-
logCount = exampleList.size
120+
logFile = LogFile(
121+
file = mockFile,
122+
fileName = "2025-11-21T10:42:06+0100.log",
123+
title = "November 21, 2025",
124+
subtitle = "10:42 AM",
125+
logLines = exampleLogLines
130126
),
131127
onBackClick = {},
132128
onShareClick = {}
@@ -137,22 +133,24 @@ private fun LogDetailScreenPreview() {
137133
@Preview(showBackground = true, name = "Log Detail Screen - Dark", uiMode = Configuration.UI_MODE_NIGHT_YES)
138134
@Composable
139135
private fun LogDetailScreenPreviewDark() {
140-
val exampleList = getExampleLogList()
136+
val exampleLogLines = getExampleLogLines()
137+
val mockFile = java.io.File("")
141138
AppThemeM3(isDarkTheme = true) {
142139
LogDetailScreen(
143-
logDay = LogDay(
144-
date = "Oct-16",
145-
displayDate = "October 16",
146-
logEntries = exampleList,
147-
logCount = exampleList.size
140+
logFile = LogFile(
141+
file = mockFile,
142+
fileName = "2025-11-21T10:42:06+0100.log",
143+
title = "November 21, 2025",
144+
subtitle = "10:42 AM",
145+
logLines = exampleLogLines
148146
),
149147
onBackClick = {},
150148
onShareClick = {}
151149
)
152150
}
153151
}
154152

155-
private fun getExampleLogList(): List<String> = listOf(
153+
private fun getExampleLogLines(): List<String> = listOf(
156154
"[Oct-16 12:34:56.789] D/MainActivity: Activity created",
157155
"[Oct-16 12:34:57.123] I/NetworkManager: Connection established",
158156
"[Oct-16 12:34:58.456] W/ImageLoader: Cache miss for image_123",

0 commit comments

Comments
 (0)