diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index b589d56..b86273d 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index b268ef3..89c2f2c 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,14 @@
+
+
+
+
+
+
+
+
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 0897082..639c779 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -4,6 +4,7 @@
-
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 6806f5a..0dfdca2 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -37,6 +37,9 @@
+
+
+
@@ -45,6 +48,9 @@
+
+
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 0ad17cb..b2c751a 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,7 +1,6 @@
-
-
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 7709fd2..eda299d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,8 +1,10 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
id("kotlin-kapt")
id("kotlin-parcelize")
+ alias(libs.plugins.kotlin.ksp)
}
android {
@@ -95,4 +97,13 @@ dependencies {
// Image loading with Coil
implementation("io.coil-kt:coil-compose:2.4.0")
+ //websocket
+ implementation("com.squareup.okhttp3:okhttp:5.1.0")
+
+ //room
+ implementation(libs.androidx.room.runtime)
+ implementation(libs.androidx.room.ktx)
+ ksp(libs.androidx.room.compiler)
+ implementation(libs.androidx.lifecycle.viewmodel.compose)
+
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d169ba2..6a7035e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,8 +3,10 @@
xmlns:tools="http://schemas.android.com/tools">
+
= Build.VERSION_CODES.TIRAMISU) {
+ if (ContextCompat.checkSelfPermission(
+ this,
+ android.Manifest.permission.POST_NOTIFICATIONS
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ ActivityCompat.requestPermissions(
+ this,
+ arrayOf(android.Manifest.permission.POST_NOTIFICATIONS),
+ 1001
+ )
+ }
+ }
+ }
+
+
}
@Composable
@@ -43,11 +73,24 @@ fun SmartHRApp() {
val context = LocalContext.current
val dataStoreManager = DataStoreManager(context)
val authRepository = AuthRepository(dataStoreManager)
+ val chatRepository = ChatRepository(dataStoreManager)
val authViewModel: AuthViewModel = viewModel { AuthViewModel(authRepository) }
+ val chatViewModel : ChatViewModel = viewModel { ChatViewModel(chatRepository) }
+ val user = authViewModel.user.collectAsState(initial = null).value
var startDestination by remember { mutableStateOf(null) }
var isInitialized by remember { mutableStateOf(false) }
+ val notificationEvent by chatViewModel.notificationEvent.collectAsState()
+
+ // Show notification
+ LaunchedEffect(notificationEvent) {
+ notificationEvent?.let { (title, message) ->
+ showNotification(context, title, message)
+ chatViewModel.clearNotificationEvent() // prevent repeat
+ }
+ }
+
// Determine start destination based on auth state with delay to check persistence
LaunchedEffect(Unit) {
// Add small delay to ensure DataStore is properly loaded
@@ -57,8 +100,8 @@ fun SmartHRApp() {
if (isLoggedIn) {
authRepository.user.collect { user ->
startDestination = when (user?.role) {
- com.example.smarthr_app.data.model.UserRole.ROLE_HR -> Screen.HRDashboard.route
- com.example.smarthr_app.data.model.UserRole.ROLE_USER -> Screen.EmployeeDashboard.route
+ com.example.smarthr_app.data.model.UserRole.ROLE_HR -> Screen.ChatList.route
+ com.example.smarthr_app.data.model.UserRole.ROLE_USER -> Screen.ChatList.route
else -> Screen.RoleSelection.route
}
isInitialized = true
@@ -78,4 +121,5 @@ fun SmartHRApp() {
startDestination = startDestination!!
)
}
-}
\ No newline at end of file
+}
+
diff --git a/app/src/main/java/com/example/smarthr_app/data/model/ChatList.kt b/app/src/main/java/com/example/smarthr_app/data/model/ChatList.kt
new file mode 100644
index 0000000..f567ada
--- /dev/null
+++ b/app/src/main/java/com/example/smarthr_app/data/model/ChatList.kt
@@ -0,0 +1,13 @@
+package com.example.smarthr_app.data.model
+
+data class Chat(
+ val companyCode: String,
+ val id: String, //chat id
+ val lastMessage: String,
+ val lastMessageStatus: String, //SEEN,DELIVERED
+ val lastMessageType: String,
+ val lastMessageSender:String,
+ val lastUpdated: String,
+ val user1: UserInfo, // me
+ val user2: UserInfo
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/smarthr_app/data/model/ChatMessage.kt b/app/src/main/java/com/example/smarthr_app/data/model/ChatMessage.kt
new file mode 100644
index 0000000..77c4663
--- /dev/null
+++ b/app/src/main/java/com/example/smarthr_app/data/model/ChatMessage.kt
@@ -0,0 +1,13 @@
+package com.example.smarthr_app.data.model
+
+data class ChatMessage(
+val id: String, // message id
+val chatId:String,
+val sender: UserInfo,
+val receiver: UserInfo,
+val content: String,
+val messageType: String,
+val companyCode: String,
+val timestamp: String,
+val messageStatus: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/smarthr_app/data/model/SeenMessage.kt b/app/src/main/java/com/example/smarthr_app/data/model/SeenMessage.kt
new file mode 100644
index 0000000..68253ad
--- /dev/null
+++ b/app/src/main/java/com/example/smarthr_app/data/model/SeenMessage.kt
@@ -0,0 +1,6 @@
+package com.example.smarthr_app.data.model
+
+data class SeenMessage(
+ val chatId:String,
+ val userId: String
+)
diff --git a/app/src/main/java/com/example/smarthr_app/data/remote/ApiService.kt b/app/src/main/java/com/example/smarthr_app/data/remote/ApiService.kt
index efa0712..be23fb6 100644
--- a/app/src/main/java/com/example/smarthr_app/data/remote/ApiService.kt
+++ b/app/src/main/java/com/example/smarthr_app/data/remote/ApiService.kt
@@ -173,4 +173,29 @@ interface ApiService {
@Path("leaveId") leaveId: String
): Response
+ @GET("chats/myChats")
+ suspend fun getMyChatList(
+ @Header("Authorization") token: String,
+ @Query("companyCode") companyCode: String
+ ) : Response>
+
+ @GET("companies/everybody")
+ suspend fun getAllHrAndEmployeeOfCompany(
+ @Header("Authorization") token: String,
+ ) : Response>
+
+ @GET("chats/history")
+ suspend fun getChatBetweenUser(
+ @Header("Authorization") token: String,
+ @Query("companyCode") companyCode: String,
+ @Query("otherUserId") otherUserId: String
+ ) : Response>
+
+ @PUT("chats/seen/{chatId}")
+ suspend fun markChatSeen(
+ @Header("Authorization") token: String,
+ @Path("chatId") chatId:String,
+ @Query("userId") userId : String,
+ ): Response
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/smarthr_app/data/remote/RetrofitInstance.kt b/app/src/main/java/com/example/smarthr_app/data/remote/RetrofitInstance.kt
index f68ae7a..2d64bff 100644
--- a/app/src/main/java/com/example/smarthr_app/data/remote/RetrofitInstance.kt
+++ b/app/src/main/java/com/example/smarthr_app/data/remote/RetrofitInstance.kt
@@ -7,7 +7,7 @@ import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
object RetrofitInstance {
- const val BASE_URL = "https://smarthr-backend-jx0v.onrender.com/"
+ const val BASE_URL = "https://smarthr-backend-jx0v.onrender.com"
private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
diff --git a/app/src/main/java/com/example/smarthr_app/data/repository/ChatRepository.kt b/app/src/main/java/com/example/smarthr_app/data/repository/ChatRepository.kt
new file mode 100644
index 0000000..477f6f7
--- /dev/null
+++ b/app/src/main/java/com/example/smarthr_app/data/repository/ChatRepository.kt
@@ -0,0 +1,99 @@
+package com.example.smarthr_app.data.repository
+
+import android.util.Log
+import com.example.smarthr_app.data.local.DataStoreManager
+import com.example.smarthr_app.data.model.Chat
+import com.example.smarthr_app.data.model.ChatMessage
+import com.example.smarthr_app.data.model.SuccessApiResponseMessage
+import com.example.smarthr_app.data.model.UserInfo
+import com.example.smarthr_app.data.remote.RetrofitInstance
+import kotlinx.coroutines.flow.first
+
+class ChatRepository(private val dataStoreManager: DataStoreManager) {
+
+
+ suspend fun getMyChatList(companyCode: String): List? {
+ return try {
+ val token = dataStoreManager.token.first()
+ if (token != null) {
+ val response = RetrofitInstance.api.getMyChatList("Bearer $token", companyCode)
+ Log.d("ChatList", "Response: ${response.body()}")
+ if (response.isSuccessful) {
+ response.body()
+ } else {
+ emptyList()
+ }
+ } else {
+ emptyList()
+ }
+ } catch (e: Exception) {
+ emptyList()
+ }
+ }
+
+ suspend fun getAllUsers(): List? {
+ return try {
+ val token = dataStoreManager.token.first()
+ if (token != null) {
+ val response = RetrofitInstance.api.getAllHrAndEmployeeOfCompany("Bearer $token")
+ Log.d("User List", "Response: ${response.body()}")
+ if (response.isSuccessful) {
+ response.body()
+ } else {
+ emptyList()
+ }
+ } else {
+ emptyList()
+ }
+ } catch (e: Exception) {
+ emptyList()
+ }
+ }
+
+ suspend fun getChatBetweenUser(
+ companyCode: String,
+ otherUerId: String
+ ): List? {
+ return try {
+ val token = dataStoreManager.token.first()
+ if (token != null) {
+ val response = RetrofitInstance.api.getChatBetweenUser("Bearer $token", companyCode = companyCode, otherUserId = otherUerId)
+ Log.i("FatUsers", "Response: ${response.body()}")
+ if (response.isSuccessful) {
+ response.body()
+ } else {
+ emptyList()
+ }
+ } else {
+ emptyList()
+ }
+ } catch (e: Exception) {
+ emptyList()
+ }
+ }
+
+ suspend fun markChatAsSeen(
+ chatId:String,
+ userId:String,
+ ): SuccessApiResponseMessage? {
+ return try {
+ val token = dataStoreManager.token.first()
+ if (token != null) {
+ val response = RetrofitInstance.api.markChatSeen(token = "Bearer $token", chatId = chatId, userId = userId)
+ Log.i("ChatSeen", "Response: ${response.body()}")
+ if (response.isSuccessful) {
+ response.body()
+ } else {
+
+null }
+ } else {
+ null
+
+ }
+ } catch (e: Exception) {
+ null
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/smarthr_app/presentation/navigation/NavGraph.kt b/app/src/main/java/com/example/smarthr_app/presentation/navigation/NavGraph.kt
index 956b522..190b91d 100644
--- a/app/src/main/java/com/example/smarthr_app/presentation/navigation/NavGraph.kt
+++ b/app/src/main/java/com/example/smarthr_app/presentation/navigation/NavGraph.kt
@@ -21,13 +21,18 @@ import com.example.smarthr_app.presentation.viewmodel.CompanyViewModel
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavType
import androidx.navigation.navArgument
+import com.example.smarthr_app.data.repository.ChatRepository
import com.example.smarthr_app.data.repository.LeaveRepository
import com.example.smarthr_app.data.repository.TaskRepository
+import com.example.smarthr_app.presentation.screen.chat.AllUserListScreen
+import com.example.smarthr_app.presentation.screen.chat.ChatListScreen
+import com.example.smarthr_app.presentation.screen.chat.ChatScreen
import com.example.smarthr_app.presentation.screen.dashboard.employee.EmployeeTaskDetailScreen
import com.example.smarthr_app.presentation.screen.dashboard.hr.CreateTaskScreen
import com.example.smarthr_app.presentation.screen.dashboard.hr.HRLeaveManagementScreen
import com.example.smarthr_app.presentation.screen.dashboard.hr.HRTaskManagementScreen
import com.example.smarthr_app.presentation.screen.dashboard.hr.TaskDetailScreen
+import com.example.smarthr_app.presentation.viewmodel.ChatViewModel
import com.example.smarthr_app.presentation.viewmodel.LeaveViewModel
import com.example.smarthr_app.presentation.viewmodel.TaskViewModel
@@ -41,11 +46,12 @@ fun NavGraph(
val authRepository = AuthRepository(dataStoreManager)
val companyRepository = CompanyRepository(dataStoreManager)
val taskRepository = TaskRepository(dataStoreManager)
+ val chatRepository = ChatRepository(dataStoreManager)
val authViewModel: AuthViewModel = viewModel { AuthViewModel(authRepository) }
val companyViewModel: CompanyViewModel = viewModel { CompanyViewModel(companyRepository) }
val taskViewModel: TaskViewModel = viewModel { TaskViewModel(taskRepository) }
-
+ val chatViewModel : ChatViewModel = viewModel { ChatViewModel(chatRepository) }
val leaveRepository = LeaveRepository(dataStoreManager)
val leaveViewModel: LeaveViewModel = viewModel { LeaveViewModel(leaveRepository) }
@@ -66,52 +72,33 @@ fun NavGraph(
composable(Screen.Register.route) {
RegisterScreen(
+ chatViewModel = chatViewModel,
viewModel = authViewModel,
onNavigateBack = {
navController.popBackStack()
},
- onNavigateToHRDashboard = {
- navController.navigate(Screen.HRDashboard.route) {
- popUpTo(Screen.RoleSelection.route) {
- inclusive = true
- }
- }
- },
- onNavigateToEmployeeDashboard = {
- navController.navigate(Screen.EmployeeDashboard.route) {
- popUpTo(Screen.RoleSelection.route) {
- inclusive = true
- }
- }
+ onNavigateToChatScreen = {
+ navController.navigate(Screen.ChatList.route)
}
)
}
composable(Screen.Login.route) {
LoginScreen(
+ chatViewModel = chatViewModel,
viewModel = authViewModel,
onNavigateBack = {
navController.popBackStack()
},
- onNavigateToHRDashboard = {
- navController.navigate(Screen.HRDashboard.route) {
- popUpTo(Screen.RoleSelection.route) {
- inclusive = true
- }
- }
- },
- onNavigateToEmployeeDashboard = {
- navController.navigate(Screen.EmployeeDashboard.route) {
- popUpTo(Screen.RoleSelection.route) {
- inclusive = true
- }
- }
+ onNavigateToChatScreen = {
+ navController.navigate(Screen.ChatList.route)
}
)
}
composable(Screen.HRDashboard.route) {
HRDashboardScreen(
+ chatViewModel = chatViewModel,
authViewModel = authViewModel,
onLogout = {
authViewModel.logout()
@@ -243,6 +230,7 @@ fun NavGraph(
composable(Screen.EmployeeDashboard.route) {
EmployeeDashboardScreen(
+ chatViewModel = chatViewModel,
authViewModel = authViewModel,
taskViewModel = taskViewModel,
leaveViewModel = leaveViewModel,
@@ -313,6 +301,58 @@ fun NavGraph(
)
}
+ composable(Screen.ChatList.route){
+ ChatListScreen(
+ chatViewModel = chatViewModel,
+ authViewModel = authViewModel,
+ onNavigateToUserListScreen = {
+ navController.navigate(Screen.AllUserListScreen.route)
+ },
+ onNavigateChatScreen = {otherUserId,imageUrl,name->
+ navController.navigate("${Screen.ChatScreen.route}/$otherUserId/$imageUrl/$name")
+ }
+ )
+ }
+
+ composable(Screen.AllUserListScreen.route){
+ AllUserListScreen(
+ chatViewModel = chatViewModel,
+ authViewModel = authViewModel,
+ goToBack = {
+ navController.popBackStack()
+ },
+ onNavigateToChatScreen = {otherUserId,imageUrl,name->
+ navController.navigate("${Screen.ChatScreen.route}/$otherUserId/$imageUrl/$name")
+ }
+ )
+ }
+
+ composable(
+ route = "${Screen.ChatScreen.route}/{otherUserId}/{imageUrl}/{name}",
+ arguments = listOf(
+ navArgument("otherUserId") { type = NavType.StringType },
+ navArgument("imageUrl") { type = NavType.StringType },
+ navArgument("name") { type = NavType.StringType }
+ ),
+
+
+ ) { backStackEntry ->
+ val otherUserId = backStackEntry.arguments?.getString("otherUserId") ?: ""
+ val imageUrl = backStackEntry.arguments?.getString("imageUrl") ?: ""
+ val name = backStackEntry.arguments?.getString("name") ?: ""
+ ChatScreen(
+ chatViewModel = chatViewModel,
+ authViewModel = authViewModel,
+ receiverId = otherUserId,
+ imageUrl = imageUrl,
+ name = name,
+ goToBack = {
+ navController.popBackStack()
+ }
+ )
+ }
+
+
}
}
@@ -331,6 +371,9 @@ sealed class Screen(val route: String) {
// Task Management Routes
object HRTaskManagement : Screen("hr_task_management")
object CreateTask : Screen("create_task")
+ object ChatList : Screen("chat_list")
+ object AllUserListScreen : Screen("user_list")
+ object ChatScreen : Screen("chat_screen")
object TaskDetail : Screen("task_detail/{taskId}") {
fun createRoute(taskId: String) = "task_detail/$taskId"
diff --git a/app/src/main/java/com/example/smarthr_app/presentation/screen/auth/LoginScreen.kt b/app/src/main/java/com/example/smarthr_app/presentation/screen/auth/LoginScreen.kt
index 9a82b8d..a1eb511 100644
--- a/app/src/main/java/com/example/smarthr_app/presentation/screen/auth/LoginScreen.kt
+++ b/app/src/main/java/com/example/smarthr_app/presentation/screen/auth/LoginScreen.kt
@@ -24,6 +24,7 @@ import com.example.smarthr_app.data.model.LoginRequest
import com.example.smarthr_app.presentation.theme.PrimaryPurple
import com.example.smarthr_app.presentation.theme.SecondaryPurple
import com.example.smarthr_app.presentation.viewmodel.AuthViewModel
+import com.example.smarthr_app.presentation.viewmodel.ChatViewModel
import com.example.smarthr_app.utils.Resource
import com.example.smarthr_app.utils.ToastHelper
import com.example.smarthr_app.utils.ValidationUtils
@@ -32,10 +33,10 @@ import kotlinx.coroutines.delay
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LoginScreen(
+ chatViewModel: ChatViewModel,
viewModel: AuthViewModel,
onNavigateBack: () -> Unit,
- onNavigateToHRDashboard: () -> Unit,
- onNavigateToEmployeeDashboard: () -> Unit
+ onNavigateToChatScreen:()->Unit
) {
val context = LocalContext.current
@@ -56,9 +57,11 @@ fun LoginScreen(
ToastHelper.showSuccessToast(context, "Login successful!")
delay(500)
if (currentState.data.user.role == "ROLE_HR") {
- onNavigateToHRDashboard()
+ chatViewModel.initSocket(currentState.data.user.userId)
+ onNavigateToChatScreen()
} else {
- onNavigateToEmployeeDashboard()
+ chatViewModel.initSocket(currentState.data.user.userId)
+ onNavigateToChatScreen()
}
viewModel.clearAuthState()
}
diff --git a/app/src/main/java/com/example/smarthr_app/presentation/screen/auth/RegisterScreen.kt b/app/src/main/java/com/example/smarthr_app/presentation/screen/auth/RegisterScreen.kt
index 632ffdb..4706098 100644
--- a/app/src/main/java/com/example/smarthr_app/presentation/screen/auth/RegisterScreen.kt
+++ b/app/src/main/java/com/example/smarthr_app/presentation/screen/auth/RegisterScreen.kt
@@ -27,6 +27,7 @@ import com.example.smarthr_app.data.model.UserRole
import com.example.smarthr_app.presentation.theme.PrimaryPurple
import com.example.smarthr_app.presentation.theme.SecondaryPurple
import com.example.smarthr_app.presentation.viewmodel.AuthViewModel
+import com.example.smarthr_app.presentation.viewmodel.ChatViewModel
import com.example.smarthr_app.utils.Resource
import com.example.smarthr_app.utils.ToastHelper
import com.example.smarthr_app.utils.ValidationUtils
@@ -36,10 +37,10 @@ import kotlinx.coroutines.delay
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RegisterScreen(
+ chatViewModel: ChatViewModel,
viewModel: AuthViewModel,
onNavigateBack: () -> Unit,
- onNavigateToHRDashboard: () -> Unit,
- onNavigateToEmployeeDashboard: () -> Unit
+ onNavigateToChatScreen:()->Unit
) {
val context = LocalContext.current
@@ -67,9 +68,11 @@ fun RegisterScreen(
ToastHelper.showSuccessToast(context, "Account created successfully!")
delay(500)
if (currentState.data.user.role == "ROLE_HR") {
- onNavigateToHRDashboard()
+ chatViewModel.initSocket(currentState.data.user.userId)
+ onNavigateToChatScreen()
} else {
- onNavigateToEmployeeDashboard()
+ chatViewModel.initSocket(currentState.data.user.userId)
+ onNavigateToChatScreen()
}
viewModel.clearRegisterState()
}
diff --git a/app/src/main/java/com/example/smarthr_app/presentation/screen/chat/AllUserListScreen.kt b/app/src/main/java/com/example/smarthr_app/presentation/screen/chat/AllUserListScreen.kt
new file mode 100644
index 0000000..2f5ac63
--- /dev/null
+++ b/app/src/main/java/com/example/smarthr_app/presentation/screen/chat/AllUserListScreen.kt
@@ -0,0 +1,121 @@
+package com.example.smarthr_app.presentation.screen.chat
+
+import android.net.Uri
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.getValue
+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.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import coil.compose.AsyncImage
+import com.example.smarthr_app.data.model.UserInfo
+import com.example.smarthr_app.presentation.viewmodel.AuthViewModel
+import com.example.smarthr_app.presentation.viewmodel.ChatViewModel
+
+@Composable
+fun AllUserListScreen(
+ chatViewModel: ChatViewModel,
+ authViewModel: AuthViewModel,
+ goToBack:()-> Unit,
+ onNavigateToChatScreen: (String,String,String) -> Unit
+) {
+ val user by authViewModel.user.collectAsState(initial = null)
+ val allUsers by chatViewModel.userList.collectAsState()
+
+ LaunchedEffect(Unit) {
+ chatViewModel.getAllUser()
+ }
+
+ val filteredUsers = remember(user, allUsers) {
+ allUsers?.filter { it.id != user?.userId } ?: emptyList()
+ }
+
+ Column(
+ modifier = Modifier
+ .statusBarsPadding()
+ .fillMaxSize()
+ .background(Color.White)
+ ) {
+ // Top App Bar
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 20.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ IconButton(onClick = goToBack) {
+ Icon(Icons.Default.ArrowBack, contentDescription = "Back")
+ }
+ Text(
+ text = "Select Contact",
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Bold,
+ modifier = Modifier.padding(start = 8.dp)
+ )
+ }
+
+ // User List
+ LazyColumn {
+ items(filteredUsers) { user ->
+ UserListItem(user = user, onClick = {
+ onNavigateToChatScreen(user.id, Uri.encode(user.imageUrl?:"https://cdn.pixabay.com/photo/2023/02/18/11/00/icon-7797704_1280.png"),user.name)
+ })
+ HorizontalDivider(color = Color(0xFFEFEFEF), thickness = 1.dp)
+ }
+ }
+ }
+}
+
+
+@Composable
+fun UserListItem(user: UserInfo, onClick: () -> Unit) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable { onClick() }
+ .padding(horizontal = 16.dp, vertical = 14.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ AsyncImage(
+ model = user.imageUrl ?: "https://cdn.pixabay.com/photo/2023/02/18/11/00/icon-7797704_1280.png",
+ contentDescription = null,
+ modifier = Modifier
+ .size(48.dp)
+ .clip(CircleShape)
+ .background(Color.LightGray),
+ contentScale = ContentScale.Crop
+ )
+
+ Spacer(modifier = Modifier.width(12.dp))
+
+ Text(user.name, fontSize = 16.sp, fontWeight = FontWeight.Medium)
+ }
+}
+
diff --git a/app/src/main/java/com/example/smarthr_app/presentation/screen/chat/ChatListScreen.kt b/app/src/main/java/com/example/smarthr_app/presentation/screen/chat/ChatListScreen.kt
new file mode 100644
index 0000000..49300ef
--- /dev/null
+++ b/app/src/main/java/com/example/smarthr_app/presentation/screen/chat/ChatListScreen.kt
@@ -0,0 +1,207 @@
+package com.example.smarthr_app.presentation.screen.chat
+
+import android.net.Uri
+import androidx.compose.foundation.background
+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.Row
+import androidx.compose.foundation.layout.Spacer
+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.layout.statusBarsPadding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material.icons.filled.Done
+import androidx.compose.material.icons.filled.DoneAll
+import androidx.compose.material3.FloatingActionButton
+import androidx.compose.material3.FloatingActionButtonDefaults
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+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.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import coil.compose.AsyncImage
+import com.example.smarthr_app.data.model.Chat
+import com.example.smarthr_app.presentation.viewmodel.AuthViewModel
+import com.example.smarthr_app.presentation.viewmodel.ChatViewModel
+import com.example.smarthr_app.utils.toReadableTime
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.format.DateTimeFormatter
+
+
+@Composable
+fun ChatListScreen(
+ chatViewModel: ChatViewModel,
+ authViewModel: AuthViewModel,
+ onNavigateToUserListScreen: () -> Unit,
+ onNavigateChatScreen: (String,String,String) -> Unit
+) {
+ val user by authViewModel.user.collectAsState(initial = null)
+ LaunchedEffect(Unit) { //only once called
+ user?.let {
+ chatViewModel.getMyChatList(companyCode = it.companyCode!!)
+ }
+ }
+ LaunchedEffect(user) {
+ user?.let {
+ chatViewModel.initSocket(it.userId)
+ }
+ }
+
+
+ val chatList = chatViewModel.chatList.collectAsState().value
+
+ Box(
+ modifier = Modifier
+ .statusBarsPadding()
+ .fillMaxSize()
+ .background(Color.White)
+ ) {
+ Column() {
+ TopAppBarContent()
+
+ LazyColumn {
+ items(chatList ?: emptyList()) { chatItem ->
+ ChatListRow(chatItem = chatItem,onNavigateChatScreen,chatViewModel)
+ HorizontalDivider(color = Color(0xFFEFEFEF), thickness = 1.dp)
+ }
+ }
+ }
+
+
+
+ // Floating Button
+ FloatingActionButton(
+ onClick = {
+ onNavigateToUserListScreen()
+ },
+ modifier = Modifier
+ .align(Alignment.BottomEnd)
+ .padding(16.dp),
+ containerColor = Color(0xFF795FFC),
+ contentColor = Color.White,
+ shape = CircleShape,
+ elevation = FloatingActionButtonDefaults.elevation(defaultElevation = 6.dp)
+ ) {
+ Icon(Icons.Default.Add, contentDescription = "Add Chat")
+ }
+ }
+}
+
+
+@Composable
+fun ChatListRow(chatItem: Chat,onNavigateChatScreen:(String,String,String)->Unit,chatViewModel: ChatViewModel) {
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable(
+ onClick = {
+ onNavigateChatScreen(chatItem.user2.id, Uri.encode(chatItem.user2.imageUrl?:"https://cdn.pixabay.com/photo/2023/02/18/11/00/icon-7797704_1280.png"),chatItem.user2.name)
+ }
+ )
+ .padding(horizontal = 16.dp, vertical = 14.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ AsyncImage(
+ model = chatItem.user2.imageUrl ?: "https://cdn.pixabay.com/photo/2023/02/18/11/00/icon-7797704_1280.png",
+ contentDescription = null,
+ modifier = Modifier
+ .size(48.dp)
+ .clip(CircleShape)
+ .background(Color.LightGray),
+ contentScale = ContentScale.Crop
+ )
+
+ Spacer(modifier = Modifier.width(12.dp))
+
+ Column(
+ modifier = Modifier.weight(1f)
+ ) {
+ Text(chatItem.user2.name, fontWeight = FontWeight.Bold, fontSize = 16.sp)
+
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ if (chatItem.lastMessageSender == chatItem.user1.id) {
+ // Show tick icons only if the sender is user1 (current user)
+ val tickIcon = when (chatItem.lastMessageStatus) {
+ "SEEN" -> Icons.Default.DoneAll // double tick
+ "DELIVERED" -> Icons.Default.Done // single tick
+ else -> null
+ }
+
+ tickIcon?.let {
+ Icon(
+ imageVector = it,
+ contentDescription = chatItem.lastMessageStatus,
+ tint = if (chatItem.lastMessageStatus == "SEEN") Color.Blue else Color.Gray,
+ modifier = Modifier.size(18.dp).padding(end = 4.dp)
+ )
+ }
+ }
+
+ Text(
+ text = chatItem.lastMessage,
+ color = Color.Gray,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+ }
+
+
+ Column(horizontalAlignment = Alignment.End) {
+ Text(
+ text = chatItem.lastUpdated.toReadableTime(),
+ color = Color.Gray,
+ fontSize = 12.sp
+ )
+ if (chatItem.lastMessageSender!=chatItem.user1.id && chatItem.lastMessageStatus != "SEEN") {
+ Spacer(modifier = Modifier.height(4.dp))
+ Box(
+ modifier = Modifier
+ .size(10.dp)
+ .clip(CircleShape)
+ .background(Color(0xFF4CAF50))
+ )
+ }
+ }
+ }
+}
+
+
+@Composable
+fun TopAppBarContent() {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 20.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(Icons.Default.ArrowBack, contentDescription = "Back")
+ Text("Messages", fontSize = 20.sp, fontWeight = FontWeight.Bold)
+ Spacer(modifier = Modifier.width(24.dp)) // to balance icon
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/smarthr_app/presentation/screen/chat/ChatScreen.kt b/app/src/main/java/com/example/smarthr_app/presentation/screen/chat/ChatScreen.kt
new file mode 100644
index 0000000..ec29a8b
--- /dev/null
+++ b/app/src/main/java/com/example/smarthr_app/presentation/screen/chat/ChatScreen.kt
@@ -0,0 +1,254 @@
+package com.example.smarthr_app.presentation.screen.chat
+
+import android.net.Uri
+import android.util.Log
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.automirrored.filled.Send
+import androidx.compose.material.icons.filled.Done
+import androidx.compose.material.icons.filled.DoneAll
+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.Color
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import coil.compose.rememberAsyncImagePainter
+import com.example.smarthr_app.data.model.ChatMessage
+import com.example.smarthr_app.presentation.viewmodel.AuthViewModel
+import com.example.smarthr_app.presentation.viewmodel.ChatViewModel
+import com.example.smarthr_app.utils.toReadableTime
+import kotlinx.coroutines.launch
+
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ChatScreen(
+ chatViewModel: ChatViewModel,
+ authViewModel: AuthViewModel,
+ receiverId: String,
+ imageUrl: String,
+ name: String,
+ goToBack: () -> Unit
+) {
+ val allMessages by chatViewModel.messages.collectAsState()
+ val chatList by chatViewModel.chatList.collectAsState()
+ val user = authViewModel.user.collectAsState(initial = null).value ?: return
+ val coroutineScope = rememberCoroutineScope()
+
+
+ val userId = user.userId
+ val companyCode = user.companyCode
+
+ val chatId = chatList?.find {
+ it.user1.id == receiverId || it.user2.id == receiverId
+ }?.id
+
+ LaunchedEffect(receiverId) {
+ chatViewModel.setActiveChatUser(receiverId)
+ }
+
+ DisposableEffect(Unit) {
+ onDispose {
+ chatViewModel.setActiveChatUser(null)
+ }
+ }
+
+ val listState = rememberLazyListState()
+ val chatMessages = allMessages[chatId] ?: emptyList()
+
+ LaunchedEffect(chatMessages.size) {
+ if (chatMessages.isNotEmpty()) {
+ coroutineScope.launch {
+ listState.animateScrollToItem(0)
+ }
+ }
+ }
+
+ LaunchedEffect(chatId) {
+ chatId?.let {
+ chatViewModel.sendSeenMessageInfo(chatId = chatId, userId = user.userId)
+ }
+ }
+
+ var messageText by remember { mutableStateOf("") }
+
+ LaunchedEffect(chatId, Unit) {
+ chatId?.let {
+ chatViewModel.getChatBetweenUsers(
+ chatId = chatId,
+ companyCode = companyCode!!,
+ otherUserId = receiverId
+ )
+ chatViewModel.markMessagesAsSeen(chatId, userId)
+ }
+ }
+
+ LaunchedEffect(chatMessages.size) {
+ chatId?.let {
+ chatViewModel.sendSeenMessageInfo(it, userId)
+ }
+ }
+
+ Column(
+ modifier = Modifier
+ .statusBarsPadding()
+ .fillMaxSize()
+ .background(Color(0xFFF5F6FA))
+ ) {
+ // Top bar with Online status
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 12.dp)
+ ) {
+ IconButton(onClick = goToBack) {
+ Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
+ }
+
+ Image(
+ painter = rememberAsyncImagePainter(model = Uri.decode(imageUrl)),
+ contentDescription = "User Image",
+ modifier = Modifier
+ .size(40.dp)
+ .clip(CircleShape)
+ .background(Color.Gray)
+ )
+
+ Spacer(modifier = Modifier.width(12.dp))
+
+ Column {
+ Text(
+ text = name,
+ style = MaterialTheme.typography.titleMedium,
+ fontWeight = FontWeight.SemiBold
+ )
+ }
+ }
+
+ Divider(color = Color.LightGray, thickness = 1.dp)
+
+ // Messages
+ LazyColumn(
+ state = listState,
+ modifier = Modifier
+ .weight(1f)
+ .padding(horizontal = 16.dp),
+ verticalArrangement = Arrangement.Bottom,
+ reverseLayout = true
+ ) {
+ items(chatMessages.reversed(), key = { it.id }) { message ->
+ Log.i("Messagexyz", message.content)
+ ChatBubble(message, isFromMe = message.sender.id == userId)
+ }
+ }
+
+ // Input box
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(12.dp)
+ .background(Color.White, RoundedCornerShape(24.dp)),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ TextField(
+ value = messageText,
+ onValueChange = { messageText = it },
+ placeholder = { Text("Type a message...") },
+ modifier = Modifier
+ .weight(1f)
+ .padding(start = 16.dp),
+ colors = TextFieldDefaults.textFieldColors(
+ containerColor = Color.Transparent,
+ unfocusedIndicatorColor = Color.Transparent,
+ focusedIndicatorColor = Color.Transparent
+ ),
+ singleLine = true
+ )
+ IconButton(
+ onClick = {
+ if (messageText.isNotBlank()) {
+ chatViewModel.sendMessage(
+ senderId = userId,
+ receiverId = receiverId,
+ content = messageText,
+ companyCode = companyCode!!
+ )
+ messageText = ""
+ }
+ }
+ ) {
+ Icon(Icons.AutoMirrored.Filled.Send, contentDescription = "Send", tint = Color(0xFF7E57C2))
+ }
+ }
+ }
+}
+@Composable
+fun ChatBubble(message: ChatMessage, isFromMe: Boolean) {
+ val bubbleColor = if (isFromMe) Color(0xFF7E57C2) else Color.White
+ val textColor = if (isFromMe) Color.White else Color.Black
+ val alignment = if (isFromMe) Arrangement.End else Arrangement.Start
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 8.dp, vertical = 4.dp),
+ horizontalArrangement = alignment
+ ) {
+ Box(
+ modifier = Modifier
+ .background(
+ color = if (isFromMe) Color(0xFF7E57C2) else Color.White,
+ shape = RoundedCornerShape(16.dp)
+ )
+ .padding(horizontal = 12.dp, vertical = 8.dp),
+ contentAlignment = Alignment.CenterEnd
+ ) {
+ Column {
+ Text(
+ text = message.content,
+ fontSize = 16.sp,
+ color = if (isFromMe) Color.White else Color.Black
+ )
+
+ Spacer(modifier = Modifier.height(4.dp))
+
+ Row(
+ horizontalArrangement = Arrangement.End,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = message.timestamp.toReadableTime(),
+ fontSize = 11.sp,
+ color = Color.LightGray
+ )
+ if (isFromMe) {
+ Spacer(modifier = Modifier.width(4.dp))
+ Icon(
+ imageVector = when (message.messageStatus) {
+ "SEEN" -> Icons.Default.DoneAll
+ else -> Icons.Default.Done
+ },
+ contentDescription = message.messageStatus,
+ tint = if (message.messageStatus == "SEEN") Color.Blue else Color.LightGray,
+ modifier = Modifier.size(16.dp)
+ )
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/smarthr_app/presentation/screen/dashboard/employee/EmployeeDashboardScreen.kt b/app/src/main/java/com/example/smarthr_app/presentation/screen/dashboard/employee/EmployeeDashboardScreen.kt
index 9c09826..93f6b88 100644
--- a/app/src/main/java/com/example/smarthr_app/presentation/screen/dashboard/employee/EmployeeDashboardScreen.kt
+++ b/app/src/main/java/com/example/smarthr_app/presentation/screen/dashboard/employee/EmployeeDashboardScreen.kt
@@ -13,16 +13,19 @@ 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.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.example.smarthr_app.presentation.theme.PrimaryPurple
import com.example.smarthr_app.presentation.viewmodel.AuthViewModel
+import com.example.smarthr_app.presentation.viewmodel.ChatViewModel
import com.example.smarthr_app.presentation.viewmodel.LeaveViewModel
import com.example.smarthr_app.presentation.viewmodel.TaskViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EmployeeDashboardScreen(
+ chatViewModel: ChatViewModel,
authViewModel: AuthViewModel,
taskViewModel: TaskViewModel,
leaveViewModel: LeaveViewModel,
@@ -30,8 +33,34 @@ fun EmployeeDashboardScreen(
onNavigateToProfile: () -> Unit,
onNavigateToTaskDetail: (String) -> Unit
) {
+ val context = LocalContext.current
val user by authViewModel.user.collectAsState(initial = null)
var selectedTabIndex by remember { mutableStateOf(0) }
+// var lastMessage by remember { mutableStateOf("") }
+// var lastStatus by remember { mutableStateOf("") }
+
+// LaunchedEffect(user, Unit) {
+// user?.let {
+// chatViewModel.initSocket(it.userId)
+// chatViewModel.getMyChatList("XrI376o")
+// }
+// }
+
+// val messages by chatViewModel.messages.collectAsState()
+// val chatList by chatViewModel.chatList.collectAsState()
+// Log.d("ChatList",chatList.toString() + " ${user?.userId.toString()}")
+// LaunchedEffect(messages.size) {
+// messages.isNotEmpty().let{
+// if(it) {
+// val chatMessage = messages[messages.size-1]
+//// chatViewModel.getMyChatList("XrI376o")
+// lastMessage = chatMessage.content
+// showNotification(context,"Message from "+chatMessage.sender.name,chatMessage.content)
+// }
+// }
+// }
+
+
LaunchedEffect(Unit) {
authViewModel.user.collect { currentUser ->
@@ -58,6 +87,7 @@ fun EmployeeDashboardScreen(
1 -> Icon(Icons.Default.Schedule, contentDescription = tab)
2 -> Icon(Icons.Default.Assignment, contentDescription = tab)
3 -> Icon(Icons.Default.BeachAccess, contentDescription = tab)
+ 4 -> Icon(Icons.Default.Chat, contentDescription = tab)
}
},
label = { Text(tab) },
@@ -80,7 +110,7 @@ fun EmployeeDashboardScreen(
.background(MaterialTheme.colorScheme.background)
) {
when (selectedTabIndex) {
- 0 -> HomeTab(user = user, authViewModel = authViewModel, onNavigateToProfile = onNavigateToProfile)
+ 0 -> HomeTab(user = user, authViewModel = authViewModel, onNavigateToProfile = onNavigateToProfile,chatViewModel)
1 -> AttendanceTab()
2 -> EmployeeTaskScreen(
taskViewModel = taskViewModel,
@@ -96,7 +126,8 @@ fun EmployeeDashboardScreen(
fun HomeTab(
user: com.example.smarthr_app.data.model.User?,
authViewModel: AuthViewModel,
- onNavigateToProfile: () -> Unit
+ onNavigateToProfile: () -> Unit,
+ chatViewModel: ChatViewModel
) {
Column(
modifier = Modifier.fillMaxSize()
@@ -305,6 +336,13 @@ fun HomeTab(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
+ Button(
+ onClick = {
+ chatViewModel.sendMessage("687c94eb2103758e56b81f71","687c94a52103758e56b81f6f","Hii How are you?","XrI376o","TEXT");
+ }
+ ) {
+ Text("Send Message")
+ }
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
diff --git a/app/src/main/java/com/example/smarthr_app/presentation/screen/dashboard/hr/HRDashboardScreen.kt b/app/src/main/java/com/example/smarthr_app/presentation/screen/dashboard/hr/HRDashboardScreen.kt
index c1ebec1..4695181 100644
--- a/app/src/main/java/com/example/smarthr_app/presentation/screen/dashboard/hr/HRDashboardScreen.kt
+++ b/app/src/main/java/com/example/smarthr_app/presentation/screen/dashboard/hr/HRDashboardScreen.kt
@@ -1,5 +1,6 @@
package com.example.smarthr_app.presentation.screen.dashboard.hr
+import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@@ -16,10 +17,15 @@ 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.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.example.smarthr_app.data.model.ChatMessage
import com.example.smarthr_app.presentation.theme.PrimaryPurple
import com.example.smarthr_app.presentation.viewmodel.AuthViewModel
+import com.example.smarthr_app.presentation.viewmodel.ChatViewModel
+import com.example.smarthr_app.utils.showNotification
data class DashboardCard(
val title: String,
@@ -31,6 +37,7 @@ data class DashboardCard(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HRDashboardScreen(
+ chatViewModel: ChatViewModel,
authViewModel: AuthViewModel,
onLogout: () -> Unit,
onNavigateToEmployees: () -> Unit,
@@ -38,8 +45,26 @@ fun HRDashboardScreen(
onNavigateToTasks: () -> Unit,
onNavigateToLeaves: () -> Unit
) {
+ val context = LocalContext.current
val user by authViewModel.user.collectAsState(initial = null)
+// LaunchedEffect(user) {
+// user?.let {
+// chatViewModel.initSocket(it.userId)
+// }
+// }
+//
+// val messages by chatViewModel.messages.collectAsState()
+// LaunchedEffect(messages.size) {
+// messages.isNotEmpty().let{
+// if(it) {
+// val chatMessage = messages[messages.size-1]
+// showNotification(context,"Message from "+chatMessage.sender.name,chatMessage.content)
+// }
+// }
+// }
+
+
LaunchedEffect(Unit) {
authViewModel.user.collect { currentUser ->
if (currentUser == null) {
diff --git a/app/src/main/java/com/example/smarthr_app/presentation/viewmodel/ChatViewModel.kt b/app/src/main/java/com/example/smarthr_app/presentation/viewmodel/ChatViewModel.kt
new file mode 100644
index 0000000..097e682
--- /dev/null
+++ b/app/src/main/java/com/example/smarthr_app/presentation/viewmodel/ChatViewModel.kt
@@ -0,0 +1,221 @@
+package com.example.smarthr_app.presentation.viewmodel
+
+import android.util.Log
+import androidx.compose.ui.platform.LocalContext
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.smarthr_app.data.model.Chat
+import com.example.smarthr_app.data.model.ChatMessage
+import com.example.smarthr_app.data.model.SeenMessage
+import com.example.smarthr_app.data.model.UserInfo
+import com.example.smarthr_app.data.repository.ChatRepository
+import com.example.smarthr_app.presentation.screen.dashboard.hr.LoadingCard
+import com.example.smarthr_app.utils.ChatWebSocketClient
+import com.example.smarthr_app.utils.showNotification
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+
+class ChatViewModel(private val chatRepository: ChatRepository) : ViewModel() {
+
+ private var webSocketClient: ChatWebSocketClient? = null
+
+ private val _chatList = MutableStateFlow?>(null)
+ val chatList: StateFlow?> = _chatList
+
+ private val _userList = MutableStateFlow?>(null)
+ val userList: StateFlow?> = _userList
+
+ private val _messages = MutableStateFlow