Skip to content

iOS crashes when trying to show tooltip #3

@kostapostolakis

Description

@kostapostolakis
@Composable
fun GameGuideScreen(
    gameGuideViewModel: GameGuideViewModel = viewModel { GameGuideViewModel() },
    navController: NavHostController
) {
    val snackBarModel: SnackBarModel? by gameGuideViewModel.snackBarModel.collectAsState()
    val baseAlert: BaseAlert? by gameGuideViewModel.baseAlert.collectAsState()
    val gameGuideState: GameGuideState by gameGuideViewModel.gameGuideState.collectAsState()

    val showTooltips: Boolean by gameGuideViewModel.showTooltips.collectAsState()
    val tooltipState = rememberTooltipBoxState()

    HandleGameGuideActions(gameGuideViewModel, navController)

    CMPTooltipBox(
        state = tooltipState,
        showTooltip = showTooltips,
        onTooltipCompleted = {
            gameGuideViewModel.tooltipsCompleted()
        }
    ) {
        BaseScreen(
            title = StringModel(Res.string.game_guide),
            navigationIcon = painterResource(Res.drawable.round_arrow_back_white_24),
            navigationButtonClick = { navController.popBackStack() },
            snackBarModel = snackBarModel,
            baseAlert = baseAlert
        ) {
            when (gameGuideState) {
                is GameGuideState.InitialState -> {
                    // Do not show anything
                }
                is GameGuideState.DayState -> {
                    GameGuideDayScreen(
                        dayModifiers = DayModifiers(
                            showCharacterModifier = Modifier.markForSimpleTooltip(
                                index = GuideTooltip.SHOW_CHARACTER_TOOLTIP.index,
                                title = GuideTooltip.SHOW_CHARACTER_TOOLTIP.title.toComposableString(),
                                description = GuideTooltip.SHOW_CHARACTER_TOOLTIP.description.toComposableString(),
                                tooltipBoxConfig = getTooltipConfig(GuideTooltip.SHOW_CHARACTER_TOOLTIP.arrowDirection)
                            ),
                            votingModifier = Modifier.markForSimpleTooltip(
                                index = GuideTooltip.VOTING_TOOLTIP.index,
                                title = GuideTooltip.VOTING_TOOLTIP.title.toComposableString(),
                                description = GuideTooltip.VOTING_TOOLTIP.description.toComposableString(),
                                tooltipBoxConfig = getTooltipConfig(GuideTooltip.VOTING_TOOLTIP.arrowDirection)
                            ),
                            restartGameModifier = Modifier.markForSimpleTooltip(
                                index = GuideTooltip.RESTART_GAME_TOOLTIP.index,
                                title = GuideTooltip.RESTART_GAME_TOOLTIP.title.toComposableString(),
                                description = GuideTooltip.RESTART_GAME_TOOLTIP.description.toComposableString(),
                                tooltipBoxConfig = getTooltipConfig(GuideTooltip.RESTART_GAME_TOOLTIP.arrowDirection)
                            ),
                            adviceModifier = Modifier.markForSimpleTooltip(
                                index = GuideTooltip.ADVICE_TOOLTIP.index,
                                title = GuideTooltip.ADVICE_TOOLTIP.title.toComposableString(),
                                description = GuideTooltip.ADVICE_TOOLTIP.description.toComposableString(),
                                tooltipBoxConfig = getTooltipConfig(GuideTooltip.ADVICE_TOOLTIP.arrowDirection)
                            ),
                            dayStoryModifier = Modifier.markForSimpleTooltip(
                                index = GuideTooltip.DAY_STORY_TOOLTIP.index,
                                title = GuideTooltip.DAY_STORY_TOOLTIP.title.toComposableString(),
                                description = GuideTooltip.DAY_STORY_TOOLTIP.description.toComposableString(),
                                tooltipBoxConfig = getTooltipConfig(GuideTooltip.DAY_STORY_TOOLTIP.arrowDirection)
                            ),
                            publicChatModifier = Modifier.markForSimpleTooltip(
                                index = GuideTooltip.PUBLIC_CHAT_TOOLTIP.index,
                                title = GuideTooltip.PUBLIC_CHAT_TOOLTIP.title.toComposableString(),
                                description = GuideTooltip.PUBLIC_CHAT_TOOLTIP.description.toComposableString(),
                                tooltipBoxConfig = getTooltipConfig(GuideTooltip.PUBLIC_CHAT_TOOLTIP.arrowDirection)
                            ),
                            completeVotingModifier = Modifier.markForSimpleTooltip(
                                index = GuideTooltip.COMPLETE_VOTING_TOOLTIP.index,
                                title = GuideTooltip.COMPLETE_VOTING_TOOLTIP.title.toComposableString(),
                                description = GuideTooltip.COMPLETE_VOTING_TOOLTIP.description.toComposableString(),
                                tooltipBoxConfig = getTooltipConfig(GuideTooltip.COMPLETE_VOTING_TOOLTIP.arrowDirection)
                            )
                        )
                    )
                }

                is GameGuideState.NightState -> {
                    GameGuideNightScreen(
                        gameGuideState as GameGuideState.NightState,
                        nightModifiers = NightModifiers(
                            killersChatModifier = Modifier.markForSimpleTooltip(
                                index = GuideTooltip.KILLERS_CHAT_TOOLTIP.index,
                                title = GuideTooltip.KILLERS_CHAT_TOOLTIP.title.toComposableString(),
                                description = GuideTooltip.KILLERS_CHAT_TOOLTIP.description.toComposableString(),
                                tooltipBoxConfig = getTooltipConfig(GuideTooltip.KILLERS_CHAT_TOOLTIP.arrowDirection)
                            ),
                            nightStoryModifier = Modifier.markForSimpleTooltip(
                                index = GuideTooltip.NIGHT_STORY_TOOLTIP.index,
                                title = GuideTooltip.NIGHT_STORY_TOOLTIP.title.toComposableString(),
                                description = GuideTooltip.NIGHT_STORY_TOOLTIP.description.toComposableString(),
                                tooltipBoxConfig = getTooltipConfig(GuideTooltip.NIGHT_STORY_TOOLTIP.arrowDirection)
                            )
                        )
                    )
                }

                is GameGuideState.CompletedState -> {
                    GameGuideCompletionScreen(
                        gameGuideState as GameGuideState.CompletedState,
                        completionModifiers = CompletionModifiers(
                            startNewGameModifier = Modifier.markForSimpleTooltip(
                                index = GuideTooltip.START_NEW_GAME_TOOLTIP.index,
                                title = GuideTooltip.START_NEW_GAME_TOOLTIP.title.toComposableString(),
                                description = GuideTooltip.START_NEW_GAME_TOOLTIP.description.toComposableString(),
                                tooltipBoxConfig = getTooltipConfig(GuideTooltip.START_NEW_GAME_TOOLTIP.arrowDirection)
                            )
                        )
                    )
                }
            }
        }
    }
}

