load conversation from API instead of FakeData
This commit is contained in:
@@ -92,11 +92,12 @@ import xyz.magicalbits.smsremote.components.JetchatAppBar
|
||||
import xyz.magicalbits.smsremote.data.exampleUiState
|
||||
import xyz.magicalbits.smsremote.theme.JetchatTheme
|
||||
import kotlinx.coroutines.launch
|
||||
import xyz.magicalbits.smsremote.data.exampleUiStateNew
|
||||
|
||||
/**
|
||||
* Entry point for a conversation screen.
|
||||
*
|
||||
* @param uiState [ConversationUiState] that contains messages to display
|
||||
* @param uiState [ConversationScreenState] that contains messages to display
|
||||
* @param navigateToProfile User action when navigation to a profile is requested
|
||||
* @param modifier [Modifier] to apply to this layout node
|
||||
* @param onNavIconPressed Sends an event up when the user clicks on the menu
|
||||
@@ -104,7 +105,7 @@ import kotlinx.coroutines.launch
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ConversationContent(
|
||||
uiState: ConversationUiState,
|
||||
uiState: ConversationScreenState,
|
||||
navigateToProfile: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onNavIconPressed: () -> Unit = { },
|
||||
@@ -167,8 +168,9 @@ fun ConversationContent(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
ChannelNameBar(
|
||||
channelName = uiState.channelName,
|
||||
channelMembers = uiState.channelMembers,
|
||||
channelName = uiState.phoneNumber,
|
||||
// TODO remove?
|
||||
channelMembers = 2,
|
||||
onNavIconPressed = onNavIconPressed,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
@@ -540,7 +542,7 @@ fun ClickableMessage(message: Message, isUserMe: Boolean, authorClicked: (String
|
||||
fun ConversationPreview() {
|
||||
JetchatTheme {
|
||||
ConversationContent(
|
||||
uiState = exampleUiState,
|
||||
uiState = exampleUiStateNew,
|
||||
navigateToProfile = { },
|
||||
)
|
||||
}
|
||||
|
||||
+8
-11
@@ -23,6 +23,8 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
@@ -31,11 +33,12 @@ import androidx.navigation.findNavController
|
||||
import xyz.magicalbits.smsremote.MainViewModel
|
||||
import xyz.magicalbits.smsremote.R
|
||||
import xyz.magicalbits.smsremote.data.exampleUiState
|
||||
import xyz.magicalbits.smsremote.data.exampleUiState2
|
||||
import xyz.magicalbits.smsremote.data.exampleUiStateNew
|
||||
import xyz.magicalbits.smsremote.theme.JetchatTheme
|
||||
|
||||
class ConversationFragment : Fragment() {
|
||||
private val activityViewModel: MainViewModel by activityViewModels()
|
||||
private val conversationViewModel: ConversationViewModel by activityViewModels()
|
||||
|
||||
var phoneNumber: String = ""
|
||||
|
||||
@@ -43,8 +46,9 @@ class ConversationFragment : Fragment() {
|
||||
super.onAttach(context)
|
||||
// Consider using safe args plugin
|
||||
val phoneNumber = arguments?.getString("phoneNumber")
|
||||
// viewModel.setDeviceId(deviceId)
|
||||
this.phoneNumber = phoneNumber!!
|
||||
// update view model with latest messages
|
||||
conversationViewModel.setConversationData(phoneNumber)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@@ -55,18 +59,11 @@ class ConversationFragment : Fragment() {
|
||||
ComposeView(inflater.context).apply {
|
||||
layoutParams = LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
||||
|
||||
val uiState =
|
||||
if (phoneNumber == "+420123456789") {
|
||||
exampleUiState
|
||||
} else {
|
||||
exampleUiState2
|
||||
}
|
||||
uiState.channelName = phoneNumber
|
||||
|
||||
setContent {
|
||||
val conversationData by conversationViewModel.conversationData.observeAsState()
|
||||
JetchatTheme {
|
||||
ConversationContent(
|
||||
uiState = uiState,
|
||||
uiState = conversationData ?: exampleUiStateNew,
|
||||
navigateToProfile = { user ->
|
||||
// Click callback
|
||||
val bundle = bundleOf("userId" to user)
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package xyz.magicalbits.smsremote.conversation
|
||||
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.launch
|
||||
import xyz.magicalbits.smsremote.network.NetworkClient
|
||||
|
||||
class ConversationViewModel : ViewModel() {
|
||||
private var phoneNumber: String = ""
|
||||
private val _conversationData = MutableLiveData<ConversationScreenState>()
|
||||
val conversationData: LiveData<ConversationScreenState> = _conversationData
|
||||
|
||||
fun setConversationData(phoneNumber: String?) {
|
||||
if (phoneNumber != null) {
|
||||
this.phoneNumber = phoneNumber
|
||||
|
||||
var messageDtoList: List<NetworkClient.SmsMessageDto> = listOf()
|
||||
viewModelScope.launch {
|
||||
val networkClient = NetworkClient()
|
||||
messageDtoList = networkClient.getSmsMessagesByLocalPhoneNumber(phoneNumber)
|
||||
}.invokeOnCompletion {
|
||||
_conversationData.value =
|
||||
ConversationScreenState(
|
||||
phoneNumber = this.phoneNumber,
|
||||
initialMessages = messageDtoList.map {
|
||||
Message(
|
||||
if (it.msg_type == "INCOMING") {
|
||||
it.remote_phone_number
|
||||
} else {
|
||||
it.local_phone_number
|
||||
},
|
||||
it.content,
|
||||
// FIXME convert to HH:MM AM/PM
|
||||
it.ts_sent.toString(),
|
||||
null,
|
||||
)
|
||||
},
|
||||
)
|
||||
println("sims live: ${_conversationData.value!!.initialMessages}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ConversationScreenState(
|
||||
val phoneNumber: String,
|
||||
val initialMessages: List<Message>,
|
||||
) {
|
||||
private val _messages: MutableList<Message> = initialMessages.toMutableStateList()
|
||||
|
||||
val messages: List<Message> = _messages
|
||||
|
||||
fun addMessage(msg: Message) {
|
||||
_messages.add(0, msg)
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,9 @@
|
||||
package xyz.magicalbits.smsremote.data
|
||||
|
||||
import xyz.magicalbits.smsremote.R
|
||||
import xyz.magicalbits.smsremote.conversation.ConversationScreenState
|
||||
import xyz.magicalbits.smsremote.conversation.ConversationUiState
|
||||
import xyz.magicalbits.smsremote.conversation.Message
|
||||
import xyz.magicalbits.smsremote.device.DeviceScreenState
|
||||
import xyz.magicalbits.smsremote.profile.ProfileScreenState
|
||||
|
||||
val initialMessages =
|
||||
@@ -31,30 +31,16 @@ val initialMessages =
|
||||
),
|
||||
)
|
||||
|
||||
val initialMessages2 =
|
||||
listOf(
|
||||
Message(
|
||||
"uahguoidahfg",
|
||||
"yolo",
|
||||
"8:05 PM",
|
||||
),
|
||||
)
|
||||
|
||||
val unreadMessages = initialMessages.filter { it.author != "me" }
|
||||
|
||||
val exampleUiState =
|
||||
ConversationUiState(
|
||||
initialMessages = initialMessages,
|
||||
channelName = "Samsung A14",
|
||||
channelMembers = 42,
|
||||
val exampleUiStateNew =
|
||||
ConversationScreenState(
|
||||
phoneNumber = "",
|
||||
initialMessages = mutableListOf()
|
||||
)
|
||||
|
||||
val exampleUiState2 =
|
||||
ConversationUiState(
|
||||
initialMessages = initialMessages2,
|
||||
channelName = "iPhone XYZ",
|
||||
channelMembers = 69,
|
||||
)
|
||||
val exampleUiState =
|
||||
ConversationUiState("name", 123, listOf())
|
||||
|
||||
/**
|
||||
* Example colleague profile
|
||||
|
||||
@@ -7,8 +7,11 @@ import io.ktor.client.plugins.auth.providers.BearerTokens
|
||||
import io.ktor.client.plugins.auth.providers.bearer
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.net.URLEncoder
|
||||
|
||||
class NetworkClient {
|
||||
// TODO make apiBaseUrl configurable with fallback to my real domain
|
||||
@@ -37,6 +40,9 @@ class NetworkClient {
|
||||
@Serializable
|
||||
data class SimCardDto(val device_access_key: String, val phone_number: String)
|
||||
|
||||
@Serializable
|
||||
data class SmsMessageDto(val content: String, val ts_received: Int, val ts_sent: Int, val msg_type: String, val local_phone_number: String, val remote_phone_number: String)
|
||||
|
||||
// GET /api/v1/devices
|
||||
suspend fun getDevices(): List<DeviceDto> {
|
||||
// TODO handle non-200 status codes
|
||||
@@ -48,7 +54,20 @@ class NetworkClient {
|
||||
// GET /api/v1/sim-cards
|
||||
suspend fun getSimCardsByAccessKey(accessKey: String): List<SimCardDto> {
|
||||
// TODO handle non-200 status codes
|
||||
// TODO handle '{"msg":"Token has expired"}' messages
|
||||
val data = networkClient.get("$apiBaseUrl/api/v1/sim-cards?access_key=$accessKey").bodyAsText()
|
||||
return Json.decodeFromString(data)
|
||||
}
|
||||
|
||||
// GET /api/v1/sms-messages
|
||||
suspend fun getSmsMessagesByLocalPhoneNumber(phoneNumber: String): List<SmsMessageDto> {
|
||||
// TODO handle non-200 status codes
|
||||
// TODO handle '{"msg":"Token has expired"}' messages
|
||||
// TODO extract encoder to a function here or to a utility class
|
||||
val encodedPhoneNumber = withContext(Dispatchers.IO) {
|
||||
URLEncoder.encode(phoneNumber, "UTF-8")
|
||||
}
|
||||
val data = networkClient.get("$apiBaseUrl/api/v1/sms-messages?local_phone_number=$encodedPhoneNumber").bodyAsText()
|
||||
return Json.decodeFromString(data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,8 @@ def get_sms_messages_by_local_phone_number():
|
||||
return make_response(jsonify(msg=msg_403_not_primary), 403)
|
||||
|
||||
local_phone_number = request.args.get("local_phone_number", None)
|
||||
# TODO set up access logging
|
||||
#print(f"/api/v1/sms-messages - local_phone_number='{local_phone_number}'")
|
||||
return make_response(jsonify([n.to_dict() for n in db.get_sms_messages_by_local_phone_number(cur, local_phone_number)]), 200)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user