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