diff --git a/README.md b/README.md index bbd3e2b..ecc0b78 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ The app demonstrates: - embedding `ConvoKitConversation` from `app.convokit:convokit-android-ui` in an Android Views application with `ComposeView`; - delegating message history, sending, editing and deleting your own messages, quoted replies and jump-to-message, media rendering, typing and read receipts to the reusable UI library; - calling the 0.9.0 core surface directly from the host: sending with `replyToMessageId`, resolving a whole page of quoted rows with one `getReplyPreviews` call, centring a window with `getMessageContext`, and telling the coded `MESSAGE_NOT_FOUND` and `CONVERSATION_NOT_FOUND` answers apart; +- calling the reaction core API directly: add/remove one exact `πŸ‘πŸ½`, read a batched summary, and load the first page of reactors, while the embedded UI handles its own reaction controls; - creating an adapter after each successful login and disposing the old conversation before disconnecting or switching users. The sample no longer maintains a second message renderer, composer, or raw @@ -36,9 +37,9 @@ The host handles system-bar, display-cutout and keyboard insets before embedding Compose, so padding is not applied twice. Local host tests use Robolectric and synthetic insets; they do not prove hosted authorization or physical-device chat. -### Published 0.9.0 SDKs +### Published 0.10.0 SDKs -The example consumes the published 0.9.0 core and UI from maven.convokit.app. +The example consumes the published 0.10.0 core and UI from maven.convokit.app. The embedded room acknowledges a concrete message for precise read positions, defers acknowledgements while the host is hidden, correlates pending sends with live/history confirmations before HTTP returns, and sends the caller's diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1a56b94..e2815ac 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -40,8 +40,8 @@ android { } dependencies { - implementation("app.convokit:convokit-android:0.9.0") - implementation("app.convokit:convokit-android-ui:0.9.0") + implementation("app.convokit:convokit-android:0.10.0") + implementation("app.convokit:convokit-android-ui:0.10.0") implementation("androidx.activity:activity-ktx:1.10.1") implementation("androidx.appcompat:appcompat:1.7.1") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.1") diff --git a/app/src/main/kotlin/app/convokit/example/MainActivity.kt b/app/src/main/kotlin/app/convokit/example/MainActivity.kt index 7e1776d..1220e41 100644 --- a/app/src/main/kotlin/app/convokit/example/MainActivity.kt +++ b/app/src/main/kotlin/app/convokit/example/MainActivity.kt @@ -72,6 +72,7 @@ class MainActivity : AppCompatActivity() { onBackPressedDispatcher.addCallback(this, roomBack) binding.joinButton.setOnClickListener { joinRoom() } binding.replyDemoButton.setOnClickListener { runReplyDemo() } + binding.reactionDemoButton.setOnClickListener { runReactionDemo() } } private fun joinRoom() { @@ -106,6 +107,7 @@ class MainActivity : AppCompatActivity() { joinedRoomId = roomId binding.replyDemoStatus.setText(R.string.reply_demo_hint) binding.replyDemoButton.isEnabled = true + binding.reactionDemoButton.isEnabled = true roomBack.isEnabled = true } catch (cause: Throwable) { withContext(NonCancellable) { convoKit.disconnectUser() } @@ -124,6 +126,7 @@ class MainActivity : AppCompatActivity() { binding.chatContent.disposeComposition() binding.room.visibility = View.GONE binding.replyDemoButton.isEnabled = false + binding.reactionDemoButton.isEnabled = false joinedRoomId = null binding.joinForm.visibility = View.VISIBLE binding.status.text = getString(R.string.disconnecting) @@ -160,6 +163,38 @@ class MainActivity : AppCompatActivity() { } } + /** Direct core API tour. The Compose conversation handles its own picker and chips. */ + private fun runReactionDemo() { + val roomId = joinedRoomId ?: return + binding.reactionDemoButton.isEnabled = false + binding.reactionDemoStatus.setText(R.string.reaction_demo_running) + lifecycleScope.launch { + try { + val newest = convoKit.getMessages(roomId, limit = 1).firstOrNull() + val report = if (newest == null) { + getString(R.string.reaction_demo_empty) + } else { + val emoji = "πŸ‘πŸ½" // Preserve the exact Unicode sequence, including its skin tone. + val added = convoKit.addReaction(newest.id, emoji) + val summary = convoKit.getReactionSummaries(roomId, listOf(newest.id)) + .firstOrNull()?.reactions?.find { it.emoji == emoji } + val reactors = convoKit.listReactionUsers(newest.id, emoji, limit = 10) + val removed = convoKit.removeReaction(newest.id, emoji) + "Added $emoji to ${newest.id}: changed=${added.changed}; " + + "count=${summary?.count ?: 0}, reactedByMe=${summary?.reactedByMe ?: false}; " + + "reactors on first page=${reactors.data.size}, next page=${reactors.nextCursor != null}; " + + "removed: changed=${removed.changed}." + } + if (joinedRoomId == roomId) binding.reactionDemoStatus.text = report + } catch (cause: Throwable) { + if (cause is CancellationException) throw cause + if (joinedRoomId == roomId) binding.reactionDemoStatus.setText(R.string.reaction_demo_failed) + } finally { + binding.reactionDemoButton.isEnabled = joinedRoomId != null + } + } + } + private suspend fun issueUserToken(appUserId: String): String { val response = postJson( "${BuildConfig.DEMO_BACKEND_URL}/api/auth/token".toHttpUrl(), diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 1621878..6b39680 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -107,6 +107,22 @@ android:textColor="@color/convokit_text" android:textSize="13sp" /> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2a6a2d3..2e08a4d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,4 +16,9 @@ Quotes the newest message, resolves every quoted row on the page in one call, centres a window on the quote and shows the two 404s a host must tell apart. Running it sends one message to this room. Running the quoted-reply demo… The quoted-reply demo could not reach ConvoKit. Check your connection and try again. + Run the reaction API demo + Adds and removes one πŸ‘πŸ½ on the newest message, reads its batched summary, and pages the reactor list. The conversation above handles reactions automatically. + Running the reaction API demo… + This room has no message to react to yet. + The reaction API demo could not reach ConvoKit. Check access and try again.