diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/MainActivity.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/MainActivity.kt
index 9bfc7b9f2aa9078ac1c1085b7d7a631c207c9fe9..81481f33e2cd0d022cf0e8ffa70a22eaf9686357 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/MainActivity.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/MainActivity.kt
@@ -44,7 +44,7 @@ class MainActivity : ComponentActivity() {
     val viewModel = viewModelProvider.get<AppViewModel>()
 
     installSplashScreen().setKeepOnScreenCondition {
-      viewModel.initState.value == AppViewModel.InitState.LOADING
+      viewModel.initialRoute.value == null
     }
 
     setContent {
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/sync/AccountProvider.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/sync/AccountProvider.kt
index 66c5b7311559271812d079b656dacc1bd8c0973b..c445d74af5d3245e4f930df4652e7273e2a48997 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/sync/AccountProvider.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/sync/AccountProvider.kt
@@ -31,40 +31,22 @@ import de.chaosdorf.meteroid.model.PinnedUserRepository
 import de.chaosdorf.meteroid.model.ServerId
 import de.chaosdorf.meteroid.model.ServerRepository
 import de.chaosdorf.meteroid.model.UserRepository
-import de.chaosdorf.meteroid.storage.AccountPreferences
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
 import javax.inject.Inject
 
 class AccountProvider @Inject constructor(
-  accountPreferences: AccountPreferences,
-  serverRepository: ServerRepository,
-  userRepository: UserRepository,
+  private val serverRepository: ServerRepository,
+  private val userRepository: UserRepository,
   private val pinnedUserRepository: PinnedUserRepository,
 ) {
-  val account: Flow<AccountInfo?> =
-    accountPreferences.state.flatMapLatest { preferences ->
-      if (preferences.server == null) {
-        flowOf(null)
-      } else {
-        serverRepository.getFlow(preferences.server).flatMapLatest { server ->
-          if (server == null) {
-            flowOf(null)
-          } else if (preferences.user == null) {
-            flowOf(AccountInfo(server, null, false))
-          } else {
-            combine(
-              userRepository.getFlow(server.serverId, preferences.user),
-              pinnedUserRepository.isPinnedFlow(server.serverId, preferences.user)
-            ) { user, pinned ->
-              AccountInfo(server, user, pinned)
-            }
-          }
-        }
-      }
-    }
+  fun accountFlow(serverId: ServerId, userId: UserId) = combine(
+    serverRepository.getFlow(serverId),
+    userRepository.getFlow(serverId, userId),
+    pinnedUserRepository.isPinnedFlow(serverId, userId)
+  ) { server, user, pinned ->
+    if (server == null || user == null) null
+    else AccountInfo(server, user, pinned)
+  }
 
   suspend fun togglePin(serverId: ServerId, userId: UserId) {
     if (pinnedUserRepository.isPinned(serverId, userId)) {
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/sync/SyncManager.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/sync/SyncManager.kt
index 5e9a4e3bc5e9580836764ed5713408abdf735a4c..e548fc19a3ad471109e264d6e2f5bac0dc6f2ecc 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/sync/SyncManager.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/sync/SyncManager.kt
@@ -32,8 +32,6 @@ import de.chaosdorf.meteroid.model.Server
 import de.chaosdorf.meteroid.model.ServerRepository
 import de.chaosdorf.meteroid.model.User
 import de.chaosdorf.meteroid.util.newServer
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
 import java.math.BigDecimal
 import javax.inject.Inject
 
@@ -75,50 +73,44 @@ class SyncManager @Inject constructor(
   }
 
   suspend fun purchase(account: AccountInfo, drink: Drink) {
-    account.user?.let { user ->
-      val api = factory.newInstance(account.server.url)
-      try {
-        api.purchase(user.userId, drink.drinkId)
-        sync(account.server, user, incremental = true)
-      } catch (e: Exception) {
-        Log.e(
-          "Sync",
-          "Could not finish transaction for ${user.name} (${user.userId}) on ${account.server.url}",
-          e
-        )
-      }
+    val api = factory.newInstance(account.server.url)
+    try {
+      api.purchase(account.user.userId, drink.drinkId)
+      sync(account.server, account.user, incremental = true)
+    } catch (e: Exception) {
+      Log.e(
+        "Sync",
+        "Could not finish transaction for ${account.user.name} (${account.user.userId}) on ${account.server.url}",
+        e
+      )
     }
   }
 
   suspend fun deposit(account: AccountInfo, amount: BigDecimal) {
-    account.user?.let { user ->
-      try {
-        val api = factory.newInstance(account.server.url)
-        api.deposit(user.userId, amount)
-        sync(account.server, user, incremental = true)
-      } catch (e: Exception) {
-        Log.e(
-          "Sync",
-          "Could not finish transaction for ${user.name} (${user.userId}) on ${account.server.url}",
-          e
-        )
-      }
+    try {
+      val api = factory.newInstance(account.server.url)
+      api.deposit(account.user.userId, amount)
+      sync(account.server, account.user, incremental = true)
+    } catch (e: Exception) {
+      Log.e(
+        "Sync",
+        "Could not finish transaction for ${account.user.name} (${account.user.userId}) on ${account.server.url}",
+        e
+      )
     }
   }
 
   suspend fun withdraw(account: AccountInfo, amount: BigDecimal) {
-    account.user?.let { user ->
-      val api = factory.newInstance(account.server.url)
-      try {
-        api.withdraw(user.userId, amount)
-        sync(account.server, user, incremental = true)
-      } catch (e: Exception) {
-        Log.e(
-          "Sync",
-          "Could not finish transaction for ${user.name} (${user.userId}) on ${account.server.url}",
-          e
-        )
-      }
+    val api = factory.newInstance(account.server.url)
+    try {
+      api.withdraw(account.user.userId, amount)
+      sync(account.server, account.user, incremental = true)
+    } catch (e: Exception) {
+      Log.e(
+        "Sync",
+        "Could not finish transaction for ${account.user.name} (${account.user.userId}) on ${account.server.url}",
+        e
+      )
     }
   }
 }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/AppRouter.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/AppRouter.kt
index b450fb48a889fa0b592c9574262c9ca238e6456a..b00553bc098a7e1d0a9971286ceed26421ef7e92 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/AppRouter.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/AppRouter.kt
@@ -24,133 +24,126 @@
 
 package de.chaosdorf.meteroid.ui
 
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
 import androidx.hilt.navigation.compose.hiltViewModel
-import androidx.lifecycle.viewmodel.compose.viewModel
-import androidx.navigation.NavOptions
+import androidx.navigation.NavType
 import androidx.navigation.compose.NavHost
 import androidx.navigation.compose.composable
-import androidx.navigation.compose.navigation
 import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navArgument
 import de.chaosdorf.meteroid.ui.drinks.DrinkListScreen
 import de.chaosdorf.meteroid.ui.drinks.DrinkListViewModel
 import de.chaosdorf.meteroid.ui.money.MoneyListScreen
 import de.chaosdorf.meteroid.ui.money.MoneyListViewModel
+import de.chaosdorf.meteroid.ui.navigation.MeteroidBottomBar
+import de.chaosdorf.meteroid.ui.navigation.MeteroidTopBar
 import de.chaosdorf.meteroid.ui.navigation.Routes
 import de.chaosdorf.meteroid.ui.servers.AddServerScreen
+import de.chaosdorf.meteroid.ui.servers.AddServerViewModel
 import de.chaosdorf.meteroid.ui.servers.ServerListScreen
+import de.chaosdorf.meteroid.ui.servers.ServerListViewModel
 import de.chaosdorf.meteroid.ui.transactions.TransactionListScreen
 import de.chaosdorf.meteroid.ui.transactions.TransactionViewModel
 import de.chaosdorf.meteroid.ui.users.UserListScreen
+import de.chaosdorf.meteroid.ui.users.UserListViewModel
 import de.chaosdorf.meteroid.ui.wrapped.WrappedScreen
 import de.chaosdorf.meteroid.ui.wrapped.WrappedViewModel
-import kotlinx.coroutines.launch
 
 @Composable
 fun AppRouter(viewModel: AppViewModel) {
-  val scope = rememberCoroutineScope()
   val navController = rememberNavController()
-  val initState by viewModel.initState.collectAsState()
+  val initialRoute by viewModel.initialRoute.collectAsState()
 
-  LaunchedEffect(initState) {
-    when (initState) {
-      AppViewModel.InitState.CREATE_SERVER -> navController.navigate(
-        Routes.Servers.Add,
-        NavOptions.Builder().setPopUpTo(Routes.Servers.Add, true).build()
-      )
+  LaunchedEffect(initialRoute) {
+    initialRoute?.let {
+      navController.navigate(it)
+    }
+  }
 
-      AppViewModel.InitState.SELECT_SERVER -> navController.navigate(
-        Routes.Servers.List,
-        NavOptions.Builder().setPopUpTo(Routes.Servers.List, true).build()
+  NavHost(navController, startDestination = Routes.Servers.List) {
+    composable(Routes.Servers.Add) { _ ->
+      val hiltViewModel = hiltViewModel<AddServerViewModel>()
+      AddServerScreen(navController, hiltViewModel)
+    }
+    composable(Routes.Servers.List) { _ ->
+      val hiltViewModel = hiltViewModel<ServerListViewModel>()
+      val navigationViewModel = hiltViewModel<NavigationViewModel>()
+      val topBar = @Composable { MeteroidTopBar(navController, navigationViewModel) }
+      ServerListScreen(navController, hiltViewModel, topBar)
+    }
+    /*
+    composable(Routes.Users.Add) { _ ->
+      AddUserScreen(
+        hiltViewModel(),
+        onAdd = { navController.navigate(Routes.Users.List) }
       )
-
-      AppViewModel.InitState.SELECT_USER -> navController.navigate(
-        Routes.Users.List,
-        NavOptions.Builder().setPopUpTo(Routes.Users.List, true).build()
+    }
+     */
+    composable(
+      Routes.Users.List,
+      arguments = listOf(
+        navArgument("server") { type = NavType.LongType }
       )
-
-      AppViewModel.InitState.HOME -> navController.navigate(
-        Routes.Home.Root,
-        NavOptions.Builder().setPopUpTo(Routes.Home.Root, true).build()
+    ) { _ ->
+      val hiltViewModel = hiltViewModel<UserListViewModel>()
+      val navigationViewModel = hiltViewModel<NavigationViewModel>()
+      val topBar = @Composable { MeteroidTopBar(navController, navigationViewModel) }
+      UserListScreen(navController, hiltViewModel, topBar)
+    }
+    composable(
+      Routes.Home.Purchase,
+      arguments = listOf(
+        navArgument("server") { type = NavType.LongType },
+        navArgument("user") { type = NavType.LongType },
       )
-      else -> Unit
+    ) { _ ->
+      val hiltViewModel = hiltViewModel<DrinkListViewModel>()
+      val navigationViewModel = hiltViewModel<NavigationViewModel>()
+      val topBar = @Composable { MeteroidTopBar(navController, navigationViewModel) }
+      val bottomBar = @Composable { MeteroidBottomBar(navController, navigationViewModel) }
+      DrinkListScreen(navController, hiltViewModel, topBar, bottomBar)
     }
-  }
-
-  NavHost(navController, startDestination = Routes.Servers.Root) {
-    navigation(route = Routes.Servers.Root, startDestination = Routes.Servers.List) {
-      composable(Routes.Servers.List) { _ ->
-        ServerListScreen(
-          hiltViewModel(),
-          onAdd = { navController.navigate(Routes.Servers.Add) },
-          onSelect = {
-            scope.launch {
-              viewModel.selectServer(it)
-              navController.navigate(Routes.Users.List)
-            }
-          }
-        )
-      }
-      composable(Routes.Servers.Add) { _ ->
-        AddServerScreen(
-          hiltViewModel(),
-          isFirst = initState == AppViewModel.InitState.CREATE_SERVER,
-          onBack = { navController.navigate(Routes.Servers.List) },
-          onAdd = { navController.navigate(Routes.Servers.List) }
-        )
-      }
+    composable(
+      Routes.Home.Deposit,
+      arguments = listOf(
+        navArgument("server") { type = NavType.LongType },
+        navArgument("user") { type = NavType.LongType },
+      )
+    ) { _ ->
+      val hiltViewModel = hiltViewModel<MoneyListViewModel>()
+      val navigationViewModel = hiltViewModel<NavigationViewModel>()
+      val topBar = @Composable { MeteroidTopBar(navController, navigationViewModel) }
+      val bottomBar = @Composable { MeteroidBottomBar(navController, navigationViewModel) }
+      MoneyListScreen(navController, hiltViewModel, topBar, bottomBar)
     }
-    navigation(route = Routes.Users.Root, startDestination = Routes.Users.List) {
-      composable(Routes.Users.List) { _ ->
-        UserListScreen(
-          hiltViewModel(),
-          onAdd = { TODO() },
-          onSelect = {
-            scope.launch {
-              viewModel.selectUser(it)
-              navController.navigate(Routes.Home.Root)
-            }
-          },
-          onBack = { navController.navigate(Routes.Servers.Root) },
-        )
-      }
-      /*
-      composable(Routes.Users.Add) { _ ->
-        AddUserScreen(
-          hiltViewModel(),
-          onAdd = { navController.navigate(Routes.Users.List) }
-        )
-      }
-       */
+    composable(
+      Routes.Home.History,
+      arguments = listOf(
+        navArgument("server") { type = NavType.LongType },
+        navArgument("user") { type = NavType.LongType },
+      )
+    ) { _ ->
+      val hiltViewModel = hiltViewModel<TransactionViewModel>()
+      val navigationViewModel = hiltViewModel<NavigationViewModel>()
+      val topBar = @Composable { MeteroidTopBar(navController, navigationViewModel) }
+      val bottomBar = @Composable { MeteroidBottomBar(navController, navigationViewModel) }
+      TransactionListScreen(hiltViewModel, topBar, bottomBar)
     }
-    navigation(route = Routes.Home.Root, startDestination = Routes.Home.Purchase) {
-      composable(Routes.Home.Purchase) { _ ->
-        val drinkListViewModel = hiltViewModel<DrinkListViewModel>()
-        DrinkListScreen(drinkListViewModel, navController::navigate)
-      }
-      composable(Routes.Home.Deposit) { _ ->
-        val moneyListViewModel = hiltViewModel<MoneyListViewModel>()
-        MoneyListScreen(moneyListViewModel, navController::navigate)
-      }
-      composable(Routes.Home.History) { _ ->
-        val transactionViewModel = hiltViewModel<TransactionViewModel>()
-        TransactionListScreen(transactionViewModel, navController::navigate)
-      }
-      composable(Routes.Home.Wrapped) { _ ->
-        val wrappedViewModel = hiltViewModel<WrappedViewModel>()
-        WrappedScreen(wrappedViewModel, navController::navigate)
-      }
+    composable(
+      Routes.Home.Wrapped,
+      arguments = listOf(
+        navArgument("server") { type = NavType.LongType },
+        navArgument("user") { type = NavType.LongType },
+      )
+    ) { _ ->
+      val hiltViewModel = hiltViewModel<WrappedViewModel>()
+      val navigationViewModel = hiltViewModel<NavigationViewModel>()
+      val topBar = @Composable { MeteroidTopBar(navController, navigationViewModel) }
+      val bottomBar = @Composable { MeteroidBottomBar(navController, navigationViewModel) }
+      WrappedScreen(hiltViewModel, topBar, bottomBar)
     }
   }
 }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/AppViewModel.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/AppViewModel.kt
index 8e0cf09af985c05ee25db0f6a43f05870e838ca7..b46536651cfc3ce91758bc124d54ba188651af97 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/AppViewModel.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/AppViewModel.kt
@@ -28,48 +28,43 @@ import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import dagger.hilt.android.lifecycle.HiltViewModel
 import de.chaosdorf.mete.model.UserId
-import de.chaosdorf.meteroid.model.Server
 import de.chaosdorf.meteroid.model.ServerId
 import de.chaosdorf.meteroid.model.ServerRepository
+import de.chaosdorf.meteroid.model.UserRepository
 import de.chaosdorf.meteroid.storage.AccountPreferences
-import de.chaosdorf.meteroid.sync.AccountProvider
-import de.chaosdorf.meteroid.sync.SyncManager
+import de.chaosdorf.meteroid.ui.navigation.Routes
 import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
 import javax.inject.Inject
 
 @HiltViewModel
 class AppViewModel @Inject constructor(
-  accountProvider: AccountProvider,
   private val accountPreferences: AccountPreferences,
   private val serverRepository: ServerRepository,
-  private val syncManager: SyncManager
+  private val userRepository: UserRepository
 ) : ViewModel() {
-  val initState: StateFlow<InitState> = accountPreferences.state
-    .flatMapLatest { preferences ->
-      serverRepository.getAllFlow()
-        .mapLatest { it.map(Server::serverId) }
-        .mapLatest { servers ->
-          if (servers.isEmpty()) InitState.CREATE_SERVER
-          else if (!servers.contains(preferences.server)) InitState.SELECT_SERVER
-          else if (preferences.user == null) InitState.SELECT_USER
-          else InitState.HOME
-        }
-    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), InitState.LOADING)
-
-  init {
-    accountProvider.account.distinctUntilChanged().onEach { account ->
-      account?.let { (server, maybeUser) ->
-        syncManager.sync(server, maybeUser, incremental = true)
+  val initialRoute = accountPreferences.state.flatMapLatest {
+    combine(
+      serverRepository.countFlow(),
+      if (it.server == null) flowOf(null)
+      else serverRepository.getFlow(it.server),
+      if (it.server == null || it.user == null) flowOf(null)
+      else userRepository.getFlow(it.server, it.user)
+    ) { serverCount, server, user ->
+      if (user != null) {
+        Routes.Home.purchase(user.serverId, user.userId)
+      } else if (server != null) {
+        Routes.Users.list(server.serverId)
+      } else if (serverCount > 0) {
+        Routes.Servers.List
+      } else {
+        Routes.Servers.Add
       }
-    }.launchIn(viewModelScope)
-  }
+    }
+  }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
 
   suspend fun selectServer(server: ServerId) {
     accountPreferences.setServer(server)
@@ -79,12 +74,4 @@ class AppViewModel @Inject constructor(
   suspend fun selectUser(user: UserId) {
     accountPreferences.setUser(user)
   }
-
-  enum class InitState {
-    LOADING,
-    CREATE_SERVER,
-    SELECT_SERVER,
-    SELECT_USER,
-    HOME
-  }
 }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/NavigationViewModel.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/NavigationViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..77537ef8ef33000281d5306e049b85b6530d4e12
--- /dev/null
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/NavigationViewModel.kt
@@ -0,0 +1,88 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2023 Chaosdorf e.V.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package de.chaosdorf.meteroid.ui
+
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import dagger.hilt.android.lifecycle.HiltViewModel
+import de.chaosdorf.mete.model.UserId
+import de.chaosdorf.meteroid.model.PinnedUserRepository
+import de.chaosdorf.meteroid.model.ServerId
+import de.chaosdorf.meteroid.model.ServerRepository
+import de.chaosdorf.meteroid.model.UserRepository
+import de.chaosdorf.meteroid.sync.AccountProvider
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class NavigationViewModel @Inject constructor(
+  savedStateHandle: SavedStateHandle,
+  serverRepository: ServerRepository,
+  userRepository: UserRepository,
+  pinnedUserRepository: PinnedUserRepository,
+  private val accountProvider: AccountProvider
+) : ViewModel() {
+  private val serverId = savedStateHandle.getStateFlow<Long?>("server", null)
+    .mapLatest { it?.let(::ServerId) }
+    .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
+
+  private val userId = savedStateHandle.getStateFlow<Long?>("user", null)
+    .mapLatest { it?.let(::UserId) }
+    .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
+
+  val server = serverId.flatMapLatest {
+    if (it == null) flowOf(null)
+    else serverRepository.getFlow(it)
+  }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
+
+  val user = combine(serverId, userId) { serverId, userId ->
+    if (serverId == null || userId == null) null
+    else Pair(serverId, userId)
+  }.flatMapLatest {
+    if (it == null) flowOf(null)
+    else userRepository.getFlow(it.first, it.second)
+  }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
+
+  val pinned = combine(serverId, userId) { serverId, userId ->
+    if (serverId == null || userId == null) null
+    else Pair(serverId, userId)
+  }.flatMapLatest {
+    if (it == null) flowOf(null)
+    else pinnedUserRepository.isPinnedFlow(it.first, it.second)
+  }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
+
+  fun togglePin(serverId: ServerId, userId: UserId) {
+    viewModelScope.launch {
+      accountProvider.togglePin(serverId, userId)
+    }
+  }
+}
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/drinks/DrinkListScreen.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/drinks/DrinkListScreen.kt
index 73332041878de24c291c4343ed23f66cdfa85e4f..d454baf878b217afe8aa4e4ce61295550150b20e 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/drinks/DrinkListScreen.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/drinks/DrinkListScreen.kt
@@ -44,30 +44,16 @@ import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
-import androidx.navigation.NavOptions
-import de.chaosdorf.meteroid.ui.navigation.HomeSections
-import de.chaosdorf.meteroid.ui.navigation.MeteroidBottomBar
-import de.chaosdorf.meteroid.ui.navigation.MeteroidTopBar
-import de.chaosdorf.meteroid.ui.navigation.Routes
-import kotlinx.datetime.Clock
-import kotlinx.datetime.TimeZone
-import kotlinx.datetime.toLocalDateTime
-import java.time.Month
+import androidx.navigation.NavController
 
 @OptIn(ExperimentalLayoutApi::class)
 @Composable
 fun DrinkListScreen(
+  navController: NavController,
   viewModel: DrinkListViewModel,
-  onNavigate: (String, NavOptions) -> Unit
+  topBar: @Composable () -> Unit,
+  bottomBar: @Composable () -> Unit,
 ) {
-  val onBack = remember {
-    {
-      onNavigate(
-        Routes.Users.List,
-        NavOptions.Builder().setPopUpTo(Routes.Users.List, false).build()
-      )
-    }
-  }
   val account by viewModel.account.collectAsState()
   val drinks by viewModel.drinks.collectAsState()
   val filters by viewModel.filters.collectAsState()
@@ -85,16 +71,8 @@ fun DrinkListScreen(
   }
 
   Scaffold(
-    topBar = { MeteroidTopBar(account, onNavigate, viewModel::togglePin) },
-    bottomBar = {
-      MeteroidBottomBar(
-        currentRoute = HomeSections.PURCHASE,
-        historyEnabled = account?.user?.audit == true,
-        wrappedEnabled = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
-          .month.let { it == Month.NOVEMBER || it == Month.DECEMBER },
-        navigateTo = onNavigate
-      )
-    },
+    topBar = topBar,
+    bottomBar = bottomBar,
     snackbarHost = {
       SnackbarHost(hostState = snackbarHostState)
     }
@@ -120,7 +98,9 @@ fun DrinkListScreen(
         modifier = Modifier.padding(horizontal = 8.dp)
       ) {
         items(drinks) { drink ->
-          DrinkTile(drink) { viewModel.purchase(it, onBack) }
+          DrinkTile(drink) {
+            viewModel.purchase(it, navController::navigateUp)
+          }
         }
       }
     }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/drinks/DrinkListViewModel.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/drinks/DrinkListViewModel.kt
index 52c751eb9fed226b7f244be5532bb4552f5aaa5d..470a91e7c9173ad630f12bbd52ea7bd1ecf673d8 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/drinks/DrinkListViewModel.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/drinks/DrinkListViewModel.kt
@@ -28,41 +28,39 @@ import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import dagger.hilt.android.lifecycle.HiltViewModel
-import de.chaosdorf.meteroid.model.AccountInfo
+import de.chaosdorf.mete.model.UserId
 import de.chaosdorf.meteroid.model.Drink
 import de.chaosdorf.meteroid.model.DrinkRepository
 import de.chaosdorf.meteroid.model.Server
+import de.chaosdorf.meteroid.model.ServerId
 import de.chaosdorf.meteroid.sync.AccountProvider
 import de.chaosdorf.meteroid.sync.SyncManager
 import de.chaosdorf.meteroid.util.update
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import javax.inject.Inject
 
 @HiltViewModel
 class DrinkListViewModel @Inject constructor(
-  private val accountProvider: AccountProvider,
-  repository: DrinkRepository,
+  private val savedStateHandle: SavedStateHandle,
+  accountProvider: AccountProvider,
   private val syncManager: SyncManager,
-  private val savedStateHandle: SavedStateHandle
+  drinkRepository: DrinkRepository,
 ) : ViewModel() {
-  val account: StateFlow<AccountInfo?> = accountProvider.account
+  private val serverId = ServerId(checkNotNull(savedStateHandle["server"]))
+  private val userId = UserId(checkNotNull(savedStateHandle["user"]))
+
+  val account = accountProvider.accountFlow(serverId, userId)
     .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
 
   val filters: StateFlow<Set<Filter>> =
     savedStateHandle.getStateFlow("filters", setOf(Filter.Active))
 
   val drinks: StateFlow<List<Drink>> = combine(
-    accountProvider.account.flatMapLatest { account ->
-      account?.let { (server, _) ->
-        repository.getAllFlow(server.serverId)
-      } ?: flowOf(emptyList())
-    },
+    drinkRepository.getAllFlow(serverId),
     filters
   ) { drinks, filters ->
     drinks.filter { item ->
@@ -88,20 +86,10 @@ class DrinkListViewModel @Inject constructor(
     }
   }
 
-  fun togglePin() {
-    account.value?.let { account ->
-      account.user?.let { user ->
-        viewModelScope.launch {
-          accountProvider.togglePin(account.server.serverId, user.userId)
-        }
-      }
-    }
-  }
-
   fun sync() {
-    account.value?.let { (server, user) ->
+    account.value?.let { account ->
       viewModelScope.launch {
-        syncManager.sync(server, user, incremental = true)
+        syncManager.sync(account.server, account.user, incremental = true)
       }
     }
   }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/money/MoneyListScreen.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/money/MoneyListScreen.kt
index c6e9dfc4e96a5a74af47b94152e9d7951a888a17..b7df7270b41aea010039266cb59c279261b13b42 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/money/MoneyListScreen.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/money/MoneyListScreen.kt
@@ -42,29 +42,15 @@ import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
-import androidx.navigation.NavOptions
-import de.chaosdorf.meteroid.ui.navigation.HomeSections
-import de.chaosdorf.meteroid.ui.navigation.MeteroidBottomBar
-import de.chaosdorf.meteroid.ui.navigation.MeteroidTopBar
-import de.chaosdorf.meteroid.ui.navigation.Routes
-import kotlinx.datetime.Clock
-import kotlinx.datetime.TimeZone
-import kotlinx.datetime.toLocalDateTime
-import java.time.Month
+import androidx.navigation.NavController
 
 @Composable
 fun MoneyListScreen(
+  navController: NavController,
   viewModel: MoneyListViewModel,
-  onNavigate: (String, NavOptions) -> Unit
+  topBar: @Composable () -> Unit,
+  bottomBar: @Composable () -> Unit,
 ) {
-  val onBack = remember {
-    {
-      onNavigate(
-        Routes.Users.List,
-        NavOptions.Builder().setPopUpTo(Routes.Users.List, false).build()
-      )
-    }
-  }
   val account by viewModel.account.collectAsState()
   val snackbarHostState = remember { SnackbarHostState() }
 
@@ -80,16 +66,8 @@ fun MoneyListScreen(
   }
 
   Scaffold(
-    topBar = { MeteroidTopBar(account, onNavigate, viewModel::togglePin) },
-    bottomBar = {
-      MeteroidBottomBar(
-        currentRoute = HomeSections.DEPOSIT,
-        historyEnabled = account?.user?.audit == true,
-        wrappedEnabled = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
-          .month.let { it == Month.NOVEMBER || it == Month.DECEMBER },
-        navigateTo = onNavigate
-      )
-    },
+    topBar = topBar,
+    bottomBar = bottomBar,
     snackbarHost = {
       SnackbarHost(hostState = snackbarHostState)
     }
@@ -102,7 +80,9 @@ fun MoneyListScreen(
         horizontalArrangement = Arrangement.SpaceBetween,
       ) {
         items(viewModel.money) { monetaryAmount ->
-          MoneyTile(monetaryAmount) { viewModel.deposit(it, onBack) }
+          MoneyTile(monetaryAmount) {
+            viewModel.deposit(it, navController::navigateUp)
+          }
         }
       }
     }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/money/MoneyListViewModel.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/money/MoneyListViewModel.kt
index eb94fb658b9382521196e769c439b7eb52c37aca..cd405b270814e68e4cfdf65039584e485b691b3b 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/money/MoneyListViewModel.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/money/MoneyListViewModel.kt
@@ -25,16 +25,17 @@
 package de.chaosdorf.meteroid.ui.money
 
 import androidx.annotation.DrawableRes
+import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import dagger.hilt.android.lifecycle.HiltViewModel
+import de.chaosdorf.mete.model.UserId
 import de.chaosdorf.meteroid.R
-import de.chaosdorf.meteroid.model.AccountInfo
 import de.chaosdorf.meteroid.model.Server
+import de.chaosdorf.meteroid.model.ServerId
 import de.chaosdorf.meteroid.sync.AccountProvider
 import de.chaosdorf.meteroid.sync.SyncManager
 import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import java.math.BigDecimal
@@ -52,10 +53,14 @@ enum class MonetaryAmount(val amount: BigDecimal, @DrawableRes val image: Int) {
 
 @HiltViewModel
 class MoneyListViewModel @Inject constructor(
+  savedStateHandle: SavedStateHandle,
   private val accountProvider: AccountProvider,
-  private val syncManager: SyncManager
+  private val syncManager: SyncManager,
 ) : ViewModel() {
-  val account: StateFlow<AccountInfo?> = accountProvider.account
+  private val serverId = ServerId(checkNotNull(savedStateHandle["server"]))
+  private val userId = UserId(checkNotNull(savedStateHandle["user"]))
+
+  val account = accountProvider.accountFlow(serverId, userId)
     .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
 
   val money: List<MonetaryAmount> = MonetaryAmount.entries
@@ -72,12 +77,8 @@ class MoneyListViewModel @Inject constructor(
   }
 
   fun togglePin() {
-    account.value?.let { account ->
-      account.user?.let { user ->
-        viewModelScope.launch {
-          accountProvider.togglePin(account.server.serverId, user.userId)
-        }
-      }
+    viewModelScope.launch {
+      accountProvider.togglePin(serverId, userId)
     }
   }
 
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidBottomBar.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidBottomBar.kt
index 760314f3b1ddb5c5684d1c73e526fcb667739afb..9710b66eb6a44abe119eb386f6dd4e5b5710a5ad 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidBottomBar.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidBottomBar.kt
@@ -29,38 +29,63 @@ import androidx.compose.material3.NavigationBar
 import androidx.compose.material3.NavigationBarItem
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.navigation.NavOptions
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.navigation.NavController
+import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.compose.currentBackStackEntryAsState
+import de.chaosdorf.meteroid.ui.NavigationViewModel
+import kotlinx.datetime.Clock
+import kotlinx.datetime.TimeZone
+import kotlinx.datetime.toLocalDateTime
+import java.time.Month
 
 @Composable
-fun <T : MeteroidNavSection> MeteroidBottomBar(
-  currentRoute: T,
-  navigateTo: (String, NavOptions) -> Unit,
-  historyEnabled: Boolean,
-  wrappedEnabled: Boolean,
-  modifier: Modifier = Modifier
+fun MeteroidBottomBar(
+  navController: NavController,
+  viewModel: NavigationViewModel
 ) {
+  val user by viewModel.user.collectAsState()
+  val historyEnabled = user?.audit == true
+  val wrappedEnabled = Clock.System.now()
+    .toLocalDateTime(TimeZone.currentSystemDefault())
+    .month.let { it == Month.NOVEMBER || it == Month.DECEMBER }
+  val navBackStackEntry by navController.currentBackStackEntryAsState()
+  val currentDestination = navBackStackEntry?.destination
+  val activeRoute = if (user == null) HomeSections.PURCHASE
+  else HomeSections.entries.find {
+    it.withArguments(
+      "server" to user!!.serverId.value.toString(),
+      "user" to user!!.userId.value.toString()
+    ) == currentDestination?.route
+  }
+
   NavigationBar {
     for (route in HomeSections.entries) {
       if (wrappedEnabled || route != HomeSections.WRAPPED) {
         NavigationBarItem(
           icon = {
             Icon(
-              if (route == currentRoute) route.iconActive else route.icon,
+              if (route == activeRoute) route.iconActive else route.icon,
               contentDescription = route.title
             )
           },
           label = { Text(route.title) },
-          selected = route == currentRoute,
+          selected = route == activeRoute,
           onClick = {
-            navigateTo(
-              route.route,
-              NavOptions.Builder()
-                .setPopUpTo(Routes.Home.Root, true)
-                .build()
-            )
+            navController.navigate(
+              route.withArguments(
+                "server" to user!!.serverId.value.toString(),
+                "user" to user!!.userId.value.toString()
+              )
+            ) {
+              popUpTo(navController.graph.findStartDestination().id) {
+                saveState = true
+              }
+              launchSingleTop = true
+              restoreState = true
+            }
           },
-          modifier = modifier,
           enabled = route != HomeSections.HISTORY || historyEnabled
         )
       }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidNavSection.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidNavSection.kt
index d56b347cb8b721e072530335fbc2ec6773f331ee..218a3d82fb12f791ec61fc8eff1cb7c9ffa1c4b8 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidNavSection.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidNavSection.kt
@@ -31,4 +31,12 @@ interface MeteroidNavSection {
   val icon: ImageVector
   val iconActive: ImageVector
   val route: String
+
+  fun withArguments(vararg args: Pair<String, String>): String {
+    var result = route
+    for ((parameter, value) in args) {
+      result = result.replace("{$parameter}", value)
+    }
+    return result
+  }
 }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidTopBar.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidTopBar.kt
index f0b6bc200d1ea6e201c7b74fe0c9ff3a88ff1798..41abc77b435a1598163f41415baeb3690367d3d9 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidTopBar.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/MeteroidTopBar.kt
@@ -42,6 +42,8 @@ import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
@@ -49,31 +51,32 @@ import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
-import androidx.navigation.NavOptions
+import androidx.navigation.NavController
 import coil.compose.AsyncImage
-import de.chaosdorf.meteroid.model.AccountInfo
 import de.chaosdorf.meteroid.ui.PriceBadge
+import de.chaosdorf.meteroid.ui.NavigationViewModel
 import okhttp3.HttpUrl.Companion.toHttpUrl
 
 @Composable
 fun MeteroidTopBar(
-  account: AccountInfo?,
-  onNavigate: (String, NavOptions) -> Unit,
-  onTogglePin: () -> Unit
+  navController: NavController,
+  viewModel: NavigationViewModel
 ) {
+  val server by viewModel.server.collectAsState()
+  val user by viewModel.user.collectAsState()
+  val pinned by viewModel.pinned.collectAsState()
+
   Surface(
     modifier = Modifier.padding(8.dp),
     color = MaterialTheme.colorScheme.surface,
     shadowElevation = 6.dp,
     tonalElevation = 6.dp,
     shape = RoundedCornerShape(32.dp),
-    onClick = {
-      onNavigate(Routes.Users.List, NavOptions.Builder().build())
-    }
+    onClick = { navController.navigateUp() }
   ) {
     Row(modifier = Modifier.padding(8.dp)) {
       AsyncImage(
-        account?.user?.gravatarUrl,
+        user?.gravatarUrl,
         contentDescription = "User List",
         contentScale = ContentScale.Crop,
         modifier = Modifier
@@ -87,48 +90,48 @@ fun MeteroidTopBar(
           .align(Alignment.CenterVertically)
           .weight(1.0f, fill = true)
       ) {
-        if (account != null) {
-          if (account.user != null) {
-            Text(
-              account.user!!.name,
-              fontWeight = FontWeight.SemiBold,
-              overflow = TextOverflow.Ellipsis,
-              softWrap = false
-            )
-            Text(
-              account.server.url.toHttpUrl().host,
-              color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.67f),
-              fontWeight = FontWeight.Medium,
-              overflow = TextOverflow.Ellipsis,
-              softWrap = false
-            )
-          } else {
-            Text(
-              account.server.url.toHttpUrl().host,
-              fontWeight = FontWeight.SemiBold,
-              overflow = TextOverflow.Ellipsis,
-              softWrap = false
-            )
-          }
-        } else {
+        if (server == null) {
           Text(
             "Meteroid",
             fontWeight = FontWeight.SemiBold,
             overflow = TextOverflow.Ellipsis,
             softWrap = false
           )
+        } else if (user == null) {
+          Text(
+            server!!.url.toHttpUrl().host,
+            fontWeight = FontWeight.SemiBold,
+            overflow = TextOverflow.Ellipsis,
+            softWrap = false
+          )
+        } else {
+          Text(
+            user!!.name,
+            fontWeight = FontWeight.SemiBold,
+            overflow = TextOverflow.Ellipsis,
+            softWrap = false
+          )
+          Text(
+            server!!.url.toHttpUrl().host,
+            color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.67f),
+            fontWeight = FontWeight.Medium,
+            overflow = TextOverflow.Ellipsis,
+            softWrap = false
+          )
         }
       }
       Spacer(Modifier.width(16.dp))
-      IconButton(onClick = onTogglePin) {
-        Icon(
-          if (account?.pinned == true) Icons.Filled.PushPin
-          else Icons.Outlined.PushPin,
-          contentDescription = null
-        )
-      }
-      Spacer(Modifier.width(8.dp))
-      account?.user?.let { user ->
+      user?.let { user ->
+        IconButton(onClick = {
+          viewModel.togglePin(user.serverId, user.userId)
+        }) {
+          Icon(
+            if (pinned == true) Icons.Filled.PushPin
+            else Icons.Outlined.PushPin,
+            contentDescription = null
+          )
+        }
+        Spacer(Modifier.width(8.dp))
         PriceBadge(
           user.balance,
           modifier = Modifier
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/Routes.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/Routes.kt
index a475733f3df74271439259ce2c0132709ba32cf6..2d7fe45a225b4842224057e9b88801f52a4f57c2 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/Routes.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/navigation/Routes.kt
@@ -24,26 +24,35 @@
 
 package de.chaosdorf.meteroid.ui.navigation
 
-object Routes {
-  const val Init = "init"
+import de.chaosdorf.mete.model.UserId
+import de.chaosdorf.meteroid.model.ServerId
 
+object Routes {
   object Servers {
-    const val Root = "servers"
-    const val List = "$Root/list"
-    const val Add = "$Root/new"
+    const val List = "server"
+    const val Add = "server/create"
   }
-
   object Users {
-    const val Root = "users"
-    const val List = "$Root/list"
-    //const val Add = "$Root/new"
+    const val List = "server/{server}"
+    fun list(server: ServerId) = List
+      .replace("{server}", server.value.toString())
   }
-
   object Home {
-    const val Root = "home"
-    const val Deposit = "$Root/deposit"
-    const val Purchase = "$Root/purchase"
-    const val History = "$Root/history"
-    const val Wrapped = "$Root/wrapped"
+    const val Purchase = "server/{server}/user/{user}/purchase"
+    const val Deposit = "server/{server}/user/{user}/deposit"
+    const val History = "server/{server}/user/{user}/history"
+    const val Wrapped = "server/{server}/user/{user}/wrapped"
+    fun purchase(server: ServerId, user: UserId) = Purchase
+      .replace("{server}", server.value.toString())
+      .replace("{user}", user.value.toString())
+    fun deposit(server: ServerId, user: UserId) = Deposit
+      .replace("{server}", server.value.toString())
+      .replace("{user}", user.value.toString())
+    fun history(server: ServerId, user: UserId) = History
+      .replace("{server}", server.value.toString())
+      .replace("{user}", user.value.toString())
+    fun wrapped(server: ServerId, user: UserId) = Wrapped
+      .replace("{server}", server.value.toString())
+      .replace("{user}", user.value.toString())
   }
 }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/AddServerScreen.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/AddServerScreen.kt
index 29ed7c615095e9da2f56f20282308d1a9fa7875c..a801dffa7c3b3ca55e519f14f3aa9f099e467248 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/AddServerScreen.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/AddServerScreen.kt
@@ -28,21 +28,17 @@ import androidx.compose.foundation.Image
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.filled.ArrowBack
 import androidx.compose.material.icons.filled.Add
-import androidx.compose.material3.Button
 import androidx.compose.material3.Card
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextField
 import androidx.compose.material3.TopAppBar
@@ -57,18 +53,17 @@ import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
-import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
 import coil.compose.AsyncImage
 import de.chaosdorf.meteroid.R
+import de.chaosdorf.meteroid.ui.navigation.Routes
 import kotlinx.coroutines.launch
 import okhttp3.HttpUrl.Companion.toHttpUrl
 
 @Composable
 fun AddServerScreen(
-  viewModel: AddServerViewModel = viewModel(),
-  isFirst: Boolean = false,
-  onBack: () -> Unit = {},
-  onAdd: () -> Unit = {}
+  navController: NavController,
+  viewModel: AddServerViewModel,
 ) {
   val scope = rememberCoroutineScope()
   val url by viewModel.url.collectAsState()
@@ -95,13 +90,6 @@ fun AddServerScreen(
             }
           }
         },
-        navigationIcon = {
-          if (!isFirst) {
-            IconButton(onClick = { onBack() }) {
-              Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = null)
-            }
-          }
-        },
         modifier = Modifier.shadow(4.dp)
       )
     },
@@ -109,7 +97,8 @@ fun AddServerScreen(
     Column(
       Modifier
         .padding(paddingValues)
-        .padding(16.dp, 8.dp)) {
+        .padding(16.dp, 8.dp)
+    ) {
       TextField(
         label = { Text("Server URL") },
         value = url,
@@ -146,12 +135,13 @@ fun AddServerScreen(
             Spacer(
               Modifier
                 .width(16.dp)
-                .weight(1.0f))
+                .weight(1.0f)
+            )
 
             IconButton(onClick = {
               scope.launch {
                 viewModel.addServer()
-                onAdd()
+                navController.navigate(Routes.Servers.List)
               }
             }) {
               Icon(Icons.Default.Add, contentDescription = "Add Server")
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/ServerListScreen.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/ServerListScreen.kt
index 577e81dbff0b90620da3b47ee28c5d574f00c58a..5625e6f1329c927d46127a543fe493fed7d68c79 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/ServerListScreen.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/ServerListScreen.kt
@@ -24,72 +24,35 @@
 
 package de.chaosdorf.meteroid.ui.servers
 
-import androidx.compose.foundation.Image
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
 import androidx.compose.material3.ListItem
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Scaffold
 import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBar
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.shadow
 import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
-import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
 import coil.compose.AsyncImage
-import de.chaosdorf.meteroid.R
-import de.chaosdorf.meteroid.model.ServerId
+import de.chaosdorf.meteroid.ui.navigation.Routes
 import okhttp3.HttpUrl.Companion.toHttpUrl
 
-@Preview
 @Composable
 fun ServerListScreen(
-  viewModel: ServerListViewModel = viewModel(),
-  onAdd: () -> Unit = {},
-  onSelect: (ServerId) -> Unit = {}
+  navController: NavController,
+  viewModel: ServerListViewModel,
+  topBar: @Composable () -> Unit,
 ) {
   val servers by viewModel.servers.collectAsState()
 
-  Scaffold(
-    topBar = {
-      TopAppBar(
-        title = {
-          Row {
-            Image(
-              painterResource(R.drawable.ic_launcher),
-              contentDescription = null,
-              contentScale = ContentScale.Crop,
-              modifier = Modifier.size(48.dp)
-            )
-            Spacer(Modifier.width(16.dp))
-            Column(modifier = Modifier.align(Alignment.CenterVertically)) {
-              Text(
-                "Meteroid",
-                fontWeight = FontWeight.SemiBold,
-                style = MaterialTheme.typography.bodyMedium
-              )
-            }
-          }
-        },
-        modifier = Modifier.shadow(4.dp)
-      )
-    }
-  ) { paddingValues ->
+  Scaffold(topBar = topBar) { paddingValues ->
     Column {
       LazyColumn(modifier = Modifier.padding(paddingValues)) {
         items(servers) { server ->
@@ -104,13 +67,18 @@ fun ServerListScreen(
                 modifier = Modifier.size(48.dp)
               )
             },
-            modifier = Modifier.clickable { onSelect(server.serverId) }
+            modifier = Modifier.clickable {
+              navController.navigate(Routes.Users.list(server.serverId))
+              viewModel.selectServer(server.serverId)
+            }
           )
         }
         item {
           ListItem(
             headlineContent = { Text("Add Server") },
-            modifier = Modifier.clickable { onAdd() }
+            modifier = Modifier.clickable {
+              navController.navigate(Routes.Servers.Add)
+            }
           )
         }
       }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/ServerListViewModel.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/ServerListViewModel.kt
index 32fb347d8a03ca03b56f49b3eff280850ecc4989..8e428018e60f9066cbd93fca483f202a6d5c34cf 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/ServerListViewModel.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/servers/ServerListViewModel.kt
@@ -28,16 +28,26 @@ import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import dagger.hilt.android.lifecycle.HiltViewModel
 import de.chaosdorf.meteroid.model.Server
+import de.chaosdorf.meteroid.model.ServerId
 import de.chaosdorf.meteroid.model.ServerRepository
+import de.chaosdorf.meteroid.storage.AccountPreferences
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 import javax.inject.Inject
 
 @HiltViewModel
 class ServerListViewModel @Inject constructor(
-  serverRepository: ServerRepository
+  serverRepository: ServerRepository,
+  private val preferences: AccountPreferences
 ) : ViewModel() {
   val servers: StateFlow<List<Server>> = serverRepository.getAllFlow()
     .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
+
+  fun selectServer(serverId: ServerId) {
+    viewModelScope.launch {
+      preferences.setServer(serverId)
+    }
+  }
 }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/transactions/PurchaseListScreen.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/transactions/PurchaseListScreen.kt
index ee0a63947b72a16fd9b80b457bb967ec0af5a41a..30620f8798a4871352cb43c017c1afeffed5fb2d 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/transactions/PurchaseListScreen.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/transactions/PurchaseListScreen.kt
@@ -36,19 +36,12 @@ import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
-import androidx.navigation.NavOptions
-import de.chaosdorf.meteroid.ui.navigation.HomeSections
-import de.chaosdorf.meteroid.ui.navigation.MeteroidBottomBar
-import de.chaosdorf.meteroid.ui.navigation.MeteroidTopBar
-import kotlinx.datetime.Clock
-import kotlinx.datetime.TimeZone
-import kotlinx.datetime.toLocalDateTime
-import java.time.Month
 
 @Composable
 fun TransactionListScreen(
   viewModel: TransactionViewModel,
-  onNavigate: (String, NavOptions) -> Unit
+  topBar: @Composable () -> Unit,
+  bottomBar: @Composable () -> Unit,
 ) {
   val account by viewModel.account.collectAsState()
   val transactions by viewModel.transactions.collectAsState()
@@ -66,16 +59,8 @@ fun TransactionListScreen(
   }
 
   Scaffold(
-    topBar = { MeteroidTopBar(account, onNavigate, viewModel::togglePin) },
-    bottomBar = {
-      MeteroidBottomBar(
-        currentRoute = HomeSections.HISTORY,
-        historyEnabled = account?.user?.audit == true,
-        wrappedEnabled = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
-          .month.let { it == Month.NOVEMBER || it == Month.DECEMBER },
-        navigateTo = onNavigate
-      )
-    },
+    topBar = topBar,
+    bottomBar = bottomBar,
     snackbarHost = {
       SnackbarHost(hostState = snackbarHostState)
     }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/transactions/PurchaseViewModel.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/transactions/PurchaseViewModel.kt
index 7242558d27e2738ca796588b9ab8eb34540a1ea9..d3efc7826b6ffb465fb89029d9d5484e9a692698 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/transactions/PurchaseViewModel.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/transactions/PurchaseViewModel.kt
@@ -24,21 +24,20 @@
 
 package de.chaosdorf.meteroid.ui.transactions
 
-import android.util.Log
+import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import dagger.hilt.android.lifecycle.HiltViewModel
-import de.chaosdorf.meteroid.model.AccountInfo
+import de.chaosdorf.mete.model.UserId
 import de.chaosdorf.meteroid.model.DrinkRepository
 import de.chaosdorf.meteroid.model.Server
+import de.chaosdorf.meteroid.model.ServerId
 import de.chaosdorf.meteroid.model.TransactionRepository
 import de.chaosdorf.meteroid.sync.AccountProvider
 import de.chaosdorf.meteroid.sync.SyncManager
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
@@ -48,47 +47,30 @@ import kotlin.time.Duration.Companion.minutes
 
 @HiltViewModel
 class TransactionViewModel @Inject constructor(
-  private val accountProvider: AccountProvider,
+  savedStateHandle: SavedStateHandle,
+  accountProvider: AccountProvider,
+  private val syncManager: SyncManager,
   repository: TransactionRepository,
-  drinkRepository: DrinkRepository,
-  private val syncManager: SyncManager
+  drinkRepository: DrinkRepository
 ) : ViewModel() {
-  val account: StateFlow<AccountInfo?> = accountProvider.account
-    .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
+  private val serverId = ServerId(checkNotNull(savedStateHandle["server"]))
+  private val userId = UserId(checkNotNull(savedStateHandle["user"]))
 
-  val transactions: StateFlow<List<TransactionInfo>> = accountProvider.account
-    .flatMapLatest { account ->
-      account?.let { (server, maybeUser) ->
-        maybeUser?.let { user ->
-          combine(
-            repository.getAllFlow(server.serverId, user.userId),
-            drinkRepository.getAllFlow(server.serverId)
-          ) { transactions, drinks ->
-            transactions.map { transaction ->
-              TransactionInfo(
-                transaction,
-                drinks.firstOrNull { drink -> drink.drinkId == transaction.drinkId }
-              )
-            }
-          }
-        }
-      } ?: flowOf(emptyList())
-    }.mapLatest { list ->
-      list.mergeAdjecentDeposits().filter {
-        it.drink != null ||
-          it.transaction.difference.abs() >= 0.01.toBigDecimal()
-      }
-    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
+  val account = accountProvider.accountFlow(serverId, userId)
+    .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
 
-  fun togglePin() {
-    account.value?.let { account ->
-      account.user?.let { user ->
-        viewModelScope.launch {
-          accountProvider.togglePin(account.server.serverId, user.userId)
-        }
-      }
+  val transactions: StateFlow<List<TransactionInfo>> = combine(
+    repository.getAllFlow(serverId, userId), drinkRepository.getAllFlow(serverId)
+  ) { transactions, drinks ->
+    transactions.map { transaction ->
+      TransactionInfo(transaction,
+        drinks.firstOrNull { drink -> drink.drinkId == transaction.drinkId })
     }
-  }
+  }.mapLatest { list ->
+    list.mergeAdjecentDeposits().filter {
+      it.drink != null || it.transaction.difference.abs() >= 0.01.toBigDecimal()
+    }
+  }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
 
   fun sync() {
     account.value?.let { (server, user) ->
@@ -98,21 +80,17 @@ class TransactionViewModel @Inject constructor(
     }
   }
 
-  suspend fun checkOffline(server: Server?): Boolean =
-    if (server == null) true
-    else syncManager.checkOffline(server)
+  suspend fun checkOffline(server: Server?): Boolean = if (server == null) true
+  else syncManager.checkOffline(server)
 }
 
 fun List<TransactionInfo>.mergeAdjecentDeposits(): List<TransactionInfo> {
   val result = mutableListOf<TransactionInfo>()
   for (entry in this) {
     val previous = result.lastOrNull()
-    if (previous != null
-      && previous.transaction.difference > BigDecimal.ZERO
-      && entry.transaction.difference > BigDecimal.ZERO
-      && previous.drink == null
-      && entry.drink == null
-      && entry.transaction.timestamp.minus(previous.transaction.timestamp) < 5.minutes
+    if (previous != null && previous.transaction.difference > BigDecimal.ZERO && entry.transaction.difference > BigDecimal.ZERO && previous.drink == null && entry.drink == null && entry.transaction.timestamp.minus(
+        previous.transaction.timestamp
+      ) < 5.minutes
     ) {
       result.removeLast()
       result.add(
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/users/UserListScreen.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/users/UserListScreen.kt
index 56aba29775041f6cfe124db213461a2715af9ce5..38218bd39cfce94104fcdc784b7acb420424c294 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/users/UserListScreen.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/users/UserListScreen.kt
@@ -24,103 +24,33 @@
 
 package de.chaosdorf.meteroid.ui.users
 
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.grid.GridCells
 import androidx.compose.foundation.lazy.grid.GridItemSpan
 import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
 import androidx.compose.foundation.lazy.grid.items
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.filled.ArrowBack
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Scaffold
 import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBar
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.shadow
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
-import coil.compose.AsyncImage
-import de.chaosdorf.mete.model.UserId
-import okhttp3.HttpUrl.Companion.toHttpUrl
+import androidx.navigation.NavController
+import de.chaosdorf.meteroid.ui.navigation.Routes
 
 @Composable
 fun UserListScreen(
+  navController: NavController,
   viewModel: UserListViewModel,
-  onAdd: () -> Unit = {},
-  onSelect: (UserId) -> Unit = {},
-  onBack: () -> Unit = {},
+  topBar: @Composable () -> Unit,
 ) {
-  val server by viewModel.account.collectAsState()
   val users by viewModel.users.collectAsState()
   val pinnedUsers by viewModel.pinnedUsers.collectAsState()
 
   Scaffold(
-    topBar = {
-      TopAppBar(
-        title = {
-          Row {
-            AsyncImage(
-              server?.server?.logoUrl,
-              contentDescription = null,
-              contentScale = ContentScale.Crop,
-              modifier = Modifier.size(48.dp)
-            )
-            Spacer(Modifier.width(16.dp))
-            Column(modifier = Modifier.align(Alignment.CenterVertically)) {
-              if (server?.server != null) {
-                if (server?.server?.name != null) {
-                  Text(
-                    server!!.server.name!!,
-                    fontWeight = FontWeight.SemiBold,
-                    style = MaterialTheme.typography.bodyMedium
-                  )
-                  Text(
-                    server!!.server.url.toHttpUrl().host,
-                    color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.67f),
-                    fontWeight = FontWeight.Medium,
-                    style = MaterialTheme.typography.bodyMedium
-                  )
-                } else {
-                  Text(
-                    server!!.server.url,
-                    fontWeight = FontWeight.SemiBold,
-                    style = MaterialTheme.typography.bodyMedium
-                  )
-                }
-              } else {
-                Text(
-                  "Meteroid",
-                  fontWeight = FontWeight.SemiBold,
-                  style = MaterialTheme.typography.bodyMedium
-                )
-              }
-            }
-          }
-        },
-        navigationIcon = {
-          IconButton(onClick = { onBack() }) {
-            Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = null)
-          }
-        },
-        modifier = Modifier
-          .shadow(4.dp)
-          .clickable { onBack() }
-      )
-    }
+    topBar = topBar,
   ) { paddingValues ->
     LazyVerticalGrid(
       GridCells.Adaptive(80.dp),
@@ -137,7 +67,9 @@ fun UserListScreen(
         }
 
         items(pinnedUsers) { user ->
-          UserTile(user, onSelect)
+          UserTile(user) {
+            navController.navigate(Routes.Home.purchase(user.serverId, user.userId))
+          }
         }
       }
 
@@ -153,7 +85,10 @@ fun UserListScreen(
           }
 
           items(group) { user ->
-            UserTile(user, onSelect)
+            UserTile(user) {
+              navController.navigate(Routes.Home.purchase(user.serverId, user.userId))
+              viewModel.selectUser(user.serverId, user.userId)
+            }
           }
         }
       }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/users/UserListViewModel.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/users/UserListViewModel.kt
index 112dfe646c361dd20a9533e5505c2e3c2ced251b..468071cfe2328c3200f87a37b9652ab308e70a83 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/users/UserListViewModel.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/users/UserListViewModel.kt
@@ -24,50 +24,46 @@
 
 package de.chaosdorf.meteroid.ui.users
 
+import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import dagger.hilt.android.lifecycle.HiltViewModel
-import de.chaosdorf.meteroid.model.AccountInfo
+import de.chaosdorf.mete.model.UserId
 import de.chaosdorf.meteroid.model.PinnedUserRepository
 import de.chaosdorf.meteroid.model.ServerId
 import de.chaosdorf.meteroid.model.User
 import de.chaosdorf.meteroid.model.UserRepository
-import de.chaosdorf.meteroid.sync.AccountProvider
-import de.chaosdorf.meteroid.sync.UserSyncHandler
+import de.chaosdorf.meteroid.storage.AccountPreferences
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 import javax.inject.Inject
 
 @HiltViewModel
 class UserListViewModel @Inject constructor(
-  accountProvider: AccountProvider,
+  savedStateHandle: SavedStateHandle,
   userRepository: UserRepository,
   pinnedUserRepository: PinnedUserRepository,
+  private val preferences: AccountPreferences,
 ) : ViewModel() {
-  val account: StateFlow<AccountInfo?> = accountProvider.account
-    .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
+  private val serverId = ServerId(checkNotNull(savedStateHandle["server"]))
 
-  val users: StateFlow<List<User>> = accountProvider.account
-    .flatMapLatest { account ->
-      account?.let { (server, _) ->
-        userRepository.getAllFlow(server.serverId)
-      } ?: flowOf(emptyList())
-    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
+  val users: StateFlow<List<User>> = userRepository.getAllFlow(serverId)
+    .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
 
-  val pinnedUsers = accountProvider.account
-    .flatMapLatest { account ->
-      account?.let { (server, _) ->
-        combine(
-          userRepository.getAllFlow(server.serverId),
-          pinnedUserRepository.getAllFlow(server.serverId)
-        ) { users, pinned ->
-          users.filter { pinned.contains(it.userId) }
-        }
-      } ?: flowOf(emptyList())
-    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
+  val pinnedUsers = combine(
+    userRepository.getAllFlow(serverId),
+    pinnedUserRepository.getAllFlow(serverId)
+  ) { users, pinned ->
+    users.filter { user -> pinned.contains(user.userId) }
+  }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
+
+  fun selectUser(serverId: ServerId, userId: UserId) {
+    viewModelScope.launch {
+      preferences.setServer(serverId)
+      preferences.setUser(userId)
+    }
+  }
 }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/wrapped/WrappedScreen.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/wrapped/WrappedScreen.kt
index b36cf87a8460da6361bb2ef208f04f0e154bfedc..73848b6807f3cdecee035e7891d0af1f956e88be 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/wrapped/WrappedScreen.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/wrapped/WrappedScreen.kt
@@ -45,22 +45,15 @@ import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.unit.dp
-import androidx.navigation.NavOptions
 import coil.compose.rememberAsyncImagePainter
 import de.chaosdorf.meteroid.R
-import de.chaosdorf.meteroid.ui.navigation.HomeSections
-import de.chaosdorf.meteroid.ui.navigation.MeteroidBottomBar
-import de.chaosdorf.meteroid.ui.navigation.MeteroidTopBar
-import kotlinx.datetime.Clock
-import kotlinx.datetime.TimeZone
-import kotlinx.datetime.toLocalDateTime
-import java.time.Month
 import java.time.format.TextStyle
 
 @Composable
 fun WrappedScreen(
   viewModel: WrappedViewModel,
-  onNavigate: (String, NavOptions) -> Unit
+  topBar: @Composable () -> Unit,
+  bottomBar: @Composable () -> Unit,
 ) {
   val account by viewModel.account.collectAsState()
   val slides by viewModel.slides.collectAsState()
@@ -78,16 +71,8 @@ fun WrappedScreen(
   }
 
   Scaffold(
-    topBar = { MeteroidTopBar(account, onNavigate, viewModel::togglePin) },
-    bottomBar = {
-      MeteroidBottomBar(
-        currentRoute = HomeSections.WRAPPED,
-        historyEnabled = account?.user?.audit == true,
-        wrappedEnabled = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
-          .month.let { it == Month.NOVEMBER || it == Month.DECEMBER },
-        navigateTo = onNavigate
-      )
-    },
+    topBar = topBar,
+    bottomBar = bottomBar,
     snackbarHost = {
       SnackbarHost(hostState = snackbarHostState)
     }
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/wrapped/WrappedViewModel.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/wrapped/WrappedViewModel.kt
index d50c28a621a80cc311df6a607dc88cc5af72aefe..712b16c4fd368efdc3b3183a57a76cf9f94c6ab8 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/wrapped/WrappedViewModel.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/wrapped/WrappedViewModel.kt
@@ -24,14 +24,16 @@
 
 package de.chaosdorf.meteroid.ui.wrapped
 
+import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import dagger.hilt.android.lifecycle.HiltViewModel
 import de.chaosdorf.mete.model.DrinkId
-import de.chaosdorf.meteroid.model.AccountInfo
+import de.chaosdorf.mete.model.UserId
 import de.chaosdorf.meteroid.model.Drink
 import de.chaosdorf.meteroid.model.DrinkRepository
 import de.chaosdorf.meteroid.model.Server
+import de.chaosdorf.meteroid.model.ServerId
 import de.chaosdorf.meteroid.model.Transaction
 import de.chaosdorf.meteroid.model.TransactionRepository
 import de.chaosdorf.meteroid.sync.AccountProvider
@@ -39,10 +41,7 @@ import de.chaosdorf.meteroid.sync.SyncManager
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
 import kotlinx.datetime.Clock
 import kotlinx.datetime.LocalDateTime
 import kotlinx.datetime.Month
@@ -53,12 +52,16 @@ import javax.inject.Inject
 
 @HiltViewModel
 class WrappedViewModel @Inject constructor(
-  private val accountProvider: AccountProvider,
+  savedStateHandle: SavedStateHandle,
+  accountProvider: AccountProvider,
+  private val syncManager: SyncManager,
   repository: TransactionRepository,
   drinkRepository: DrinkRepository,
-  private val syncManager: SyncManager
 ) : ViewModel() {
-  val account: StateFlow<AccountInfo?> = accountProvider.account
+  private val serverId = ServerId(checkNotNull(savedStateHandle["server"]))
+  private val userId = UserId(checkNotNull(savedStateHandle["user"]))
+
+  val account = accountProvider.accountFlow(serverId, userId)
     .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
 
   private fun List<Transaction>.filterAudits(year: Int): List<Transaction> {
@@ -71,37 +74,20 @@ class WrappedViewModel @Inject constructor(
     }
   }
 
-  val slides: StateFlow<List<WrappedSlide>> = accountProvider.account
-    .flatMapLatest { account ->
-      account?.let { (server, maybeUser) ->
-        maybeUser?.let { user ->
-          combine(
-            repository.getAllFlow(server.serverId, user.userId),
-            drinkRepository.getAllFlow(server.serverId)
-          ) { transactions, drinks ->
-            val drinkMap: Map<DrinkId, Drink> = drinks.associateBy(Drink::drinkId)
-            val factories = listOf(
-              WrappedSlide.MostBoughtDrink,
-              WrappedSlide.Caffeine,
-              WrappedSlide.MostActive
-            )
-            val now = Clock.System.now().toLocalDateTime(TimeZone.UTC)
-            val content = transactions.filterAudits(now.year)
-            factories.mapNotNull { it.create(content, drinkMap) }
-          }
-        }
-      } ?: flowOf(emptyList())
-    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
-
-  fun togglePin() {
-    account.value?.let { account ->
-      account.user?.let { user ->
-        viewModelScope.launch {
-          accountProvider.togglePin(account.server.serverId, user.userId)
-        }
-      }
-    }
-  }
+  val slides: StateFlow<List<WrappedSlide>> = combine(
+    repository.getAllFlow(serverId, userId),
+    drinkRepository.getAllFlow(serverId)
+  ) { transactions, drinks ->
+    val drinkMap: Map<DrinkId, Drink> = drinks.associateBy(Drink::drinkId)
+    val factories = listOf(
+      WrappedSlide.MostBoughtDrink,
+      WrappedSlide.Caffeine,
+      WrappedSlide.MostActive
+    )
+    val now = Clock.System.now().toLocalDateTime(TimeZone.UTC)
+    val content = transactions.filterAudits(now.year)
+    factories.mapNotNull { it.create(content, drinkMap) }
+  }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
 
   suspend fun checkOffline(server: Server?): Boolean =
     if (server == null) true
diff --git a/persistence/src/main/kotlin/de/chaosdorf/meteroid/model/AccountInfo.kt b/persistence/src/main/kotlin/de/chaosdorf/meteroid/model/AccountInfo.kt
index cd9af862ae68d104fcaa82745f783db2226ba16a..3c7150ebe54e9193cd5c1b8e92f7a2939d76c950 100644
--- a/persistence/src/main/kotlin/de/chaosdorf/meteroid/model/AccountInfo.kt
+++ b/persistence/src/main/kotlin/de/chaosdorf/meteroid/model/AccountInfo.kt
@@ -26,6 +26,6 @@ package de.chaosdorf.meteroid.model
 
 data class AccountInfo(
   val server: Server,
-  val user: User?,
+  val user: User,
   val pinned: Boolean
 )
diff --git a/persistence/src/main/kotlin/de/chaosdorf/meteroid/model/Server.kt b/persistence/src/main/kotlin/de/chaosdorf/meteroid/model/Server.kt
index 8036071ab3df2798b379a28d2ba11b789d462c41..0e94a9b1620592af7dae9064b7bb02c6d60f6b70 100644
--- a/persistence/src/main/kotlin/de/chaosdorf/meteroid/model/Server.kt
+++ b/persistence/src/main/kotlin/de/chaosdorf/meteroid/model/Server.kt
@@ -51,6 +51,8 @@ interface ServerRepository {
 
   @Query("SELECT * FROM Server WHERE serverId = :id LIMIT 1")
   fun getFlow(id: ServerId): Flow<Server?>
+  @Query("SELECT count(*) FROM Server")
+  fun countFlow(): Flow<Int>
 
   @Query("SELECT * FROM Server")
   suspend fun getAll(): List<Server>