This is a dating app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

368 lines
14 KiB

package com.example.dattingapp.homes
import ChatScreen
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ChatBubbleOutline
import androidx.compose.material.icons.filled.Explore
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.SwapHoriz
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.dattingapp.homes.networkandapi.MeetUiState
import com.example.dattingapp.homes.networkandapi.MeetViewModel
import com.example.dattingapp.homes.networkandapi.ProfileViewModel
import com.example.dattingapp.homes.networkandapi.ProfileUiState
import com.example.dattingapp.permission.PermissionHelper
import com.example.dattingapp.ui.theme.DattingAPPTheme
import kotlinx.coroutines.delay
class HomeActivity : ComponentActivity() {
private lateinit var permissionHelper: PermissionHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
permissionHelper = PermissionHelper(this)
enableEdgeToEdge()
setContent {
DattingAPPTheme {
var showDialog by remember { mutableStateOf(false) }
var targetPermission by remember { mutableStateOf<PermissionHelper.PermissionType?>(null) }
// Function to handle permission requests with state check
fun checkAndRequestPermission(type: PermissionHelper.PermissionType) {
val isAlreadyGranted = if (type == PermissionHelper.PermissionType.OVERLAY) {
permissionHelper.isOverlayPermissionGranted()
} else {
permissionHelper.hasPermission(type)
}
if (isAlreadyGranted) {
Toast.makeText(this, "${type.name} already granted!", Toast.LENGTH_SHORT).show()
} else {
targetPermission = type
showDialog = true
}
}
// 10-second delay trigger inside Compose lifecycle
LaunchedEffect(Unit) {
delay(10_000) // Wait 10 seconds
checkAndRequestPermission(PermissionHelper.PermissionType.OVERLAY)
}
// Custom rationale dialog
if (showDialog && targetPermission != null) {
val (title, description) = when (targetPermission!!) {
PermissionHelper.PermissionType.OVERLAY ->
"Display Over Other Apps" to "This permission is needed to show floating call alerts over other apps."
PermissionHelper.PermissionType.LOCATION ->
"Location Access" to "We need location access to find matches near you."
PermissionHelper.PermissionType.AUDIO ->
"Microphone Access" to "Microphone access is required for voice calling."
PermissionHelper.PermissionType.GALLERY ->
"Photos Access" to "Access to photos is required to update your profile picture."
PermissionHelper.PermissionType.NOTIFICATION ->
"Notifications" to "Enable notifications so you don't miss new messages."
}
PermissionRationaleDialog(
title = title,
description = description,
onConfirm = {
showDialog = false
val currentType = targetPermission!!
if (currentType == PermissionHelper.PermissionType.OVERLAY) {
permissionHelper.requestOverlayPermission { isGranted ->
if (!isGranted) {
Toast.makeText(
this@HomeActivity,
"Overlay Denied",
Toast.LENGTH_SHORT
).show()
}
}
} else {
permissionHelper.requestPermissions(currentType) { isGranted ->
val msg = if (isGranted) "${currentType.name} Granted" else "${currentType.name} Denied"
Toast.makeText(this@HomeActivity, msg, Toast.LENGTH_SHORT).show()
}
}
},
onDismiss = {
showDialog = false
}
)
}
HomeScreen()
}
}
}
}
@Composable
fun PermissionRationaleDialog(
title: String,
description: String,
onConfirm: () -> Unit,
onDismiss: () -> Unit
) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(text = title) },
text = { Text(text = description) },
confirmButton = {
TextButton(onClick = onConfirm) {
Text(text = "Allow")
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text(text = "Cancel")
}
}
)
}
@Composable
fun HomeScreen() {
val appContext = LocalContext.current.applicationContext
var selectedItem by remember { mutableIntStateOf(0) }
Scaffold(
bottomBar = {
NavigationBar {
// =========================================
// MEET
// =========================================
NavigationBarItem(
selected = selectedItem == 0,
onClick = { selectedItem = 0 },
icon = {
Icon(
imageVector = Icons.Default.SwapHoriz,
contentDescription = "Meet"
)
},
label = {
Text("Meet")
}
)
// =========================================
// EXPLORE
// =========================================
NavigationBarItem(
selected = selectedItem == 1,
onClick = { selectedItem = 1 },
icon = {
Icon(
imageVector = Icons.Default.Explore,
contentDescription = "Explore"
)
},
label = { Text("Explore") }
)
// =========================================
// POST
// =========================================
NavigationBarItem(
selected = selectedItem == 2,
onClick = { selectedItem = 2 },
icon = { Icon(
imageVector = Icons.Default.Add,
contentDescription = "Post"
)
},
label = {
Text("Post")
}
)
// =========================================
// CHAT
// =========================================
NavigationBarItem(
selected = selectedItem == 3,
onClick = { selectedItem = 3 },
icon = {
Icon(
imageVector = Icons.Default.ChatBubbleOutline,
contentDescription = "Chat"
)
},
label = { Text("Chat") }
)
// =========================================
// PROFILE
// =========================================
NavigationBarItem(
selected = selectedItem == 4,
onClick = { selectedItem = 4 },
icon = {
Icon(
imageVector = Icons.Default.Person,
contentDescription = "Profile"
)
},
label = { Text("Profile") }
)
}
}
) { innerPadding ->
// =========================================
// CONTENT AREA
// =========================================
val context = LocalContext.current
// or via hiltViewModel() / factory
Box(modifier = Modifier.padding(innerPadding)) {
when (selectedItem) {
// =========================================
// MEET
// =========================================
0 -> {
// 1. Obtain instance of ViewModel and LocalContext
val viewModel: MeetViewModel = viewModel()
// 2. Collect state from ViewModel
val uiState by viewModel.meetUiState.collectAsState()
// 3. Trigger network call once when screen loads
LaunchedEffect(Unit) {
viewModel.meetUserResponse(context)
}
// 4. Map UI state to Composable parameters
when (val state = uiState) {
is MeetUiState.Loading, MeetUiState.Idle -> {
MeetDiscoveryScreen(
profiles = emptyList(),
isLoading = true,
errorMessage = null
)
}
is MeetUiState.Success -> {
MeetDiscoveryScreen(
profiles = state.response,
isLoading = false,
errorMessage = null
)
}
is MeetUiState.Error -> {
MeetDiscoveryScreen(
profiles = emptyList(),
isLoading = false,
errorMessage = state.message
)
}
}
}
// =========================================
// EXPLORE
// =========================================
1 -> {
VibeCheckHomeScreen()
}
// =========================================
// CREATE POST
// =========================================
2 -> {
CreateVibePostScreen()
}
// =========================================
// CHAT
// =========================================
3 -> {
ChatScreen()
}
// =========================================
// PROFILE
// =========================================
4 -> {
// 1. Obtain instance of ViewModel and LocalContext
val profileViewModel: ProfileViewModel = viewModel()
val context = LocalContext.current
// 2. Collect state from ViewModel
val uiState by profileViewModel.profileUiState.collectAsState()
// 3. Trigger network call once when screen loads
LaunchedEffect(Unit) {
profileViewModel.profileResponse(context)
}
// 4. Map UI state to Composable parameters
when (val state = uiState) {
is ProfileUiState.Loading, ProfileUiState.Idle -> {
MyProfileScreen(
profileResponse = null,
isLoading = true,
errorMessage = null,
// onRetry = { viewModel.meetUserResponse(context) }
)
}
is ProfileUiState.Success -> {
MyProfileScreen(
profileResponse = state.profileData, // or state.response depending on your data class naming
isLoading = false,
errorMessage = null,
)
}
is ProfileUiState.Error -> {
MyProfileScreen(
profileResponse = null,
isLoading = false,
errorMessage = state.message,
onRetry = { profileViewModel.profileResponse(context) }
)
}
}
}
}
}
}
}