package com.suno.android.ui.screens.hooks.clip import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.suno.android.common_core_utils.Id import com.suno.android.common_core_utils.constants.ReactionType import com.suno.android.common_core_utils.model.UserHandle import com.suno.android.common_data.comments.MediaEntity import com.suno.android.common_data.mappers.clips.SongListData import com.suno.android.common_data.mappers.clips.xAsSongListDataOrNull import com.suno.android.common_data.mappers.hooks.LocalHookData import com.suno.android.common_ui.components.bottom_sheet.ShareLinkBottomSheet import com.suno.android.common_ui.components.bottom_sheet.StateBottomSheet import com.suno.android.common_ui.components.bottom_sheet.handleShareClicked import com.suno.android.omniplayer.OmniPlayerPage import com.suno.android.ui.bottom_sheets.comments.CommentsBottomSheet import com.suno.android.ui.bottom_sheets.create.RemixTypeSelectionSheetContent import com.suno.android.ui.bottom_sheets.song_actions.SongActionBottomSheetConsumerEvent import com.suno.android.ui.bottom_sheets.song_actions.SongActionsBottomSheet import com.suno.android.ui.screens.create.CreatePromptForm import com.suno.android.ui.screens.create.audio.CreateAudioState import com.suno.android.ui.screens.hooks.feed.BottomSheetDismissalAction import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @Composable fun HookClipPlayerContent( controller: HookClipPlayerController, onDismiss: (BottomSheetDismissalAction) -> Unit, onArtistHandleClick: (handle: UserHandle) -> Unit, navigateToAssetEdit: (songId: Id) -> Unit, onReusePromptClick: (CreatePromptForm) -> Unit, navigateToRemix: (songId: Id, task: CreateAudioState.AudioCreateTask) -> Unit, ) { val viewState by controller.state.collectAsStateWithLifecycle() val context = LocalContext.current val clipboardManager = LocalClipboardManager.current val snackbarHostState = remember { SnackbarHostState() } val coroutineScope = rememberCoroutineScope() val pagerState = rememberPagerState( initialPage = 0, pageCount = { 1 }, ) LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { controller.sendEvent(HookClipPlayerEvent.OnResume) } LifecycleEventEffect(Lifecycle.Event.ON_PAUSE) { controller.sendEvent(HookClipPlayerEvent.OnPause) } LaunchedEffect(viewState.clip.clipId) { controller.sendEvent(HookClipPlayerEvent.StartPlayback) } LaunchedEffect(Unit) { controller.effects.collectLatest { effect -> when (effect) { is HookClipPlayerEffect.ShareLink -> { coroutineScope.launch { handleShareClicked( platform = effect.sharePlatform, shareLink = effect.link.url, context = context, clipboardManager = clipboardManager, ) } } is HookClipPlayerEffect.ShareVideo -> { navigateToAssetEdit(effect.clipId) } is HookClipPlayerEffect.NavigateToProfile -> { onArtistHandleClick(effect.userHandle) } } } } Scaffold( modifier = Modifier .fillMaxSize() .background(Color.Black), snackbarHost = { SnackbarHost(snackbarHostState) }, ) { innerPadding -> Column( modifier = Modifier .background(Color.Black) .padding(innerPadding), ) { val clip = viewState.clip OmniPlayerPage( page = 0, parentPagerState = pagerState, exoPlayer = controller.providePlayer(), mediaType = clip.mediaType, timestamp = viewState.timestamp, percentageComplete = viewState.progressPercentage, albumArtImageUrl = clip.albumImageUrl, videoCoverUrl = clip.videoCoverUrl, titleTextString = clip.nowPlayingTitle, commentCount = clip.commentCount, upvoteCount = clip.upvoteCount, artistAvatarImageUrl = clip.artistAvatarUrl, artistName = clip.artistName, onArtistHandleClicked = { targetHandle -> val handle = targetHandle ?: clip.handle handle?.let { controller.sendEvent(HookClipPlayerEvent.NavigateToProfile(it)) } }, isPlaying = viewState.isPlaying, lyricsString = clip.displayLyrics, caption = clip.caption, captionMentions = clip.captionMentions, liked = clip.reaction == ReactionType.LIKE, onPlayPauseClicked = { controller.sendEvent(HookClipPlayerEvent.PlayPause) }, onDismissClicked = { onDismiss(BottomSheetDismissalAction.CloseButton) }, onScrubbed = { percentage -> controller.sendEvent(HookClipPlayerEvent.Seek(percentage)) }, onCommentsClicked = { controller.sendEvent(HookClipPlayerEvent.ShowComments) }, onOverflowMenuClicked = { controller.sendEvent(HookClipPlayerEvent.ShowSongActions) }, onLikeClicked = { controller.sendEvent(HookClipPlayerEvent.ToggleLike) }, onShareClicked = { controller.sendEvent(HookClipPlayerEvent.ShowShare) }, onReplayClicked = { controller.sendEvent(HookClipPlayerEvent.Seek(0f)) }, onRemixClicked = if (viewState.isRemixClipGateEnabled && viewState.clip.canRemix == true) { { controller.sendEvent(HookClipPlayerEvent.ShowRemixSelectionSheet) } } else { null }, onCenterTap = { controller.sendEvent(HookClipPlayerEvent.PlayPause) }, onLeftTap = { controller.sendEvent(HookClipPlayerEvent.ScrubBackward) }, onRightTap = { controller.sendEvent(HookClipPlayerEvent.ScrubForward) }, ) } HookClipPlayerBottomSheets( hook = viewState.hook, state = viewState.bottomSheetState, controller = controller, snackbarHostState = snackbarHostState, coroutineScope = coroutineScope, onDismissRequest = { controller.sendEvent(HookClipPlayerEvent.OnCloseBottomSheetRequested) }, onArtistHandleClick = onArtistHandleClick, navigateToAssetEdit = navigateToAssetEdit, onReusePromptClick = onReusePromptClick, navigateToRemix = { songId, task -> navigateToRemix(songId, task) }, ) } } @Composable private fun HookClipPlayerBottomSheets( hook: LocalHookData, state: HookClipPlayerUiState.BottomSheetState, controller: HookClipPlayerController, snackbarHostState: SnackbarHostState, coroutineScope: CoroutineScope, onDismissRequest: () -> Unit, onArtistHandleClick: (handle: UserHandle) -> Unit, navigateToAssetEdit: (songId: Id) -> Unit, onReusePromptClick: (CreatePromptForm) -> Unit, navigateToRemix: (songId: Id, task: CreateAudioState.AudioCreateTask) -> Unit, modifier: Modifier = Modifier, ) { StateBottomSheet( state = state, isShowing = { it != HookClipPlayerUiState.BottomSheetState.Hidden }, showHandle = { it is HookClipPlayerUiState.BottomSheetState.HasHandle }, onDismissRequest = onDismissRequest, modifier = modifier, ) { when (state) { HookClipPlayerUiState.BottomSheetState.Hidden -> Unit is HookClipPlayerUiState.BottomSheetState.CommentsVisible -> { val clip = hook.clip CommentsBottomSheet( mediaEntity = MediaEntity.ClipEntity(clipId = clip.clipId.map()), authorId = clip.artistUserId, authorHandle = clip.handle, onDismiss = onDismissRequest, onProfileClicked = { userHandle -> onArtistHandleClick(userHandle) }, ) } is HookClipPlayerUiState.BottomSheetState.ShareVisible -> ShareLinkBottomSheet( isShowShareVideoGateEnabled = state.showShareVideo, onShareClicked = { platform -> controller.sendEvent(HookClipPlayerEvent.StartShare(platform)) }, onDismiss = onDismissRequest, ) is HookClipPlayerUiState.BottomSheetState.SongActionsVisible -> hook.clip.xAsSongListDataOrNull()?.let { song -> SongActionsBottomSheet( song = song, canRemoveFromPlaylist = false, isShowShareVideoGateEnabled = state.showShareVideo, songActionBottomSheetConsumerEvents = { event -> when (event) { is SongActionBottomSheetConsumerEvent.OnReusePromptClicked -> { onReusePromptClick.invoke(event.formData) } is SongActionBottomSheetConsumerEvent.OnSongDeleted -> { Unit } is SongActionBottomSheetConsumerEvent.OnSongRenamed -> { Unit } is SongActionBottomSheetConsumerEvent.OnSongReported -> { coroutineScope.launch { snackbarHostState.showSnackbar(event.message) } } is SongActionBottomSheetConsumerEvent.OnSongUndoDeleted -> { Unit } is SongActionBottomSheetConsumerEvent.OnSongUndoReported -> { TODO() } SongActionBottomSheetConsumerEvent.OnUpgradeClicked -> { TODO() } SongActionBottomSheetConsumerEvent.DismissSongActionsContainerHost -> { onDismissRequest.invoke() } is SongActionBottomSheetConsumerEvent.NavigateToRemix -> { navigateToRemix.invoke( event.songId, event.task, ) } is SongActionBottomSheetConsumerEvent.NavigateToShareVideo -> { navigateToAssetEdit.invoke(event.songId) } is SongActionBottomSheetConsumerEvent.OnShowSnackbar -> { coroutineScope.launch { snackbarHostState.showSnackbar(event.message) } } is SongActionBottomSheetConsumerEvent.OnRemixClicked -> { } is SongActionBottomSheetConsumerEvent.OnRemoveFromPlaylistTapped -> TODO() } }, hook = hook, ) } is HookClipPlayerUiState.BottomSheetState.RemixTypeSelectionVisible -> { val clip = hook.clip RemixTypeSelectionSheetContent( clip = clip, onDismiss = { onDismissRequest.invoke() }, onOptionClick = { remixType -> navigateToRemix(clip.clipId, remixType.task) }, ) } } } }