McEna
11/30/2023, 7:58 AMPablichjenkov
11/30/2023, 8:03 AMMcEna
11/30/2023, 8:03 AM@Composable
fun Render(viewModel: MLSearchViewModel = MLSearchViewModel()){
val time = getTimeMillis()
val name = "start_${time}"
EventTracer.instance.trace(name, "mainview", time)
val searchTerms = remember { mutableStateOf<String>("") }
val searchResult = viewModel.searchStateFlow.collectAsState()
if(searchResult.value.success && searchTerms.value.isNotBlank()){
EventTracer.instance.trace("search_${searchTerms.value}", "mainview", getTimeMillis())
}
MainView.MyApp( {
if(it.isNotBlank()){
EventTracer.instance.trace("search_$it", "mainview", getTimeMillis())
viewModel.loadSearch(it)
}
searchTerms.value = it
}, {
MainView.ItemList(searchResult.value.data.results)
})
EventTracer.instance.trace(name, "mainview", getTimeMillis())
}
McEna
11/30/2023, 8:04 AMMcEna
11/30/2023, 8:04 AM@Composable
fun MyApp(onSearch: (String) -> Unit, content: @Composable () -> Unit) {
MaterialTheme {
Scaffold(
topBar = {
TopBarWithSearch(onSearch)
}
) { innerPadding ->
content()
}
}
}
McEna
11/30/2023, 8:04 AMMcEna
11/30/2023, 8:04 AM@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun TopBarWithSearch(onSearch: (String) -> Unit) {
var text by remember { mutableStateOf("") }
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
TopAppBar(
title = { Text("My App") },
navigationIcon = {
IconButton(onClick = {
EventTracer.instance.write()
}) {
Icon(<http://Icons.Filled.Menu|Icons.Filled.Menu>, contentDescription = "Navigation Drawer Button")
}
},
actions = {
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Search") },
leadingIcon = { Icon(Icons.Filled.Search, contentDescription = null) },
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions = KeyboardActions(
onSearch = {
onSearch(text)
keyboardController?.hide()
focusManager.clearFocus()
}
)
)
}
)
}
McEna
11/30/2023, 8:05 AMMcEna
11/30/2023, 8:05 AM@Composable
fun ItemList(mlItems : List<Results>){
LazyColumn(modifier = Modifier.padding(4.dp)) {
items(mlItems, key = {it.id!!}){ item ->
val bookmarked = remember { mutableStateOf(false) }
Card(
shape = RoundedCornerShape(4.dp),
modifier = Modifier
.fillMaxWidth()
.padding(4.dp)
) {
val time = getTimeMillis()
val args = mutableMapOf<String, String>().apply {
item.id?.let {
this["itemId"] = it
}
}
val safeId = if(item.id != null){
item.id!!
} else {
""
}
val categories = mutableListOf<String>("mainview", safeId)
EventTracer.instance.trace("render${item.id}_$time", categories, time, 0, 0, args)
Row(verticalAlignment = Alignment.CenterVertically) {
Box(modifier = Modifier.size(120.dp)) {
val firstImage = if(item.thumbnail != null){
item.thumbnail
} else {
""
}
ImageLoader.getInstance()!!.load(firstImage)
FloatingActionButton(
onClick = {
bookmarked.value = !bookmarked.value
},
modifier = Modifier.align(Alignment.TopEnd).then(Modifier.size(20.dp, 20.dp))
) {
if(bookmarked.value){
Icon(Icons.Filled.Favorite, contentDescription = null)
} else {
Icon(Icons.Outlined.FavoriteBorder, contentDescription = null)
}
}
}
Column(modifier = Modifier.padding(start = 8.dp)) {
Text(text = "${item.title}", style = MaterialTheme.typography.body1)
Spacer(modifier = Modifier.height(3.dp))
Text(text = "${item.price}", style = MaterialTheme.typography.h6)
Spacer(modifier = Modifier.height(2.dp))
Text(text = "${item.address?.cityName}", style = MaterialTheme.typography.body2)
Spacer(modifier = Modifier.height(1.dp))
Text(text = "${item.condition}", style = MaterialTheme.typography.body2)
Spacer(Modifier.weight(1f))
Text(text = "${item.installments?.amount} X ${item.installments?.quantity}", style = MaterialTheme.typography.body1)
}
}
EventTracer.instance.trace("render${item.id}_$time", categories, getTimeMillis(), 0 ,0, args)
}
}
}
}
McEna
11/30/2023, 8:06 AMImageLoader.getInstance()!!.load(firstImage)
return an Image after downloading & creating an ImageBitmapMcEna
11/30/2023, 8:06 AM@Composable
actual fun loadNetwork(imageUri: String, modifier: Modifier) {
val bitmapState: MutableState<LoadedFile?> = remember { mutableStateOf(null) }
if(bitmapState.value?.isAddressEqual(imageUri) != true){
jobScope.getScope().launch {
NSURL.URLWithString(imageUri)?.let { url ->
val request = NSMutableURLRequest.requestWithURL(url)
request.setHTTPMethod("GET")
val task = NSURLSession.sharedSession.dataTaskWithRequest(request) { data , res, err ->
jobScope.getScope().launch {
data?.let {
val image = UIImage(it).toImageBitmap()
withContext(Dispatchers.Main) {
bitmapState.value = LoadedFile(imageUri).apply {
this.bitmap = image
}
}
}
}
}
task.resume()
}
}
}
if(bitmapState.value?.bitmap!=null){
Image(
bitmap = bitmapState.value!!.bitmap!!,
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = modifier.then(Modifier.fillMaxSize())
)
}
}
McEna
11/30/2023, 8:07 AMMcEna
11/30/2023, 8:08 AMPablichjenkov
11/30/2023, 12:21 PM@Composable
Render (viewModel: MLSearchViewModel = MLSearchViewModel())
try
@Composable
Render (viewModel: MLSearchViewModel)
I don't see where you call Render() but you should host the ViewModel outside otherwise you will be creating a new instance on each recomposition.