@@ -33,10 +33,16 @@ import androidx.compose.ui.Alignment
3333import androidx.compose.ui.Modifier
3434import androidx.compose.ui.graphics.Color
3535import androidx.compose.ui.unit.dp
36+ import kotlinx.coroutines.CancellationException
37+ import kotlinx.coroutines.delay
38+ import kotlinx.coroutines.flow.catch
39+ import kotlinx.coroutines.flow.retryWhen
3640import kotlinx.coroutines.launch
3741import kotlinx.datetime.TimeZone
3842import kotlinx.datetime.toLocalDateTime
39- import kotlinx.rpc.grpc.GrpcClient
43+ import kotlinx.rpc.grpc.StatusException
44+ import kotlinx.rpc.grpc.client.GrpcClient
45+ import kotlinx.rpc.grpc.statusCode
4046import kotlinx.rpc.internal.utils.ExperimentalRpcApi
4147import kotlinx.rpc.sample.messages.ChatEntry
4248import kotlinx.rpc.sample.messages.MessageService
@@ -45,7 +51,9 @@ import kotlinx.rpc.sample.messages.SendMessageRequest
4551import kotlinx.rpc.sample.messages.invoke
4652import kotlinx.rpc.withService
4753import org.jetbrains.compose.ui.tooling.preview.Preview
54+ import kotlin.random.Random
4855import kotlin.time.Clock
56+ import kotlin.time.Duration.Companion.seconds
4957import kotlin.time.ExperimentalTime
5058import kotlin.time.Instant
5159
@@ -56,7 +64,7 @@ fun App() {
5664
5765 val grpcClient = remember {
5866 GrpcClient (" localhost" , 8080 ) {
59- usePlaintext ()
67+ credentials = plaintext ()
6068 }
6169 }
6270
@@ -72,34 +80,63 @@ fun App() {
7280private fun ChatScreen (service : MessageService ) {
7381 val scope = rememberCoroutineScope()
7482
75- var me by remember { mutableStateOf(" me " ) }
83+ var me by remember { mutableStateOf(" user- ${ Random .nextInt(until = 999 )} " ) }
7684 var input by remember { mutableStateOf(" " ) }
7785 val messages = remember { mutableStateListOf<ChatEntry >() }
86+ var error by remember { mutableStateOf<String ?>(null ) }
7887
7988 fun sendMessage () {
8089 scope.launch {
81- val result = service.SendMessage (
82- SendMessageRequest {
83- user = me
84- text = input
85- }
86- )
87- if (result.success) {
88- messages + = ChatEntry {
89- user = me
90- text = input
91- tsMillis = Clock .System .now().toEpochMilliseconds()
90+ try {
91+ val result = service.SendMessage (
92+ SendMessageRequest {
93+ user = me
94+ text = input
95+ }
96+ )
97+ if (result.success) {
98+ messages + = ChatEntry {
99+ user = me
100+ text = input
101+ tsMillis = Clock .System .now().toEpochMilliseconds()
102+ }
103+ input = " "
104+ error = null
105+ } else {
106+ error = " Message not accepted by server."
92107 }
93- input = " "
108+ } catch (e: CancellationException ) {
109+ throw e
110+ } catch (e: StatusException ) {
111+ error = e.getStatus().getDescription()?.let { " ${e.getStatus().statusCode} : $it " } ? : " gRPC error: ${e.getStatus().statusCode} "
112+ } catch (e: Throwable ) {
113+ error = e.message ? : " Unknown error while sending."
94114 }
95115 }
96116 }
97117
98118 LaunchedEffect (me) {
99119 messages.clear()
120+ error = null
100121 val req = ReceiveMessagesRequest { user = me }
101- service.ReceiveMessages (req).collect { msg ->
102- messages + = msg
122+ try {
123+ service.ReceiveMessages (req)
124+ .retryWhen { cause, attempt ->
125+ if (cause is CancellationException ) false
126+ else {
127+ error = (cause as ? StatusException )?.getStatus()?.getDescription()?.let { " ${cause.getStatus().statusCode} : $it " }
128+ ? : cause.message ? : " Error receiving messages."
129+ delay(2 .seconds)
130+ true
131+ }
132+ }
133+ .collect { msg -> messages + = msg; error = null }
134+ } catch (e: CancellationException ) {
135+ throw e
136+ } catch (e: StatusException ) {
137+ error = e.getStatus().getDescription() ? : " gRPC error: ${e.getStatus().statusCode} "
138+ } catch (e: Throwable ) {
139+ error = e.message ? : " Unknown error while receiving."
103140 }
104141 }
105142
@@ -108,6 +145,18 @@ private fun ChatScreen(service: MessageService) {
108145 .windowInsetsPadding(WindowInsets .safeDrawing)
109146 .padding(16 .dp)
110147 ) {
148+
149+ if (error != null ) {
150+ Text (
151+ error!! ,
152+ color = MaterialTheme .colorScheme.error,
153+ modifier = Modifier
154+ .fillMaxWidth()
155+ .background(MaterialTheme .colorScheme.errorContainer)
156+ .padding(8 .dp)
157+ )
158+ }
159+
111160 OutlinedTextField (
112161 value = me,
113162 onValueChange = { me = it },
0 commit comments