data class DayModifiers(
    var showCharacterModifier: Modifier,
    var votingModifier: Modifier,
    var restartGameModifier: Modifier,
    var adviceModifier: Modifier,
    var dayStoryModifier: Modifier,
    var publicChatModifier: Modifier,
    var completeVotingModifier: Modifier
)

@Composable
fun GameGuideDayScreen(dayModifiers: DayModifiers) {
    Box(Modifier.fillMaxSize()) {
        LazyColumn(
            modifier = Modifier.fillMaxSize(),
            contentPadding = PaddingValues(
                start = 16.dp,
                top = 16.dp,
                end = 16.dp,
                bottom = 16.dp + BottomNavigationHeight.dp
            ),
            verticalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            item {
                Text(
                    modifier = Modifier.padding(top = 8.dp),
                    text = stringResource(Res.string.alive_players, 5.toString()),
                    maxLines = 1,
                    color = MaterialTheme.colorScheme.onBackground,
                    style = MaterialTheme.typography.titleLarge
                )
            }

            item {
                PlayerCardItem(
                    modifier = dayModifiers.showCharacterModifier,
                    playerAvatar = Singleton.shared().defaultAvatarUrls?.getCorrectUrl(),
                    playerName = "Konstantinos",
                    playerDescription = null,
                    endButtonIcon = Res.drawable.person_search_24dp,
                    onEndButtonClick = {}
                )
            }

            item {
                PlayerCardItem(
                    playerAvatar = Singleton.shared().defaultAvatarUrls?.getCorrectUrl(),
                    playerName = "Zoe",
                    playerDescription = null,
                    endButtonIcon = Res.drawable.vote_black_24,
                    onEndButtonClick = {}
                )
            }

            item {
                PlayerCardItem(
                    modifier = dayModifiers.votingModifier,
                    playerAvatar = Singleton.shared().defaultAvatarUrls?.getCorrectUrl(),
                    playerName = "John",
                    playerDescription = null,
                    endButtonIcon = Res.drawable.vote_black_24,
                    onEndButtonClick = {}
                )
            }

            item {
                PlayerCardItem(
                    playerAvatar = Singleton.shared().defaultAvatarUrls?.getCorrectUrl(),
                    playerName = "Maria",
                    playerDescription = null,
                    endButtonIcon = Res.drawable.vote_black_24,
                    onEndButtonClick = {}
                )
            }

            item {
                PlayerCardItem(
                    playerAvatar = Singleton.shared().defaultAvatarUrls?.getCorrectUrl(),
                    playerName = "Helen",
                    playerDescription = null,
                    endButtonIcon = Res.drawable.vote_black_24,
                    onEndButtonClick = {}
                )
            }

            item {
                Text(
                    modifier = Modifier.padding(top = 8.dp),
                    text = stringResource(Res.string.dead_players, 2.toString()),
                    maxLines = 1,
                    color = MaterialTheme.colorScheme.onBackground,
                    style = MaterialTheme.typography.titleLarge
                )
            }

            item {
                PlayerCardItem(
                    playerAvatar = Singleton.shared().defaultAvatarUrls?.getCorrectUrl(),
                    playerName = "Stella",
                    playerDescription = null,
                )

            }

            item {
                PlayerCardItem(
                    playerAvatar = Singleton.shared().defaultAvatarUrls?.getCorrectUrl(),
                    playerName = "George",
                    playerDescription = null,
                )
            }
        }

        BaseBottomAppBar(
            modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 16.dp),
            actions = {
                IconButton(
                    modifier = dayModifiers.restartGameModifier,
                    onClick = { }
                ) {
                    Icon(
                        painter = painterResource(Res.drawable.ic_restart_white_24dp),
                        contentDescription = stringResource(Res.string.new_game),
                        tint = MaterialTheme.colorScheme.onSurface
                    )
                }

                IconButton(
                    modifier = dayModifiers.adviceModifier,
                    onClick = { }
                ) {
                    Icon(
                        painter = painterResource(Res.drawable.ic_lightbulb_on_outline_white_24dp),
                        contentDescription = stringResource(Res.string.advice),
                        tint = MaterialTheme.colorScheme.onSurface
                    )
                }

                IconButton(
                    modifier = dayModifiers.publicChatModifier,
                    onClick = { }
                ) {
                    Icon(
                        painter = painterResource(Res.drawable.icon_chat_24),
                        contentDescription = stringResource(Res.string.public_chat),
                        tint = MaterialTheme.colorScheme.onSurface
                    )
                }

                IconButton(
                    modifier = dayModifiers.dayStoryModifier,
                    onClick = { }
                ) {
                    Icon(
                        painter = painterResource(Res.drawable.sound_sampler_24dp),
                        contentDescription = stringResource(Res.string.day_story),
                        tint = MaterialTheme.colorScheme.onSurface
                    )
                }
            },
            floatingActionButton = {
                BaseFloatingButton(
                    modifier = dayModifiers.completeVotingModifier,
                    icon = Res.drawable.person_complete_24dp,
                    label = stringResource(Res.string.finish_voting),
                    onClick = {}
                )
            }
        )
    }
}

