|
@@ -3,6 +3,7 @@ package io.nexilis.alpha.ui.main
|
|
import android.text.format.DateUtils
|
|
import android.text.format.DateUtils
|
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
import androidx.compose.foundation.background
|
|
import androidx.compose.foundation.background
|
|
|
|
+import androidx.compose.foundation.clickable
|
|
import androidx.compose.foundation.combinedClickable
|
|
import androidx.compose.foundation.combinedClickable
|
|
import androidx.compose.foundation.layout.*
|
|
import androidx.compose.foundation.layout.*
|
|
import androidx.compose.foundation.lazy.LazyColumn
|
|
import androidx.compose.foundation.lazy.LazyColumn
|
|
@@ -15,27 +16,37 @@ import androidx.compose.material.icons.automirrored.filled.Send
|
|
import androidx.compose.material.icons.filled.AccessTime
|
|
import androidx.compose.material.icons.filled.AccessTime
|
|
import androidx.compose.material.icons.filled.Add
|
|
import androidx.compose.material.icons.filled.Add
|
|
import androidx.compose.material.icons.filled.AttachFile
|
|
import androidx.compose.material.icons.filled.AttachFile
|
|
-import androidx.compose.material.icons.filled.AudioFile
|
|
|
|
import androidx.compose.material.icons.filled.Camera
|
|
import androidx.compose.material.icons.filled.Camera
|
|
import androidx.compose.material.icons.filled.Done
|
|
import androidx.compose.material.icons.filled.Done
|
|
import androidx.compose.material.icons.filled.DoneAll
|
|
import androidx.compose.material.icons.filled.DoneAll
|
|
|
|
+import androidx.compose.material.icons.filled.Headset
|
|
import androidx.compose.material.icons.filled.Image
|
|
import androidx.compose.material.icons.filled.Image
|
|
|
|
+import androidx.compose.material.icons.filled.LocationOn
|
|
|
|
+import androidx.compose.material.icons.filled.Person
|
|
import androidx.compose.material3.*
|
|
import androidx.compose.material3.*
|
|
import androidx.compose.runtime.*
|
|
import androidx.compose.runtime.*
|
|
import androidx.compose.runtime.livedata.observeAsState
|
|
import androidx.compose.runtime.livedata.observeAsState
|
|
import androidx.compose.runtime.saveable.rememberSaveable
|
|
import androidx.compose.runtime.saveable.rememberSaveable
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.Modifier
|
|
|
|
+import androidx.compose.ui.focus.FocusRequester
|
|
|
|
+import androidx.compose.ui.focus.focusRequester
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.compose.ui.graphics.graphicsLayer
|
|
import androidx.compose.ui.graphics.graphicsLayer
|
|
|
|
+import androidx.compose.ui.layout.onSizeChanged
|
|
import androidx.compose.ui.platform.LocalContext
|
|
import androidx.compose.ui.platform.LocalContext
|
|
|
|
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
|
import androidx.compose.ui.text.TextRange
|
|
import androidx.compose.ui.text.TextRange
|
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
|
import androidx.compose.ui.text.input.TextFieldValue
|
|
import androidx.compose.ui.text.input.TextFieldValue
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
+import androidx.compose.ui.window.Popup
|
|
import androidx.hilt.navigation.compose.hiltViewModel
|
|
import androidx.hilt.navigation.compose.hiltViewModel
|
|
import androidx.navigation.NavHostController
|
|
import androidx.navigation.NavHostController
|
|
import io.nexilis.alpha.ui.viewmodel.MainViewModel
|
|
import io.nexilis.alpha.ui.viewmodel.MainViewModel
|
|
|
|
+import io.nexilis.service.core.DarkLimeGreen
|
|
|
|
+import io.nexilis.service.core.Orange
|
|
|
|
+import io.nexilis.service.core.Purple
|
|
import io.nexilis.service.data.entities.Message
|
|
import io.nexilis.service.data.entities.Message
|
|
import io.nexilis.service.data.viewmodels.BuddyViewModel
|
|
import io.nexilis.service.data.viewmodels.BuddyViewModel
|
|
import io.nexilis.service.data.viewmodels.MessageStatusViewModel
|
|
import io.nexilis.service.data.viewmodels.MessageStatusViewModel
|
|
@@ -44,6 +55,7 @@ import io.nexilis.service.data.viewmodels.MessageViewModel
|
|
@OptIn(ExperimentalFoundationApi::class)
|
|
@OptIn(ExperimentalFoundationApi::class)
|
|
@Composable
|
|
@Composable
|
|
fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewModel) {
|
|
fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewModel) {
|
|
|
|
+ val focusRequester = remember { FocusRequester() }
|
|
var textInput by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
|
var textInput by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
|
mutableStateOf(TextFieldValue("", TextRange(0, 7)))
|
|
mutableStateOf(TextFieldValue("", TextRange(0, 7)))
|
|
}
|
|
}
|
|
@@ -62,6 +74,9 @@ fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewM
|
|
Column(
|
|
Column(
|
|
modifier = Modifier
|
|
modifier = Modifier
|
|
.fillMaxSize()
|
|
.fillMaxSize()
|
|
|
|
+ .onSizeChanged {
|
|
|
|
+
|
|
|
|
+ }
|
|
.background(color = MaterialTheme.colorScheme.surfaceContainer)
|
|
.background(color = MaterialTheme.colorScheme.surfaceContainer)
|
|
.padding(start = 8.dp, end = 8.dp, bottom = 8.dp)
|
|
.padding(start = 8.dp, end = 8.dp, bottom = 8.dp)
|
|
) {
|
|
) {
|
|
@@ -184,6 +199,7 @@ fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewM
|
|
shape = CircleShape
|
|
shape = CircleShape
|
|
clip = true
|
|
clip = true
|
|
}
|
|
}
|
|
|
|
+ .focusRequester(focusRequester)
|
|
.imePadding(),
|
|
.imePadding(),
|
|
value = textInput,
|
|
value = textInput,
|
|
onValueChange = { textInput = it },
|
|
onValueChange = { textInput = it },
|
|
@@ -192,7 +208,18 @@ fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewM
|
|
Text(text = "Typing here...", color = MaterialTheme.colorScheme.onSurface)
|
|
Text(text = "Typing here...", color = MaterialTheme.colorScheme.onSurface)
|
|
},
|
|
},
|
|
leadingIcon = {
|
|
leadingIcon = {
|
|
- Attachments()
|
|
|
|
|
|
+ Attachments(
|
|
|
|
+ modifier = Modifier
|
|
|
|
+ .height(300.dp)
|
|
|
|
+ .fillMaxWidth()
|
|
|
|
+ .padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
|
|
|
|
+ .graphicsLayer {
|
|
|
|
+ shape = RoundedCornerShape(16.dp)
|
|
|
|
+ clip = true
|
|
|
|
+ }
|
|
|
|
+ .background(Color(0xEFFFFFFF)),
|
|
|
|
+ focusRequester = focusRequester
|
|
|
|
+ )
|
|
},
|
|
},
|
|
trailingIcon = {
|
|
trailingIcon = {
|
|
Row {
|
|
Row {
|
|
@@ -245,12 +272,17 @@ fun Chat(navController: NavHostController, pin: String, mainViewModel: MainViewM
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+@OptIn(ExperimentalMaterial3Api::class)
|
|
@Composable
|
|
@Composable
|
|
-fun Attachments() {
|
|
|
|
|
|
+fun Attachments(modifier: Modifier, focusRequester: FocusRequester) {
|
|
|
|
+ val keyboardController = LocalSoftwareKeyboardController.current
|
|
|
|
+ val positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider()
|
|
var isOpenMenu by remember {
|
|
var isOpenMenu by remember {
|
|
mutableStateOf(false)
|
|
mutableStateOf(false)
|
|
}
|
|
}
|
|
IconButton(onClick = {
|
|
IconButton(onClick = {
|
|
|
|
+ focusRequester.requestFocus()
|
|
|
|
+ keyboardController?.hide()
|
|
isOpenMenu = !isOpenMenu
|
|
isOpenMenu = !isOpenMenu
|
|
}
|
|
}
|
|
) {
|
|
) {
|
|
@@ -260,40 +292,107 @@ fun Attachments() {
|
|
tint = MaterialTheme.colorScheme.primary
|
|
tint = MaterialTheme.colorScheme.primary
|
|
)
|
|
)
|
|
}
|
|
}
|
|
- DropdownMenu(
|
|
|
|
- expanded = isOpenMenu,
|
|
|
|
- onDismissRequest = { isOpenMenu = false }) {
|
|
|
|
- DropdownMenuItem(
|
|
|
|
- text = { Text(text = "Camera") },
|
|
|
|
- onClick = { },
|
|
|
|
- leadingIcon = {
|
|
|
|
- Icon(imageVector = Icons.Default.Camera, contentDescription = "")
|
|
|
|
- },
|
|
|
|
- colors = MenuDefaults.itemColors(leadingIconColor = Color.Red)
|
|
|
|
- )
|
|
|
|
- DropdownMenuItem(
|
|
|
|
- text = { Text(text = "Gallery") },
|
|
|
|
- onClick = { },
|
|
|
|
- leadingIcon = {
|
|
|
|
- Icon(imageVector = Icons.Default.Image, contentDescription = "")
|
|
|
|
- },
|
|
|
|
- colors = MenuDefaults.itemColors(leadingIconColor = Color(0xFF800080))
|
|
|
|
- )
|
|
|
|
- DropdownMenuItem(
|
|
|
|
- text = { Text(text = "Documents") },
|
|
|
|
- onClick = { },
|
|
|
|
- leadingIcon = {
|
|
|
|
- Icon(imageVector = Icons.Default.AttachFile, contentDescription = "")
|
|
|
|
- },
|
|
|
|
- colors = MenuDefaults.itemColors(leadingIconColor = Color.Blue)
|
|
|
|
- )
|
|
|
|
- DropdownMenuItem(
|
|
|
|
- text = { Text(text = "Audio") },
|
|
|
|
- onClick = { },
|
|
|
|
- leadingIcon = {
|
|
|
|
- Icon(imageVector = Icons.Default.AudioFile, contentDescription = "")
|
|
|
|
- },
|
|
|
|
- colors = MenuDefaults.itemColors(leadingIconColor = Color(0xFFFFA500))
|
|
|
|
- )
|
|
|
|
|
|
+ if (isOpenMenu) {
|
|
|
|
+ Popup(popupPositionProvider = positionProvider, onDismissRequest = { isOpenMenu = false }) {
|
|
|
|
+ Box(modifier = modifier) {
|
|
|
|
+ Row(Modifier.align(Alignment.Center)) {
|
|
|
|
+ Column {
|
|
|
|
+ AttachmentButton(
|
|
|
|
+ color = MaterialTheme.colorScheme.primary,
|
|
|
|
+ onClick = {},
|
|
|
|
+ text = {
|
|
|
|
+ Text(text = "File", style = MaterialTheme.typography.bodySmall)
|
|
|
|
+ }) {
|
|
|
|
+ Icon(
|
|
|
|
+ imageVector = Icons.Default.AttachFile,
|
|
|
|
+ contentDescription = "",
|
|
|
|
+ tint = Color.White
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+ Spacer(modifier = Modifier.size(32.dp))
|
|
|
|
+ AttachmentButton(color = Color.Orange, onClick = {}, text = {
|
|
|
|
+ Text(text = "Audio", style = MaterialTheme.typography.bodySmall)
|
|
|
|
+ }) {
|
|
|
|
+ Icon(
|
|
|
|
+ imageVector = Icons.Default.Headset,
|
|
|
|
+ contentDescription = "",
|
|
|
|
+ tint = Color.White
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ Spacer(modifier = Modifier.size(16.dp))
|
|
|
|
+ Column {
|
|
|
|
+ AttachmentButton(color = Color.Red, onClick = {}, text = {
|
|
|
|
+ Text(text = "Camera", style = MaterialTheme.typography.bodySmall)
|
|
|
|
+ }) {
|
|
|
|
+ Icon(
|
|
|
|
+ imageVector = Icons.Default.Camera,
|
|
|
|
+ contentDescription = "",
|
|
|
|
+ tint = Color.White
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+ Spacer(modifier = Modifier.size(32.dp))
|
|
|
|
+ AttachmentButton(color = Color.DarkLimeGreen, onClick = {}, text = {
|
|
|
|
+ Text(text = "Location", style = MaterialTheme.typography.bodySmall)
|
|
|
|
+ }) {
|
|
|
|
+ Icon(
|
|
|
|
+ imageVector = Icons.Default.LocationOn,
|
|
|
|
+ contentDescription = "",
|
|
|
|
+ tint = Color.White
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ Spacer(modifier = Modifier.size(16.dp))
|
|
|
|
+ Column {
|
|
|
|
+ AttachmentButton(color = Color.Purple, onClick = {}, text = {
|
|
|
|
+ Text(text = "Gallery", style = MaterialTheme.typography.bodySmall)
|
|
|
|
+ }) {
|
|
|
|
+ Icon(
|
|
|
|
+ imageVector = Icons.Default.Image,
|
|
|
|
+ contentDescription = "",
|
|
|
|
+ tint = Color.White
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+ Spacer(modifier = Modifier.size(32.dp))
|
|
|
|
+ AttachmentButton(color = Color.Blue, onClick = {}, text = {
|
|
|
|
+ Text(text = "Contact", style = MaterialTheme.typography.bodySmall)
|
|
|
|
+ }) {
|
|
|
|
+ Icon(
|
|
|
|
+ imageVector = Icons.Default.Person,
|
|
|
|
+ contentDescription = "",
|
|
|
|
+ tint = Color.White
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@Composable
|
|
|
|
+fun AttachmentButton(
|
|
|
|
+ color: Color = MaterialTheme.colorScheme.primary,
|
|
|
|
+ onClick: () -> Unit,
|
|
|
|
+ text: @Composable () -> Unit,
|
|
|
|
+ content: @Composable () -> Unit
|
|
|
|
+) {
|
|
|
|
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
|
|
|
+ Box(
|
|
|
|
+ modifier = Modifier
|
|
|
|
+ .then(Modifier.size(72.dp))
|
|
|
|
+ .graphicsLayer {
|
|
|
|
+ shape = CircleShape
|
|
|
|
+ clip = true
|
|
|
|
+ }
|
|
|
|
+ .clickable(onClick = onClick)
|
|
|
|
+ .padding(0.dp)
|
|
|
|
+ .background(color),
|
|
|
|
+ contentAlignment = Alignment.Center
|
|
|
|
+ ) {
|
|
|
|
+ content()
|
|
|
|
+ }
|
|
|
|
+ Spacer(modifier = Modifier.size(6.dp))
|
|
|
|
+ text()
|
|
}
|
|
}
|
|
}
|
|
}
|