Per-stream visibility: isPublic field, Room migration 6→7, publish toggle in CreatePlan
This commit is contained in:
@@ -16,7 +16,7 @@ import com.omixlab.lckcontrol.data.local.entity.StreamPlanEntity
|
||||
StreamPlanEntity::class,
|
||||
StreamDestinationEntity::class,
|
||||
],
|
||||
version = 6,
|
||||
version = 7,
|
||||
exportSchema = false,
|
||||
)
|
||||
abstract class LckDatabase : RoomDatabase() {
|
||||
@@ -116,5 +116,11 @@ abstract class LckDatabase : RoomDatabase() {
|
||||
db.execSQL("ALTER TABLE linked_accounts ADD COLUMN streamKey TEXT")
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_6_7 = object : Migration(6, 7) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("ALTER TABLE stream_plans ADD COLUMN isPublic INTEGER NOT NULL DEFAULT 1")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,5 +10,6 @@ data class StreamPlanEntity(
|
||||
val status: String = "DRAFT",
|
||||
val executionMode: String = "IN_GAME",
|
||||
val gameId: String = "",
|
||||
val isPublic: Boolean = true,
|
||||
val createdAt: Long = System.currentTimeMillis(),
|
||||
)
|
||||
|
||||
@@ -101,6 +101,7 @@ data class CreateStreamPlanRequest(
|
||||
val name: String,
|
||||
val executionMode: String? = null,
|
||||
val gameId: String? = null,
|
||||
val isPublic: Boolean? = null,
|
||||
val destinations: List<CreateDestinationRequest>,
|
||||
)
|
||||
|
||||
@@ -109,6 +110,7 @@ data class UpdateStreamPlanRequest(
|
||||
val name: String? = null,
|
||||
val executionMode: String? = null,
|
||||
val gameId: String? = null,
|
||||
val isPublic: Boolean? = null,
|
||||
val destinations: List<CreateDestinationRequest>? = null,
|
||||
)
|
||||
|
||||
@@ -131,6 +133,7 @@ data class StreamPlanResponse(
|
||||
val status: String,
|
||||
val executionMode: String? = null,
|
||||
val gameId: String? = null,
|
||||
val isPublic: Boolean = true,
|
||||
val createdAt: String,
|
||||
val updatedAt: String,
|
||||
val destinations: List<StreamDestinationResponse>,
|
||||
|
||||
@@ -48,11 +48,13 @@ class StreamPlanRepository @Inject constructor(
|
||||
destinations: List<StreamDestination>,
|
||||
executionMode: String = "IN_GAME",
|
||||
gameId: String = "",
|
||||
isPublic: Boolean = true,
|
||||
): StreamPlan {
|
||||
val request = CreateStreamPlanRequest(
|
||||
name = name,
|
||||
executionMode = executionMode,
|
||||
gameId = gameId.ifBlank { null },
|
||||
isPublic = isPublic,
|
||||
destinations = destinations.map { dest ->
|
||||
CreateDestinationRequest(
|
||||
linkedAccountId = dest.linkedAccountId.ifBlank { null },
|
||||
@@ -78,11 +80,13 @@ class StreamPlanRepository @Inject constructor(
|
||||
destinations: List<StreamDestination>,
|
||||
executionMode: String,
|
||||
gameId: String,
|
||||
isPublic: Boolean = true,
|
||||
): StreamPlan {
|
||||
val request = UpdateStreamPlanRequest(
|
||||
name = name,
|
||||
executionMode = executionMode,
|
||||
gameId = gameId.ifBlank { null },
|
||||
isPublic = isPublic,
|
||||
destinations = destinations.map { dest ->
|
||||
CreateDestinationRequest(
|
||||
linkedAccountId = dest.linkedAccountId.ifBlank { null },
|
||||
@@ -146,6 +150,7 @@ class StreamPlanRepository @Inject constructor(
|
||||
status = remote.status,
|
||||
executionMode = remote.executionMode ?: "IN_GAME",
|
||||
gameId = remote.gameId ?: "",
|
||||
isPublic = remote.isPublic,
|
||||
)
|
||||
val destEntities = remote.destinations.map { d ->
|
||||
StreamDestinationEntity(
|
||||
@@ -173,6 +178,7 @@ class StreamPlanRepository @Inject constructor(
|
||||
status = plan.status,
|
||||
executionMode = plan.executionMode,
|
||||
gameId = plan.gameId,
|
||||
isPublic = plan.isPublic,
|
||||
destinations = destinations.map { it.toStreamDestination() },
|
||||
)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ object DatabaseModule {
|
||||
@Singleton
|
||||
fun provideDatabase(@ApplicationContext context: Context): LckDatabase =
|
||||
Room.databaseBuilder(context, LckDatabase::class.java, "lck_control.db")
|
||||
.addMigrations(LckDatabase.MIGRATION_1_2, LckDatabase.MIGRATION_2_3, LckDatabase.MIGRATION_3_4, LckDatabase.MIGRATION_4_5, LckDatabase.MIGRATION_5_6)
|
||||
.addMigrations(LckDatabase.MIGRATION_1_2, LckDatabase.MIGRATION_2_3, LckDatabase.MIGRATION_3_4, LckDatabase.MIGRATION_4_5, LckDatabase.MIGRATION_5_6, LckDatabase.MIGRATION_6_7)
|
||||
.build()
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -206,9 +206,9 @@ fun DashboardScreen(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text("Show on Portal", style = MaterialTheme.typography.titleSmall)
|
||||
Text("Public Profile", style = MaterialTheme.typography.titleSmall)
|
||||
Text(
|
||||
"Allow others to discover your live streams",
|
||||
"Allow others to find your profile",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
|
||||
@@ -30,6 +30,7 @@ import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -38,6 +39,7 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
@@ -56,6 +58,7 @@ fun CreatePlanScreen(
|
||||
val planName by viewModel.planName.collectAsStateWithLifecycle()
|
||||
val executionMode by viewModel.executionMode.collectAsStateWithLifecycle()
|
||||
val gameId by viewModel.gameId.collectAsStateWithLifecycle()
|
||||
val isPublic by viewModel.isPublic.collectAsStateWithLifecycle()
|
||||
val connectedClients by viewModel.connectedClients.collectAsStateWithLifecycle()
|
||||
val destinations by viewModel.destinations.collectAsStateWithLifecycle()
|
||||
val linkedAccounts by viewModel.linkedAccounts.collectAsStateWithLifecycle()
|
||||
@@ -101,6 +104,27 @@ fun CreatePlanScreen(
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text("Publish to Portal", style = MaterialTheme.typography.titleSmall)
|
||||
Text(
|
||||
"Show this stream in the public feed",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
Switch(
|
||||
checked = isPublic,
|
||||
onCheckedChange = viewModel::setIsPublic,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text("Execution Mode", style = MaterialTheme.typography.titleMedium)
|
||||
|
||||
@@ -66,6 +66,9 @@ class CreatePlanViewModel @Inject constructor(
|
||||
private val _gameId = MutableStateFlow("")
|
||||
val gameId: StateFlow<String> = _gameId.asStateFlow()
|
||||
|
||||
private val _isPublic = MutableStateFlow(true)
|
||||
val isPublic: StateFlow<Boolean> = _isPublic.asStateFlow()
|
||||
|
||||
private val _connectedClients = MutableStateFlow<List<ConnectedClientInfo>>(emptyList())
|
||||
val connectedClients: StateFlow<List<ConnectedClientInfo>> = _connectedClients.asStateFlow()
|
||||
|
||||
@@ -134,6 +137,7 @@ class CreatePlanViewModel @Inject constructor(
|
||||
_planName.value = plan.name
|
||||
_executionMode.value = plan.executionMode
|
||||
_gameId.value = plan.gameId
|
||||
_isPublic.value = plan.isPublic
|
||||
|
||||
// Wait for linked accounts to load for label resolution
|
||||
val accounts = accountRepository.getAccounts()
|
||||
@@ -172,6 +176,10 @@ class CreatePlanViewModel @Inject constructor(
|
||||
_gameId.value = gameId
|
||||
}
|
||||
|
||||
fun setIsPublic(isPublic: Boolean) {
|
||||
_isPublic.value = isPublic
|
||||
}
|
||||
|
||||
fun addDestination() {
|
||||
_destinations.value = _destinations.value + DestinationInput()
|
||||
}
|
||||
@@ -243,9 +251,9 @@ class CreatePlanViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
val plan = if (isEditMode) {
|
||||
streamPlanRepository.updatePlan(editingPlanId!!, name, streamDests, _executionMode.value, _gameId.value)
|
||||
streamPlanRepository.updatePlan(editingPlanId!!, name, streamDests, _executionMode.value, _gameId.value, _isPublic.value)
|
||||
} else {
|
||||
streamPlanRepository.createPlan(name, streamDests, _executionMode.value, _gameId.value)
|
||||
streamPlanRepository.createPlan(name, streamDests, _executionMode.value, _gameId.value, _isPublic.value)
|
||||
}
|
||||
onSaved(plan.planId)
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -10,6 +10,7 @@ data class StreamPlan(
|
||||
val destinations: List<StreamDestination> = emptyList(),
|
||||
val executionMode: String = "IN_GAME",
|
||||
val gameId: String = "",
|
||||
val isPublic: Boolean = true,
|
||||
) : Parcelable {
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
@@ -19,6 +20,7 @@ data class StreamPlan(
|
||||
destinations = parcel.createTypedArrayList(StreamDestination.CREATOR) ?: emptyList(),
|
||||
executionMode = if (parcel.dataAvail() > 0) parcel.readString() ?: "IN_GAME" else "IN_GAME",
|
||||
gameId = if (parcel.dataAvail() > 0) parcel.readString() ?: "" else "",
|
||||
isPublic = if (parcel.dataAvail() > 0) parcel.readInt() == 1 else true,
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
@@ -28,6 +30,7 @@ data class StreamPlan(
|
||||
parcel.writeTypedList(destinations)
|
||||
parcel.writeString(executionMode)
|
||||
parcel.writeString(gameId)
|
||||
parcel.writeInt(if (isPublic) 1 else 0)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int = 0
|
||||
|
||||
Reference in New Issue
Block a user