379 lines
15 KiB
Kotlin
379 lines
15 KiB
Kotlin
package com.shaarit.presentation.nav
|
|
|
|
import android.app.Activity
|
|
import android.content.Intent
|
|
import android.net.Uri
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.ui.platform.LocalContext
|
|
import androidx.core.net.toUri
|
|
import androidx.navigation.NavType
|
|
import androidx.navigation.compose.NavHost
|
|
import androidx.navigation.compose.composable
|
|
import androidx.navigation.compose.rememberNavController
|
|
import androidx.navigation.navArgument
|
|
import androidx.navigation.navDeepLink
|
|
import java.net.URLEncoder
|
|
|
|
sealed class Screen(val route: String) {
|
|
object Login : Screen("login")
|
|
object Feed : Screen("feed?tag={tag}&collectionId={collectionId}") {
|
|
fun createRoute(tag: String? = null, collectionId: Long? = null): String {
|
|
val params = mutableListOf<String>()
|
|
if (tag != null) {
|
|
val encoded = URLEncoder.encode(tag, "UTF-8")
|
|
params.add("tag=$encoded")
|
|
}
|
|
if (collectionId != null) {
|
|
params.add("collectionId=$collectionId")
|
|
}
|
|
return if (params.isEmpty()) {
|
|
"feed"
|
|
} else {
|
|
"feed?" + params.joinToString("&")
|
|
}
|
|
}
|
|
}
|
|
object Add : Screen("add?url={url}&title={title}&isShare={isShare}")
|
|
object Edit : Screen("edit/{linkId}") {
|
|
fun createRoute(linkId: Int): String = "edit/$linkId"
|
|
}
|
|
object Tags : Screen("tags")
|
|
object Collections : Screen("collections")
|
|
object Dashboard : Screen("dashboard")
|
|
object Settings : Screen("settings")
|
|
object Help : Screen("help")
|
|
object DeadLinks : Screen("dead_links")
|
|
object Pinned : Screen("pinned")
|
|
object Reader : Screen("reader/{linkId}") {
|
|
fun createRoute(linkId: Int): String = "reader/$linkId"
|
|
}
|
|
object Reminders : Screen("reminders")
|
|
object Todo : Screen("todo")
|
|
object TodoDetail : Screen("todoDetail/{linkId}") {
|
|
fun createRoute(linkId: Int): String = "todoDetail/$linkId"
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun AppNavGraph(
|
|
startDestination: String = Screen.Login.route,
|
|
shareUrl: String? = null,
|
|
shareTitle: String? = null,
|
|
shareDescription: String? = null,
|
|
shareTags: List<String>? = null,
|
|
isFileShare: Boolean = false,
|
|
initialDeepLink: String? = null,
|
|
onPlayAudio: ((com.shaarit.domain.model.ShaarliLink) -> Unit)? = null
|
|
) {
|
|
val navController = rememberNavController()
|
|
val context = LocalContext.current
|
|
|
|
// If user is already logged in and has share data, navigate directly to Add screen
|
|
val hasShareData = shareUrl != null || (isFileShare && shareTitle != null)
|
|
androidx.compose.runtime.LaunchedEffect(hasShareData) {
|
|
if (hasShareData && startDestination != Screen.Login.route) {
|
|
val route = if (isFileShare && shareTitle != null) {
|
|
val encodedTitle = URLEncoder.encode(shareTitle, "UTF-8")
|
|
val encodedDesc = if (shareDescription != null) URLEncoder.encode(shareDescription, "UTF-8") else ""
|
|
val encodedTags = shareTags?.joinToString(",") { URLEncoder.encode(it, "UTF-8") } ?: ""
|
|
"add?url=&title=$encodedTitle&isShare=true&isFileShare=true&description=$encodedDesc&tags=$encodedTags"
|
|
} else if (shareUrl != null) {
|
|
val encodedUrl = URLEncoder.encode(shareUrl, "UTF-8")
|
|
val encodedTitle = if (shareTitle != null) URLEncoder.encode(shareTitle, "UTF-8") else ""
|
|
"add?url=$encodedUrl&title=$encodedTitle&isShare=true&isFileShare=false&description=&tags="
|
|
} else null
|
|
|
|
route?.let {
|
|
navController.navigate(it) {
|
|
popUpTo(startDestination) { inclusive = true }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NavHost(navController = navController, startDestination = startDestination) {
|
|
composable(Screen.Login.route) {
|
|
com.shaarit.presentation.auth.LoginScreen(
|
|
onLoginSuccess = {
|
|
if (isFileShare && shareTitle != null) {
|
|
// File share - navigate to add screen with file data
|
|
val encodedTitle = URLEncoder.encode(shareTitle, "UTF-8")
|
|
val encodedDesc = if (shareDescription != null) URLEncoder.encode(shareDescription, "UTF-8") else ""
|
|
val encodedTags = shareTags?.joinToString(",") { URLEncoder.encode(it, "UTF-8") } ?: ""
|
|
navController.navigate("add?url=&title=$encodedTitle&isShare=true&isFileShare=true&description=$encodedDesc&tags=$encodedTags") {
|
|
popUpTo(Screen.Login.route) { inclusive = true }
|
|
}
|
|
} else if (shareUrl != null) {
|
|
// Use proper URL encoding that handles spaces correctly
|
|
val encodedUrl = URLEncoder.encode(shareUrl, "UTF-8")
|
|
val encodedTitle =
|
|
if (shareTitle != null) {
|
|
URLEncoder.encode(shareTitle, "UTF-8")
|
|
} else ""
|
|
navController.navigate("add?url=$encodedUrl&title=$encodedTitle&isShare=true&isFileShare=false&description=&tags=") {
|
|
popUpTo(Screen.Login.route) { inclusive = true }
|
|
}
|
|
} else if (initialDeepLink != null) {
|
|
// Handle deep link after login
|
|
navController.navigate(initialDeepLink) {
|
|
popUpTo(Screen.Login.route) { inclusive = true }
|
|
}
|
|
} else {
|
|
navController.navigate(Screen.Feed.createRoute()) {
|
|
popUpTo(Screen.Login.route) { inclusive = true }
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = "feed?tag={tag}&collectionId={collectionId}",
|
|
arguments = listOf(
|
|
navArgument("tag") {
|
|
type = NavType.StringType
|
|
nullable = true
|
|
defaultValue = null
|
|
},
|
|
navArgument("collectionId") {
|
|
type = NavType.LongType
|
|
nullable = false
|
|
defaultValue = -1L
|
|
}
|
|
),
|
|
deepLinks = listOf(
|
|
navDeepLink { uriPattern = "shaarit://feed" },
|
|
navDeepLink { uriPattern = "shaarit://search" }
|
|
)
|
|
) { backStackEntry ->
|
|
val tag = backStackEntry.arguments?.getString("tag")
|
|
val collectionId = backStackEntry.arguments
|
|
?.getLong("collectionId")
|
|
?.takeIf { it != -1L }
|
|
com.shaarit.presentation.feed.FeedScreen(
|
|
onNavigateToAdd = { navController.navigate("add?url=&title=&isShare=false") },
|
|
onNavigateToEdit = { linkId ->
|
|
navController.navigate(Screen.Edit.createRoute(linkId))
|
|
},
|
|
onNavigateToTags = { navController.navigate(Screen.Tags.route) },
|
|
onNavigateToCollections = { navController.navigate(Screen.Collections.route) },
|
|
onNavigateToSettings = { navController.navigate(Screen.Settings.route) },
|
|
onNavigateToHelp = { navController.navigate(Screen.Help.route) },
|
|
onNavigateToDeadLinks = { navController.navigate(Screen.DeadLinks.route) },
|
|
onNavigateToPinned = { navController.navigate(Screen.Pinned.route) },
|
|
onNavigateToReader = { linkId ->
|
|
navController.navigate(Screen.Reader.createRoute(linkId))
|
|
},
|
|
onNavigateToReminders = { navController.navigate(Screen.Reminders.route) },
|
|
onNavigateToTodo = { navController.navigate(Screen.Todo.route) },
|
|
onNavigateToTodoDetail = { linkId -> navController.navigate(Screen.TodoDetail.createRoute(linkId)) },
|
|
onPlayAudio = onPlayAudio,
|
|
initialTagFilter = tag,
|
|
initialCollectionId = collectionId
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = "add?url={url}&title={title}&isShare={isShare}&isFileShare={isFileShare}&description={description}&tags={tags}",
|
|
arguments = listOf(
|
|
navArgument("url") {
|
|
type = NavType.StringType
|
|
defaultValue = ""
|
|
nullable = true
|
|
},
|
|
navArgument("title") {
|
|
type = NavType.StringType
|
|
defaultValue = ""
|
|
nullable = true
|
|
},
|
|
navArgument("isShare") {
|
|
type = NavType.BoolType
|
|
defaultValue = false
|
|
},
|
|
navArgument("isFileShare") {
|
|
type = NavType.BoolType
|
|
defaultValue = false
|
|
},
|
|
navArgument("description") {
|
|
type = NavType.StringType
|
|
defaultValue = ""
|
|
nullable = true
|
|
},
|
|
navArgument("tags") {
|
|
type = NavType.StringType
|
|
defaultValue = ""
|
|
nullable = true
|
|
}
|
|
),
|
|
deepLinks = listOf(
|
|
navDeepLink { uriPattern = "shaarit://add" }
|
|
)
|
|
) { backStackEntry ->
|
|
val isShare = backStackEntry.arguments?.getBoolean("isShare") ?: false
|
|
com.shaarit.presentation.add.AddLinkScreen(
|
|
onNavigateBack = { navController.popBackStack() },
|
|
onShareSuccess = if (isShare) {
|
|
{ (context as? Activity)?.finish() }
|
|
} else null
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = "edit/{linkId}",
|
|
arguments = listOf(
|
|
navArgument("linkId") {
|
|
type = NavType.IntType
|
|
}
|
|
)
|
|
) {
|
|
com.shaarit.presentation.edit.EditLinkScreen(
|
|
onNavigateBack = { navController.popBackStack() }
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = Screen.Tags.route,
|
|
deepLinks = listOf(
|
|
navDeepLink { uriPattern = "shaarit://tags" }
|
|
)
|
|
) {
|
|
com.shaarit.presentation.tags.TagsScreen(
|
|
onNavigateBack = { navController.popBackStack() },
|
|
onNavigateToFeedWithTag = { tag ->
|
|
navController.navigate(Screen.Feed.createRoute(tag)) {
|
|
popUpTo(Screen.Tags.route) { inclusive = true }
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = Screen.Collections.route,
|
|
deepLinks = listOf(
|
|
navDeepLink { uriPattern = "shaarit://collections" }
|
|
)
|
|
) {
|
|
com.shaarit.presentation.collections.CollectionsScreen(
|
|
onNavigateBack = { navController.popBackStack() },
|
|
onCollectionClick = { collectionId, isSmart, query ->
|
|
navController.navigate(
|
|
if (isSmart) {
|
|
Screen.Feed.createRoute(tag = query)
|
|
} else {
|
|
Screen.Feed.createRoute(collectionId = collectionId)
|
|
}
|
|
) {
|
|
popUpTo(Screen.Collections.route) { inclusive = true }
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = Screen.Dashboard.route,
|
|
deepLinks = listOf(
|
|
navDeepLink { uriPattern = "shaarit://dashboard" }
|
|
)
|
|
) {
|
|
com.shaarit.presentation.dashboard.DashboardScreen(
|
|
onNavigateBack = { navController.popBackStack() }
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = Screen.Settings.route
|
|
) {
|
|
com.shaarit.presentation.settings.SettingsScreen(
|
|
onNavigateBack = { navController.popBackStack() },
|
|
onNavigateToDashboard = { navController.navigate(Screen.Dashboard.route) }
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = Screen.Help.route,
|
|
deepLinks = listOf(
|
|
navDeepLink { uriPattern = "shaarit://help" }
|
|
)
|
|
) {
|
|
com.shaarit.presentation.help.HelpScreen(
|
|
onNavigateBack = { navController.popBackStack() }
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = Screen.DeadLinks.route
|
|
) {
|
|
com.shaarit.presentation.deadlinks.DeadLinksScreen(
|
|
onNavigateBack = { navController.popBackStack() },
|
|
onNavigateToEdit = { linkId ->
|
|
navController.navigate(Screen.Edit.createRoute(linkId))
|
|
}
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = Screen.Pinned.route
|
|
) {
|
|
com.shaarit.presentation.pinned.PinnedScreen(
|
|
onNavigateBack = { navController.popBackStack() },
|
|
onNavigateToEdit = { linkId ->
|
|
navController.navigate(Screen.Edit.createRoute(linkId))
|
|
}
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = "reader/{linkId}",
|
|
arguments = listOf(
|
|
navArgument("linkId") {
|
|
type = NavType.IntType
|
|
}
|
|
),
|
|
deepLinks = listOf(
|
|
navDeepLink { uriPattern = "shaarit://reader/{linkId}" }
|
|
)
|
|
) {
|
|
com.shaarit.presentation.reader.ReaderModeScreen(
|
|
onNavigateBack = { navController.popBackStack() }
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = Screen.Reminders.route
|
|
) {
|
|
com.shaarit.presentation.reminders.RemindersScreen(
|
|
onNavigateBack = { navController.popBackStack() },
|
|
onNavigateToReader = { linkId ->
|
|
navController.navigate(Screen.Reader.createRoute(linkId))
|
|
}
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = Screen.Todo.route,
|
|
deepLinks = listOf(
|
|
navDeepLink { uriPattern = "shaarit://todo" }
|
|
)
|
|
) {
|
|
com.shaarit.presentation.todo.TodoScreen(
|
|
onNavigateBack = { navController.popBackStack() }
|
|
)
|
|
}
|
|
|
|
composable(
|
|
route = Screen.TodoDetail.route,
|
|
arguments = listOf(
|
|
navArgument("linkId") {
|
|
type = NavType.IntType
|
|
}
|
|
)
|
|
) { backStackEntry ->
|
|
val linkId = backStackEntry.arguments?.getInt("linkId") ?: 0
|
|
com.shaarit.presentation.todo.TodoDetailScreen(
|
|
linkId = linkId,
|
|
onNavigateBack = { navController.popBackStack() }
|
|
)
|
|
}
|
|
}
|
|
}
|