diff --git a/composeApp/src/commonMain/composeResources/drawable/app-icon.png b/composeApp/src/commonMain/composeResources/drawable/app-icon.png deleted file mode 100644 index b065b7791..000000000 Binary files a/composeApp/src/commonMain/composeResources/drawable/app-icon.png and /dev/null differ diff --git a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt index e2e4b824b..d9cb817b7 100644 --- a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt +++ b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/Main.kt @@ -5,29 +5,35 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import org.koin.compose.viewmodel.koinViewModel import zed.rainxch.core.presentation.theme.GithubStoreTheme import zed.rainxch.core.presentation.utils.ApplyAndroidSystemBars +import zed.rainxch.core.presentation.utils.ObserveAsEvents import zed.rainxch.githubstore.app.components.RateLimitDialog import zed.rainxch.githubstore.app.components.SessionExpiredDialog import zed.rainxch.githubstore.app.deeplink.DeepLinkDestination import zed.rainxch.githubstore.app.deeplink.DeepLinkParser +import zed.rainxch.githubstore.app.desktop.KeyboardNavigation +import zed.rainxch.githubstore.app.desktop.KeyboardNavigationEvent import zed.rainxch.githubstore.app.navigation.AppNavigation import zed.rainxch.githubstore.app.navigation.GithubStoreGraph +import zed.rainxch.githubstore.app.navigation.getCurrentScreen @Composable fun App(deepLinkUri: String? = null) { val viewModel: MainViewModel = koinViewModel() val state by viewModel.state.collectAsStateWithLifecycle() - val navBackStack = rememberNavController() + val navController = rememberNavController() + val currentScreen = navController.currentBackStackEntryAsState().value.getCurrentScreen() LaunchedEffect(deepLinkUri) { deepLinkUri?.let { uri -> when (val destination = DeepLinkParser.parse(uri)) { is DeepLinkDestination.Repository -> { - navBackStack.navigate( + navController.navigate( GithubStoreGraph.DetailsScreen( owner = destination.owner, repo = destination.repo, @@ -42,6 +48,23 @@ fun App(deepLinkUri: String? = null) { } } + ObserveAsEvents(KeyboardNavigation.events) { event -> + when (event) { + KeyboardNavigationEvent.OnCtrlFClick -> { + if (currentScreen !is GithubStoreGraph.SearchScreen) { + navController.navigate(GithubStoreGraph.SearchScreen) { + popUpTo(GithubStoreGraph.HomeScreen) { + saveState = true + } + + launchSingleTop = true + restoreState = true + } + } + } + } + } + GithubStoreTheme( fontTheme = state.currentFontTheme, appTheme = state.currentColorTheme, @@ -50,21 +73,19 @@ fun App(deepLinkUri: String? = null) { ) { ApplyAndroidSystemBars(state.isDarkTheme) - if (state.showRateLimitDialog) { - state.rateLimitInfo?.let { - RateLimitDialog( - rateLimitInfo = it, - isAuthenticated = state.isLoggedIn, - onDismiss = { - viewModel.onAction(MainAction.DismissRateLimitDialog) - }, - onSignIn = { - viewModel.onAction(MainAction.DismissRateLimitDialog) + if (state.showRateLimitDialog && state.rateLimitInfo != null) { + RateLimitDialog( + rateLimitInfo = state.rateLimitInfo!!, + isAuthenticated = state.isLoggedIn, + onDismiss = { + viewModel.onAction(MainAction.DismissRateLimitDialog) + }, + onSignIn = { + viewModel.onAction(MainAction.DismissRateLimitDialog) - navBackStack.navigate(GithubStoreGraph.AuthenticationScreen) - }, - ) - } + navController.navigate(GithubStoreGraph.AuthenticationScreen) + }, + ) } if (state.showSessionExpiredDialog) { @@ -74,13 +95,13 @@ fun App(deepLinkUri: String? = null) { }, onSignIn = { viewModel.onAction(MainAction.DismissSessionExpiredDialog) - navBackStack.navigate(GithubStoreGraph.AuthenticationScreen) + navController.navigate(GithubStoreGraph.AuthenticationScreen) }, ) } AppNavigation( - navController = navBackStack, + navController = navController, ) } } diff --git a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/desktop/KeyboardNavigation.kt b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/desktop/KeyboardNavigation.kt new file mode 100644 index 000000000..47fe7ed7d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/desktop/KeyboardNavigation.kt @@ -0,0 +1,13 @@ +package zed.rainxch.githubstore.app.desktop + +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.receiveAsFlow + +object KeyboardNavigation { + private val _events = Channel() + val events = _events.receiveAsFlow() + + fun onKeyClicked(event: KeyboardNavigationEvent) { + _events.trySend(event) + } +} diff --git a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/desktop/KeyboardNavigationEvent.kt b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/desktop/KeyboardNavigationEvent.kt new file mode 100644 index 000000000..d6d74c9fc --- /dev/null +++ b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/desktop/KeyboardNavigationEvent.kt @@ -0,0 +1,5 @@ +package zed.rainxch.githubstore.app.desktop + +sealed interface KeyboardNavigationEvent { + data object OnCtrlFClick : KeyboardNavigationEvent +} diff --git a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/AppNavigation.kt b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/AppNavigation.kt index 46358f901..70b0655a6 100644 --- a/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/AppNavigation.kt +++ b/composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/navigation/AppNavigation.kt @@ -140,7 +140,12 @@ fun AppNavigation(navController: NavHostController) { }, viewModel = koinViewModel { - parametersOf(args.repositoryId, args.owner, args.repo, args.isComingFromUpdate) + parametersOf( + args.repositoryId, + args.owner, + args.repo, + args.isComingFromUpdate, + ) }, ) } @@ -272,7 +277,8 @@ fun AppNavigation(navController: NavHostController) { } } - val currentScreen = navController.currentBackStackEntryAsState().value.getCurrentScreen() + val currentScreen = + navController.currentBackStackEntryAsState().value.getCurrentScreen() currentScreen?.let { BottomNavigation( @@ -294,7 +300,8 @@ fun AppNavigation(navController: NavHostController) { .navigationBarsPadding() .padding(bottom = 24.dp) .onGloballyPositioned { coordinates -> - bottomNavigationHeight = with(density) { coordinates.size.height.toDp() } + bottomNavigationHeight = + with(density) { coordinates.size.height.toDp() } }, ) } diff --git a/composeApp/src/jvmMain/kotlin/zed/rainxch/githubstore/DesktopApp.kt b/composeApp/src/jvmMain/kotlin/zed/rainxch/githubstore/DesktopApp.kt index 1a4560aff..d99234fee 100644 --- a/composeApp/src/jvmMain/kotlin/zed/rainxch/githubstore/DesktopApp.kt +++ b/composeApp/src/jvmMain/kotlin/zed/rainxch/githubstore/DesktopApp.kt @@ -4,16 +4,28 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyEventType +import androidx.compose.ui.input.key.isCtrlPressed +import androidx.compose.ui.input.key.isMetaPressed +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.type import androidx.compose.ui.window.Window import androidx.compose.ui.window.application -import githubstore.composeapp.generated.resources.Res -import githubstore.composeapp.generated.resources.app_icon import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource +import zed.rainxch.githubstore.app.desktop.KeyboardNavigation +import zed.rainxch.githubstore.app.desktop.KeyboardNavigationEvent import zed.rainxch.githubstore.app.di.initKoin +import zed.rainxch.githubstore.core.presentation.res.Res +import zed.rainxch.githubstore.core.presentation.res.app_icon +import zed.rainxch.githubstore.core.presentation.res.app_name import java.awt.Desktop import kotlin.system.exitProcess fun main(args: Array) { + initKoin() + val deepLinkArg = args.firstOrNull() if (deepLinkArg != null && DesktopDeepLink.tryForwardToRunningInstance(deepLinkArg)) { @@ -21,7 +33,6 @@ fun main(args: Array) { } DesktopDeepLink.registerUriSchemeIfNeeded() - initKoin() application { var deepLinkUri by mutableStateOf(deepLinkArg) @@ -44,8 +55,20 @@ fun main(args: Array) { Window( onCloseRequest = ::exitApplication, - title = "GitHub Store", + title = stringResource(Res.string.app_name), icon = painterResource(Res.drawable.app_icon), + onKeyEvent = { keyEvent -> + if (keyEvent.key == Key.F && keyEvent.type == KeyEventType.KeyDown) { + if (keyEvent.isCtrlPressed || keyEvent.isMetaPressed) { + KeyboardNavigation.onKeyClicked(KeyboardNavigationEvent.OnCtrlFClick) + true + } else { + false + } + } else { + false + } + }, ) { App(deepLinkUri = deepLinkUri) } diff --git a/core/presentation/src/commonMain/composeResources/drawable/app_icon.png b/core/presentation/src/commonMain/composeResources/drawable/app_icon.png index b065b7791..74f743b34 100644 Binary files a/core/presentation/src/commonMain/composeResources/drawable/app_icon.png and b/core/presentation/src/commonMain/composeResources/drawable/app_icon.png differ