diff --git a/.idea/.name b/.idea/.name
index 9ef16e6b..8a14fc66 100644
--- a/.idea/.name
+++ b/.idea/.name
@@ -1 +1 @@
-PEP
\ No newline at end of file
+Revolt
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 3cd01369..2ffd8dde 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -77,7 +77,7 @@ android {
compileSdk = 35
defaultConfig {
- applicationId = "chat.revolt"
+ applicationId = "chat.peptide"
minSdk = 24
targetSdk = 35
versionCode = Integer.parseInt("001_003_106".replace("_", ""), 10)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index af21b348..2b273b0d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -161,6 +161,34 @@
android:value="true" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/assets/README_APP_LINKS.md b/app/src/main/assets/README_APP_LINKS.md
new file mode 100644
index 00000000..e73a9197
--- /dev/null
+++ b/app/src/main/assets/README_APP_LINKS.md
@@ -0,0 +1,71 @@
+# Android App Links Setup Instructions
+
+This document provides instructions for setting up Android App Links for the Revolt app.
+
+## What are Android App Links?
+
+Android App Links are HTTP URLs that bring users directly to specific content in your Android app. When a user clicks an App Link, the app opens immediately if it's installed, without showing the app chooser dialog.
+
+## Requirements
+
+1. Your app must have an intent filter that handles the specific URLs
+2. Your website must have a Digital Asset Links JSON file that verifies the association with your app
+
+## Steps to Complete the Setup
+
+### 1. Get your app's signing certificate fingerprint
+
+Run the following command to get your app's signing certificate fingerprint:
+
+```bash
+keytool -list -v -keystore your_keystore.jks -alias your_alias
+```
+
+Look for the SHA-256 fingerprint in the output. Remove all colons (:) from the fingerprint.
+
+### 2. Update the assetlinks.json file
+
+Open the `assetlinks.json` file in this directory and replace the placeholder with your actual SHA-256 fingerprint:
+
+```json
+[{
+ "relation": ["delegate_permission/common.handle_all_urls"],
+ "target": {
+ "namespace": "android_app",
+ "package_name": "chat.revolt",
+ "sha256_cert_fingerprints": [
+ "YOUR_SHA256_FINGERPRINT_HERE"
+ ]
+ }
+}]
+```
+
+### 3. Host the assetlinks.json file on your server
+
+Upload the `assetlinks.json` file to the following location on your server:
+
+```
+https://revolt.chat/.well-known/assetlinks.json
+https://app.revolt.chat/.well-known/assetlinks.json
+```
+
+Make sure the file is accessible via HTTPS and returns with Content-Type: application/json.
+
+### 4. Test your App Links
+
+Use the Android Debug Bridge (ADB) to test your App Links:
+
+```bash
+adb shell am start -a android.intent.action.VIEW -d "https://revolt.chat/channels/CHANNEL_ID" chat.revolt
+```
+
+## Troubleshooting
+
+If your App Links aren't working:
+
+1. Verify that the assetlinks.json file is accessible via HTTPS
+2. Check that the SHA-256 fingerprint in the assetlinks.json file matches your app's signing certificate
+3. Make sure your app is using the correct signing certificate
+4. Use the App Links Assistant in Android Studio to verify your App Links setup
+
+For more information, see the [Android App Links documentation](https://developer.android.com/training/app-links).
diff --git a/app/src/main/assets/assetlinks.json b/app/src/main/assets/assetlinks.json
new file mode 100644
index 00000000..d80aa2a7
--- /dev/null
+++ b/app/src/main/assets/assetlinks.json
@@ -0,0 +1,10 @@
+[{
+ "relation": ["delegate_permission/common.handle_all_urls"],
+ "target": {
+ "namespace": "android_app",
+ "package_name": "chat.peptide",
+ "sha256_cert_fingerprints": [
+ "PUT_YOUR_APP_SIGNING_CERTIFICATE_SHA256_FINGERPRINT_HERE"
+ ]
+ }
+}]
diff --git a/app/src/main/java/chat/revolt/activities/DeepLinkActivity.kt b/app/src/main/java/chat/revolt/activities/DeepLinkActivity.kt
new file mode 100644
index 00000000..0ad65539
--- /dev/null
+++ b/app/src/main/java/chat/revolt/activities/DeepLinkActivity.kt
@@ -0,0 +1,169 @@
+package chat.revolt.activities
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.lifecycleScope
+import chat.revolt.persistence.KVStorage
+import chat.revolt.utils.DeepLinkUtils
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class DeepLinkActivity : AppCompatActivity() {
+
+ @Inject
+ lateinit var kvStorage: KVStorage
+
+ private val viewModel by viewModels()
+
+ companion object {
+ private const val TAG = "DeepLinkActivity"
+
+ // Constants for deep link paths
+ const val PATH_CHANNEL = "channel"
+ const val PATH_SERVER = "server"
+ const val PATH_USER = "user"
+ const val PATH_MESSAGE = "message"
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // Handle the incoming intent
+ handleIntent(intent)
+ }
+
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+
+ // Handle the new intent
+ handleIntent(intent)
+ }
+
+ private fun handleIntent(intent: Intent) {
+ val appLinkAction = intent.action
+ val appLinkData: Uri? = intent.data
+
+ if (Intent.ACTION_VIEW == appLinkAction && appLinkData != null) {
+ Log.d(TAG, "Deep link received: $appLinkData")
+
+ // Check if this is a valid Revolt deep link
+ if (!DeepLinkUtils.isRevoltDeepLink(appLinkData)) {
+ Log.d(TAG, "Not a valid Revolt deep link: $appLinkData")
+ startMainActivity()
+ return
+ }
+
+ // Check if user is logged in and handle deep link in a coroutine
+ lifecycleScope.launch {
+ val token = kvStorage.get("sessionToken")
+ if (token == null) {
+ // User is not logged in, redirect to login screen
+ Log.d(TAG, "User not logged in, redirecting to login screen")
+ startMainActivity("choose-platform")
+ return@launch
+ }
+
+ // First, check for server/channel/message format
+ val serverChannelMessage = DeepLinkUtils.extractServerChannelMessage(appLinkData)
+ if (serverChannelMessage != null) {
+ Log.d(TAG, "Detected server/channel/message format: ${serverChannelMessage.serverId}/${serverChannelMessage.channelId}/${serverChannelMessage.messageId}")
+
+ if (serverChannelMessage.messageId != null) {
+ // Navigate to the specific message in the channel
+ navigateToMessage(serverChannelMessage.messageId, serverChannelMessage.channelId)
+ } else {
+ // Navigate to the channel in the server
+ navigateToChannel(serverChannelMessage.channelId)
+ }
+ return@launch
+ }
+
+ // Extract IDs from the URI using our utility class for other formats
+ val channelId = DeepLinkUtils.extractChannelId(appLinkData)
+ val serverId = DeepLinkUtils.extractServerId(appLinkData)
+ val userId = DeepLinkUtils.extractUserId(appLinkData)
+ val messageId = DeepLinkUtils.extractMessageId(appLinkData)
+
+ when {
+ channelId != null -> {
+ Log.d(TAG, "Navigating to channel: $channelId")
+ navigateToChannel(channelId)
+ }
+ serverId != null -> {
+ Log.d(TAG, "Navigating to server: $serverId")
+ navigateToServer(serverId)
+ }
+ userId != null -> {
+ Log.d(TAG, "Navigating to user: $userId")
+ navigateToUser(userId)
+ }
+ messageId != null -> {
+ Log.d(TAG, "Navigating to message: $messageId")
+ val channelParam = appLinkData.getQueryParameter("channel")
+ navigateToMessage(messageId, channelParam)
+ }
+ else -> {
+ // Default case, just open the main activity
+ Log.d(TAG, "No specific path, opening main activity")
+ startMainActivity()
+ }
+ }
+ }
+ } else {
+ // Not a deep link, just open the main activity
+ Log.d(TAG, "Not a deep link, opening main activity")
+ startMainActivity()
+ }
+ }
+
+ private fun navigateToChannel(channelId: String) {
+ Log.d(TAG, "Navigating to channel: $channelId")
+
+ // Use our utility class to create the intent
+ val intent = DeepLinkUtils.createChannelIntent(this, channelId)
+ startActivity(intent)
+ finish()
+ }
+
+ private fun navigateToServer(serverId: String) {
+ Log.d(TAG, "Navigating to server: $serverId")
+
+ // Use our utility class to create the intent
+ val intent = DeepLinkUtils.createServerIntent(this, serverId)
+ startActivity(intent)
+ finish()
+ }
+
+ private fun navigateToUser(userId: String) {
+ Log.d(TAG, "Navigating to user: $userId")
+
+ // Use our utility class to create the intent
+ val intent = DeepLinkUtils.createUserIntent(this, userId)
+ startActivity(intent)
+ finish()
+ }
+
+ private fun navigateToMessage(messageId: String, channelId: String?) {
+ Log.d(TAG, "Navigating to message: $messageId in channel: $channelId")
+
+ // Use our utility class to create the intent
+ val intent = DeepLinkUtils.createMessageIntent(this, messageId, channelId)
+ startActivity(intent)
+ finish()
+ }
+
+ private fun startMainActivity(destination: String? = null) {
+ val intent = Intent(this, MainActivity::class.java)
+ if (destination != null) {
+ intent.putExtra("next_destination", destination)
+ }
+ startActivity(intent)
+ finish()
+ }
+}
diff --git a/app/src/main/java/chat/revolt/activities/MainActivity.kt b/app/src/main/java/chat/revolt/activities/MainActivity.kt
index a41044ef..c8215eca 100644
--- a/app/src/main/java/chat/revolt/activities/MainActivity.kt
+++ b/app/src/main/java/chat/revolt/activities/MainActivity.kt
@@ -2,6 +2,7 @@ package chat.revolt.activities
import android.annotation.SuppressLint
import android.content.Context
+import android.content.Intent
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Bundle
@@ -127,6 +128,7 @@ import chat.revolt.screens.settings.channel.ChannelSettingsHome
import chat.revolt.screens.settings.channel.ChannelSettingsOverview
import chat.revolt.screens.settings.channel.ChannelSettingsPermissions
import chat.revolt.ui.theme.RevoltTheme
+import chat.revolt.utils.DeepLinkHandler
import com.google.android.material.color.DynamicColors
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -383,6 +385,9 @@ class MainActivity : AppCompatActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false)
RevoltAPI.hydrateFromPersistentCache()
+
+ // Handle deep link data if available
+ handleDeepLinkData(intent)
setContent {
val windowSizeClass = calculateWindowSizeClass(this)
@@ -419,6 +424,70 @@ class MainActivity : AppCompatActivity() {
)
}
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+ handleDeepLinkData(intent)
+ }
+
+ private fun handleDeepLinkData(intent: Intent) {
+ // Check for deep link data from DeepLinkActivity
+ val channelId = intent.getStringExtra(DeepLinkActivity.PATH_CHANNEL)
+ val serverId = intent.getStringExtra(DeepLinkActivity.PATH_SERVER)
+ val userId = intent.getStringExtra(DeepLinkActivity.PATH_USER)
+ val messageId = intent.getStringExtra(DeepLinkActivity.PATH_MESSAGE)
+ val nextDestination = intent.getStringExtra("next_destination")
+
+ if (nextDestination != null) {
+ Log.d(TAG, "Setting next destination from deep link: $nextDestination")
+ viewModel.updateNextDestination(nextDestination)
+ return
+ }
+
+ // Store deep link data in a static companion object for ChatRouterScreen to access
+ DeepLinkHandler.channelId = channelId
+ DeepLinkHandler.serverId = serverId
+ DeepLinkHandler.userId = userId
+ DeepLinkHandler.messageId = messageId
+ DeepLinkHandler.hasDeepLink = channelId != null || serverId != null || userId != null || messageId != null
+
+ // Wait for the app to be ready before navigating to the deep link destination
+ viewModel.viewModelScope.launch {
+ viewModel.isReady.collect { isReady ->
+ if (isReady) {
+ when {
+ channelId != null -> {
+ Log.d(TAG, "Navigating to channel from deep link: $channelId")
+ // Check if Polar mode is enabled
+ if (Experiments.usePolar.isEnabled) {
+ viewModel.updateNextDestination("main/conversation/$channelId")
+ } else {
+ // For non-Polar mode, we'll use the DeepLinkHandler to communicate with ChatRouterScreen
+ viewModel.updateNextDestination("chat")
+ }
+ }
+ serverId != null -> {
+ Log.d(TAG, "Navigating to server from deep link: $serverId")
+ // Navigate to chat screen, the ChatRouterScreen will handle the server navigation
+ viewModel.updateNextDestination("chat")
+ }
+ userId != null -> {
+ Log.d(TAG, "Navigating to user from deep link: $userId")
+ // Navigate to chat screen, the ChatRouterScreen will handle the user navigation
+ viewModel.updateNextDestination("chat")
+ }
+ messageId != null -> {
+ Log.d(TAG, "Navigating to message from deep link: $messageId")
+ // Navigate to chat screen, the ChatRouterScreen will handle the message navigation
+ viewModel.updateNextDestination("chat")
+ }
+ }
+ // Only collect once
+ return@collect
+ }
+ }
+ }
+ }
+
override fun onProvideKeyboardShortcuts(
data: MutableList?,
menu: Menu?,
@@ -444,6 +513,7 @@ class MainActivity : AppCompatActivity() {
}
companion object {
+ private const val TAG = "MainActivity"
init {
NativeLibraries.init()
}
diff --git a/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt b/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt
index ef660e5e..236eb076 100644
--- a/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt
+++ b/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt
@@ -74,6 +74,7 @@ import chat.revolt.composables.chat.DisconnectedNotice
import chat.revolt.composables.screens.chat.drawer.ChannelSideDrawer
import chat.revolt.dialogs.NotificationRationaleDialog
import chat.revolt.internals.Changelogs
+import chat.revolt.utils.DeepLinkHandler
import chat.revolt.persistence.KVStorage
import chat.revolt.screens.chat.dialogs.safety.ReportMessageDialog
import chat.revolt.screens.chat.dialogs.safety.ReportServerDialog
@@ -112,12 +113,12 @@ sealed class ChatRouterDestination {
data class NoCurrentChannel(val serverId: String?) : ChatRouterDestination()
companion object {
- val default = Settings
+ val default = Home // Changed default from Settings to Home
fun fromString(destination: String): ChatRouterDestination {
return when {
- destination == "home" -> Settings // previous name for overview
- destination == "overview" -> Settings
+ destination == "home" -> Home // Changed from Settings to Home
+ destination == "overview" -> Home // Changed from Settings to Home
destination == "friends" -> Friends
destination.startsWith("no_current_channel/") -> NoCurrentChannel(
destination.removePrefix(
@@ -150,8 +151,15 @@ class ChatRouterViewModel @Inject constructor(
init {
viewModelScope.launch {
- val current = kvStorage.get("currentDestination")
- setSaveDestination(ChatRouterDestination.fromString(current ?: ""))
+ // Check if there's a deep link to handle first
+ if (DeepLinkHandler.hasDeepLink) {
+ Log.d("ChatRouterViewModel", "Deep link detected in init, skipping saved destination")
+ // Don't set any destination here, let the LaunchedEffect in ChatRouterScreen handle it
+ } else {
+ // No deep link, load the saved destination
+ val current = kvStorage.get("currentDestination")
+ setSaveDestination(ChatRouterDestination.fromString(current ?: ""))
+ }
latestChangelogRead = changelogs.hasSeenCurrent()
latestChangelog = changelogs.getLatestChangelogCode()
@@ -361,6 +369,63 @@ fun ChatRouterScreen(
}
}
+ // Handle deep links when the ChatRouterScreen is first composed
+ LaunchedEffect(Unit) {
+ // Check if there's a deep link to handle
+ if (DeepLinkHandler.hasDeepLink) {
+ Log.d("ChatRouterScreen", "Processing deep link data")
+
+ // Process the deep link based on priority
+ when {
+ // Priority 1: Handle server/channel/message format (most specific)
+ DeepLinkHandler.messageId != null && DeepLinkHandler.channelId != null -> {
+ val messageId = DeepLinkHandler.messageId!!
+ val channelId = DeepLinkHandler.channelId!!
+ Log.d("ChatRouterScreen", "Setting destination to message: $messageId in channel: $channelId")
+
+ val resolvedChannel = RevoltAPI.channelCache[channelId]
+ if (resolvedChannel == null) {
+ showChannelUnavailableAlert = true
+ } else {
+ // Navigate to the channel first, then the message will be handled by the channel screen
+ viewModel.setSaveDestination(ChatRouterDestination.Channel(channelId))
+ // TODO: Add logic to scroll to the specific message in the channel
+ }
+ }
+
+ // Priority 2: Handle channel deep link
+ DeepLinkHandler.channelId != null -> {
+ val channelId = DeepLinkHandler.channelId!!
+ Log.d("ChatRouterScreen", "Setting destination to channel: $channelId")
+ val resolvedChannel = RevoltAPI.channelCache[channelId]
+
+ if (resolvedChannel == null) {
+ showChannelUnavailableAlert = true
+ } else {
+ viewModel.setSaveDestination(ChatRouterDestination.Channel(channelId))
+ }
+ }
+
+ // Priority 3: Handle server deep link
+ DeepLinkHandler.serverId != null -> {
+ val serverId = DeepLinkHandler.serverId!!
+ Log.d("ChatRouterScreen", "Setting destination to server: $serverId")
+ viewModel.navigateToServer(serverId)
+ }
+
+ // Priority 4: Handle user deep link
+ DeepLinkHandler.userId != null -> {
+ val userId = DeepLinkHandler.userId!!
+ Log.d("ChatRouterScreen", "Setting destination to user: $userId")
+ // TODO: Implement user navigation if needed
+ }
+ }
+
+ // Clear the deep link data after processing
+ DeepLinkHandler.clear()
+ }
+ }
+
LaunchedEffect(Unit) {
while (true) {
ActionChannel.receive().let { action ->
diff --git a/app/src/main/java/chat/revolt/utils/DeepLinkUtils.kt b/app/src/main/java/chat/revolt/utils/DeepLinkUtils.kt
new file mode 100644
index 00000000..7175517c
--- /dev/null
+++ b/app/src/main/java/chat/revolt/utils/DeepLinkUtils.kt
@@ -0,0 +1,218 @@
+package chat.revolt.utils
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.util.Log
+import chat.revolt.activities.DeepLinkActivity
+import chat.revolt.activities.MainActivity
+
+/**
+ * Utility class for handling deep links in the Revolt app.
+ */
+object DeepLinkUtils {
+ private const val TAG = "DeepLinkUtils"
+
+ /**
+ * Checks if the given URI is a valid Revolt deep link.
+ *
+ * @param uri The URI to check
+ * @return true if the URI is a valid Revolt deep link, false otherwise
+ */
+ fun isRevoltDeepLink(uri: Uri?): Boolean {
+ if (uri == null) return false
+
+ val host = uri.host ?: return false
+ val scheme = uri.scheme ?: return false
+
+ // Check if the scheme is http or https
+ if (scheme != "http" && scheme != "https") return false
+
+ // Check if the host is a valid Revolt host
+ return host == "peptide.chat" || host == "app.peptide.chat"
+ }
+
+ /**
+ * Extracts the channel ID from a channel deep link URI.
+ *
+ * @param uri The URI to extract the channel ID from
+ * @return The channel ID, or null if the URI is not a valid channel deep link
+ */
+ fun extractChannelId(uri: Uri?): String? {
+ if (uri == null) return null
+
+ val pathSegments = uri.pathSegments
+
+ // Handle /channels/CHANNEL_ID format
+ if (pathSegments.size >= 2 && pathSegments[0] == "channels") {
+ return pathSegments[1]
+ }
+
+ // Handle /server/SERVER_ID/channel/CHANNEL_ID format
+ if (pathSegments.size >= 4 &&
+ pathSegments[0] == "server" &&
+ pathSegments[2] == "channel") {
+ return pathSegments[3]
+ }
+
+ return null
+ }
+
+ /**
+ * Extracts the server ID from a server deep link URI.
+ *
+ * @param uri The URI to extract the server ID from
+ * @return The server ID, or null if the URI is not a valid server deep link
+ */
+ fun extractServerId(uri: Uri?): String? {
+ if (uri == null) return null
+
+ val pathSegments = uri.pathSegments
+
+ // Handle /servers/SERVER_ID format
+ if (pathSegments.size >= 2 && pathSegments[0] == "servers") {
+ return pathSegments[1]
+ }
+
+ // Handle /server/SERVER_ID format
+ if (pathSegments.size >= 2 && pathSegments[0] == "server") {
+ return pathSegments[1]
+ }
+
+ return null
+ }
+
+ /**
+ * Extracts the user ID from a user deep link URI.
+ *
+ * @param uri The URI to extract the user ID from
+ * @return The user ID, or null if the URI is not a valid user deep link
+ */
+ fun extractUserId(uri: Uri?): String? {
+ if (uri == null) return null
+
+ val pathSegments = uri.pathSegments
+ if (pathSegments.size < 2 || pathSegments[0] != "users") return null
+
+ return pathSegments[1]
+ }
+
+ /**
+ * Extracts the message ID from a message deep link URI.
+ *
+ * @param uri The URI to extract the message ID from
+ * @return The message ID, or null if the URI is not a valid message deep link
+ */
+ fun extractMessageId(uri: Uri?): String? {
+ if (uri == null) return null
+
+ val pathSegments = uri.pathSegments
+
+ // Handle /messages/MESSAGE_ID format
+ if (pathSegments.size >= 2 && pathSegments[0] == "messages") {
+ return pathSegments[1]
+ }
+
+ // Handle /server/SERVER_ID/channel/CHANNEL_ID/MESSAGE_ID format
+ if (pathSegments.size >= 5 &&
+ pathSegments[0] == "server" &&
+ pathSegments[2] == "channel") {
+ return pathSegments[4]
+ }
+
+ return null
+ }
+
+ /**
+ * Data class to hold server, channel, and message IDs extracted from a URI.
+ */
+ data class ServerChannelMessage(
+ val serverId: String,
+ val channelId: String,
+ val messageId: String? = null
+ )
+
+ /**
+ * Extracts server, channel, and message IDs from a server channel message deep link URI.
+ *
+ * @param uri The URI to extract the IDs from
+ * @return A ServerChannelMessage object containing the extracted IDs, or null if the URI is not a valid server channel message deep link
+ */
+ fun extractServerChannelMessage(uri: Uri?): ServerChannelMessage? {
+ if (uri == null) return null
+
+ val pathSegments = uri.pathSegments
+
+ // Handle /server/SERVER_ID/channel/CHANNEL_ID format
+ if (pathSegments.size >= 4 &&
+ pathSegments[0] == "server" &&
+ pathSegments[2] == "channel") {
+
+ val serverId = pathSegments[1]
+ val channelId = pathSegments[3]
+
+ // Check if there's a message ID (5th segment)
+ val messageId = if (pathSegments.size >= 5) pathSegments[4] else null
+
+ return ServerChannelMessage(serverId, channelId, messageId)
+ }
+
+ return null
+ }
+
+ /**
+ * Creates an intent to navigate to a specific channel.
+ *
+ * @param context The context to use for creating the intent
+ * @param channelId The ID of the channel to navigate to
+ * @return An intent that will navigate to the specified channel
+ */
+ fun createChannelIntent(context: Context, channelId: String): Intent {
+ return Intent(context, MainActivity::class.java).apply {
+ putExtra(DeepLinkActivity.PATH_CHANNEL, channelId)
+ }
+ }
+
+ /**
+ * Creates an intent to navigate to a specific server.
+ *
+ * @param context The context to use for creating the intent
+ * @param serverId The ID of the server to navigate to
+ * @return An intent that will navigate to the specified server
+ */
+ fun createServerIntent(context: Context, serverId: String): Intent {
+ return Intent(context, MainActivity::class.java).apply {
+ putExtra(DeepLinkActivity.PATH_SERVER, serverId)
+ }
+ }
+
+ /**
+ * Creates an intent to navigate to a specific user.
+ *
+ * @param context The context to use for creating the intent
+ * @param userId The ID of the user to navigate to
+ * @return An intent that will navigate to the specified user
+ */
+ fun createUserIntent(context: Context, userId: String): Intent {
+ return Intent(context, MainActivity::class.java).apply {
+ putExtra(DeepLinkActivity.PATH_USER, userId)
+ }
+ }
+
+ /**
+ * Creates an intent to navigate to a specific message.
+ *
+ * @param context The context to use for creating the intent
+ * @param messageId The ID of the message to navigate to
+ * @param channelId The ID of the channel containing the message, if known
+ * @return An intent that will navigate to the specified message
+ */
+ fun createMessageIntent(context: Context, messageId: String, channelId: String? = null): Intent {
+ return Intent(context, MainActivity::class.java).apply {
+ putExtra(DeepLinkActivity.PATH_MESSAGE, messageId)
+ if (channelId != null) {
+ putExtra(DeepLinkActivity.PATH_CHANNEL, channelId)
+ }
+ }
+ }
+}