diff --git a/app/feature/feature-help-center/build.gradle.kts b/app/feature/feature-help-center/build.gradle.kts index 4da8050c43..a649f773eb 100644 --- a/app/feature/feature-help-center/build.gradle.kts +++ b/app/feature/feature-help-center/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi + plugins { id("hedvig.multiplatform.library") id("hedvig.multiplatform.library.android") @@ -13,6 +15,15 @@ hedvig { kotlin { sourceSets { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + applyDefaultHierarchyTemplate { + common { + group("jvmAndAndroid") { + withAndroidLibraryTarget() + withJvm() + } + } + } commonMain.dependencies { implementation(libs.apollo.runtime) implementation(libs.apollo.normalizedCache) diff --git a/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/home/HelpCenterHomeDestination.kt b/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/home/HelpCenterHomeDestination.kt index 1c264c6872..c742d6653f 100644 --- a/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/home/HelpCenterHomeDestination.kt +++ b/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/home/HelpCenterHomeDestination.kt @@ -308,7 +308,7 @@ private fun HelpCenterHomeScreen( quickLinksForSearch = ( quickLinksUiState as? HelpCenterUiState.QuickLinkUiState.QuickLinks - )?.quickLinks ?: listOf(), + )?.quickLinks ?: listOf(), questionsForSearch = topics.flatMap { it.commonFAQ + it.otherFAQ }, ) onUpdateSearchResults(it, results) diff --git a/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyArticleDestination.kt b/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyArticleDestination.kt index a34e62ba9a..6ae4fb3a12 100644 --- a/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyArticleDestination.kt +++ b/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyArticleDestination.kt @@ -58,7 +58,7 @@ import com.hedvig.android.design.system.hedvig.Surface import com.hedvig.android.design.system.hedvig.rememberPreviewImageLoader import com.hedvig.android.feature.help.center.data.PuppyGuideStory import com.hedvig.android.feature.help.center.ui.MarkdownText -import com.hedvig.android.feature.help.center.ui.toHapticFeedbackType +import com.hedvig.android.feature.help.center.ui.rememberPerformRatingHaptic import hedvig.resources.PUPPY_GUIDE_RATING_NOT_HELPFUL import hedvig.resources.PUPPY_GUIDE_RATING_QUESTION import hedvig.resources.PUPPY_GUIDE_RATING_VERY_HELPFUL @@ -234,7 +234,7 @@ private fun RatingSection(selectedRating: Int?, onRatingClick: (Int) -> Unit, mo horizontalAlignment = Alignment.CenterHorizontally, ) { val ratings = listOf(1, 2, 3, 4, 5) - val hapticFeedback = LocalHapticFeedback.current + val performRatingHaptic = rememberPerformRatingHaptic(LocalHapticFeedback.current) Row( horizontalArrangement = Arrangement.SpaceAround, modifier = Modifier, @@ -244,7 +244,7 @@ private fun RatingSection(selectedRating: Int?, onRatingClick: (Int) -> Unit, mo HedvigCard( modifier = Modifier.weight(1f), onClick = { - hapticFeedback.performHapticFeedback(rating.toHapticFeedbackType()) + performRatingHaptic(rating) onRatingClick(rating) }, color = if (isSelectedRating) { diff --git a/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyGuideDestination.kt b/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyGuideDestination.kt index 24c584e5fb..77e1b7a74e 100644 --- a/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyGuideDestination.kt +++ b/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyGuideDestination.kt @@ -43,9 +43,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.painter.ColorPainter import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter @@ -225,10 +227,12 @@ private fun PuppyGuideSuccessScreen( modifier = Modifier.fillMaxWidth(), ) { Column(modifier = Modifier.padding(top = stickyTopPadding)) { + val hapticFeedback = LocalHapticFeedback.current GuideCategoriesRow( categories = categories, contentPadding = sectionContentPadding, onCategoryClick = onClick@{ category -> + hapticFeedback.performHapticFeedback(HapticFeedbackType.ContextClick) val index = categories.indexOf(category) if (index == -1) return@onClick val stickyInfo = listState.layoutInfo.visibleItemsInfo diff --git a/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/ui/MarkdownText.kt b/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/ui/MarkdownText.kt index ed1d849cbd..44e0864469 100644 --- a/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/ui/MarkdownText.kt +++ b/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/ui/MarkdownText.kt @@ -134,11 +134,3 @@ fun MarkdownText(markdown: String, modifier: Modifier = Modifier, withArticleSty }, ) } - -fun Int.toHapticFeedbackType(): HapticFeedbackType { - return when (this) { - 1, 2 -> HapticFeedbackType.TextHandleMove - 4, 5 -> HapticFeedbackType.Confirm - else -> HapticFeedbackType.LongPress - } -} diff --git a/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/ui/RatingHaptic.kt b/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/ui/RatingHaptic.kt new file mode 100644 index 0000000000..2bb7abf3e7 --- /dev/null +++ b/app/feature/feature-help-center/src/commonMain/kotlin/com/hedvig/android/feature/help/center/ui/RatingHaptic.kt @@ -0,0 +1,14 @@ +package com.hedvig.android.feature.help.center.ui + +import androidx.compose.runtime.Composable +import androidx.compose.ui.hapticfeedback.HapticFeedback + +/** + * Returns a function that plays a haptic for the given article rating (1..5). + * + * Compose Multiplatform's iOS [androidx.compose.ui.hapticfeedback.HapticFeedback] maps most "subtle" + * [androidx.compose.ui.hapticfeedback.HapticFeedbackType] values to UISelectionFeedbackGenerator, + * which is imperceptible. This abstraction bypasses that on iOS by calling UIKit generators directly. + */ +@Composable +expect fun rememberPerformRatingHaptic(hapticFeedback: HapticFeedback): (rating: Int) -> Unit diff --git a/app/feature/feature-help-center/src/androidMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyTopAppBar.kt b/app/feature/feature-help-center/src/jvmAndAndroidMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyTopAppBar.kt similarity index 100% rename from app/feature/feature-help-center/src/androidMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyTopAppBar.kt rename to app/feature/feature-help-center/src/jvmAndAndroidMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyTopAppBar.kt diff --git a/app/feature/feature-help-center/src/jvmAndAndroidMain/kotlin/com/hedvig/android/feature/help/center/ui/RatingHaptic.kt b/app/feature/feature-help-center/src/jvmAndAndroidMain/kotlin/com/hedvig/android/feature/help/center/ui/RatingHaptic.kt new file mode 100644 index 0000000000..737e841caa --- /dev/null +++ b/app/feature/feature-help-center/src/jvmAndAndroidMain/kotlin/com/hedvig/android/feature/help/center/ui/RatingHaptic.kt @@ -0,0 +1,19 @@ +package com.hedvig.android.feature.help.center.ui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.hapticfeedback.HapticFeedback +import androidx.compose.ui.hapticfeedback.HapticFeedbackType + +@Composable +actual fun rememberPerformRatingHaptic(hapticFeedback: HapticFeedback): (rating: Int) -> Unit { + return remember(hapticFeedback) { + { rating -> + val type = when (rating) { + 4, 5 -> HapticFeedbackType.Confirm + else -> HapticFeedbackType.TextHandleMove + } + hapticFeedback.performHapticFeedback(type) + } + } +} diff --git a/app/feature/feature-help-center/src/jvmMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyTopAppBar.kt b/app/feature/feature-help-center/src/jvmMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyTopAppBar.kt deleted file mode 100644 index ce2fdc5eb0..0000000000 --- a/app/feature/feature-help-center/src/jvmMain/kotlin/com/hedvig/android/feature/help/center/puppyguide/PuppyTopAppBar.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.hedvig.android.feature.help.center.puppyguide - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.hedvig.android.design.system.hedvig.TopAppBarWithBack - -@Composable -internal actual fun PuppyTopAppBar(title: String, onBack: () -> Unit, modifier: Modifier) { - TopAppBarWithBack(title = title, onClick = onBack, modifier = modifier) -} diff --git a/app/feature/feature-help-center/src/nativeMain/kotlin/com/hedvig/android/feature/help/center/data/PuppyGuideAvailability.kt b/app/feature/feature-help-center/src/nativeMain/kotlin/com/hedvig/android/feature/help/center/data/PuppyGuideAvailability.kt index 9263e957ff..debe85a433 100644 --- a/app/feature/feature-help-center/src/nativeMain/kotlin/com/hedvig/android/feature/help/center/data/PuppyGuideAvailability.kt +++ b/app/feature/feature-help-center/src/nativeMain/kotlin/com/hedvig/android/feature/help/center/data/PuppyGuideAvailability.kt @@ -8,9 +8,7 @@ import kotlinx.coroutines.launch import org.koin.mp.KoinPlatform @Suppress("unused") // Used from iOS -fun observePuppyGuideAvailability( - onResult: (PuppyGuidePresentation?) -> Unit, -): PuppyGuideAvailabilityCancellable { +fun observePuppyGuideAvailability(onResult: (PuppyGuidePresentation?) -> Unit): PuppyGuideAvailabilityCancellable { val useCase = KoinPlatform.getKoin().get() val scope = CoroutineScope(Dispatchers.Main) val job: Job = scope.launch { diff --git a/app/feature/feature-help-center/src/nativeMain/kotlin/com/hedvig/android/feature/help/center/ui/RatingHaptic.native.kt b/app/feature/feature-help-center/src/nativeMain/kotlin/com/hedvig/android/feature/help/center/ui/RatingHaptic.native.kt new file mode 100644 index 0000000000..007187e97d --- /dev/null +++ b/app/feature/feature-help-center/src/nativeMain/kotlin/com/hedvig/android/feature/help/center/ui/RatingHaptic.native.kt @@ -0,0 +1,40 @@ +package com.hedvig.android.feature.help.center.ui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.hapticfeedback.HapticFeedback +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import platform.UIKit.UIImpactFeedbackGenerator +import platform.UIKit.UIImpactFeedbackStyle + +/** + * `Confirm` and `LongPress` are delegated to Compose's iOS impl (they map to + * `UINotificationFeedbackGenerator.success` and a medium `UIImpactFeedbackGenerator` respectively). + * + * The "subtle" 1-3 case and the extra thump on 5 use UIKit directly because Compose's iOS impl + * routes every other "light" `HapticFeedbackType` to `UISelectionFeedbackGenerator` + * (imperceptible) and never exposes `UIImpactFeedbackStyleLight`/`Heavy`. + */ +@Composable +actual fun rememberPerformRatingHaptic(hapticFeedback: HapticFeedback): (rating: Int) -> Unit { + return remember(hapticFeedback) { + val lightImpact = UIImpactFeedbackGenerator(style = UIImpactFeedbackStyle.UIImpactFeedbackStyleLight) + val heavyImpact = UIImpactFeedbackGenerator(style = UIImpactFeedbackStyle.UIImpactFeedbackStyleHeavy); + { rating: Int -> + when (rating) { + 4 -> { + hapticFeedback.performHapticFeedback(HapticFeedbackType.Confirm) + } + + 5 -> { + hapticFeedback.performHapticFeedback(HapticFeedbackType.Confirm) + heavyImpact.impactOccurred() + } + + else -> { + lightImpact.impactOccurred() + } + } + } + } +}