Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
Expand Up @@ -46,6 +46,9 @@ import rs.wordpress.example.shared.ui.wpcom.WpComBotConversationsScreen
import rs.wordpress.example.shared.ui.wpcom.WpComMeScreen
import rs.wordpress.example.shared.ui.wpcom.WpComSiteScreen
import rs.wordpress.example.shared.ui.wpcom.WpComSupportConversationsScreen
import rs.wordpress.example.shared.ui.wpcom.WpComUnifiedConversationScreen
import rs.wordpress.example.shared.ui.wpcom.WpComUnifiedConversationsScreen
import uniffi.wp_api.ConversationId
import uniffi.wp_api.PostEndpointType
import uniffi.wp_api.TermEndpointType
import uniffi.wp_mobile.Account
Expand All @@ -65,6 +68,7 @@ fun App(authenticationEnabled: Boolean, authenticateSite: (String, onSuccess: ()
var currentSiteViewModel by remember { mutableStateOf<SiteViewModel?>(null) }
var currentPostType by remember { mutableStateOf<SitePostType?>(null) }
var currentTaxonomy by remember { mutableStateOf<SiteTaxonomy?>(null) }
var currentUnifiedConversationId by remember { mutableStateOf<ConversationId?>(null) }

MaterialTheme {
NavHost(navController, startDestination = "welcome") {
Expand Down Expand Up @@ -364,6 +368,7 @@ fun App(authenticationEnabled: Boolean, authenticateSite: (String, onSuccess: ()
WpComSiteScreen(
onMeClicked = { navController.navigate("wpcom_me") },
onSupportConversationsClicked = { navController.navigate("wpcom_support") },
onUnifiedConversationsClicked = { navController.navigate("wpcom_unified") },
onBotConversationsClicked = { navController.navigate("wpcom_bots") },
onBackClicked = { navController.popBackStack() }
)
Expand Down Expand Up @@ -401,6 +406,38 @@ fun App(authenticationEnabled: Boolean, authenticateSite: (String, onSuccess: ()
onBackClicked = { navController.popBackStack() }
)
}
composable("wpcom_unified") {
val wpComClient = currentWpComClient
if (wpComClient == null) {
ErrorMessage("No WordPress.com account connected")
return@composable
}
WpComUnifiedConversationsScreen(
wpComApiClient = wpComClient,
onConversationClicked = { conversationId ->
currentUnifiedConversationId = conversationId
navController.navigate("wpcom_unified_detail")
},
onBackClicked = { navController.popBackStack() }
)
}
composable("wpcom_unified_detail") {
val wpComClient = currentWpComClient
val conversationId = currentUnifiedConversationId
if (wpComClient == null) {
ErrorMessage("No WordPress.com account connected")
return@composable
}
if (conversationId == null) {
ErrorMessage("No conversation selected")
return@composable
}
WpComUnifiedConversationScreen(
wpComApiClient = wpComClient,
conversationId = conversationId,
onBackClicked = { navController.popBackStack() }
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
fun WpComSiteScreen(
onMeClicked: () -> Unit,
onSupportConversationsClicked: () -> Unit,
onUnifiedConversationsClicked: () -> Unit,
onBotConversationsClicked: () -> Unit,
onBackClicked: () -> Unit = {}
) {
Expand Down Expand Up @@ -60,6 +61,15 @@ fun WpComSiteScreen(
modifier = Modifier.clickable(onClick = onSupportConversationsClicked)
)
}
item {
ListItem(
headlineContent = { Text("Unified Conversations") },
trailingContent = {
Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null)
},
modifier = Modifier.clickable(onClick = onUnifiedConversationsClicked)
)
}
item {
ListItem(
headlineContent = { Text("Bot Conversations") },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package rs.wordpress.example.shared.ui.wpcom

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.ui.tooling.preview.Preview
import rs.wordpress.api.kotlin.WpComApiClient
import rs.wordpress.api.kotlin.WpRequestResult
import uniffi.wp_api.ConversationId
import uniffi.wp_api.UnifiedConversation
import uniffi.wp_api.UnifiedMessage

@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Preview
fun WpComUnifiedConversationScreen(
wpComApiClient: WpComApiClient,
conversationId: ConversationId,
onBackClicked: () -> Unit = {}
) {
var conversation by remember { mutableStateOf<UnifiedConversation?>(null) }
var error by remember { mutableStateOf<String?>(null) }
var isLoading by remember { mutableStateOf(true) }

LaunchedEffect(conversationId) {
when (
val result = wpComApiClient.request {
it.unifiedConversations().getUnifiedConversation(conversationId)
}
) {
is WpRequestResult.Success -> {
conversation = result.response.data
isLoading = false
}
else -> {
error = result.toString()
isLoading = false
}
}
}

Scaffold(
topBar = {
TopAppBar(
title = { Text(conversation?.title ?: "Conversation") },
navigationIcon = {
IconButton(onClick = onBackClicked) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
}
}
)
}
) { paddingValues ->
when {
isLoading -> {
Box(
modifier = Modifier.fillMaxSize().padding(paddingValues),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
error != null -> {
Box(
modifier = Modifier.fillMaxSize().padding(paddingValues),
contentAlignment = Alignment.Center
) {
Text(
text = "Error: $error",
color = MaterialTheme.colorScheme.error
)
}
}
conversation != null -> {
val messages = conversation!!.messages
LazyColumn(
modifier = Modifier.fillMaxSize().padding(paddingValues),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
items(messages) { message ->
MessageRow(message)
HorizontalDivider()
}
}
}
}
}
}

@Composable
private fun MessageRow(message: UnifiedMessage) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
Text(
text = "${message.authorName} (${message.authorRole})",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.primary
)
Text(
text = message.message,
style = MaterialTheme.typography.bodyMedium
)
if (message.attachments.isNotEmpty()) {
Text(
text = "Attachments: ${message.attachments.size}",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.secondary,
modifier = Modifier.padding(top = 4.dp)
)
}
Text(
text = formatUnifiedDate(message.createdAt),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.outline,
modifier = Modifier.padding(top = 4.dp)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package rs.wordpress.example.shared.ui.wpcom

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.ui.tooling.preview.Preview
import rs.wordpress.api.kotlin.WpComApiClient
import rs.wordpress.api.kotlin.WpRequestResult
import uniffi.wp_api.ConversationId
import uniffi.wp_api.UnifiedConversationSummary
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Preview
fun WpComUnifiedConversationsScreen(
wpComApiClient: WpComApiClient,
onConversationClicked: (ConversationId) -> Unit,
onBackClicked: () -> Unit = {}
) {
var conversations by remember { mutableStateOf<List<UnifiedConversationSummary>>(emptyList()) }
var error by remember { mutableStateOf<String?>(null) }
var isLoading by remember { mutableStateOf(true) }

LaunchedEffect(Unit) {
when (
val result = wpComApiClient.request {
it.unifiedConversations().getUnifiedConversationList()
}
) {
is WpRequestResult.Success -> {
conversations = result.response.data
isLoading = false
}
else -> {
error = result.toString()
isLoading = false
}
}
}

Scaffold(
topBar = {
TopAppBar(
title = { Text("Unified Conversations") },
navigationIcon = {
IconButton(onClick = onBackClicked) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
}
}
)
}
) { paddingValues ->
when {
isLoading -> {
Box(
modifier = Modifier.fillMaxSize().padding(paddingValues),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
error != null -> {
Box(
modifier = Modifier.fillMaxSize().padding(paddingValues),
contentAlignment = Alignment.Center
) {
Text(
text = "Error: $error",
color = MaterialTheme.colorScheme.error
)
}
}
conversations.isEmpty() -> {
Box(
modifier = Modifier.fillMaxSize().padding(paddingValues),
contentAlignment = Alignment.Center
) {
Text("No unified conversations found.")
}
}
else -> {
LazyColumn(
modifier = Modifier.fillMaxSize().padding(paddingValues)
) {
items(conversations) { conversation ->
ListItem(
headlineContent = { Text(conversation.title) },
supportingContent = {
Text("${conversation.status} · ${formatUnifiedDate(conversation.updatedAt)}")
},
modifier = Modifier.clickable {
onConversationClicked(conversation.id)
}
)
}
}
}
}
}
}

internal fun formatUnifiedDate(date: Date): String {
return try {
SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(date)
} catch (e: Exception) {
date.toString()
}
}
13 changes: 13 additions & 0 deletions native/swift/Example/Example/Services/WPComService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ final class WPComService: ObservableObject {
}
},

RootListData(name: "Unified Conversations", category: .system) {
let client = try await WPComApiClient.instance(loginManager: self.loginManager)
return try await client.unifiedConversations.getUnifiedConversationList().data
.map {
ListViewData(
id: String($0.id),
title: $0.title,
subtitle: "\($0.status) · \($0.createdAt.formatted())",
fields: [:]
)
}
},

RootListData(
name: "Bot Conversations",
category: .system,
Expand Down
Loading