diff --git a/WordPress/src/main/java/org/wordpress/android/support/common/ui/ConversationsSupportViewModel.kt b/WordPress/src/main/java/org/wordpress/android/support/common/ui/ConversationsSupportViewModel.kt index 981bebf04ff0..c7085bddab62 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/common/ui/ConversationsSupportViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/common/ui/ConversationsSupportViewModel.kt @@ -132,6 +132,53 @@ abstract class ConversationsSupportViewModel( } } + // Guards against overlapping silent refreshes (e.g. the minute timer and onStart firing close + // together) whose out-of-order responses could show an older list. Confined to the main thread + // (viewModelScope runs on Main), so no synchronization is needed. + private var isSilentRefreshInFlight = false + + /** + * Reloads the conversation list from the server without showing the pull-to-refresh spinner or a + * blocking loader. Used by the list screen's periodic auto-refresh and its refresh-on-resume so + * new or updated conversations appear while the screen stays open. Leaves the current list and + * error state untouched on failure (a background refresh should not surface an error). + */ + @Suppress("TooGenericExceptionCaught") + fun refreshConversationsSilently() { + // Skip while a visible load or pull-to-refresh owns the state (so we neither cut its spinner + // short nor overwrite its newer result), and coalesce overlapping silent refreshes. + val state = _conversationsState.value + if (isSilentRefreshInFlight || + state == ConversationsState.Loading || + state == ConversationsState.Refreshing + ) { + return + } + isSilentRefreshInFlight = true + viewModelScope.launch { + try { + if (!networkUtilsWrapper.isNetworkAvailable()) return@launch + val conversations = getConversations() + // A visible load/refresh may have started while we were fetching; don't stomp it. + val currentState = _conversationsState.value + if (conversations != null && + currentState != ConversationsState.Loading && + currentState != ConversationsState.Refreshing + ) { + _conversations.value = conversations + _conversationsState.value = ConversationsState.Loaded + } + } catch (throwable: Throwable) { + appLogWrapper.e( + AppLog.T.SUPPORT, "Error silently refreshing support conversations: " + + "${throwable.message} - ${throwable.stackTraceToString()}" + ) + } finally { + isSilentRefreshInFlight = false + } + } + } + fun clearError() { _errorMessage.value = null } diff --git a/WordPress/src/main/java/org/wordpress/android/support/unified/model/ConversationStatus.kt b/WordPress/src/main/java/org/wordpress/android/support/unified/model/ConversationStatus.kt index 61a22ac98727..9d640be11c7c 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/unified/model/ConversationStatus.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/unified/model/ConversationStatus.kt @@ -1,8 +1,7 @@ package org.wordpress.android.support.unified.model enum class ConversationStatus { - WAITING_FOR_SUPPORT, - WAITING_FOR_USER, + ONGOING, CLOSED, SOLVED, UNKNOWN; @@ -10,9 +9,8 @@ enum class ConversationStatus { companion object { fun fromStatus(status: String): ConversationStatus { return when (status.lowercase()) { - "open", "new", "hold" -> WAITING_FOR_SUPPORT + "open", "new", "hold", "pending" -> ONGOING "closed" -> CLOSED - "pending" -> WAITING_FOR_USER "solved" -> SOLVED else -> UNKNOWN } diff --git a/WordPress/src/main/java/org/wordpress/android/support/unified/ui/ConversationStatusBadge.kt b/WordPress/src/main/java/org/wordpress/android/support/unified/ui/ConversationStatusBadge.kt index 291e2054b6be..00c4ac2a261d 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/unified/ui/ConversationStatusBadge.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/unified/ui/ConversationStatusBadge.kt @@ -19,16 +19,11 @@ fun ConversationStatusBadge( ) { val conversationStatus = ConversationStatus.fromStatus(status) val (statusText, backgroundColor, textColor) = when (conversationStatus) { - ConversationStatus.WAITING_FOR_SUPPORT -> Triple( - stringResource(R.string.he_support_status_waiting_for_support), + ConversationStatus.ONGOING -> Triple( + stringResource(R.string.he_support_status_ongoing), MaterialTheme.colorScheme.primaryContainer, MaterialTheme.colorScheme.onPrimaryContainer ) - ConversationStatus.WAITING_FOR_USER -> Triple( - stringResource(R.string.he_support_status_waiting_for_user), - MaterialTheme.colorScheme.secondaryContainer, - MaterialTheme.colorScheme.onSecondaryContainer - ) ConversationStatus.SOLVED -> Triple( stringResource(R.string.he_support_status_solved), MaterialTheme.colorScheme.primary, diff --git a/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedConversationDetailScreen.kt b/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedConversationDetailScreen.kt index 1251610177f7..11eae669698e 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedConversationDetailScreen.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedConversationDetailScreen.kt @@ -82,6 +82,10 @@ import kotlinx.coroutines.launch import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.ui.text.style.TextAlign +import androidx.annotation.StringRes +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.repeatOnLifecycle import org.wordpress.android.R import org.wordpress.android.util.WPUrlUtils import org.wordpress.android.support.unified.util.formatRelativeTime @@ -114,6 +118,7 @@ fun UnifiedConversationDetailScreen( onReplyMessageChange: (String) -> Unit, onReplyIncludeAppLogsChange: (Boolean) -> Unit, onReplyBottomSheetVisibilityChange: (Boolean) -> Unit, + onAutoRefresh: () -> Unit, attachmentActionsListener: AttachmentActionsListener, ) { var previewAttachment by remember { mutableStateOf(null) } @@ -123,6 +128,24 @@ fun UnifiedConversationDetailScreen( val resources = LocalResources.current val isBot = conversation.isBot val isBotTyping = isBot && isSendingReply + val ctaLabelRes = replyCtaLabelRes(conversation) + val lifecycleOwner = LocalLifecycleOwner.current + + // Auto-refresh the open conversation every minute so replies from support appear without the + // user leaving the screen. Bots update locally, so we only poll HE conversations. The poll is + // scoped to STARTED via repeatOnLifecycle so it pauses while the app is backgrounded (onStart + // already refreshes on return). Keying on the conversation id restarts the timer when a + // different conversation is opened. + if (!isBot) { + LaunchedEffect(conversation.id, lifecycleOwner) { + lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + while (true) { + kotlinx.coroutines.delay(CONVERSATION_AUTO_REFRESH_INTERVAL_MS) + onAutoRefresh() + } + } + } + } // Keep the conversation pinned to its last item whenever a message is added or the bot's typing // indicator appears/disappears. We scroll in reaction to layoutInfo.totalItemsCount rather than @@ -164,6 +187,8 @@ fun UnifiedConversationDetailScreen( ConversationBottomBar( isBot = isBot, canAcceptReply = conversation.canAcceptReply, + replyLabelRes = ctaLabelRes, + isLoading = isLoading, // The chat input is backed by the shared reply form state so the typed text is // retained on send failure (the VM clears it only on success) and survives // configuration changes via the ViewModel. @@ -259,6 +284,7 @@ fun UnifiedConversationDetailScreen( } UnifiedReplyBottomSheet( sheetState = sheetState, + titleRes = ctaLabelRes, isSending = isSendingReply, messageText = replyFormState.message, includeAppLogs = replyFormState.includeAppLogs, @@ -291,6 +317,8 @@ fun UnifiedConversationDetailScreen( private fun ConversationBottomBar( isBot: Boolean, canAcceptReply: Boolean, + @StringRes replyLabelRes: Int, + isLoading: Boolean, messageText: String, canSendMessage: Boolean, onMessageTextChange: (String) -> Unit, @@ -316,6 +344,8 @@ private fun ConversationBottomBar( canAcceptReply -> { Box(modifier = Modifier.navigationBarsPadding()) { ReplyButton( + labelRes = replyLabelRes, + isLoading = isLoading, enabled = replyEnabled, onClick = onReplyClick ) @@ -532,6 +562,7 @@ private fun ChatInputBar( @Composable private fun UnifiedReplyBottomSheet( sheetState: SheetState, + @StringRes titleRes: Int, isSending: Boolean, messageText: String, includeAppLogs: Boolean, @@ -572,7 +603,7 @@ private fun UnifiedReplyBottomSheet( } Text( - text = stringResource(R.string.he_support_reply_button), + text = stringResource(titleRes), style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, modifier = Modifier.semantics { heading() } @@ -611,10 +642,12 @@ private fun UnifiedReplyBottomSheet( @Composable private fun ReplyButton( + @StringRes labelRes: Int, + isLoading: Boolean, enabled: Boolean = true, onClick: () -> Unit ) { - val replyButtonLabel = stringResource(R.string.he_support_reply_button) + val replyButtonLabel = stringResource(labelRes) Box( modifier = Modifier .fillMaxWidth() @@ -622,23 +655,37 @@ private fun ReplyButton( ) { Button( onClick = onClick, - enabled = enabled, + enabled = enabled && !isLoading, modifier = Modifier .fillMaxWidth() .height(56.dp) - .semantics { contentDescription = replyButtonLabel }, + // Only expose the label once the conversation has loaded, so neither TalkBack nor + // the button flashes a transient/incorrect action while it's still being resolved. + .semantics { if (!isLoading) contentDescription = replyButtonLabel }, shape = RoundedCornerShape(28.dp) ) { - Icon( - imageVector = Icons.AutoMirrored.Filled.Reply, - contentDescription = null, - modifier = Modifier.size(20.dp) - ) - Spacer(modifier = Modifier.size(8.dp)) - Text( - text = replyButtonLabel, - style = MaterialTheme.typography.titleMedium - ) + if (isLoading) { + // The button is disabled while loading, so its container is greyed + // (onSurface @ 12%). Tint the spinner with the Material 3 disabled content + // color (onSurface @ 38%) so it stays visible in both light and dark themes, + // matching the greyed-out label the button shows when disabled. + CircularProgressIndicator( + modifier = Modifier.size(24.dp), + strokeWidth = 2.dp, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) + ) + } else { + Icon( + imageVector = Icons.AutoMirrored.Filled.Reply, + contentDescription = null, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.size(8.dp)) + Text( + text = replyButtonLabel, + style = MaterialTheme.typography.titleMedium + ) + } } } } @@ -692,13 +739,13 @@ private fun TypingIndicatorBubble() { bottomEnd = 16.dp ) ) - .padding(16.dp) + .padding(horizontal = 12.dp, vertical = 10.dp) .semantics { contentDescription = typingDescription } ) { Row( - horizontalArrangement = Arrangement.spacedBy(4.dp), + horizontalArrangement = Arrangement.spacedBy(3.dp), verticalAlignment = Alignment.CenterVertically ) { TypingDot(delay = 0) @@ -730,7 +777,7 @@ private fun TypingDot(delay: Int) { color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = alpha), shape = RoundedCornerShape(50) ) - .padding(4.dp) + .size(6.dp) ) } @@ -1058,7 +1105,28 @@ private fun WelcomeHeader(userName: String) { } } +/** + * Chooses the CTA label for an HE conversation: + * - "Reply" when the last message is from support (not the user and not the bot) — it's the user's + * turn to answer. + * - "Add more info" when the last message is the user's own, or the bot's (a freshly created + * ticket still shows the bot's opening message), i.e. the user is adding to an existing thread. + */ +@StringRes +private fun replyCtaLabelRes(conversation: UnifiedConversation): Int { + val lastMessage = conversation.messages.lastOrNull() + val isSupportAwaitingReply = lastMessage != null && + lastMessage.authorRole != UnifiedMessage.AUTHOR_ROLE_USER && + lastMessage.authorRole != UnifiedMessage.AUTHOR_ROLE_BOT + return if (isSupportAwaitingReply) { + R.string.he_support_reply_button + } else { + R.string.he_support_add_more_info_button + } +} + private const val PERCENT_MULTIPLIER = 100 private const val TYPING_DOT_DELAY_STEP = 150 private const val TYPING_DOT_PULSE_MS = 600L private const val TYPING_DOT_MIN_ALPHA = 0.3f +private const val CONVERSATION_AUTO_REFRESH_INTERVAL_MS = 60_000L diff --git a/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedSupportActivity.kt b/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedSupportActivity.kt index 89e997f0e9f5..3c7243330108 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedSupportActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedSupportActivity.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.core.net.toUri import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavHostController @@ -27,6 +28,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.wordpress.android.R import org.wordpress.android.fluxc.utils.AppLogWrapper @@ -52,6 +54,10 @@ class UnifiedSupportActivity : AppCompatActivity() { private lateinit var composeView: ComposeView private lateinit var navController: NavHostController + // Tracks whether onStart has already fired once, so the initial start (covered by init()'s load) + // doesn't trigger a redundant refresh. Reset when the Activity is recreated. + private var hasStarted = false + @Suppress("TooGenericExceptionCaught") private val photoPickerLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult() @@ -92,6 +98,19 @@ class UnifiedSupportActivity : AppCompatActivity() { } } + override fun onStart() { + super.onStart() + // Refresh both the list and the open conversation each time the screen returns to the + // foreground so support replies are up to date. Skip the very first onStart (right after + // onCreate's initial load) to avoid a redundant request. On config-change recreation the + // ViewModel keeps its data, so refreshing again here is harmless. + if (hasStarted) { + viewModel.refreshConversationsSilently() + viewModel.refreshSelectedConversation() + } + hasStarted = true + } + private fun observeNavigationEvents() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -121,6 +140,17 @@ class UnifiedSupportActivity : AppCompatActivity() { val scope = rememberCoroutineScope() val errorMessage by viewModel.errorMessage.collectAsState() + // Confirm a successful HE ticket reply with a transient snackbar. The event is one-shot, so + // it never re-shows on recomposition or configuration change. + LaunchedEffect(Unit) { + viewModel.replySentEvents.collect { + snackbarHostState.showSnackbar( + message = getString(R.string.he_support_reply_sent_confirmation), + duration = SnackbarDuration.Long + ) + } + } + val errorType = errorMessage if (errorType != null) { val message = when (errorType) { @@ -148,6 +178,19 @@ class UnifiedSupportActivity : AppCompatActivity() { composable(route = UnifiedScreen.List.name) { val conversationsState by viewModel.conversationsState.collectAsState() val conversations by viewModel.conversations.collectAsState() + val lifecycleOwner = LocalLifecycleOwner.current + // Auto-refresh the list every minute while it's on screen so new or updated + // conversations appear without the user pulling to refresh. Scoped to STARTED via + // repeatOnLifecycle so the timer pauses while the app is backgrounded (onStart + // already refreshes on return). + LaunchedEffect(lifecycleOwner) { + lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + while (true) { + delay(CONVERSATIONS_LIST_AUTO_REFRESH_INTERVAL_MS) + viewModel.refreshConversationsSilently() + } + } + } UnifiedConversationsListScreen( snackbarHostState = snackbarHostState, conversations = conversations, @@ -207,6 +250,7 @@ class UnifiedSupportActivity : AppCompatActivity() { onReplyBottomSheetVisibilityChange = { viewModel.updateReplyBottomSheetVisibility(it) }, + onAutoRefresh = { viewModel.refreshSelectedConversation() }, attachmentActionsListener = attachmentActionsListener, ) } @@ -247,6 +291,7 @@ class UnifiedSupportActivity : AppCompatActivity() { companion object { const val AUTHORIZATION_TAG = "Authorization" + private const val CONVERSATIONS_LIST_AUTO_REFRESH_INTERVAL_MS = 60_000L @JvmStatic fun createIntent(context: Context): Intent = Intent(context, UnifiedSupportActivity::class.java) diff --git a/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedSupportViewModel.kt b/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedSupportViewModel.kt index 701e49b45258..9abe2be88630 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedSupportViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/unified/ui/UnifiedSupportViewModel.kt @@ -4,9 +4,12 @@ import android.net.Uri import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.wordpress.android.fluxc.store.AccountStore @@ -44,6 +47,22 @@ class UnifiedSupportViewModel @Inject constructor( private val _isSendingReply = MutableStateFlow(false) val isSendingReply: StateFlow = _isSendingReply.asStateFlow() + // Consumable one-shot event emitted after a Happiness Engineer ticket reply is successfully sent, + // so the UI can confirm it to the user (not emitted for bot chat sends — they reply in-thread and + // have no email follow-up). A CONFLATED channel buffers the event when no collector is attached + // (e.g. the composition-restart gap during rotation) so it isn't dropped, and trySend never + // suspends, so emitting it can't stall the reply's finally block. + private val _replySentEvents = Channel(Channel.CONFLATED) + val replySentEvents: Flow = _replySentEvents.receiveAsFlow() + + // Auto-refresh race guards (all touched only on the main thread): + // [conversationMutationGeneration] is bumped synchronously the moment a reply send begins, so an + // auto-refresh poll that started before the send discards its now-stale result instead of + // restoring a pre-reply snapshot over the just-sent reply. [isRefreshingSelectedConversation] + // serialises polls so overlapping responses can't land out of order. + private var conversationMutationGeneration = 0L + private var isRefreshingSelectedConversation = false + // Reply form state for HE-style conversations (survives configuration changes) private val _replyFormState = MutableStateFlow(ConversationReplyFormState()) val replyFormState: StateFlow = _replyFormState.asStateFlow() @@ -141,6 +160,53 @@ class UnifiedSupportViewModel @Inject constructor( override suspend fun getConversation(conversationId: Long): UnifiedConversation? = repository.loadConversation(conversationId) + /** + * Silently reloads the currently open conversation from the server, without showing the + * full-screen loading indicator. Used by the detail screen's periodic auto-refresh so new + * replies from support show up while the screen stays open. + * + * No-ops for bot conversations (they have no server-side changes to poll and a not-yet-created + * new conversation has no id to fetch), while a reply is in flight or the conversation is still + * loading (so we never clobber the optimistic message or the initial load), and while another + * poll is already running (so overlapping responses can't land out of order). + */ + @Suppress("TooGenericExceptionCaught") + fun refreshSelectedConversation() { + val conversation = _selectedConversation.value ?: return + val canRefresh = !conversation.isBot && + conversation.id != NEW_CONVERSATION_ID && + !_isSendingReply.value && + !isLoadingConversation.value && + !isRefreshingSelectedConversation + if (!canRefresh) return + // Snapshot the generation before fetching; if a send bumps it while we're on the network, + // this poll's result is stale and must be dropped (see conversationMutationGeneration). + val generationAtStart = conversationMutationGeneration + isRefreshingSelectedConversation = true + viewModelScope.launch { + try { + if (!networkUtilsWrapper.isNetworkAvailable()) return@launch + val updated = repository.loadConversation(conversation.id) + // Apply only if nothing changed the open conversation while we were fetching: same + // conversation still open, no reply sending, and no send began since we started. + val stillCurrent = updated != null && + _selectedConversation.value?.id == updated.id && + !_isSendingReply.value && + conversationMutationGeneration == generationAtStart + if (stillCurrent) { + _selectedConversation.value = updated + } + } catch (throwable: Throwable) { + appLogWrapper.e( + AppLog.T.SUPPORT, "Error auto-refreshing conversation: " + + "${throwable.message} - ${throwable.stackTraceToString()}" + ) + } finally { + isRefreshingSelectedConversation = false + } + } + } + @Suppress("TooGenericExceptionCaught", "LongMethod") fun sendReply(message: String, includeAppLogs: Boolean = false) { val conversation = _selectedConversation.value ?: return @@ -149,6 +215,9 @@ class UnifiedSupportViewModel @Inject constructor( // gets to set it. if (_isSendingReply.value) return _isSendingReply.value = true + // Invalidate any auto-refresh poll already in flight: its response predates this send, so it + // must not overwrite the reply we're about to add. + conversationMutationGeneration++ viewModelScope.launch { if (!networkUtilsWrapper.isNetworkAvailable()) { @@ -197,6 +266,11 @@ class UnifiedSupportViewModel @Inject constructor( } else { replaceInList(updated) } + // Confirm HE ticket replies only; bot chat sends need no confirmation. trySend is + // non-suspending, so it never delays the finally block below. + if (!conversation.isBot) { + _replySentEvents.trySend(Unit) + } } else { rollbackOptimisticMessage(conversation, optimisticMessage.id) rollbackInputMessage(message) diff --git a/WordPress/src/main/res/values-ar/strings.xml b/WordPress/src/main/res/values-ar/strings.xml index 1da5ec36b3f0..41cf04d53ddd 100644 --- a/WordPress/src/main/res/values-ar/strings.xml +++ b/WordPress/src/main/res/values-ar/strings.xml @@ -311,8 +311,6 @@ Language: ar يمكن أن يساعد تضمين السجلات فريقنا في التحقيق في المشكلات. قد تحتوي السجلات على نشاط التطبيق الأخير. تنزيل المرفق تحديد المرفقات - في انتظار الدعم - في انتظار المستخدم تم الحل مغلق غير معروف diff --git a/WordPress/src/main/res/values-bg/strings.xml b/WordPress/src/main/res/values-bg/strings.xml index 5b33b8b0f758..2b26c037ec32 100644 --- a/WordPress/src/main/res/values-bg/strings.xml +++ b/WordPress/src/main/res/values-bg/strings.xml @@ -14,8 +14,6 @@ Language: bg Неизвестно Затворено Разрешено - Изчаква потребителя - Изчаква поддръжката Избор на прикачени файлове Изтегляне на прикачен файл Включването на дневници ще помогне на екипа ни да разреши проблема. Дневниците могат да съдържат скорошна дейност. diff --git a/WordPress/src/main/res/values-cs/strings.xml b/WordPress/src/main/res/values-cs/strings.xml index c8c61016396c..86fff0058d49 100644 --- a/WordPress/src/main/res/values-cs/strings.xml +++ b/WordPress/src/main/res/values-cs/strings.xml @@ -313,8 +313,6 @@ Language: cs_CZ Neznámý Uzavřeno Vyřešeno - Čeká na uživatele - Čeká na podporu Vybrat přílohy Stáhnout přílohu Přiložení záznamů může pomoci našemu týmu při vyšetřování problémů. Záznamy mohou obsahovat nedávnou aktivitu aplikace. diff --git a/WordPress/src/main/res/values-de/strings.xml b/WordPress/src/main/res/values-de/strings.xml index 83ddd43f5296..5a8b385a0f43 100644 --- a/WordPress/src/main/res/values-de/strings.xml +++ b/WordPress/src/main/res/values-de/strings.xml @@ -313,8 +313,6 @@ Language: de Unbekannt Geschlossen Gelöst - Wartet auf Benutzer - Wartet auf Support Anhänge auswählen Anhang herunterladen Das Hinzufügen von Protokollen kann unserem Team helfen, Probleme zu untersuchen. Protokolle können aktuelle App-Aktivitäten enthalten. diff --git a/WordPress/src/main/res/values-en-rGB/strings.xml b/WordPress/src/main/res/values-en-rGB/strings.xml index 737eb990436b..3e3bcb7a0744 100644 --- a/WordPress/src/main/res/values-en-rGB/strings.xml +++ b/WordPress/src/main/res/values-en-rGB/strings.xml @@ -313,8 +313,6 @@ Language: en_GB Unknown Closed Solved - Waiting for User - Waiting for Support Select attachments Download attachment Including logs can help our team investigate issues. Logs may contain recent app activity. diff --git a/WordPress/src/main/res/values-es-rCO/strings.xml b/WordPress/src/main/res/values-es-rCO/strings.xml index 9f53c0360a6f..40cca8e25a9f 100644 --- a/WordPress/src/main/res/values-es-rCO/strings.xml +++ b/WordPress/src/main/res/values-es-rCO/strings.xml @@ -313,8 +313,6 @@ Language: es_CO No se puede reproducir el vídeo Esta conversación está cerrada. Ya no puedes responder a ella. Solucionado - Esperando al usuario - Esperando al equipo de soporte Seleccionar adjuntos Descargar adjunto Incluir registros puede ayudar a nuestro equipo a investigar los problemas. Los registros pueden contener actividad reciente de la aplicación. diff --git a/WordPress/src/main/res/values-es/strings.xml b/WordPress/src/main/res/values-es/strings.xml index 9a9033f66dc3..a711d8188ad3 100644 --- a/WordPress/src/main/res/values-es/strings.xml +++ b/WordPress/src/main/res/values-es/strings.xml @@ -313,8 +313,6 @@ Language: es Desconocido Cerrado Solucionado - Esperando al usuario - Esperando al equipo de soporte Seleccionar adjuntos Descargar adjunto Incluir registros puede ayudar a nuestro equipo a investigar los problemas. Los registros pueden contener actividad reciente de la aplicación. diff --git a/WordPress/src/main/res/values-fr-rCA/strings.xml b/WordPress/src/main/res/values-fr-rCA/strings.xml index 08ece7c24f7e..7009ea1414b9 100644 --- a/WordPress/src/main/res/values-fr-rCA/strings.xml +++ b/WordPress/src/main/res/values-fr-rCA/strings.xml @@ -300,8 +300,6 @@ Language: fr Inconnu Fermé Résolu - En attente de l’utilisateur - En attente d’assistance Sélectionner les pièces jointes Télécharger la pièce jointe Inclure des journaux peut aider notre équipe à examiner les problèmes. Les journaux peuvent contenir une activité récente de l’application. diff --git a/WordPress/src/main/res/values-fr/strings.xml b/WordPress/src/main/res/values-fr/strings.xml index 08ece7c24f7e..7009ea1414b9 100644 --- a/WordPress/src/main/res/values-fr/strings.xml +++ b/WordPress/src/main/res/values-fr/strings.xml @@ -300,8 +300,6 @@ Language: fr Inconnu Fermé Résolu - En attente de l’utilisateur - En attente d’assistance Sélectionner les pièces jointes Télécharger la pièce jointe Inclure des journaux peut aider notre équipe à examiner les problèmes. Les journaux peuvent contenir une activité récente de l’application. diff --git a/WordPress/src/main/res/values-he/strings.xml b/WordPress/src/main/res/values-he/strings.xml index 69acb83a8e62..47af509ed1fa 100644 --- a/WordPress/src/main/res/values-he/strings.xml +++ b/WordPress/src/main/res/values-he/strings.xml @@ -309,8 +309,6 @@ Language: he_IL לא ידוע סגור נפתר - בהמתנה למשתמש - בהמתנה לתמיכה לבחור קבצים מצורפים להוריד את הקובץ המצורף הוספה של יומני פעילות יכולה לעזור לצוות שלנו לחקור בעיות. יומני הפעילות עשויים להכיל פעולות שבוצעו באפליקציה לאחרונה. diff --git a/WordPress/src/main/res/values-id/strings.xml b/WordPress/src/main/res/values-id/strings.xml index 0e952931f060..d305e6e271de 100644 --- a/WordPress/src/main/res/values-id/strings.xml +++ b/WordPress/src/main/res/values-id/strings.xml @@ -305,8 +305,6 @@ Language: id Tidak diketahui Ditutup Teratasi - Menunggu Pengguna - Menunggu Dukungan Pilih lampiran Unduh lampiran Menyertakan log dapat membantu tim kami menyelidiki kendala. Log mungkin berisi aktivitas aplikasi terbaru. diff --git a/WordPress/src/main/res/values-it/strings.xml b/WordPress/src/main/res/values-it/strings.xml index 7b1769f8c278..f3a29024a086 100644 --- a/WordPress/src/main/res/values-it/strings.xml +++ b/WordPress/src/main/res/values-it/strings.xml @@ -313,8 +313,6 @@ Language: it Sconosciuto Chiuso Risolto - In attesa dell\'utente - In attesa di supporto Seleziona allegati Scarica allegato Includere i log può aiutare il nostro team a investigare i problemi. I log potrebbero contenere attività recenti delle app. diff --git a/WordPress/src/main/res/values-ja/strings.xml b/WordPress/src/main/res/values-ja/strings.xml index 5a2e13d49c17..8e4b22783582 100644 --- a/WordPress/src/main/res/values-ja/strings.xml +++ b/WordPress/src/main/res/values-ja/strings.xml @@ -310,8 +310,6 @@ Language: ja_JP 不明 クローズド 解決済み - ユーザーを待機中 - サポートを待機中 添付ファイルを選択 添付ファイルをダウンロード ログを添付すると問題の調査に役立ちます。 ログには最近のアプリのアクティビティが含まれている可能性があります。 diff --git a/WordPress/src/main/res/values-ko/strings.xml b/WordPress/src/main/res/values-ko/strings.xml index e20e89544358..fe3a054c9d6a 100644 --- a/WordPress/src/main/res/values-ko/strings.xml +++ b/WordPress/src/main/res/values-ko/strings.xml @@ -308,8 +308,6 @@ Language: ko_KR 알 수 없음 닫힘 해결됨 - 사용자 대기 중 - 지원 대기 중 첨부 파일 선택 첨부 파일 다운로드 로그를 포함하면 팀에서 문제를 조사하는 데 도움이 될 수 있습니다. 로그에 최근 앱 활동이 포함될 수 있습니다. diff --git a/WordPress/src/main/res/values-nl/strings.xml b/WordPress/src/main/res/values-nl/strings.xml index f5564ef3a4f0..03c4b41c6e94 100644 --- a/WordPress/src/main/res/values-nl/strings.xml +++ b/WordPress/src/main/res/values-nl/strings.xml @@ -313,8 +313,6 @@ Language: nl Onbekend Gesloten Opgelost - Wachten op gebruiker - Wachten op steun Bijlagen selecteren Download bijlage Het toevoegen van logs kan ons team helpen bij het onderzoeken van problemen. Logs kunnen recente app-activiteit bevatten. diff --git a/WordPress/src/main/res/values-pl/strings.xml b/WordPress/src/main/res/values-pl/strings.xml index 869056cf4697..840be18956fb 100644 --- a/WordPress/src/main/res/values-pl/strings.xml +++ b/WordPress/src/main/res/values-pl/strings.xml @@ -313,8 +313,6 @@ Language: pl Nieznany Zamknięty Rozwiązany - Oczekiwanie na użytkownika - Oczekiwanie na pomoc techniczną Wybierz załączniki Pobierz załącznik Dołączenie dzienników może pomóc naszemu zespołowi w badaniu problemów. Dzienniki mogą zawierać ostatnią aktywność aplikacji. diff --git a/WordPress/src/main/res/values-pt-rBR/strings.xml b/WordPress/src/main/res/values-pt-rBR/strings.xml index c3ab24584370..abd14164a065 100644 --- a/WordPress/src/main/res/values-pt-rBR/strings.xml +++ b/WordPress/src/main/res/values-pt-rBR/strings.xml @@ -14,8 +14,6 @@ Language: pt_BR Desconhecido Encerrado Resolvido - Aguardando usuário - Aguardando suporte Selecionar anexos Fazer download do anexo Incluir registros pode ajudar nossa equipe a investigar problemas. Os registros podem conter atividades recentes do aplicativo. diff --git a/WordPress/src/main/res/values-ro/strings.xml b/WordPress/src/main/res/values-ro/strings.xml index 4171917cc63b..489bed158c8f 100644 --- a/WordPress/src/main/res/values-ro/strings.xml +++ b/WordPress/src/main/res/values-ro/strings.xml @@ -313,8 +313,6 @@ Language: ro Necunoscută Închisă Rezolvată - Așteaptă suport - Așteaptă utilizatorul Selectează atașamente Descarcă atașamente Includerea jurnalelor poate ajuta echipa noastră să investigheze problemele. Jurnalele pot să conțină activități recente din aplicație. diff --git a/WordPress/src/main/res/values-ru/strings.xml b/WordPress/src/main/res/values-ru/strings.xml index 78edb5e20242..2cf7926361f5 100644 --- a/WordPress/src/main/res/values-ru/strings.xml +++ b/WordPress/src/main/res/values-ru/strings.xml @@ -313,8 +313,6 @@ Language: ru Неизвестно Закрыто Задача решена - Ожидание пользователя - Ожидание поддержки Выбрать вложения Скачать вложение Приложенные журналы помогут нам понять, в чём причина проблем. Журналы могут содержать сведения о последних действиях в приложении. diff --git a/WordPress/src/main/res/values-sq/strings.xml b/WordPress/src/main/res/values-sq/strings.xml index 5b44a3833ae7..5729bac694d6 100644 --- a/WordPress/src/main/res/values-sq/strings.xml +++ b/WordPress/src/main/res/values-sq/strings.xml @@ -212,8 +212,6 @@ Language: sq_AL E panjohur E mbyllur E zgjidhur - Po pritet për Përdorues - Po pritet për Asistencën Përzgjidhni bashkëngjitje Shkarkoje bashkëngjitjen Përfshirja e regjistrave mund ta ndihmojë ekipin tonë të hetojë probleme. Regjistrat mund të përmbajnë veprimtari aplikacioni së fundi. diff --git a/WordPress/src/main/res/values-sv/strings.xml b/WordPress/src/main/res/values-sv/strings.xml index 54b6af3e2e6f..9a5790338f24 100644 --- a/WordPress/src/main/res/values-sv/strings.xml +++ b/WordPress/src/main/res/values-sv/strings.xml @@ -313,8 +313,6 @@ Language: sv_SE Okänd Stängt Löst - Väntar på användare - Väntar på support Välj bilagor Ladda ner bilaga Att inkludera loggar kan hjälpa vårt team att undersöka problem. Loggar kan innehålla den senaste appaktiviteten. diff --git a/WordPress/src/main/res/values-tr/strings.xml b/WordPress/src/main/res/values-tr/strings.xml index 524ababd2754..6796c98a73d9 100644 --- a/WordPress/src/main/res/values-tr/strings.xml +++ b/WordPress/src/main/res/values-tr/strings.xml @@ -313,8 +313,6 @@ Language: tr Bilinmiyor Kapalı Çözüldü - Kullanıcı Bekleniyor - Destek Bekleniyor Ek seç Eki indir Günlükleri dahil etmek, ekibimizin sorunları araştırmasına yardımcı olabilir. Günlükler son uygulama etkinliğini içerebilir. diff --git a/WordPress/src/main/res/values-zh-rCN/strings.xml b/WordPress/src/main/res/values-zh-rCN/strings.xml index 203688beb11a..b76fe66e1db6 100644 --- a/WordPress/src/main/res/values-zh-rCN/strings.xml +++ b/WordPress/src/main/res/values-zh-rCN/strings.xml @@ -310,8 +310,6 @@ Language: zh_CN 未知 已关闭 已解决 - 正在等待用户 - 正在等待支持人员 选择附件 下载附件 包含日志可以帮助我们的团队调查问题。 日志可能包含最近的应用程序活动。 diff --git a/WordPress/src/main/res/values-zh-rHK/strings.xml b/WordPress/src/main/res/values-zh-rHK/strings.xml index f35d2ff98218..08ba9d9d56c7 100644 --- a/WordPress/src/main/res/values-zh-rHK/strings.xml +++ b/WordPress/src/main/res/values-zh-rHK/strings.xml @@ -312,8 +312,6 @@ Language: zh_TW 不明 已關閉 已解決 - 正在等候使用者 - 正在等候支援 選取附件 下載附件 若包含記錄,就能協助我們的團隊調查問題。 記錄檔可能包含最近的應用程式活動。 diff --git a/WordPress/src/main/res/values-zh-rTW/strings.xml b/WordPress/src/main/res/values-zh-rTW/strings.xml index f35d2ff98218..08ba9d9d56c7 100644 --- a/WordPress/src/main/res/values-zh-rTW/strings.xml +++ b/WordPress/src/main/res/values-zh-rTW/strings.xml @@ -312,8 +312,6 @@ Language: zh_TW 不明 已關閉 已解決 - 正在等候使用者 - 正在等候支援 選取附件 下載附件 若包含記錄,就能協助我們的團隊調查問題。 記錄檔可能包含最近的應用程式活動。 diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index c1b8f8ee58d3..2ec243578c38 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -4714,7 +4714,9 @@ translators: %s: Select control option value e.g: "Auto, 25%". --> Last updated %1$s Reply + Add more info Send + Your reply has been sent. Check your email for updates. Message Downloading %1$s… Loading image @@ -4732,8 +4734,7 @@ translators: %s: Select control option value e.g: "Auto, 25%". --> Including logs can help our team investigate issues. Logs may contain recent app activity. Download attachment Select attachments - Waiting for Support - Waiting for User + Ongoing Solved Closed Unknown