data class NightModifiers(
    var killersChatModifier: Modifier,
    var nightStoryModifier: Modifier
)

@Composable
fun GameGuideNightScreen(
    nightState: GameGuideState.NightState,
    nightModifiers: NightModifiers
) {
    Box(Modifier.fillMaxSize().padding(16.dp)) {
        GenericScreen(
            nightState.image,
            nightState.title,
            nightState.description
        )

        BaseBottomAppBar(
            modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 16.dp),
            actions = {
                IconButton(
                    modifier = nightModifiers.killersChatModifier,
                    onClick = { }
                ) {
                    Icon(
                        painter = painterResource(Res.drawable.icon_chat_24),
                        contentDescription = stringResource(Res.string.public_chat),
                        tint = MaterialTheme.colorScheme.onSurface
                    )
                }
            },
            floatingActionButton = {
                BaseFloatingButton(
                    modifier = nightModifiers.nightStoryModifier,
                    icon = Res.drawable.night_moon_cloud_black_24,
                    label = stringResource(Res.string.night_instructions),
                    onClick = {}
                )
            }
        )
    }
}

data class CompletionModifiers(
    var startNewGameModifier: Modifier
)

@Composable
fun GameGuideCompletionScreen(
    completedState: GameGuideState.CompletedState,
    completionModifiers: CompletionModifiers
) {
    Box(Modifier.fillMaxSize().padding(16.dp)) {
        GenericScreen(
            completedState.image,
            completedState.title,
            completedState.description
        )

        BaseBottomAppBar(
            modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 16.dp),
            actions = {},
            floatingActionButton = {
                BaseFloatingButton(
                    modifier = completionModifiers.startNewGameModifier,
                    icon = Res.drawable.ic_restart_white_24dp,
                    label = stringResource(Res.string.new_game),
                    onClick = {}
                )
            }
        )
    }
}

