-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add basic chat app, that sends and receives messages using ChatApi
#10
feat: add basic chat app, that sends and receives messages using ChatApi
#10
Conversation
WalkthroughThe changes involve updates to the Android project configuration and the Changes
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
c67bcbc
to
80f12d7
Compare
08ad21b
to
727b8d2
Compare
80f12d7
to
1a5dea1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (3)
- example/build.gradle.kts (4 hunks)
- example/src/main/java/com/ably/chat/example/MainActivity.kt (2 hunks)
- gradle.properties (1 hunks)
Additional comments not posted (5)
gradle.properties (1)
19-19
: LGTM!The code change is approved.
example/build.gradle.kts (4)
1-3
: LGTM!The imports are necessary for the
getLocalProperty
function.
26-27
: LGTM!The change is useful for managing sensitive keys like
ABLY_KEY
without hardcoding them in the source code. ThegetLocalProperty
function is used correctly to read the value ofABLY_KEY
from the local properties file.
47-47
: LGTM!Enabling the
buildConfig
feature is necessary for the newbuildConfigField
declaration forABLY_KEY
to be recognized during the build process.
79-87
: LGTM!The
getLocalProperty
function is well-implemented and follows best practices:
- It handles the case when the local properties file does not exist by returning null.
- It uses
InputStreamReader
with UTF-8 charset to read the file contents.- It closes the reader properly using the
use
function.The function is useful for reading sensitive keys from a local properties file without hardcoding them in the source code.
fun AblyChat(chatApi: ChatApi, modifier: Modifier = Modifier) { | ||
var messageText by remember { mutableStateOf(TextFieldValue("")) } | ||
var sending by remember { mutableStateOf(false) } | ||
var messages by remember { mutableStateOf(listOf<Message>()) } | ||
val coroutineScope = rememberCoroutineScope() | ||
|
||
@Preview(showBackground = true) | ||
@Composable | ||
fun GreetingPreview() { | ||
AblyChatExampleTheme { | ||
Greeting("Android") | ||
val roomId = "my-room" | ||
|
||
Column( | ||
modifier = Modifier.fillMaxSize(), | ||
verticalArrangement = Arrangement.SpaceBetween, | ||
) { | ||
Button(modifier = modifier.align(Alignment.CenterHorizontally), onClick = { | ||
coroutineScope.launch { | ||
messages = chatApi.getMessages(roomId, QueryOptions()).items | ||
} | ||
}) { | ||
Text("Load") | ||
} | ||
|
||
LazyColumn( | ||
modifier = Modifier.weight(1f).padding(16.dp), | ||
) { | ||
items(messages.size) { index -> | ||
Text(text = messages[index].text) | ||
} | ||
} | ||
|
||
Row( | ||
modifier = Modifier.padding(16.dp), | ||
horizontalArrangement = Arrangement.spacedBy(8.dp), | ||
) { | ||
TextField( | ||
value = messageText, | ||
onValueChange = { messageText = it }, | ||
modifier = Modifier.weight(1f), | ||
readOnly = sending, | ||
) | ||
Button(enabled = !sending, onClick = { | ||
sending = true | ||
coroutineScope.launch { | ||
chatApi.sendMessage( | ||
roomId, | ||
SendMessageParams( | ||
text = messageText.text, | ||
), | ||
) | ||
messageText = TextFieldValue("") | ||
sending = false | ||
} | ||
}) { | ||
Text("Send") | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve the reusability and accessibility of the AblyChat
function.
The AblyChat
function is well-structured and uses appropriate Jetpack Compose components and state management techniques. However, there are a few areas that can be improved:
- Pass the
roomId
as a parameter to make the function more reusable. - Use the
items
extension function instead ofitemsIndexed
for better performance. - Use the
OutlinedTextField
component for better accessibility. - Use the
OutlinedButton
component for better accessibility. - Disable the
Button
when the message text is empty. - Show a loading indicator when a message is being sent.
- Show an error message if sending a message fails.
- Show a success message if sending a message succeeds.
Apply this diff to implement the suggested improvements:
-fun AblyChat(chatApi: ChatApi, modifier: Modifier = Modifier) {
+fun AblyChat(chatApi: ChatApi, roomId: String, modifier: Modifier = Modifier) {
var messageText by remember { mutableStateOf(TextFieldValue("")) }
var sending by remember { mutableStateOf(false) }
+ var sendingError by remember { mutableStateOf(false) }
+ var sendingSuccess by remember { mutableStateOf(false) }
var messages by remember { mutableStateOf(listOf<Message>()) }
val coroutineScope = rememberCoroutineScope()
- val roomId = "my-room"
-
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween,
) {
- Button(modifier = modifier.align(Alignment.CenterHorizontally), onClick = {
+ OutlinedButton(modifier = modifier.align(Alignment.CenterHorizontally), onClick = {
coroutineScope.launch {
messages = chatApi.getMessages(roomId, QueryOptions()).items
}
}) {
Text("Load")
}
LazyColumn(
modifier = Modifier.weight(1f).padding(16.dp),
) {
- items(messages.size) { index ->
- Text(text = messages[index].text)
+ items(messages) { message ->
+ Text(text = message.text)
}
}
Row(
modifier = Modifier.padding(16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
- TextField(
+ OutlinedTextField(
value = messageText,
onValueChange = { messageText = it },
modifier = Modifier.weight(1f),
readOnly = sending,
)
- Button(enabled = !sending, onClick = {
+ OutlinedButton(
+ enabled = !sending && messageText.text.isNotEmpty(),
+ onClick = {
sending = true
+ sendingError = false
+ sendingSuccess = false
coroutineScope.launch {
- chatApi.sendMessage(
- roomId,
- SendMessageParams(
- text = messageText.text,
- ),
- )
+ try {
+ chatApi.sendMessage(
+ roomId,
+ SendMessageParams(
+ text = messageText.text,
+ ),
+ )
+ sendingSuccess = true
+ } catch (e: Exception) {
+ sendingError = true
+ }
messageText = TextFieldValue("")
sending = false
}
}
) {
- Text("Send")
+ when {
+ sending -> {
+ CircularProgressIndicator(
+ modifier = Modifier.size(24.dp),
+ strokeWidth = 2.dp,
+ )
+ }
+ sendingError -> {
+ Text("Error")
+ }
+ sendingSuccess -> {
+ Text("Sent")
+ }
+ else -> {
+ Text("Send")
+ }
+ }
}
}
}
}
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
fun AblyChat(chatApi: ChatApi, modifier: Modifier = Modifier) { | |
var messageText by remember { mutableStateOf(TextFieldValue("")) } | |
var sending by remember { mutableStateOf(false) } | |
var messages by remember { mutableStateOf(listOf<Message>()) } | |
val coroutineScope = rememberCoroutineScope() | |
@Preview(showBackground = true) | |
@Composable | |
fun GreetingPreview() { | |
AblyChatExampleTheme { | |
Greeting("Android") | |
val roomId = "my-room" | |
Column( | |
modifier = Modifier.fillMaxSize(), | |
verticalArrangement = Arrangement.SpaceBetween, | |
) { | |
Button(modifier = modifier.align(Alignment.CenterHorizontally), onClick = { | |
coroutineScope.launch { | |
messages = chatApi.getMessages(roomId, QueryOptions()).items | |
} | |
}) { | |
Text("Load") | |
} | |
LazyColumn( | |
modifier = Modifier.weight(1f).padding(16.dp), | |
) { | |
items(messages.size) { index -> | |
Text(text = messages[index].text) | |
} | |
} | |
Row( | |
modifier = Modifier.padding(16.dp), | |
horizontalArrangement = Arrangement.spacedBy(8.dp), | |
) { | |
TextField( | |
value = messageText, | |
onValueChange = { messageText = it }, | |
modifier = Modifier.weight(1f), | |
readOnly = sending, | |
) | |
Button(enabled = !sending, onClick = { | |
sending = true | |
coroutineScope.launch { | |
chatApi.sendMessage( | |
roomId, | |
SendMessageParams( | |
text = messageText.text, | |
), | |
) | |
messageText = TextFieldValue("") | |
sending = false | |
} | |
}) { | |
Text("Send") | |
} | |
} | |
} | |
} | |
fun AblyChat(chatApi: ChatApi, roomId: String, modifier: Modifier = Modifier) { | |
var messageText by remember { mutableStateOf(TextFieldValue("")) } | |
var sending by remember { mutableStateOf(false) } | |
var sendingError by remember { mutableStateOf(false) } | |
var sendingSuccess by remember { mutableStateOf(false) } | |
var messages by remember { mutableStateOf(listOf<Message>()) } | |
val coroutineScope = rememberCoroutineScope() | |
Column( | |
modifier = Modifier.fillMaxSize(), | |
verticalArrangement = Arrangement.SpaceBetween, | |
) { | |
OutlinedButton(modifier = modifier.align(Alignment.CenterHorizontally), onClick = { | |
coroutineScope.launch { | |
messages = chatApi.getMessages(roomId, QueryOptions()).items | |
} | |
}) { | |
Text("Load") | |
} | |
LazyColumn( | |
modifier = Modifier.weight(1f).padding(16.dp), | |
) { | |
items(messages) { message -> | |
Text(text = message.text) | |
} | |
} | |
Row( | |
modifier = Modifier.padding(16.dp), | |
horizontalArrangement = Arrangement.spacedBy(8.dp), | |
) { | |
OutlinedTextField( | |
value = messageText, | |
onValueChange = { messageText = it }, | |
modifier = Modifier.weight(1f), | |
readOnly = sending, | |
) | |
OutlinedButton( | |
enabled = !sending && messageText.text.isNotEmpty(), | |
onClick = { | |
sending = true | |
sendingError = false | |
sendingSuccess = false | |
coroutineScope.launch { | |
try { | |
chatApi.sendMessage( | |
roomId, | |
SendMessageParams( | |
text = messageText.text, | |
), | |
) | |
sendingSuccess = true | |
} catch (e: Exception) { | |
sendingError = true | |
} | |
messageText = TextFieldValue("") | |
sending = false | |
} | |
} | |
) { | |
when { | |
sending -> { | |
CircularProgressIndicator( | |
modifier = Modifier.size(24.dp), | |
strokeWidth = 2.dp, | |
) | |
} | |
sendingError -> { | |
Text("Error") | |
} | |
sendingSuccess -> { | |
Text("Sent") | |
} | |
else -> { | |
Text("Send") | |
} | |
} | |
} | |
} | |
} | |
} |
5c3bcaf
to
783cd4a
Compare
1a5dea1
to
7c0ec73
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (6)
- .editorconfig (1 hunks)
- .idea/codeStyles/Project.xml (2 hunks)
- example/build.gradle.kts (4 hunks)
- example/src/main/AndroidManifest.xml (2 hunks)
- example/src/main/java/com/ably/chat/example/MainActivity.kt (2 hunks)
- gradle.properties (1 hunks)
Files skipped from review due to trivial changes (2)
- .editorconfig
- .idea/codeStyles/Project.xml
Files skipped from review as they are similar to previous changes (2)
- example/build.gradle.kts
- gradle.properties
Additional comments not posted (6)
example/src/main/AndroidManifest.xml (1)
16-16
: Approved: Addition ofandroid:windowSoftInputMode="adjustResize"
This change is crucial for chat applications as it prevents the keyboard from covering important UI elements, such as the message input field. Good attention to detail in enhancing user experience.
example/src/main/java/com/ably/chat/example/MainActivity.kt (5)
44-44
: Approved: Initialization ofRealtimeClient
andChatApi
The initialization of
RealtimeClient
with appropriate client options and the setup ofChatApi
are well-implemented. Using a random client ID for each instance of the app is a good practice for identifying different users in a chat environment.Also applies to: 49-56
61-61
: Approved: Integration ofChat
function in UIThe integration of the
Chat
function within theScaffold
layout ensures that the chat interface is properly managed within the app's UI hierarchy. This setup facilitates a clean and functional user interface.
72-119
: Approved: Implementation ofChat
functionThe
Chat
function is well-structured, utilizing Jetpack Compose's powerful features for state management and layout. The use ofremember
andmutableStateOf
for managing the state of messages and the sending status is appropriate. The coroutine setup for fetching and sending messages is correctly implemented.
121-142
: Approved:MessageBubble
Composable FunctionThe
MessageBubble
function is a good example of modular code. It encapsulates the UI logic for displaying individual messages, which enhances readability and maintainability. The conditional arrangement and coloring based on the message's owner are well-thought-out, improving the user experience by making it easy to distinguish between sent and received messages.
146-171
: Approved:ChatInputField
Composable FunctionThe
ChatInputField
function effectively handles user input for sending messages. It appropriately disables the input field when a message is being sent, preventing user actions during transmission. The layout and functionality are well-implemented, contributing to a smooth user experience.
Add some functionality to the sample app
Summary by CodeRabbit
New Features
Bug Fixes
Chores