Skip to content
This repository has been archived by the owner on Aug 20, 2024. It is now read-only.

Commit

Permalink
ForumPreviewScreen: Show user's inventory
Browse files Browse the repository at this point in the history
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
  • Loading branch information
theimpulson committed Dec 9, 2023
1 parent b331860 commit c1a3286
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 9 deletions.
4 changes: 2 additions & 2 deletions app/src/main/java/io/aayush/relabs/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class MainActivity : AppCompatActivity() {
val topRoutes: List<Screen> = listOf(
Screen.ThreadPreview,
Screen.News,
// Screen.ForumPreview,
Screen.Alerts,
Screen.ForumPreview,
Screen.Alerts
// Screen.More
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.aayush.relabs.network
import io.aayush.relabs.network.data.alert.Alerts
import io.aayush.relabs.network.data.common.MarkResponse
import io.aayush.relabs.network.data.conversation.Conversations
import io.aayush.relabs.network.data.devices.Devices
import io.aayush.relabs.network.data.node.Nodes
import io.aayush.relabs.network.data.post.PostInfo
import io.aayush.relabs.network.data.post.PostReply
Expand Down Expand Up @@ -101,4 +102,7 @@ interface XenforoInterface {
@Path("id") postID: Int,
@Query("reaction_id") reactionID: Int
): Response<PostReact>

@GET("xda-devices/inventory/")
suspend fun getInventory(): Response<Devices>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.util.Log
import io.aayush.relabs.network.data.alert.Alerts
import io.aayush.relabs.network.data.common.MarkResponse
import io.aayush.relabs.network.data.conversation.Conversations
import io.aayush.relabs.network.data.node.Node
import io.aayush.relabs.network.data.node.Nodes
import io.aayush.relabs.network.data.post.PostInfo
import io.aayush.relabs.network.data.post.PostReply
Expand Down Expand Up @@ -139,6 +140,10 @@ class XenforoRepository @Inject constructor(
return safeExecute { xenforoInterface.postReact(postID, reactID.ordinal + 1) }
}

suspend fun getInventory(): List<Node>? {
return safeExecute { xenforoInterface.getInventory() }?.devices?.map { it.Node }
}

private inline fun <T> safeExecute(block: () -> Response<T>): T? {
return try {
val response = block()
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/io/aayush/relabs/network/data/devices/Device.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.aayush.relabs.network.data.devices

import io.aayush.relabs.network.data.node.Node

data class Device(
val Node: Node,
val description: String,
val device_id: Int,
val name: String,
val view_url: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.aayush.relabs.network.data.devices

data class Devices(
val devices: List<Device>
)
109 changes: 109 additions & 0 deletions app/src/main/java/io/aayush/relabs/ui/components/NodePreviewItem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package io.aayush.relabs.ui.components

import android.text.format.DateUtils
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.aayush.relabs.R
import io.aayush.relabs.ui.extensions.shimmer
import java.text.NumberFormat
import java.util.Date
import java.util.Locale

@Composable
fun NodePreviewItem(
modifier: Modifier = Modifier,
title: String = "",
company: String = "",
threads: Int = 0,
lastUpdated: Int = 0,
lastThreadTitle: String = "",
unread: Boolean = false,
onClicked: () -> Unit = {},
loading: Boolean = false
) {
Row(
modifier = modifier
.fillMaxWidth()
.clickable { onClicked() },
verticalAlignment = Alignment.Top,
horizontalArrangement = Arrangement.Start
) {
Image(
painter = painterResource(id = R.drawable.ic_phone),
contentDescription = "",
modifier = Modifier
.requiredSize(48.dp)
.clip(RoundedCornerShape(20.dp))
.shimmer(loading)
)
Spacer(modifier = Modifier.width(10.dp))
Column(
verticalArrangement = Arrangement.spacedBy(5.dp, Alignment.Top),
horizontalAlignment = Alignment.Start
) {
Text(
text = title,
fontSize = 15.sp,
fontWeight = if (unread) FontWeight.Medium else FontWeight.Light,
modifier = Modifier
.fillMaxWidth()
.shimmer(loading)
)
Text(
text = if (company.isNotBlank()) {
stringResource(
id = R.string.company_threads_lastPostDate,
company,
NumberFormat.getInstance(Locale.getDefault()).format(threads),
if (lastUpdated != 0) {
DateUtils.getRelativeTimeSpanString(
lastUpdated.toLong() * 1000L,
Date().time,
DateUtils.MINUTE_IN_MILLIS
).toString().lowercase()
} else {
String()
}
)
} else {
String()
},
fontSize = 14.sp,
fontWeight = if (unread) FontWeight.Normal else FontWeight.Light,
modifier = Modifier
.fillMaxWidth(if (company.isBlank()) 0.75F else 1F)
.shimmer(loading)
)
Text(
text = if (lastThreadTitle.isNotBlank()) {
stringResource(id = R.string.last_thread, lastThreadTitle)
} else {
String()
},
fontSize = 13.sp,
fontWeight = FontWeight.Light,
modifier = Modifier
.fillMaxWidth(if (lastThreadTitle.isBlank()) 0.5F else 1F)
.shimmer(loading)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,112 @@
package io.aayush.relabs.ui.screens.forumpreview

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import io.aayush.relabs.R
import io.aayush.relabs.network.data.node.Node
import io.aayush.relabs.ui.components.NodePreviewItem
import kotlinx.coroutines.launch

@Composable
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
fun ForumPreviewScreen(
navHostController: NavHostController,
viewModel: ForumPreviewViewModel = hiltViewModel()
) {
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(
title = { Text(text = stringResource(id = R.string.forum_preview)) }
)
}
topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.forum_preview)) }) }
) {
val tabData = listOf(R.string.inventory, R.string.watched, R.string.whats_new)
val pagerState = rememberPagerState(
initialPage = 0,
initialPageOffsetFraction = 0f,
pageCount = { tabData.size }
)
val tabIndex = pagerState.currentPage
val coroutineScope = rememberCoroutineScope()

val loading: Boolean by viewModel.loading.collectAsStateWithLifecycle()
val inventory: List<Node>? by viewModel.inventory.collectAsStateWithLifecycle()

LaunchedEffect(key1 = pagerState) {
snapshotFlow { pagerState.currentPage }.collect { page ->
when (page) {
0 -> viewModel.getInventory()
}
}
}

Column(modifier = Modifier.padding(it)) {
TabRow(selectedTabIndex = tabIndex) {
tabData.fastForEachIndexed { index, _ ->
Tab(selected = tabIndex == index, onClick = {
coroutineScope.launch {
pagerState.animateScrollToPage(index)
}
}, text = {
Text(text = stringResource(id = tabData[index]))
})
}
}
HorizontalPager(
state = pagerState
) {
if (loading) {
LazyColumn(modifier = Modifier.fillMaxHeight()) {
items(20) {
NodePreviewItem(modifier = Modifier.padding(10.dp), loading = true)
}
}
return@HorizontalPager
}

LazyColumn(modifier = Modifier.fillMaxHeight()) {
items(
items = when (it) {
0 -> inventory ?: emptyList()

else -> emptyList()
},
key = { n -> n.node_id }
) { node ->
NodePreviewItem(
modifier = Modifier.padding(10.dp),
title = node.title,
company = node.breadcrumbs.firstOrNull()?.title ?: String(),
lastUpdated = node.type_data.last_post_date,
lastThreadTitle = node.type_data.last_thread_title,
unread = node.type_data.is_unread,
threads = node.type_data.discussion_count
)
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,47 @@
package io.aayush.relabs.ui.screens.forumpreview

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import io.aayush.relabs.network.XenforoRepository
import io.aayush.relabs.network.data.node.Node
import io.aayush.relabs.network.data.thread.Thread
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class ForumPreviewViewModel @Inject constructor(
private val xenforoRepository: XenforoRepository
) : ViewModel()
) : ViewModel() {

private val _loading = MutableStateFlow(false)
val loading = _loading.asStateFlow()

private val _inventory = MutableStateFlow<List<Node>?>(emptyList())
val inventory = _inventory.asStateFlow()

private val _trendingNodes = MutableStateFlow<List<Node>?>(emptyList())
val trendingNodes = _trendingNodes.asStateFlow()

private val _watchedNodes = MutableStateFlow<List<Thread>?>(emptyList())
val watchedNodes = _watchedNodes.asStateFlow()

fun getInventory() {
if (!inventory.value.isNullOrEmpty()) return
viewModelScope.launch(Dispatchers.IO) {
fetch { _inventory.value = xenforoRepository.getInventory() }
}
}

private inline fun <T> fetch(block: () -> T): T? {
return try {
_loading.value = true
block()
} finally {
_loading.value = false
}
}
}
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,7 @@

<!-- ForumPreviewScreen -->
<string name="forum_preview">Forums</string>
<string name="inventory">Inventory</string>
<string name="company_threads_lastPostDate"><xliff:g id="company">%1$s</xliff:g> &#8226; <xliff:g id="threads">%2$s</xliff:g> threads &#8226; last updated <xliff:g id="lastPostDate">%3$s</xliff:g></string>
<string name="last_thread">Last thread: <xliff:g id="lastThreadTitle">%1$s</xliff:g></string>
</resources>

0 comments on commit c1a3286

Please sign in to comment.