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 }