166 lines
5.1 KiB
Kotlin
166 lines
5.1 KiB
Kotlin
package stories.presentation
|
|
|
|
import android.net.Uri
|
|
import androidx.compose.runtime.getValue
|
|
import androidx.compose.runtime.mutableStateListOf
|
|
import androidx.compose.runtime.mutableStateOf
|
|
import androidx.compose.runtime.setValue
|
|
import androidx.compose.ui.geometry.Offset
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.lifecycle.ViewModel
|
|
import androidx.lifecycle.viewModelScope
|
|
import core.network.ServerConfig
|
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
import kotlinx.coroutines.flow.asStateFlow
|
|
import kotlinx.coroutines.launch
|
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
|
import okhttp3.MultipartBody
|
|
import okhttp3.RequestBody.Companion.asRequestBody
|
|
import stories.data.remote.api.CreateStoryRequest
|
|
import stories.data.remote.api.StoryApi
|
|
import java.io.File
|
|
import javax.inject.Inject
|
|
|
|
data class TextObject(
|
|
val id: Long = System.currentTimeMillis(),
|
|
val text: String,
|
|
val offset: Offset = Offset(100f, 100f),
|
|
val color: Color = Color.White,
|
|
val fontSize: Float = 24f,
|
|
val scale: Float = 1f
|
|
)
|
|
|
|
data class StickerObject(
|
|
val id: Long = System.currentTimeMillis(),
|
|
val emoji: String,
|
|
val offset: Offset = Offset(200f, 200f),
|
|
val scale: Float = 1.5f
|
|
)
|
|
|
|
sealed class StoryTool {
|
|
object None : StoryTool()
|
|
object Text : StoryTool()
|
|
object Crop : StoryTool()
|
|
object Stickers : StoryTool()
|
|
object Brush : StoryTool()
|
|
object Filters : StoryTool()
|
|
object Trim : StoryTool()
|
|
}
|
|
|
|
@HiltViewModel
|
|
class CreateStoryViewModel @Inject constructor(
|
|
private val storyApi: StoryApi,
|
|
private val serverConfig: ServerConfig
|
|
) : ViewModel() {
|
|
|
|
var selectedMediaUri by mutableStateOf<Uri?>(null)
|
|
private set
|
|
|
|
var isVideo by mutableStateOf(false)
|
|
private set
|
|
|
|
var currentTool by mutableStateOf<StoryTool>(StoryTool.None)
|
|
private set
|
|
|
|
var bgColor by mutableStateOf(Color(0xFF1E1E2E))
|
|
private set
|
|
|
|
val textObjects = mutableStateListOf<TextObject>()
|
|
val stickerObjects = mutableStateListOf<StickerObject>()
|
|
|
|
var isUploading by mutableStateOf(false)
|
|
private set
|
|
|
|
private val _uploadSuccess = MutableStateFlow(false)
|
|
val uploadSuccess = _uploadSuccess.asStateFlow()
|
|
|
|
private val _errorMessage = MutableStateFlow<String?>(null)
|
|
val errorMessage = _errorMessage.asStateFlow()
|
|
|
|
fun onMediaSelected(uri: Uri, isVideo: Boolean) {
|
|
this.selectedMediaUri = uri
|
|
this.isVideo = isVideo
|
|
if (isVideo) {
|
|
currentTool = StoryTool.Trim
|
|
} else {
|
|
currentTool = StoryTool.Crop
|
|
}
|
|
}
|
|
|
|
fun setTool(tool: StoryTool) {
|
|
currentTool = tool
|
|
}
|
|
|
|
fun addText(text: String) {
|
|
textObjects.add(TextObject(text = text))
|
|
currentTool = StoryTool.Text
|
|
}
|
|
|
|
fun addSticker(emoji: String) {
|
|
stickerObjects.add(StickerObject(emoji = emoji))
|
|
}
|
|
|
|
fun updateBgColor(color: Color) {
|
|
bgColor = color
|
|
}
|
|
|
|
fun removeMedia() {
|
|
selectedMediaUri = null
|
|
isVideo = false
|
|
currentTool = StoryTool.None
|
|
textObjects.clear()
|
|
stickerObjects.clear()
|
|
}
|
|
|
|
fun publishStory(context: android.content.Context) {
|
|
val uri = selectedMediaUri ?: return
|
|
val maxFileSize = serverConfig.getServerConfig().limits.maxFileSize
|
|
|
|
viewModelScope.launch {
|
|
isUploading = true
|
|
try {
|
|
val file = uriToFile(context, uri)
|
|
|
|
if (file.length() > maxFileSize) {
|
|
_errorMessage.value = "File too large for this server"
|
|
return@launch
|
|
}
|
|
|
|
val mediaUrl = if (isVideo) {
|
|
val part = MultipartBody.Part.createFormData(
|
|
"file", file.name, file.asRequestBody("video/*".toMediaTypeOrNull())
|
|
)
|
|
storyApi.uploadVideo(part).url
|
|
} else {
|
|
// Simulating image upload via same or different endpoint
|
|
"https://example.com/simulated_upload.jpg"
|
|
}
|
|
|
|
val request = CreateStoryRequest(
|
|
type = if (isVideo) "video" else "image",
|
|
mediaUrl = mediaUrl,
|
|
bgColor = String.format("#%06X", (0xFFFFFF and bgColor.value.toInt())),
|
|
content = null // Serialize stickers/text if needed
|
|
)
|
|
|
|
storyApi.createStory(request)
|
|
_uploadSuccess.value = true
|
|
} catch (e: Exception) {
|
|
_errorMessage.value = e.message ?: "Upload failed"
|
|
} finally {
|
|
isUploading = false
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun uriToFile(context: android.content.Context, uri: Uri): File {
|
|
val inputStream = context.contentResolver.openInputStream(uri)
|
|
val file = File(context.cacheDir, "temp_story_${System.currentTimeMillis()}")
|
|
file.outputStream().use { outputStream ->
|
|
inputStream?.copyTo(outputStream)
|
|
}
|
|
return file
|
|
}
|
|
}
|