diff --git a/app-ios-shared/src/commonMain/composeResources/values-ja/strings.xml b/app-ios-shared/src/commonMain/composeResources/values-ja/strings.xml
index 04ed06b08..ca02fc4a3 100644
--- a/app-ios-shared/src/commonMain/composeResources/values-ja/strings.xml
+++ b/app-ios-shared/src/commonMain/composeResources/values-ja/strings.xml
@@ -1,4 +1,5 @@
セッションを予定として追加するには、カレンダーへのアクセス権限が必要です。
+ 設定を開く
diff --git a/app-ios-shared/src/commonMain/composeResources/values/strings.xml b/app-ios-shared/src/commonMain/composeResources/values/strings.xml
index b6e4f8499..e758198ee 100644
--- a/app-ios-shared/src/commonMain/composeResources/values/strings.xml
+++ b/app-ios-shared/src/commonMain/composeResources/values/strings.xml
@@ -1,4 +1,5 @@
To add a session as a scheduled event, you need access permission to the calendar.
+ Open settings.
diff --git a/app-ios-shared/src/commonMain/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiApp.kt b/app-ios-shared/src/commonMain/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiApp.kt
index c91f03397..7fcbe7e53 100644
--- a/app-ios-shared/src/commonMain/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiApp.kt
+++ b/app-ios-shared/src/commonMain/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiApp.kt
@@ -10,6 +10,7 @@ import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
@@ -22,6 +23,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import co.touchlab.kermit.Logger
import conference_app_2024.app_ios_shared.generated.resources.permission_required
+import conference_app_2024.app_ios_shared.generated.resources.open_settings
import io.github.droidkaigi.confsched.about.aboutScreen
import io.github.droidkaigi.confsched.about.aboutScreenRoute
import io.github.droidkaigi.confsched.about.navigateAboutScreen
@@ -93,11 +95,13 @@ import platform.Foundation.NSDate
import platform.Foundation.NSURL
import platform.Foundation.dateWithTimeIntervalSince1970
import platform.UIKit.UIApplication
+import platform.UIKit.UIApplicationOpenSettingsURLString
import platform.UIKit.UIViewController
import platform.darwin.NSObject
data class IosComposeKaigiAppUiState(
val userMessageStateHolder: UserMessageStateHolder,
+ val shouldGoToSettingsApp: Boolean,
)
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
@@ -142,6 +146,7 @@ fun KaigiApp(
snackbarHostState: SnackbarHostState,
onLicenseScreenRequest: () -> Unit,
modifier: Modifier = Modifier,
+ externalNavController: ExternalNavController = rememberExternalNavController(),
) {
val eventFlow = rememberEventFlow()
val uiState = iosComposeKaigiAppPresenter(events = eventFlow)
@@ -151,6 +156,13 @@ fun KaigiApp(
userMessageStateHolder = uiState.userMessageStateHolder,
)
+ LaunchedEffect(uiState.shouldGoToSettingsApp) {
+ if (uiState.shouldGoToSettingsApp) {
+ eventFlow.tryEmit(IosComposeKaigiAppEvent.SettingsAppNavigated)
+ externalNavController.navigateToSettingsApp()
+ }
+ }
+
KaigiTheme(
fontFamily = fontFamily,
) {
@@ -159,13 +171,17 @@ fun KaigiApp(
color = MaterialTheme.colorScheme.background,
) {
val snackbarMessage = stringResource(AppIosSharedRes.string.permission_required)
+ val snackbarActionLabel = stringResource(AppIosSharedRes.string.open_settings)
+
KaigiNavHost(
windowSize = windowSize,
+ externalNavController = externalNavController,
onLicenseScreenRequest = onLicenseScreenRequest,
onAccessCalendarIsDenied = {
eventFlow.tryEmit(
IosComposeKaigiAppEvent.ShowRequiresAuthorization(
snackbarMessage = snackbarMessage,
+ actionLabel = snackbarActionLabel,
)
)
}
@@ -177,10 +193,10 @@ fun KaigiApp(
@Composable
private fun KaigiNavHost(
windowSize: WindowSizeClass,
+ externalNavController: ExternalNavController,
onLicenseScreenRequest: () -> Unit,
onAccessCalendarIsDenied: () -> Unit,
navController: NavHostController = rememberNavController(),
- externalNavController: ExternalNavController = rememberExternalNavController(),
) {
NavHostWithSharedAxisX(navController = navController, startDestination = mainScreenRoute) {
mainScreen(
@@ -357,7 +373,7 @@ private fun rememberExternalNavController(): ExternalNavController {
}
}
-private class ExternalNavController(
+class ExternalNavController(
private val shareNavigator: ShareNavigator,
private val coroutineScope: CoroutineScope,
) {
@@ -430,6 +446,13 @@ private class ExternalNavController(
}
}
+ fun navigateToSettingsApp() {
+ val settingsUrl = NSURL.URLWithString(UIApplicationOpenSettingsURLString)
+ if (settingsUrl != null) {
+ UIApplication.sharedApplication.openURL(settingsUrl)
+ }
+ }
+
fun onShareClick(timetableItem: TimetableItem) {
shareNavigator.share(
"[${timetableItem.room.name.currentLangTitle}] ${timetableItem.startsTimeString} - ${timetableItem.endsTimeString}\n" +
diff --git a/app-ios-shared/src/commonMain/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiAppPresenter.kt b/app-ios-shared/src/commonMain/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiAppPresenter.kt
index db59c70e0..92ae2c93c 100644
--- a/app-ios-shared/src/commonMain/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiAppPresenter.kt
+++ b/app-ios-shared/src/commonMain/kotlin/io/github/droidkaigi/confsched/shared/IosComposeKaigiAppPresenter.kt
@@ -1,34 +1,51 @@
package io.github.droidkaigi.confsched.shared
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import io.github.droidkaigi.confsched.compose.EventEffect
import io.github.droidkaigi.confsched.compose.EventFlow
+import io.github.droidkaigi.confsched.droidkaigiui.UserMessageResult.ActionPerformed
import io.github.droidkaigi.confsched.droidkaigiui.providePresenterDefaults
+import io.github.droidkaigi.confsched.shared.IosComposeKaigiAppEvent.SettingsAppNavigated
import io.github.droidkaigi.confsched.shared.IosComposeKaigiAppEvent.ShowRequiresAuthorization
sealed interface IosComposeKaigiAppEvent {
- val snackbarMessage: String
-
data class ShowRequiresAuthorization(
- override val snackbarMessage: String,
+ val snackbarMessage: String,
+ val actionLabel: String,
) : IosComposeKaigiAppEvent
+
+ data object SettingsAppNavigated : IosComposeKaigiAppEvent
}
@Composable
fun iosComposeKaigiAppPresenter(
events: EventFlow
) : IosComposeKaigiAppUiState = providePresenterDefaults { userMessageStateHolder ->
+ var shouldGoToSettingsApp by remember { mutableStateOf(false) }
+
EventEffect(events) { event ->
when (event) {
is ShowRequiresAuthorization -> {
- userMessageStateHolder.showMessage(
+ val result = userMessageStateHolder.showMessage(
message = event.snackbarMessage,
- // TODO Add code to transition to the settings screen when the action button is pressed.
- // TODO Perhaps UIApplication.openSettingsURLString can be used to achieve this.
- actionLabel = null,
+ actionLabel = event.actionLabel,
)
+ if (result == ActionPerformed) {
+ shouldGoToSettingsApp = true
+ }
+ }
+
+ SettingsAppNavigated -> {
+ shouldGoToSettingsApp = false
}
}
}
- IosComposeKaigiAppUiState(userMessageStateHolder)
+ IosComposeKaigiAppUiState(
+ userMessageStateHolder = userMessageStateHolder,
+ shouldGoToSettingsApp = shouldGoToSettingsApp,
+ )
}