Routes.kt (7107B) - raw
1 package me.rhunk.snapenhance.ui.manager 2 3 import androidx.compose.foundation.layout.RowScope 4 import androidx.compose.material.icons.Icons 5 import androidx.compose.material.icons.filled.* 6 import androidx.compose.runtime.Composable 7 import androidx.compose.ui.graphics.vector.ImageVector 8 import androidx.navigation.NavBackStackEntry 9 import androidx.navigation.NavController 10 import androidx.navigation.NavDestination.Companion.hierarchy 11 import androidx.navigation.NavGraph.Companion.findStartDestination 12 import androidx.navigation.NavGraphBuilder 13 import me.rhunk.snapenhance.RemoteSideContext 14 import me.rhunk.snapenhance.ui.manager.pages.FileImportsRoot 15 import me.rhunk.snapenhance.ui.manager.pages.LoggerHistoryRoot 16 import me.rhunk.snapenhance.ui.manager.pages.ManageReposSection 17 import me.rhunk.snapenhance.ui.manager.pages.TasksRootSection 18 import me.rhunk.snapenhance.ui.manager.pages.features.FeaturesRootSection 19 import me.rhunk.snapenhance.ui.manager.pages.features.ManageRuleFeature 20 import me.rhunk.snapenhance.ui.manager.pages.home.HomeLogs 21 import me.rhunk.snapenhance.ui.manager.pages.home.HomeRootSection 22 import me.rhunk.snapenhance.ui.manager.pages.home.HomeSettings 23 import me.rhunk.snapenhance.ui.manager.pages.location.BetterLocationRoot 24 import me.rhunk.snapenhance.ui.manager.pages.scripting.ScriptingRootSection 25 import me.rhunk.snapenhance.ui.manager.pages.social.LoggedStories 26 import me.rhunk.snapenhance.ui.manager.pages.social.ManageScope 27 import me.rhunk.snapenhance.ui.manager.pages.social.MessagingPreview 28 import me.rhunk.snapenhance.ui.manager.pages.social.SocialRootSection 29 import me.rhunk.snapenhance.ui.manager.pages.tracker.EditRule 30 import me.rhunk.snapenhance.ui.manager.pages.tracker.FriendTrackerManagerRoot 31 32 33 data class RouteInfo( 34 val id: String, 35 val key: String = id, 36 val icon: ImageVector = Icons.Default.Home, 37 val primary: Boolean = false, 38 val showInNavBar: Boolean = primary, 39 ) { 40 var translatedKey: Lazy<String?>? = null 41 val childIds = mutableListOf<String>() 42 } 43 44 @Suppress("unused", "MemberVisibilityCanBePrivate") 45 class Routes( 46 private val context: RemoteSideContext, 47 ) { 48 lateinit var navController: NavController 49 private val routes = mutableListOf<Route>() 50 51 val tasks = route(RouteInfo("tasks", icon = Icons.Default.TaskAlt, primary = true), TasksRootSection()) 52 53 val features = route(RouteInfo("features", icon = Icons.Default.Stars, primary = true), FeaturesRootSection()) 54 val manageRuleFeature = route(RouteInfo("manage_rule_feature/?rule_type={rule_type}"), ManageRuleFeature()).parent(features) 55 56 val home = route(RouteInfo("home", icon = Icons.Default.Home, primary = true), HomeRootSection()) 57 val settings = route(RouteInfo("home_settings"), HomeSettings()).parent(home) 58 val homeLogs = route(RouteInfo("home_logs"), HomeLogs()).parent(home) 59 val loggerHistory = route(RouteInfo("logger_history"), LoggerHistoryRoot()).parent(home) 60 val friendTracker = route(RouteInfo("friend_tracker"), FriendTrackerManagerRoot()).parent(home) 61 val editRule = route(RouteInfo("edit_rule/?rule_id={rule_id}"), EditRule()) 62 63 val fileImports = route(RouteInfo("file_imports"), FileImportsRoot()).parent(home) 64 val manageRepos = route(RouteInfo("manage_repos"), ManageReposSection()) 65 66 val social = route(RouteInfo("social", icon = Icons.Default.Group, primary = true), SocialRootSection()) 67 val manageScope = route(RouteInfo("manage_scope/?scope={scope}&id={id}"), ManageScope()).parent(social) 68 val messagingPreview = route(RouteInfo("messaging_preview/?scope={scope}&id={id}"), MessagingPreview()).parent(social) 69 val loggedStories = route(RouteInfo("logged_stories/?id={id}"), LoggedStories()).parent(social) 70 71 val scripting = route(RouteInfo("scripts", icon = Icons.Filled.DataObject, primary = true), ScriptingRootSection()) 72 73 val betterLocation = route(RouteInfo("better_location", showInNavBar = false, primary = true), BetterLocationRoot()) 74 75 open class Route { 76 open val init: () -> Unit = { } 77 open val title: @Composable (() -> Unit)? = null 78 open val topBarActions: @Composable RowScope.() -> Unit = {} 79 open val floatingActionButton: @Composable () -> Unit = {} 80 open val content: @Composable (NavBackStackEntry) -> Unit = {} 81 open val customComposables: NavGraphBuilder.() -> Unit = {} 82 83 var parentRoute: Route? = null 84 private set 85 86 lateinit var context: RemoteSideContext 87 lateinit var routeInfo: RouteInfo 88 lateinit var routes: Routes 89 90 val translation by lazy { context.translation.getCategory("manager.sections.${routeInfo.key.substringBefore("/")}")} 91 92 private fun replaceArguments(id: String, args: Map<String, String>) = args.takeIf { it.isNotEmpty() }?.let { 93 args.entries.fold(id) { acc, (key, value) -> 94 acc.replace("{$key}", value) 95 } 96 } ?: id 97 98 fun navigate(args: MutableMap<String, String>.() -> Unit = {}) { 99 routes.navController.navigate(replaceArguments(routeInfo.id, HashMap<String, String>().apply { args() })) 100 } 101 102 fun navigateReload() { 103 routes.navController.navigate(routeInfo.id) { 104 popUpTo(routeInfo.id) { inclusive = true } 105 } 106 } 107 108 fun navigateReset(args: MutableMap<String, String>.() -> Unit = {}) { 109 routes.navController.navigate(replaceArguments(routeInfo.id, HashMap<String, String>().apply { args() })) { 110 popUpTo(routes.navController.graph.findStartDestination().id) { 111 saveState = true 112 } 113 launchSingleTop = true 114 restoreState = true 115 } 116 } 117 118 fun parent(route: Route): Route { 119 assert(route.routeInfo.primary) { "Parent route must be a primary route" } 120 parentRoute = route 121 return this 122 } 123 } 124 125 val currentRoute: Route? 126 get() = routes.firstOrNull { route -> 127 navController.currentBackStackEntry?.destination?.hierarchy?.any { it.route == route.routeInfo.id } ?: false 128 } 129 130 val currentDestination: String? 131 get() = navController.currentBackStackEntry?.destination?.route 132 133 fun getCurrentRoute(navBackStackEntry: NavBackStackEntry?): Route? { 134 if (navBackStackEntry == null) return null 135 136 return navBackStackEntry.destination.hierarchy.firstNotNullOfOrNull { destination -> 137 routes.firstOrNull { route -> 138 route.routeInfo.id == destination.route || route.routeInfo.childIds.contains(destination.route) 139 } 140 } 141 } 142 143 fun getRoutes(): List<Route> = routes 144 145 private fun route(routeInfo: RouteInfo, route: Route): Route { 146 route.apply { 147 this.routeInfo = routeInfo 148 routes = this@Routes 149 context = this@Routes.context 150 this.routeInfo.translatedKey = lazy { context.translation.getOrNull("manager.routes.${route.routeInfo.key.substringBefore("/")}") } 151 } 152 routes.add(route) 153 return route 154 } 155 }