@Composable
fun GenericScreen(
    image: FileResource,
    title: StringModel,
    description: StringModel,
    buttonText: StringModel? = null,
    onButtonClick: (() -> Unit)? = null
) {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        AnimatedImage(
            modifier = Modifier
                .size(200.dp)
                .background(
                    color = Color.White, shape = CircleShape
                ),
            image = image
        )
        Spacer(modifier = Modifier.height(24.dp))
        Text(
            text = title.toComposableString(),
            style = MaterialTheme.typography.titleMedium,
            color = MaterialTheme.colorScheme.onBackground,
            textAlign = TextAlign.Center
        )
        Spacer(modifier = Modifier.height(8.dp))
        Text(
            text = description.toComposableString(),
            style = MaterialTheme.typography.bodyLarge,
            color = MaterialTheme.colorScheme.onBackground,
            textAlign = TextAlign.Center
        )
        Spacer(modifier = Modifier.height(16.dp))

        buttonText?.let {
            BaseButtonContained(
                text = buttonText.toComposableString(),
                onClick = { onButtonClick?.invoke() }
            )
        }
    }
}

@Composable
private fun HandleGameGuideActions(
    gameGuideViewModel: GameGuideViewModel,
    navController: NavHostController
) {
    val context = getContext()

    LaunchedEffect(Unit) {
        gameGuideViewModel.actions.collect { action ->
            when (action) {
                GameGuideActions.OpenCharacters -> {
                    navController.navigate(AppRoute.WebView(
                        title = StringModel(Res.string.characters).toString(context),
                        url = StringModel(BaseLinks.PALERMO_WEBSITE_CHARACTERS.url).toString(
                            context
                        )
                    ))
                }
            }
        }
    }
}

