From 0a70df9e2d08a412d340ba95243cc77909838e60 Mon Sep 17 00:00:00 2001 From: tapframe <85391825+tapframe@users.noreply.github.com> Date: Wed, 11 Mar 2026 21:55:12 +0530 Subject: [PATCH] feat: enhance iOS run script to support simulator and physical device options --- .../app/features/streams/StreamsScreen.kt | 82 ++++++++++++++----- scripts/run-mobile.sh | 72 ++++++++++++++-- 2 files changed, 127 insertions(+), 27 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt index d52a67a8e..0a8ab1d68 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt @@ -85,16 +85,21 @@ fun StreamsScreen( StreamsRepository.load(type, videoId) } + val heroArtwork = if (isEpisode) { + episodeThumbnail ?: background ?: poster + } else { + background ?: poster + } + Box( modifier = modifier .fillMaxSize() .background(MaterialTheme.colorScheme.background), ) { // Background artwork - val backdropUrl = background ?: poster - if (backdropUrl != null) { + if (heroArtwork != null) { AsyncImage( - model = backdropUrl, + model = heroArtwork, contentDescription = null, modifier = Modifier .fillMaxSize() @@ -105,10 +110,12 @@ fun StreamsScreen( Box( modifier = Modifier .fillMaxSize() - .background(Color.Black.copy(alpha = 0.82f)), + .background(Color.Black.copy(alpha = if (isEpisode) 0.9f else 0.82f)), ) } + val streamBlendColor = MaterialTheme.colorScheme.background + // Main content column Column(modifier = Modifier.fillMaxSize()) { // Hero block @@ -117,7 +124,7 @@ fun StreamsScreen( seasonNumber = seasonNumber, episodeNumber = episodeNumber, episodeTitle = episodeTitle ?: title, - thumbnail = episodeThumbnail ?: background ?: poster, + thumbnail = heroArtwork, showTitle = title, ) } else { @@ -127,18 +134,42 @@ fun StreamsScreen( ) } - // Provider filter chips - ProviderFilterRow( - groups = uiState.groups, - selectedFilter = uiState.selectedFilter, - onFilterSelected = { addonId -> StreamsRepository.selectFilter(addonId) }, - ) + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + ) { + if (isEpisode) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(132.dp) + .background( + Brush.verticalGradient( + colors = listOf( + streamBlendColor.copy(alpha = 0.98f), + streamBlendColor.copy(alpha = 0.84f), + streamBlendColor.copy(alpha = 0.52f), + Color.Transparent, + ), + ), + ), + ) + } - // Stream list - StreamList( - uiState = uiState, - modifier = Modifier.weight(1f), - ) + Column(modifier = Modifier.fillMaxSize()) { + ProviderFilterRow( + groups = uiState.groups, + selectedFilter = uiState.selectedFilter, + onFilterSelected = { addonId -> StreamsRepository.selectFilter(addonId) }, + ) + + StreamList( + uiState = uiState, + modifier = Modifier.weight(1f), + ) + } + } } // Back button overlay (top-left) @@ -221,6 +252,8 @@ private fun EpisodeHeroBlock( showTitle: String, modifier: Modifier = Modifier, ) { + val heroBlendColor = MaterialTheme.colorScheme.background + Box( modifier = modifier .fillMaxWidth() @@ -242,11 +275,12 @@ private fun EpisodeHeroBlock( .fillMaxSize() .background( Brush.verticalGradient( - colors = listOf( - Color.Transparent, - Color.Black.copy(alpha = 0.5f), - Color.Black.copy(alpha = 0.9f), - Color.Black.copy(alpha = 0.98f), + colorStops = arrayOf( + 0.0f to Color.Transparent, + 0.58f to Color.Transparent, + 0.8f to Color.Black.copy(alpha = 0.42f), + 0.93f to heroBlendColor.copy(alpha = 0.84f), + 1.0f to heroBlendColor.copy(alpha = 0.97f), ), startY = 0f, endY = Float.POSITIVE_INFINITY, @@ -254,6 +288,12 @@ private fun EpisodeHeroBlock( ), ) + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Black.copy(alpha = 0.1f)), + ) + // Safe-area push-down for status bar, then content pinned to bottom Column( modifier = Modifier diff --git a/scripts/run-mobile.sh b/scripts/run-mobile.sh index 5b8bde5b9..a79cced0f 100755 --- a/scripts/run-mobile.sh +++ b/scripts/run-mobile.sh @@ -18,10 +18,12 @@ usage() { cat <<'EOF' Usage: ./scripts/run-mobile.sh android - ./scripts/run-mobile.sh ios + ./scripts/run-mobile.sh ios s + ./scripts/run-mobile.sh ios p Builds the debug app for the selected platform, installs it on the first running -Android emulator or the configured iOS device target, and launches the app. +Android emulator, a booted iOS simulator, or the configured iOS physical device, +and launches the app. EOF } @@ -94,7 +96,44 @@ run_android() { adb -s "$serial" shell am start -n "$ANDROID_APP_ID/$ANDROID_ACTIVITY" } -run_ios() { +run_ios_simulator() { + require_command xcodebuild + require_command xcrun + + local simulator_id + simulator_id="$(first_booted_ios_simulator)" + + if [[ -z "$simulator_id" ]]; then + echo "No booted iOS simulator found." >&2 + echo "Boot a simulator first, then rerun: ./scripts/run-mobile.sh ios s" >&2 + exit 1 + fi + + local simulator_app_path + simulator_app_path="$IOS_DERIVED_DATA/Build/Products/Debug-iphonesimulator/$IOS_APP_NAME" + + echo "Building iOS debug app for simulator $simulator_id..." + xcodebuild \ + -project "$IOS_PROJECT" \ + -scheme "$IOS_SCHEME" \ + -configuration Debug \ + -destination "id=$simulator_id" \ + -derivedDataPath "$IOS_DERIVED_DATA" \ + build + + if [[ ! -d "$simulator_app_path" ]]; then + echo "Expected iOS simulator app not found at: $simulator_app_path" >&2 + exit 1 + fi + + echo "Installing on simulator $simulator_id..." + xcrun simctl install "$simulator_id" "$simulator_app_path" + + echo "Launching app..." + xcrun simctl launch "$simulator_id" "$IOS_BUNDLE_ID" +} + +run_ios_physical() { require_command xcodebuild require_command xcrun @@ -128,22 +167,43 @@ run_ios() { fi echo "Preferred iOS device not available: $IOS_PREFERRED_DEVICE_MODEL" >&2 - echo "Connect and unlock that device, then rerun: ./scripts/run-mobile.sh ios" >&2 + echo "Connect and unlock that device, then rerun: ./scripts/run-mobile.sh ios p" >&2 exit 1 } main() { - if [[ $# -ne 1 ]]; then + if [[ $# -lt 1 ]]; then usage exit 1 fi case "$1" in android) + if [[ $# -ne 1 ]]; then + usage + exit 1 + fi run_android ;; ios) - run_ios + if [[ $# -ne 2 ]]; then + usage + exit 1 + fi + + case "$2" in + s) + run_ios_simulator + ;; + p) + run_ios_physical + ;; + *) + echo "Unknown iOS target: $2" >&2 + usage + exit 1 + ;; + esac ;; -h|--help|help) usage