diff --git a/app/src/main/java/com/example/dattingapp/CreatePost.kt b/app/src/main/java/com/example/dattingapp/CreatePost.kt deleted file mode 100644 index 831123c..0000000 --- a/app/src/main/java/com/example/dattingapp/CreatePost.kt +++ /dev/null @@ -1,1043 +0,0 @@ -package com.example.dattingapp - -import android.Manifest -import android.content.ContentUris -import android.content.Context -import android.content.Intent -import android.content.pm.PackageManager -import android.net.Uri -import android.os.Build -import android.os.Bundle -import android.provider.MediaStore -import android.widget.Toast -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.itemsIndexed -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.CameraAlt -import androidx.compose.material.icons.outlined.Close -import androidx.compose.material.icons.outlined.Edit -import androidx.compose.material.icons.outlined.KeyboardArrowDown -import androidx.compose.material.icons.outlined.PhotoCamera -import androidx.compose.material.icons.outlined.Videocam -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.core.content.ContextCompat -import coil.compose.AsyncImage -import com.example.dattingapp.ui.theme.DattingAPPTheme - -private val BrandMagenta = Color(0xFFB5005A) -private val TextDark = Color(0xFF1E1E1E) - - -// ============================================================ -// CREATE POST ACTIVITY -// ============================================================ - -class CreatePost : ComponentActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - - super.onCreate(savedInstanceState) - - enableEdgeToEdge() - - setContent { - - DattingAPPTheme { - - NewPostScreen( - - onCloseClick = { - finish() - }, - - onNextClick = { - - Toast.makeText( - this, - "Next clicked", - Toast.LENGTH_SHORT - ).show() - }, - - onCameraClick = { - - val intent = - Intent( - MediaStore.ACTION_IMAGE_CAPTURE - ) - - if ( - intent.resolveActivity( - packageManager - ) != null - ) { - - startActivity(intent) - - } else { - - Toast.makeText( - this, - "Camera app not found", - Toast.LENGTH_SHORT - ).show() - } - }, - - onEditMediaClick = { - - Toast.makeText( - this, - "Edit media clicked", - Toast.LENGTH_SHORT - ).show() - } - ) - } - } - } -} - - -// ============================================================ -// NEW POST SCREEN -// ============================================================ - -@Composable -fun NewPostScreen( - - onCloseClick: () -> Unit = {}, - - onNextClick: () -> Unit = {}, - - onCameraClick: () -> Unit = {}, - - onEditMediaClick: () -> Unit = {} -) { - - val context = - LocalContext.current - - - // ======================================================== - // GALLERY IMAGES - // ======================================================== - - var galleryImages by remember { - - mutableStateOf>( - emptyList() - ) - } - - - // ======================================================== - // SELECTED IMAGE - // ======================================================== - - var selectedImageIndex by remember { - - mutableIntStateOf(0) - } - - - // ======================================================== - // PERMISSION - // ======================================================== - - var hasGalleryPermission by remember { - - mutableStateOf(false) - } - - - val permissionToRequest = - - if ( - Build.VERSION.SDK_INT >= - Build.VERSION_CODES.TIRAMISU - ) { - - Manifest.permission.READ_MEDIA_IMAGES - - } else { - - Manifest.permission.READ_EXTERNAL_STORAGE - } - - - // ======================================================== - // PERMISSION LAUNCHER - // ======================================================== - - val permissionLauncher = - - rememberLauncherForActivityResult( - - ActivityResultContracts.RequestPermission() - - ) { granted -> - - hasGalleryPermission = - granted - - if (granted) { - - galleryImages = - getGalleryImages(context) - - selectedImageIndex = 0 - } - } - - - // ======================================================== - // CHECK PERMISSION - // ======================================================== - - LaunchedEffect(Unit) { - - val granted = - - ContextCompat.checkSelfPermission( - context, - permissionToRequest - ) == PackageManager.PERMISSION_GRANTED - - - hasGalleryPermission = - granted - - - if (granted) { - - galleryImages = - getGalleryImages(context) - - } else { - - permissionLauncher.launch( - permissionToRequest - ) - } - } - - - val selectedUri = - galleryImages.getOrNull( - selectedImageIndex - ) - - - // ======================================================== - // MAIN SCREEN - // ======================================================== - - Column( - - modifier = Modifier - .fillMaxSize() - .background(Color.White) - ) { - - - // ==================================================== - // TOP BAR - // ==================================================== - - Row( - - modifier = Modifier - .fillMaxWidth() - .padding( - horizontal = 16.dp, - vertical = 12.dp - ), - - horizontalArrangement = - Arrangement.SpaceBetween, - - verticalAlignment = - Alignment.CenterVertically - ) { - - - // CLOSE - - IconButton( - onClick = - onCloseClick - ) { - - Icon( - imageVector = - Icons.Outlined.Close, - - contentDescription = - "Close", - - tint = - TextDark - ) - } - - - // TITLE - - Text( - text = - "New Post", - - fontSize = - 20.sp, - - fontWeight = - FontWeight.Bold, - - color = - BrandMagenta - ) - - - // NEXT - - Text( - - text = - "Next", - - modifier = - Modifier.clickable { - onNextClick() - }, - - fontSize = - 15.sp, - - fontWeight = - FontWeight.Bold, - - color = - BrandMagenta - ) - } - - - // ==================================================== - // LARGE IMAGE PREVIEW - // ==================================================== - - Box( - - modifier = Modifier - .fillMaxWidth() - .padding( - horizontal = 16.dp - ) - .aspectRatio(1f) - .clip( - RoundedCornerShape(20.dp) - ) - .background( - Color(0xFFF0F0F0) - ) - ) { - - - // SELECTED IMAGE - - if (selectedUri != null) { - - AsyncImage( - - model = - selectedUri, - - contentDescription = - "Selected Image", - - modifier = - Modifier.fillMaxSize(), - - contentScale = - ContentScale.Crop - ) - - } else { - - Text( - - text = - "No image selected", - - modifier = - Modifier.align( - Alignment.Center - ), - - color = - Color.Gray - ) - } - - - // ================================================= - // IMAGE COUNT - // ================================================= - - if (galleryImages.isNotEmpty()) { - - Box( - - modifier = Modifier - .align( - Alignment.TopEnd - ) - .padding(12.dp) - .clip( - RoundedCornerShape(12.dp) - ) - .background( - Color.Black.copy( - alpha = 0.5f - ) - ) - .padding( - horizontal = 10.dp, - vertical = 4.dp - ) - ) { - - Text( - - text = - "${selectedImageIndex + 1}/${galleryImages.size}", - - color = - Color.White, - - fontSize = - 12.sp - ) - } - } - - - // ================================================= - // CAMERA + VIDEO - // CENTER - // ================================================= - - Row( - - modifier = Modifier - .align( - Alignment.BottomCenter - ) - .padding( - bottom = 12.dp - ), - - horizontalArrangement = - Arrangement.Center, - - verticalAlignment = - Alignment.CenterVertically - ) { - - Row( - - modifier = Modifier - .clip( - RoundedCornerShape(20.dp) - ) - .background( - Color.Black.copy( - alpha = 0.45f - ) - ) - .padding( - horizontal = 14.dp, - vertical = 7.dp - ), - - horizontalArrangement = - Arrangement.spacedBy(14.dp), - - verticalAlignment = - Alignment.CenterVertically - ) { - - Icon( - - imageVector = - Icons.Outlined.PhotoCamera, - - contentDescription = - "Photo", - - tint = - Color.White, - - modifier = - Modifier.size(20.dp) - ) - - Icon( - - imageVector = - Icons.Outlined.Videocam, - - contentDescription = - "Video", - - tint = - Color.White, - - modifier = - Modifier.size(20.dp) - ) - } - } - - - // ================================================= - // EDIT BUTTON - // ================================================= - - Surface( - - onClick = - onEditMediaClick, - - modifier = Modifier - .align( - Alignment.BottomEnd - ) - .padding( - bottom = 12.dp, - end = 16.dp - ), - - shape = - RoundedCornerShape(20.dp), - - color = - Color.White.copy( - alpha = 0.9f - ) - ) { - - Row( - - modifier = - Modifier.padding( - horizontal = 14.dp, - vertical = 7.dp - ), - - verticalAlignment = - Alignment.CenterVertically - ) { - - Icon( - - imageVector = - Icons.Outlined.Edit, - - contentDescription = - "Edit", - - tint = - TextDark, - - modifier = - Modifier.size(14.dp) - ) - - Spacer( - modifier = - Modifier.size(4.dp) - ) - - Text( - - text = - "Edit", - - fontSize = - 12.sp, - - fontWeight = - FontWeight.Bold - ) - } - } - } - - - Spacer( - modifier = - Modifier.height(16.dp) - ) - - - // ==================================================== - // RECENTS - // ==================================================== - - Row( - - modifier = Modifier - .fillMaxWidth() - .padding( - horizontal = 16.dp - ), - - horizontalArrangement = - Arrangement.SpaceBetween, - - verticalAlignment = - Alignment.CenterVertically - ) { - - - Row( - - verticalAlignment = - Alignment.CenterVertically - ) { - - Text( - - text = - "Recents", - - fontSize = - 18.sp, - - fontWeight = - FontWeight.Bold - ) - - Icon( - - imageVector = - Icons.Outlined.KeyboardArrowDown, - - contentDescription = - "Albums" - ) - } - - - IconButton( - - onClick = - onCameraClick, - - modifier = Modifier - .size(38.dp) - .clip( - CircleShape - ) - .background( - Color(0xFFF2F2F2) - ) - ) { - - Icon( - - imageVector = - Icons.Outlined.CameraAlt, - - contentDescription = - "Camera", - - tint = - TextDark - ) - } - } - - - Spacer( - modifier = - Modifier.height(8.dp) - ) - - - // ==================================================== - // GALLERY - // ==================================================== - - if (hasGalleryPermission) { - - LazyVerticalGrid( - - columns = - GridCells.Fixed(4), - - modifier = Modifier - .fillMaxWidth() - .weight(1f), - - horizontalArrangement = - Arrangement.spacedBy(6.dp), - - verticalArrangement = - Arrangement.spacedBy(6.dp), - - contentPadding = - PaddingValues( - start = 16.dp, - end = 16.dp, - bottom = 20.dp - ) - ) { - - itemsIndexed( - - items = - galleryImages, - - key = { _, uri -> - uri.toString() - } - - ) { index, uri -> - - val isSelected = - index == - selectedImageIndex - - - Box( - - modifier = Modifier - .fillMaxWidth() - .aspectRatio(1f) - .clip( - RoundedCornerShape(8.dp) - ) - .clickable { - - selectedImageIndex = - index - } - .then( - - if (isSelected) { - - Modifier.border( - - width = 2.dp, - - color = - BrandMagenta, - - shape = - RoundedCornerShape( - 8.dp - ) - ) - - } else { - - Modifier - } - ) - ) { - - AsyncImage( - - model = - uri, - - contentDescription = - "Gallery Image", - - modifier = - Modifier.fillMaxSize(), - - contentScale = - ContentScale.Crop - ) - - - if (isSelected) { - - Box( - - modifier = Modifier - .align( - Alignment.TopEnd - ) - .padding(4.dp) - .size(22.dp) - .clip( - CircleShape - ) - .background( - BrandMagenta - ), - - contentAlignment = - Alignment.Center - ) { - - Text( - - text = - "✓", - - color = - Color.White, - - fontSize = - 12.sp, - - fontWeight = - FontWeight.Bold - ) - } - } - } - } - } - - } else { - - // ================================================= - // NO GALLERY PERMISSION - // ================================================= - - Box( - - modifier = Modifier - .fillMaxWidth() - .weight(1f), - - contentAlignment = - Alignment.Center - ) { - - Column( - - horizontalAlignment = - Alignment.CenterHorizontally, - - verticalArrangement = - Arrangement.spacedBy(12.dp) - ) { - - Icon( - - imageVector = - Icons.Outlined.PhotoCamera, - - contentDescription = - null, - - tint = - BrandMagenta, - - modifier = - Modifier.size(48.dp) - ) - - - Text( - - text = - "Need Gallery Access", - - fontSize = - 19.sp, - - fontWeight = - FontWeight.Bold - ) - - - Text( - - text = - "Allow access to your photos\nto select media for your post.", - - fontSize = - 14.sp, - - color = - Color.Gray - ) - - - Button( - - onClick = { - - permissionLauncher.launch( - permissionToRequest - ) - }, - - colors = - ButtonDefaults.buttonColors( - containerColor = - BrandMagenta - ), - - shape = - RoundedCornerShape(22.dp) - ) { - - Text( - text = - "Allow Gallery Access", - - color = - Color.White - ) - } - } - } - } - } -} - - -// ============================================================ -// GET GALLERY IMAGES -// ============================================================ - -private fun getGalleryImages( - context: Context -): List { - - val imageUris = - mutableListOf() - - - val projection = - arrayOf( - MediaStore.Images.Media._ID - ) - - - val sortOrder = - "${MediaStore.Images.Media.DATE_ADDED} DESC" - - - context.contentResolver.query( - - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - - projection, - - null, - - null, - - sortOrder - - )?.use { cursor -> - - val idColumn = - cursor.getColumnIndexOrThrow( - MediaStore.Images.Media._ID - ) - - - while (cursor.moveToNext()) { - - val id = - cursor.getLong( - idColumn - ) - - - val uri = - ContentUris.withAppendedId( - - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - - id - ) - - - imageUris.add(uri) - } - } - - - return imageUris -} - - -// ============================================================ -// PREVIEW -// ============================================================ - -@Preview( - showBackground = true, - showSystemUi = true -) -@Composable -fun NewPostScreenPreview() { - - DattingAPPTheme { - - NewPostScreen( - onCloseClick = {}, - onNextClick = {}, - onCameraClick = {}, - onEditMediaClick = {} - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/RegistrationScreen.kt b/app/src/main/java/com/example/dattingapp/RegistrationScreen.kt deleted file mode 100644 index 87fb5ba..0000000 --- a/app/src/main/java/com/example/dattingapp/RegistrationScreen.kt +++ /dev/null @@ -1,938 +0,0 @@ -package com.example.dattingapp - -import android.content.Intent -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.compose.animation.* -import androidx.compose.animation.core.tween -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.runtime.snapshots.SnapshotStateList -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import java.text.SimpleDateFormat -import java.util.* -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Explore -import androidx.compose.material.icons.outlined.FavoriteBorder -import androidx.compose.material.icons.outlined.Group -import androidx.compose.material.icons.outlined.LocalBar -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.style.TextAlign -import com.example.dattingapp.registration.UploadProfileImageActivity - -// Global Email Validation Regex -val EMAIL_PATTERN_REGEX = Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$") - -data class VibeOption( - val title: String, - val subtitle: String, - val icon: ImageVector, - val iconBgColor: Color, - val iconTintColor: Color -) -class RegistrationScreen : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - enableEdgeToEdge() - super.onCreate(savedInstanceState) - setContent { - MaterialTheme { - FullscreenRegistrationWizard() - } - } - } -} - -@Composable -fun FullscreenRegistrationWizard() { - // Active step state - var currentStep by remember { mutableIntStateOf(1) } - val totalSteps = 7 - - // Form Field States - var isPhoneSelected by remember { mutableStateOf(false) } - var countryCode by remember { mutableStateOf("+880") } - var contactInput by remember { mutableStateOf("") } - var contactError by remember { mutableStateOf(null) } - - var passwordInput by remember { mutableStateOf("") } - var confirmPasswordInput by remember { mutableStateOf("") } - var passwordError by remember { mutableStateOf(null) } - - var nameInput by remember { mutableStateOf("") } - var nameError by remember { mutableStateOf(null) } - - var dobInput by remember { mutableStateOf("") } - var dobError by remember { mutableStateOf(null) } - - var genderInput by remember { mutableStateOf("") } - var genderError by remember { mutableStateOf(null) } - - val selectedInterests = remember { mutableStateListOf() } - var interestsError by remember { mutableStateOf(null) } - - - var vibeInput by remember { mutableStateOf("") } - var vibeError by remember { mutableStateOf(null) } - - // Inside your FullscreenRegistrationWizard composable: - val context = LocalContext.current - - // Validation & Navigation - fun validateAndProceed() { - when (currentStep) { - - - - 1 -> { - if (nameInput.trim().isBlank()) { - nameError = "Please enter your name" - } else { - nameError = null - currentStep = 2 - } - - } - - 2 -> { - if (isPhoneSelected) { - when { - contactInput.isBlank() -> contactError = "Please enter a phone number" - contactInput.length < 7 || contactInput.length > 15 -> contactError = "Enter 7-15 digits" - else -> { contactError = null; currentStep = 2 } - } - } else { - val trimmedEmail = contactInput.trim() - when { - trimmedEmail.isBlank() -> contactError = "Please enter an email address" - !EMAIL_PATTERN_REGEX.matches(trimmedEmail) -> contactError = "Invalid email format" - else -> { contactError = null; currentStep = 4 } - } - } - - } - 3 -> { - - when { - passwordInput.length < 6 -> passwordError = "Password must be at least 6 characters" - passwordInput != confirmPasswordInput -> passwordError = "Passwords do not match" - else -> { passwordError = null; currentStep = 3 } - } - } - - 4 -> { - if (dobInput.isBlank()) { - dobError = "Please select your date of birth" - } else { - dobError = null - currentStep = 5 - } - } - 5 -> { - if (genderInput.isBlank()) { - genderError = "Please select your gender" - } else { - genderError = null - currentStep = 6 - } - } - 6 -> { - if (selectedInterests.size < 2) { - interestsError = "Please select at least 2 interests" - } else { - interestsError = null - // Submit registration payload - currentStep = 7 - } - } - - - 7 -> { - if (vibeInput.isBlank()) { - vibeError = "Please select your vibe to continue" - } else { - vibeError = null - - // Launch UploadImageActivity with all collected data - val intent = Intent(context, UploadProfileImageActivity::class.java).apply { - putExtra("EXTRA_IS_PHONE", isPhoneSelected) - putExtra("EXTRA_COUNTRY_CODE", countryCode) - putExtra("EXTRA_CONTACT_VALUE", contactInput) - putExtra("EXTRA_PASSWORD", passwordInput) - putExtra("EXTRA_NAME", nameInput) - putExtra("EXTRA_DOB", dobInput) - putExtra("EXTRA_GENDER", genderInput) - putExtra("EXTRA_INTERESTS", ArrayList(selectedInterests)) - putExtra("EXTRA_VIBE", vibeInput) - } - context.startActivity(intent) - } - } - } - } - - Box( - modifier = Modifier - .fillMaxSize() - .background(Color(0xFFF8F9FA)) - .systemBarsPadding() - ) { - Column(modifier = Modifier.fillMaxSize()) { - // TOP BAR & PROGRESS - Column( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 16.dp) - ) { - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - if (currentStep > 1) { - TextButton( - onClick = { currentStep-- }, - contentPadding = PaddingValues(0.dp) - ) { - Text("Back", color = Color.Gray, fontSize = 16.sp) - } - } else { - Spacer(modifier = Modifier.width(48.dp)) - } - - Text( - text = "Step $currentStep of $totalSteps", - fontWeight = FontWeight.Bold, - fontSize = 16.sp, - color = Color.Black - ) - - Spacer(modifier = Modifier.width(48.dp)) - } - - Spacer(modifier = Modifier.height(12.dp)) - - LinearProgressIndicator( - progress = { currentStep / totalSteps.toFloat() }, - modifier = Modifier - .fillMaxWidth() - .height(6.dp), - color = Color.Black, - trackColor = Color(0xFFE0E0E0) - ) - } - - // SLIDING CONTENT CONTAINER - Box( - modifier = Modifier - .weight(1f) - .fillMaxWidth() - .padding(horizontal = 24.dp), - contentAlignment = Alignment.Center - ) { - AnimatedContent( - targetState = currentStep, - transitionSpec = { - if (targetState > initialState) { - slideIntoContainer( - AnimatedContentTransitionScope.SlideDirection.Left, - animationSpec = tween(400) - ) + fadeIn(animationSpec = tween(400)) togetherWith - slideOutOfContainer( - AnimatedContentTransitionScope.SlideDirection.Left, - animationSpec = tween(400) - ) + fadeOut(animationSpec = tween(400)) - } else { - slideIntoContainer( - AnimatedContentTransitionScope.SlideDirection.Right, - animationSpec = tween(400) - ) + fadeIn(animationSpec = tween(400)) togetherWith - slideOutOfContainer( - AnimatedContentTransitionScope.SlideDirection.Right, - animationSpec = tween(400) - ) + fadeOut(animationSpec = tween(400)) - } - }, - label = "WizardSlideAnimation" - ) { step -> - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally - ) { - when (step) { - - 1 -> NameInputWidget( - name = nameInput, - onNameChange = { nameInput = it; nameError = null }, - errorMessage = nameError - ) - 2 -> ContactInputWidget( - isPhoneSelected = isPhoneSelected, - onModeChange = { isPhoneSelected = it; contactError = null }, - countryCode = countryCode, - onCountryCodeChange = { countryCode = it }, - contactValue = contactInput, - onContactChange = { contactInput = it; contactError = null }, - errorMessage = contactError - ) - 3 -> PasswordInputWidget( - password = passwordInput, - onPasswordChange = { passwordInput = it; passwordError = null }, - confirmPassword = confirmPasswordInput, - onConfirmPasswordChange = { confirmPasswordInput = it; passwordError = null }, - errorMessage = passwordError - ) - - 4 -> DobInputWidget( - dob = dobInput, - onDobChange = { dobInput = it; dobError = null }, - errorMessage = dobError - ) - 5 -> GenderInputWidget( - selectedGender = genderInput, - onGenderSelect = { genderInput = it; genderError = null }, - errorMessage = genderError - ) - 6 -> InterestsInputWidget( - selectedInterests = selectedInterests, - errorMessage = interestsError, - onSelectionChanged = { interestsError = null } - ) - 7 -> VibeCheckWidget( - selectedVibe = vibeInput, - onVibeSelected = { - vibeInput = it - vibeError = null - }, - errorMessage = vibeError - ) - } - } - } - } - - // BOTTOM ACTION BUTTON - Box( - modifier = Modifier - .fillMaxWidth() - .padding(20.dp) - ) { - Button( - onClick = { validateAndProceed() }, - modifier = Modifier - .fillMaxWidth() - .height(54.dp), - shape = RoundedCornerShape(16.dp), - colors = ButtonDefaults.buttonColors(containerColor = Color.Black) - ) { - Text( - text = if (currentStep == totalSteps) "Complete Registration" else "Continue", - color = Color.White, - fontSize = 16.sp, - fontWeight = FontWeight.Bold - ) - } - } - } - } -} - -// ==================== STEP WIDGET COMPOSABLES ==================== - -@Composable -fun ContactInputWidget( - isPhoneSelected: Boolean, - onModeChange: (Boolean) -> Unit, - countryCode: String, - onCountryCodeChange: (String) -> Unit, - contactValue: String, - onContactChange: (String) -> Unit, - errorMessage: String? -) { - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - Text( - text = "ENTER YOUR CONTACT INFORMATION", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) - - Row( - modifier = Modifier - .fillMaxWidth() - .height(48.dp) - .clip(RoundedCornerShape(24.dp)) - .background(Color(0xFFEFEFEF)) - .padding(4.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Box( - modifier = Modifier - .weight(1f) - .fillMaxHeight() - .clip(RoundedCornerShape(20.dp)) - .background(if (isPhoneSelected) Color.Black else Color.Transparent) - .clickable { onModeChange(true) }, - contentAlignment = Alignment.Center - ) { - Text( - text = "Phone", - fontWeight = FontWeight.Bold, - fontSize = 14.sp, - color = if (isPhoneSelected) Color.White else Color.DarkGray - ) - } - - Box( - modifier = Modifier - .weight(1f) - .fillMaxHeight() - .clip(RoundedCornerShape(20.dp)) - .background(if (!isPhoneSelected) Color.Black else Color.Transparent) - .clickable { onModeChange(false) }, - contentAlignment = Alignment.Center - ) { - Text( - text = "Email", - fontWeight = FontWeight.Bold, - fontSize = 14.sp, - color = if (!isPhoneSelected) Color.White else Color.DarkGray - ) - } - } - - if (isPhoneSelected) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - OutlinedTextField( - value = countryCode, - onValueChange = { if (it.length <= 4) onCountryCodeChange(it) }, - placeholder = { Text("+880") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone), - singleLine = true, - isError = errorMessage != null, - modifier = Modifier.width(90.dp), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White - ) - ) - - OutlinedTextField( - value = contactValue, - onValueChange = { if (it.length <= 15) onContactChange(it) }, - placeholder = { Text("Phone number") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - singleLine = true, - isError = errorMessage != null, - modifier = Modifier.weight(1f), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White - ) - ) - } - } else { - OutlinedTextField( - value = contactValue, - onValueChange = onContactChange, - placeholder = { Text("Enter email address") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email), - singleLine = true, - isError = errorMessage != null, - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White - ) - ) - } - - if (errorMessage != null) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium, - modifier = Modifier.padding(start = 4.dp) - ) - } - } -} - -@Composable -fun PasswordInputWidget( - password: String, - onPasswordChange: (String) -> Unit, - confirmPassword: String, - onConfirmPasswordChange: (String) -> Unit, - errorMessage: String? -) { - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - Text( - text = "CREATE A PASSWORD", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) - - OutlinedTextField( - value = password, - onValueChange = onPasswordChange, - placeholder = { Text("Enter password") }, - visualTransformation = PasswordVisualTransformation(), - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), - singleLine = true, - isError = errorMessage != null, - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White - ) - ) - - OutlinedTextField( - value = confirmPassword, - onValueChange = onConfirmPasswordChange, - placeholder = { Text("Confirm password") }, - visualTransformation = PasswordVisualTransformation(), - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), - singleLine = true, - isError = errorMessage != null, - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White - ) - ) - - if (errorMessage != null) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium, - modifier = Modifier.padding(start = 4.dp) - ) - } - } -} - -@Composable -fun NameInputWidget( - name: String, - onNameChange: (String) -> Unit, - errorMessage: String? -) { - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - Text( - text = "WHAT IS YOUR NAME?", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) - - OutlinedTextField( - value = name, - onValueChange = onNameChange, - placeholder = { Text("Enter your full name") }, - singleLine = true, - isError = errorMessage != null, - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White - ) - ) - - if (errorMessage != null) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium, - modifier = Modifier.padding(start = 4.dp) - ) - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun DobInputWidget( - dob: String, - onDobChange: (String) -> Unit, - errorMessage: String? -) { - var showDatePicker by remember { mutableStateOf(false) } - val datePickerState = rememberDatePickerState() - - if (showDatePicker) { - DatePickerDialog( - onDismissRequest = { showDatePicker = false }, - confirmButton = { - TextButton(onClick = { - datePickerState.selectedDateMillis?.let { millis -> - val formatter = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) - onDobChange(formatter.format(Date(millis))) - } - showDatePicker = false - }) { - Text("OK") - } - }, - dismissButton = { - TextButton(onClick = { showDatePicker = false }) { - Text("Cancel") - } - } - ) { - DatePicker(state = datePickerState) - } - } - - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - Text( - text = "YOUR DATE OF BIRTH", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) - - Box(modifier = Modifier.fillMaxWidth()) { - OutlinedTextField( - value = dob, - onValueChange = {}, - readOnly = true, - placeholder = { Text("DD/MM/YYYY") }, - isError = errorMessage != null, - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White - ) - ) - Box( - modifier = Modifier - .matchParentSize() - .clickable { showDatePicker = true } - ) - } - - if (errorMessage != null) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium, - modifier = Modifier.padding(start = 4.dp) - ) - } - } -} - -@Composable -fun GenderInputWidget( - selectedGender: String, - onGenderSelect: (String) -> Unit, - errorMessage: String? -) { - val options = listOf("Male", "Female", "Other") - - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - Text( - text = "SELECT YOUR GENDER", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) - - options.forEach { gender -> - val isSelected = selectedGender == gender - Card( - shape = RoundedCornerShape(12.dp), - colors = CardDefaults.cardColors( - containerColor = if (isSelected) Color.Black else Color.White - ), - modifier = Modifier - .fillMaxWidth() - .clickable { onGenderSelect(gender) } - ) { - Box( - modifier = Modifier.padding(16.dp), - contentAlignment = Alignment.CenterStart - ) { - Text( - text = gender, - color = if (isSelected) Color.White else Color.Black, - fontWeight = FontWeight.SemiBold, - fontSize = 15.sp - ) - } - } - } - - if (errorMessage != null) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium, - modifier = Modifier.padding(start = 4.dp) - ) - } - } -} - -@OptIn(ExperimentalLayoutApi::class) -@Composable -fun InterestsInputWidget( - selectedInterests: SnapshotStateList, - errorMessage: String? = null, - onSelectionChanged: () -> Unit = {} -) { - val allInterests = listOf("Photography", "Dogs", "Travel", "Music", "Gaming", "Fitness", "Cooking") - - Card( - shape = RoundedCornerShape(16.dp), - colors = CardDefaults.cardColors(containerColor = Color.White), - modifier = Modifier.fillMaxWidth() - ) { - Column(modifier = Modifier.padding(16.dp)) { - Text( - text = "INTERESTS (SELECT AT LEAST 2)", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) - Spacer(modifier = Modifier.height(12.dp)) - - FlowRow( - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - allInterests.forEach { interest -> - val isSelected = selectedInterests.contains(interest) - FilterChip( - selected = isSelected, - onClick = { - if (isSelected) selectedInterests.remove(interest) - else selectedInterests.add(interest) - onSelectionChanged() - }, - label = { Text(interest) } - ) - } - } - - if (errorMessage != null) { - Spacer(modifier = Modifier.height(8.dp)) - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium - ) - } - } - } -} - - - - -@Composable -fun VibeCheckWidget( - selectedVibe: String, - onVibeSelected: (String) -> Unit, - errorMessage: String? = null -) { - val options = listOf( - VibeOption("Serious Relationship", "Looking for the one", Icons.Outlined.FavoriteBorder, Color(0xFFFFEAEA), Color(0xFFE91E63)), - VibeOption("Casual Dates", "Drinks & good times", Icons.Outlined.LocalBar, Color(0xFFEDE7F6), Color(0xFF673AB7)), - VibeOption("New Friends", "Expand my circle", Icons.Outlined.Group, Color(0xFFFFECB3), Color(0xFFFF8F00)), - VibeOption("Marraige", "Seeing where things go", Icons.Outlined.Explore, Color(0xFFE0F2F1), Color(0xFF00897B)) - ) - - Column( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 8.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - // App Title / Brand Header - Text( - text = "VibeCheck", - fontSize = 24.sp, - fontWeight = FontWeight.Bold, - color = Color(0xFFC2185B) - ) - - Spacer(modifier = Modifier.height(8.dp)) - - // Three Indicator Bars - Row( - horizontalArrangement = Arrangement.spacedBy(6.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Box(modifier = Modifier.width(20.dp).height(4.dp).background(Color(0xFFE0E0E0), CircleShape)) - Box(modifier = Modifier.width(20.dp).height(4.dp).background(Color(0xFFC2185B), CircleShape)) - Box(modifier = Modifier.width(20.dp).height(4.dp).background(Color(0xFFE0E0E0), CircleShape)) - } - - Spacer(modifier = Modifier.height(20.dp)) - - // Title & Description - Text( - text = "What's your vibe?", - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - color = Color.Black - ) - - Spacer(modifier = Modifier.height(8.dp)) - - Text( - text = "Select your main goal right now to help us find the right connections.", - fontSize = 13.sp, - color = Color.Gray, - textAlign = TextAlign.Center, - modifier = Modifier.padding(horizontal = 16.dp) - ) - - Spacer(modifier = Modifier.height(24.dp)) - - // Vibe Cards List - Column( - verticalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier.fillMaxWidth() - ) { - options.forEach { option -> - val isSelected = selectedVibe == option.title - - Card( - shape = RoundedCornerShape(16.dp), - colors = CardDefaults.cardColors(containerColor = Color.White), - border = BorderStroke( - width = if (isSelected) 2.dp else 1.dp, - color = if (isSelected) Color(0xFFC2185B) else Color(0xFFEEEEEE) - ), - modifier = Modifier - .fillMaxWidth() - .clickable { onVibeSelected(option.title) } - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - // Round Icon Container - Box( - modifier = Modifier - .size(42.dp) - .background(option.iconBgColor, CircleShape), - contentAlignment = Alignment.Center - ) { - Icon( - imageVector = option.icon, - contentDescription = option.title, - tint = option.iconTintColor, - modifier = Modifier.size(22.dp) - ) - } - - Spacer(modifier = Modifier.width(16.dp)) - - // Text Labels - Column { - Text( - text = option.title, - fontSize = 15.sp, - fontWeight = FontWeight.Bold, - color = Color.Black - ) - Text( - text = option.subtitle, - fontSize = 12.sp, - color = Color.Gray - ) - } - } - } - } - } - - if (errorMessage != null) { - Spacer(modifier = Modifier.height(12.dp)) - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium - ) - } - - Spacer(modifier = Modifier.height(16.dp)) - - Text( - text = "You can always change this later in your profile.", - fontSize = 11.sp, - color = Color.Gray - ) - } -} - -@Preview( - name = "Light Mode - Full Wizard", - showBackground = true, - showSystemUi = true -) -@Composable -fun FullscreenRegistrationWizardPreview() { - Surface(modifier = Modifier.fillMaxSize()) { - FullscreenRegistrationWizard() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/homes/CreatePostScreen.kt b/app/src/main/java/com/example/dattingapp/homes/CreatePostScreen.kt new file mode 100644 index 0000000..4dc2051 --- /dev/null +++ b/app/src/main/java/com/example/dattingapp/homes/CreatePostScreen.kt @@ -0,0 +1,692 @@ +package com.example.dattingapp.homes + + +import android.net.Uri +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.outlined.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.content.FileProvider +import androidx.media3.common.MediaItem +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.ui.PlayerView +import coil.compose.rememberAsyncImagePainter +import com.example.dattingapp.homes.networkandapi.VibeFeeling +import com.example.dattingapp.homes.networkandapi.vibeFeelingsList +import java.io.File +import com.example.dattingapp.ui.theme.LightPinkBg +import com.example.dattingapp.ui.theme.LightPurpleBg +import com.example.dattingapp.ui.theme.DarkOverlay +import com.example.dattingapp.ui.theme.LightBlueBg + +import androidx.compose.foundation.lazy.grid.items + +// Sealed class representing selectable media types (Photo or Video) +sealed class MediaContentItem(val uri: Uri) { + class Photo(uri: Uri) : MediaContentItem(uri) + class Video(uri: Uri) : MediaContentItem(uri) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CreateVibePostScreen( + onCloseClick: () -> Unit = {}, + onShareClick: () -> Unit = {} +) { + val context = LocalContext.current + var textState by remember { mutableStateOf("") } + val maxChar = 500 + + // Holds selected media items (photos, videos, or both) + val selectedMediaList = remember { mutableStateListOf() } + + var tempCameraImageUri by remember { mutableStateOf(null) } + var tempCameraVideoUri by remember { mutableStateOf(null) } + var showImagePickerDialog by remember { mutableStateOf(false) } + + var showFeelingSheet by remember { mutableStateOf(false) } + var selectedFeeling by remember { mutableStateOf(null) } + + // Launcher for Multi-Media Gallery Picker (Supports both Photos and Videos simultaneously) + val galleryLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.PickMultipleVisualMedia() + ) { uris: List -> + uris.forEach { uri -> + val mimeType = context.contentResolver.getType(uri) + if (mimeType?.startsWith("video") == true) { + selectedMediaList.add(MediaContentItem.Video(uri)) + } else { + selectedMediaList.add(MediaContentItem.Photo(uri)) + } + } + } + + // Temporary file helper for Camera captures + fun createTempFileUri(extension: String, prefix: String): Uri { + val tempFile = File.createTempFile(prefix, extension, context.cacheDir).apply { + createNewFile() + deleteOnExit() + } + return FileProvider.getUriForFile( + context, + "${context.packageName}.provider", + tempFile + ) + } + + // Camera Launchers for taking photos or recording videos + val cameraPhotoLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.TakePicture() + ) { success: Boolean -> + if (success && tempCameraImageUri != null) { + selectedMediaList.add(MediaContentItem.Photo(tempCameraImageUri!!)) + } + } + + val cameraVideoLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.CaptureVideo() + ) { success: Boolean -> + if (success && tempCameraVideoUri != null) { + selectedMediaList.add(MediaContentItem.Video(tempCameraVideoUri!!)) + } + } + + Scaffold( + bottomBar = { + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.White) + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + // 1. Scrollable option chips on the left + LazyRow( + modifier = Modifier.weight(1f), + horizontalArrangement = Arrangement.spacedBy(10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + item { + VibeOptionChip( + icon = Icons.Outlined.PhotoLibrary, + backgroundColor = LightPinkBg, + contentColor = Color(0xFFE91E63), + onClick = { showImagePickerDialog = true } + ) + } + item { + VibeOptionChip( + icon = Icons.Outlined.MyLocation, + backgroundColor = LightBlueBg, + contentColor = Color(0xFF1E88E5) + ) + } + item { + VibeOptionChip( + icon = Icons.Outlined.Mood, + backgroundColor = LightPinkBg, + contentColor = Color(0xFFE91E63), + onClick = { showFeelingSheet = true } // Opens bottom sheet + ) + } + item { + VibeOptionChip( + icon = Icons.Outlined.MusicNote, + backgroundColor = LightPurpleBg, + contentColor = Color(0xFF7B1FA2) + ) + } + } + + Spacer(modifier = Modifier.width(8.dp)) + + // 2. Share button on the right + Button( + onClick = onShareClick, + modifier = Modifier + .wrapContentWidth() + .height(40.dp), + shape = RoundedCornerShape(12.dp), + colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent), + contentPadding = PaddingValues(0.dp) + ) { + Box( + modifier = Modifier + .background( + brush = Brush.horizontalGradient( + colors = listOf(Color(0xFFC2185B), Color(0xFF7B1FA2)) + ), + shape = RoundedCornerShape(12.dp) + ) + .padding(horizontal = 12.dp, vertical = 8.dp), + contentAlignment = Alignment.Center + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = "Share", + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = 14.sp + ) + Spacer(modifier = Modifier.width(6.dp)) + Icon( + imageVector = Icons.Default.Send, + contentDescription = null, + tint = Color.White, + modifier = Modifier.size(16.dp) + ) + } + } + } + } + }, + containerColor = Color.White + ) { innerPadding -> + + Column( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()) + + ) { + // User Profile Header + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { + Box(modifier = Modifier.size(48.dp)) { + Box( + modifier = Modifier + .size(48.dp) + .clip(CircleShape) + .background(Color.Gray) + ) + Box( + modifier = Modifier + .size(12.dp) + .clip(CircleShape) + .background(Color(0xFFE91E63)) + .align(Alignment.BottomEnd) + .border(2.dp, Color.White, CircleShape) + ) + } + + Spacer(modifier = Modifier.width(12.dp)) + + Column(modifier = Modifier.weight(1f)) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = buildAnnotatedString { + // "Alex Rivera " - Bold + withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { + append("Alex Rivera ") + } + + // "- feeling " - Normal weight (also fixed "felling" to "feeling") + selectedFeeling?.let { feeling -> + withStyle(style = SpanStyle(fontWeight = FontWeight.Normal)) { + append(" — feeling ") + } + withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { + // Change feeling.name to feeling.label (or feeling.title depending on your data class) + append("${feeling.name} ${feeling.emoji}") + } + } + }, + fontSize = 16.sp + ) + Spacer(modifier = Modifier.width(4.dp)) + + //this is for vibe + + } + + Spacer(modifier = Modifier.height(4.dp)) + + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + imageVector = Icons.Outlined.LocationOn, + contentDescription = null, + tint = Color(0xFFE91E63), + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = "Blue Bottle Coffee, Soho NYC", + fontWeight = FontWeight.Medium, + fontSize = 13.sp + ) + } + Icon( + imageVector = Icons.Default.Close, + contentDescription = "Remove location", + tint = Color.Gray, + modifier = Modifier.size(16.dp) + ) + } + + + + + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + // Text Area + TextField( + value = textState, + onValueChange = { if (it.length <= maxChar) textState = it }, + placeholder = { + Text( + "What's your vibe today? Share a moment, date idea, or story...", + color = Color.Gray, + fontSize = 15.sp + ) + }, + modifier = Modifier.fillMaxWidth(), + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent + ) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + // Formatting & Character Counter Bar + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { + Text("B", fontWeight = FontWeight.Bold, fontSize = 16.sp, color = Color.Gray) + Text("I", fontWeight = FontWeight.Bold, fontSize = 16.sp, color = Color.Gray) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + // Horizontal Scrollable Media Row (Contains Photos and Videos) + if (selectedMediaList.isNotEmpty()) { + LazyRow( + horizontalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier.fillMaxWidth() + ) { + itemsIndexed(selectedMediaList) { index, item -> + Card( + shape = RoundedCornerShape(20.dp), + modifier = Modifier + .width(280.dp) + .height(220.dp) + ) { + Box(modifier = Modifier.fillMaxSize()) { + when (item) { + is MediaContentItem.Photo -> { + Image( + painter = rememberAsyncImagePainter(item.uri), + contentDescription = "Photo Preview", + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() + ) + } + is MediaContentItem.Video -> { + VideoPlayerItem(videoUri = item.uri) + } + } + + // Delete Card Button + Box( + modifier = Modifier + .align(Alignment.TopEnd) + .padding(10.dp) + ) { + OverlayIconButton( + icon = Icons.Default.Close, + onClick = { selectedMediaList.removeAt(index) } + ) + } + + // Media Type Badge Overlay (Top Left) + Surface( + color = DarkOverlay, + shape = RoundedCornerShape(8.dp), + modifier = Modifier + .align(Alignment.TopStart) + .padding(10.dp) + ) { + Row( + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = if (item is MediaContentItem.Video) Icons.Default.Videocam else Icons.Default.Image, + contentDescription = null, + tint = Color.White, + modifier = Modifier.size(14.dp) + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = if (item is MediaContentItem.Video) "Video" else "Photo ${index + 1}", + color = Color.White, + fontSize = 11.sp + ) + } + } + } + } + } + } + + Spacer(modifier = Modifier.height(16.dp)) + } + + // Location Tag Bar + + + Spacer(modifier = Modifier.height(20.dp)) + + + Spacer(modifier = Modifier.height(10.dp)) + + Spacer(modifier = Modifier.height(24.dp)) + } + + // Camera / Gallery Choice Bottom Sheet Dialog + if (showImagePickerDialog) { + ModalBottomSheet( + onDismissRequest = { showImagePickerDialog = false }, + containerColor = Color.White + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(24.dp) + ) { + Text( + text = "Add Photos or Videos", + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(bottom = 16.dp) + ) + + // Pick from Gallery (Photo + Video combined option) + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + showImagePickerDialog = false + galleryLauncher.launch( + PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo) + ) + } + .padding(vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.PhotoLibrary, + contentDescription = "Gallery", + tint = Color(0xFF7B1FA2), + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(16.dp)) + Text(text = "Choose Photos & Videos from Gallery", fontSize = 16.sp) + } + + // Take Photo + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + showImagePickerDialog = false + val uri = createTempFileUri(".jpg", "camera_photo_") + tempCameraImageUri = uri + cameraPhotoLauncher.launch(uri) + } + .padding(vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.CameraAlt, + contentDescription = "Take Photo", + tint = Color(0xFFE91E63), + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(16.dp)) + Text(text = "Take Photo", fontSize = 16.sp) + } + + // Record Video + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + showImagePickerDialog = false + val uri = createTempFileUri(".mp4", "camera_video_") + tempCameraVideoUri = uri + cameraVideoLauncher.launch(uri) + } + .padding(vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Videocam, + contentDescription = "Record Video", + tint = Color(0xFF1E88E5), + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(16.dp)) + Text(text = "Record Video", fontSize = 16.sp) + } + Spacer(modifier = Modifier.height(16.dp)) + } + } + } + + if (showFeelingSheet) { + FeelingPickerBottomSheet( + onDismiss = { showFeelingSheet = false }, + onFeelingSelected = { feeling -> + selectedFeeling = feeling + } + ) + } + } +} + +@Composable +fun VideoPlayerItem(videoUri: Uri) { + val context = LocalContext.current + val exoPlayer = remember(videoUri) { + ExoPlayer.Builder(context).build().apply { + setMediaItem(MediaItem.fromUri(videoUri)) + prepare() + playWhenReady = false + } + } + + DisposableEffect(Unit) { + onDispose { + exoPlayer.release() + } + } + + AndroidView( + factory = { ctx -> + PlayerView(ctx).apply { + player = exoPlayer + useController = true + } + }, + modifier = Modifier.fillMaxSize() + ) +} + +@Composable +fun OverlayIconButton(icon: ImageVector, onClick: () -> Unit = {}) { + Box( + modifier = Modifier + .size(32.dp) + .background(DarkOverlay, CircleShape) + .clickable { onClick() }, + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = Color.White, + modifier = Modifier.size(16.dp) + ) + } +} + +@Composable +fun VibeOptionChip( + icon: ImageVector, + backgroundColor: Color, + contentColor: Color, + onClick: () -> Unit = {} +) { + Surface( + shape = RoundedCornerShape(12.dp), + color = backgroundColor, + modifier = Modifier + .height(40.dp) + .clickable { onClick() } + ) { + Row( + modifier = Modifier.padding(horizontal = 14.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = contentColor, + modifier = Modifier.size(18.dp) + ) + Spacer(modifier = Modifier.width(6.dp)) + + } + } +} + + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun FeelingPickerBottomSheet( + onDismiss: () -> Unit, + onFeelingSelected: (VibeFeeling) -> Unit +) { + ModalBottomSheet( + onDismissRequest = onDismiss, + containerColor = Color.White + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) + ) { + Text( + text = "How are you feeling?", + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + color = Color.Black, + modifier = Modifier.padding(bottom = 16.dp) + ) + + LazyVerticalGrid( + columns = GridCells.Fixed(2), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(bottom = 24.dp) + ) { + items(vibeFeelingsList) { feeling -> + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(Color(0xFFF8F9FA)) + .clickable { + onFeelingSelected(feeling) + onDismiss() + } + .padding(horizontal = 12.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = feeling.emoji, + fontSize = 20.sp + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = feeling.name, + fontSize = 13.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF212121), + maxLines = 1 + ) + } + } + } + } + } +} + +@Preview( + name = "Create Vibe Post Screen", + showBackground = true, + showSystemUi = true +) +@Composable +fun CreateVibePostScreenPreview() { + MaterialTheme { + CreateVibePostScreen( + onCloseClick = {}, + onShareClick = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/homes/ExploreScreen.kt b/app/src/main/java/com/example/dattingapp/homes/ExploreScreen.kt index 8ce2a01..2cf0b5b 100644 --- a/app/src/main/java/com/example/dattingapp/homes/ExploreScreen.kt +++ b/app/src/main/java/com/example/dattingapp/homes/ExploreScreen.kt @@ -126,7 +126,6 @@ fun VibeCheckHomeScreen( badgeColor = Color(0xFF7C4DFF) ) } - item { Spacer(modifier = Modifier.height(16.dp)) } diff --git a/app/src/main/java/com/example/dattingapp/homes/HomeActivity.kt b/app/src/main/java/com/example/dattingapp/homes/HomeActivity.kt index 142a190..dc19496 100644 --- a/app/src/main/java/com/example/dattingapp/homes/HomeActivity.kt +++ b/app/src/main/java/com/example/dattingapp/homes/HomeActivity.kt @@ -30,7 +30,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.viewmodel.compose.viewModel import com.example.dattingapp.ChatScreen -import com.example.dattingapp.CreatePost import com.example.dattingapp.homes.networkandapi.HomeNetworkModule import com.example.dattingapp.homes.networkandapi.MeetUiState import com.example.dattingapp.homes.networkandapi.MeetUserProfile @@ -214,7 +213,7 @@ fun HomeScreen() { // CREATE POST // ========================================= 2 -> { - CreatePost() + CreateVibePostScreen() } // ========================================= // CHAT diff --git a/app/src/main/java/com/example/dattingapp/homes/MeetScreen.kt b/app/src/main/java/com/example/dattingapp/homes/MeetScreen.kt index 88f090b..9ae11f5 100644 --- a/app/src/main/java/com/example/dattingapp/homes/MeetScreen.kt +++ b/app/src/main/java/com/example/dattingapp/homes/MeetScreen.kt @@ -397,19 +397,3 @@ private fun handleSwipeAction( swipeViewModel.sendSwipeResponse(context, swipeRequest) } -@Preview( - name = "Discovery Screen - Active Card", - showBackground = true, - widthDp = 390, - heightDp = 844 -) -@Composable -fun MeetDiscoveryScreenPreview() { - MaterialTheme { - MeetDiscoveryScreen( - profiles = emptyList(), - isLoading = false, - errorMessage = null - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeDataClass.kt b/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeDataClass.kt index 9fb640e..c09b7a4 100644 --- a/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeDataClass.kt +++ b/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeDataClass.kt @@ -111,4 +111,37 @@ data class SwipeResponse( val matched: Boolean = false, @SerializedName("match_id") val matchId: Int? = null +) + +//feeling data for create post +data class VibeFeeling( + val name: String, + val emoji: String, + val category: String +) + +val vibeFeelingsList = listOf( + // Feelings + VibeFeeling("Happy", "😊", "Feelings"), + VibeFeeling("Blessed", "😇", "Feelings"), + VibeFeeling("Loved", "ðŸĨ°", "Feelings"), + VibeFeeling("Excited", "ðŸĪĐ", "Feelings"), + VibeFeeling("Crazy", "ðŸĪŠ", "Feelings"), + VibeFeeling("Sad", "ðŸ˜Ē", "Feelings"), + VibeFeeling("Angry", "ðŸ˜Ą", "Feelings"), + VibeFeeling("Relaxed", "😌", "Feelings"), + VibeFeeling("Tired", "ðŸ˜ī", "Feelings"), + VibeFeeling("Anxious", "😰", "Feelings"), + VibeFeeling("Motivated", "ðŸ”Ĩ", "Feelings"), + VibeFeeling("Grateful", "🙏", "Feelings"), + + // Activities + VibeFeeling("Eating", "🍔", "Activities"), + VibeFeeling("Drinking", "☕", "Activities"), + VibeFeeling("Traveling", "✈ïļ", "Activities"), + VibeFeeling("Listening to Music", "🎧", "Activities"), + VibeFeeling("Watching TV", "📚", "Activities"), + VibeFeeling("Working Out", "🏋ïļâ€â™‚ïļ", "Activities"), + VibeFeeling("Coding", "ðŸ’ŧ", "Activities"), + VibeFeeling("Gaming", "ðŸŽŪ", "Activities") ) \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt b/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt index a88474e..51dc96b 100644 --- a/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt +++ b/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt @@ -86,4 +86,13 @@ val textPink = Color(0xFFE91E63) val PinkGradientStart = Color(0xFFE91E63) val PurpleGradientEnd = Color(0xFF8E24AA) val LightBorderColor = Color(0xFFFFD1DC) -val InputBg = Color(0xFFF9FAFB) \ No newline at end of file +val InputBg = Color(0xFFF9FAFB) + + +// Custom Colors matching the UI design +val PurplePrimary = Color(0xFF9C27B0) +val RedPrimary = Color(0xFFE91E63) +val LightPinkBg = Color(0xFFFDE8E8) +val LightBlueBg = Color(0xFFEEF5FF) +val LightPurpleBg = Color(0xFFF5EEFF) +val DarkOverlay = Color(0xCC212529)