enum class GuideTooltip(
    val index: Int,
    val title: StringModel,
    val description: StringModel,
    val arrowDirection: TooltipBoxArrowDirection
) {
    GAME_GUIDE_TOOLTIP(
        index = 0,
        title = StringModel(Res.string.game_guide_tooltip_title),
        description = StringModel(Res.string.game_guide_tooltip_description),
        arrowDirection = TooltipBoxArrowDirection.Bottom
    ),

    SHOW_CHARACTER_TOOLTIP(
        index = 0,
        title = StringModel(Res.string.see_character_tooltip_title),
        description = StringModel(Res.string.see_character_tooltip_description),
        arrowDirection = TooltipBoxArrowDirection.Bottom
    ),
    DAY_STORY_TOOLTIP(
        index = 1,
        title = StringModel(Res.string.day_story_tooltip_title),
        description = StringModel(Res.string.day_story_tooltip_description),
        arrowDirection = TooltipBoxArrowDirection.Top
    ),
    PUBLIC_CHAT_TOOLTIP(
        index = 2,
        title = StringModel(Res.string.chat_tooltip_title),
        description = StringModel(Res.string.chat_tooltip_description),
        arrowDirection = TooltipBoxArrowDirection.Top
    ),
    ADVICE_TOOLTIP(
        index = 3,
        title = StringModel(Res.string.advice_tooltip_title),
        description = StringModel(Res.string.advice_tooltip_description),
        arrowDirection = TooltipBoxArrowDirection.Top
    ),
    RESTART_GAME_TOOLTIP(
        index = 4,
        title = StringModel(Res.string.restart_game_tooltip_title),
        description = StringModel(Res.string.restart_game_tooltip_description),
        arrowDirection = TooltipBoxArrowDirection.Top
    ),
    VOTING_TOOLTIP(
        index = 5,
        title = StringModel(Res.string.voting_tooltip_title),
        description = StringModel(Res.string.voting_tooltip_description),
        arrowDirection = TooltipBoxArrowDirection.Bottom
    ),
    COMPLETE_VOTING_TOOLTIP(
        index = 6,
        title = StringModel(Res.string.complete_voting_tooltip_title),
        description = StringModel(Res.string.complete_voting_tooltip_description),
        arrowDirection = TooltipBoxArrowDirection.Top
    ),
    KILLERS_CHAT_TOOLTIP(
        index = 7,
        title = StringModel(Res.string.night_killers_chat_tooltip_title),
        description = StringModel(Res.string.night_killers_chat_tooltip_description),
        arrowDirection = TooltipBoxArrowDirection.Top
    ),
    NIGHT_STORY_TOOLTIP(
        index = 8,
        title = StringModel(Res.string.night_story_tooltip_title),
        description = StringModel(Res.string.night_story_tooltip_description),
        arrowDirection = TooltipBoxArrowDirection.Top
    ),
    START_NEW_GAME_TOOLTIP(
        index = 9,
        title = StringModel(Res.string.start_new_game_tooltip_title),
        description = StringModel(Res.string.start_new_game_tooltip_description),
        arrowDirection = TooltipBoxArrowDirection.Top
    )
}

@Composable
fun getTooltipConfig(arrowDirection: TooltipBoxArrowDirection): TooltipBoxConfig {
    return TooltipBoxConfig(
        backgroundColor = BaseColors.LightSurface.color,
        arrowColor = BaseColors.LightSurface.color,
        arrowHeadSize = 25f,
        tooltipBoxArrowDirection = arrowDirection,
        titleTextStyle = MaterialTheme.typography.titleLarge.copy(color = BaseColors.DarkBackground.color),
        descriptionTextStyle = MaterialTheme.typography.bodyLarge.copy(color = BaseColors.DarkBackground.color)
    )
}

Creating this class, it is crashing in iOS when trying to show the first tooltip.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions