InputChat.kt 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package io.nexilis.alpha.ui.components
  2. import android.net.Uri
  3. import androidx.compose.foundation.background
  4. import androidx.compose.foundation.layout.Row
  5. import androidx.compose.foundation.layout.fillMaxWidth
  6. import androidx.compose.foundation.layout.height
  7. import androidx.compose.foundation.layout.padding
  8. import androidx.compose.foundation.shape.CircleShape
  9. import androidx.compose.foundation.shape.RoundedCornerShape
  10. import androidx.compose.foundation.text.KeyboardOptions
  11. import androidx.compose.material.icons.Icons
  12. import androidx.compose.material.icons.automirrored.filled.Send
  13. import androidx.compose.material3.AlertDialog
  14. import androidx.compose.material3.Icon
  15. import androidx.compose.material3.IconButton
  16. import androidx.compose.material3.MaterialTheme
  17. import androidx.compose.material3.Text
  18. import androidx.compose.material3.TextButton
  19. import androidx.compose.material3.TextField
  20. import androidx.compose.material3.TextFieldDefaults
  21. import androidx.compose.runtime.Composable
  22. import androidx.compose.runtime.getValue
  23. import androidx.compose.runtime.livedata.observeAsState
  24. import androidx.compose.runtime.mutableStateListOf
  25. import androidx.compose.runtime.mutableStateOf
  26. import androidx.compose.runtime.remember
  27. import androidx.compose.runtime.saveable.rememberSaveable
  28. import androidx.compose.runtime.setValue
  29. import androidx.compose.ui.Modifier
  30. import androidx.compose.ui.graphics.Color
  31. import androidx.compose.ui.graphics.graphicsLayer
  32. import androidx.compose.ui.platform.LocalContext
  33. import androidx.compose.ui.text.TextRange
  34. import androidx.compose.ui.text.input.KeyboardCapitalization
  35. import androidx.compose.ui.text.input.TextFieldValue
  36. import androidx.compose.ui.unit.dp
  37. import androidx.hilt.navigation.compose.hiltViewModel
  38. import androidx.navigation.NavHostController
  39. import io.nexilis.alpha.ui.screen.Screen
  40. import io.nexilis.service.data.entities.Buddy
  41. import io.nexilis.service.data.entities.Message
  42. import io.nexilis.service.data.viewmodels.BuddyViewModel
  43. import io.nexilis.service.data.viewmodels.MessageViewModel
  44. import kotlinx.serialization.encodeToString
  45. import kotlinx.serialization.json.Json
  46. @Composable
  47. fun InputChat(
  48. navController: NavHostController,
  49. pin: String,
  50. me: Buddy,
  51. placeHolderText: String = "Typing here...",
  52. enableAttachment: Boolean = true,
  53. enableEmpty: Boolean = false,
  54. popAfterSent: Boolean = false,
  55. attachments: ArrayList<Uri> = arrayListOf()
  56. ) {
  57. val context = LocalContext.current
  58. val messageModel: MessageViewModel = hiltViewModel()
  59. var textInput by rememberSaveable(stateSaver = TextFieldValue.Saver) {
  60. mutableStateOf(TextFieldValue("", TextRange(0, 7)))
  61. }
  62. var openAlertDialog by remember { mutableStateOf(false) }
  63. val attachmentState = remember { mutableStateListOf<Uri>() }
  64. attachmentState.addAll(attachments)
  65. val leadingIcon: (@Composable () -> Unit)? = if (enableAttachment) {
  66. {
  67. Attachments(
  68. modifier = Modifier
  69. .height(300.dp)
  70. .fillMaxWidth()
  71. .padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
  72. .graphicsLayer {
  73. shape = RoundedCornerShape(16.dp)
  74. clip = true
  75. }
  76. .background(MaterialTheme.colorScheme.surfaceContainerLowest),
  77. onAttachment = { list ->
  78. attachmentState.addAll(list)
  79. val items = list.map {
  80. context.toAttachmentItem(it)
  81. }
  82. if (items.size == 1) {
  83. val params = AttachmentItemList(items)
  84. val data = Uri.encode(Json.encodeToString(params))
  85. navController.navigate(Screen.AttachmentCaption.route + "?data=${data}&pin=${pin}") {
  86. launchSingleTop = true
  87. restoreState = true
  88. }
  89. } else if (items.size > 1) {
  90. openAlertDialog = true
  91. }
  92. }
  93. )
  94. }
  95. } else null
  96. TextField(
  97. modifier = Modifier
  98. .fillMaxWidth()
  99. .padding(4.dp)
  100. .graphicsLayer {
  101. shape = RoundedCornerShape(16.dp)
  102. clip = true
  103. },
  104. value = textInput,
  105. onValueChange = { textInput = it },
  106. label = null,
  107. placeholder = {
  108. Text(text = placeHolderText, color = MaterialTheme.colorScheme.onSurface)
  109. },
  110. leadingIcon = leadingIcon,
  111. trailingIcon = {
  112. Row {
  113. IconButton(onClick = {
  114. if (pin.trim().isEmpty()) {
  115. return@IconButton
  116. }
  117. if (!enableEmpty && textInput.text.trim().isEmpty()) {
  118. return@IconButton
  119. }
  120. me.let {
  121. messageModel.send(
  122. context = context,
  123. entity = Message(
  124. message_id = System.nanoTime().toString(),
  125. f_pin = it.f_pin,
  126. l_pin = pin,
  127. message_scope_id = "3", // 3: chat, 15: sms, 16: email
  128. server_date = System.currentTimeMillis(),
  129. status = "1",
  130. message_text = textInput.text,
  131. opposite_pin = pin,
  132. f_display_name = "${it.first_name} ${it.last_name}".trim(),
  133. message_large_text = "",
  134. message_text_plain = "",
  135. uri = if (attachmentState.size > 0) attachmentState[0] else Uri.EMPTY
  136. )
  137. )
  138. textInput = TextFieldValue("")
  139. if (popAfterSent) navController.popBackStack()
  140. }
  141. }) {
  142. Icon(
  143. imageVector = Icons.AutoMirrored.Filled.Send,
  144. contentDescription = "",
  145. tint = MaterialTheme.colorScheme.primary
  146. )
  147. }
  148. }
  149. },
  150. colors = TextFieldDefaults.colors(
  151. focusedContainerColor = MaterialTheme.colorScheme.surfaceContainerLowest,
  152. unfocusedContainerColor = MaterialTheme.colorScheme.surfaceContainerLowest,
  153. disabledContainerColor = MaterialTheme.colorScheme.surfaceContainerLowest,
  154. focusedIndicatorColor = Color.Transparent,
  155. unfocusedIndicatorColor = Color.Transparent,
  156. disabledIndicatorColor = Color.Transparent,
  157. errorIndicatorColor = Color.Transparent,
  158. ),
  159. keyboardOptions = KeyboardOptions(KeyboardCapitalization.Sentences),
  160. maxLines = 3
  161. )
  162. if (openAlertDialog) {
  163. val buddyModel: BuddyViewModel = hiltViewModel()
  164. val buddy by buddyModel.getBuddy(pin).observeAsState()
  165. buddy?.let {
  166. AlertDialog(
  167. text = {
  168. Text(text = "Send ${attachmentState.size} documents to ${it.first_name} ${it.last_name}?".trim())
  169. },
  170. onDismissRequest = {
  171. openAlertDialog = false
  172. },
  173. confirmButton = {
  174. TextButton(
  175. onClick = {
  176. openAlertDialog = false
  177. me.let {
  178. attachmentState.forEach { uri ->
  179. messageModel.send(
  180. context = context,
  181. entity = Message(
  182. message_id = System.nanoTime().toString(),
  183. f_pin = it.f_pin,
  184. l_pin = pin,
  185. message_scope_id = "3", // 3: chat, 15: sms, 16: email
  186. server_date = System.currentTimeMillis(),
  187. status = "1",
  188. message_text = "",
  189. opposite_pin = pin,
  190. f_display_name = "${it.first_name} ${it.last_name}".trim(),
  191. message_large_text = "",
  192. message_text_plain = "",
  193. uri = uri
  194. )
  195. )
  196. }
  197. textInput = TextFieldValue("")
  198. }
  199. }
  200. ) {
  201. Text("Send")
  202. }
  203. },
  204. dismissButton = {
  205. TextButton(
  206. onClick = {
  207. openAlertDialog = false
  208. }
  209. ) {
  210. Text("Cancel")
  211. }
  212. }
  213. )
  214. }
  215. }
  216. }