Skip to content

Commit 6b91933

Browse files
committed
Add hierarchical post list skeleton for Pages
Add separate UI for hierarchical post types (like Pages) that displays posts with visual indentation based on parent-child relationships. Changes: - Add `hierarchical` flag to `CptPostTypeItem` model - Create `CptHierarchicalPostListActivity` and ViewModel with mock hierarchy - Create `CptHierarchicalPostListScreen` with indent-based display - Route Pages to hierarchical screen, Posts to flat screen
1 parent cb5945f commit 6b91933

File tree

6 files changed

+247
-7
lines changed

6 files changed

+247
-7
lines changed

libs/posttypes/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,9 @@
1313
android:name="org.wordpress.android.posttypes.CptFlatPostListActivity"
1414
android:theme="@style/Cpt.NoActionBar"
1515
android:exported="false" />
16+
<activity
17+
android:name="org.wordpress.android.posttypes.CptHierarchicalPostListActivity"
18+
android:theme="@style/Cpt.NoActionBar"
19+
android:exported="false" />
1620
</application>
1721
</manifest>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.wordpress.android.posttypes
2+
3+
import android.content.Context
4+
import android.content.Intent
5+
import android.os.Bundle
6+
import androidx.activity.compose.setContent
7+
import androidx.activity.viewModels
8+
import androidx.appcompat.app.AppCompatActivity
9+
import androidx.compose.runtime.collectAsState
10+
import androidx.compose.runtime.getValue
11+
import dagger.hilt.android.AndroidEntryPoint
12+
import org.wordpress.android.posttypes.bridge.BridgeConstants
13+
import org.wordpress.android.posttypes.bridge.CptActivity
14+
import org.wordpress.android.posttypes.bridge.CptTheme
15+
import org.wordpress.android.posttypes.bridge.SiteReference
16+
import org.wordpress.android.posttypes.bridge.applyBaseSetup
17+
import org.wordpress.android.posttypes.compose.CptHierarchicalPostListScreen
18+
19+
@AndroidEntryPoint
20+
class CptHierarchicalPostListActivity : AppCompatActivity(), CptActivity {
21+
private val viewModel: CptHierarchicalPostListViewModel by viewModels()
22+
23+
override fun onCreate(savedInstanceState: Bundle?) {
24+
applyBaseSetup()
25+
super.onCreate(savedInstanceState)
26+
setContent {
27+
CptTheme {
28+
val uiState by viewModel.uiState.collectAsState()
29+
CptHierarchicalPostListScreen(
30+
uiState = uiState,
31+
onBackClick = { onBackPressedDispatcher.onBackPressed() },
32+
onPostClick = viewModel::onPostClick
33+
)
34+
}
35+
}
36+
}
37+
38+
companion object {
39+
const val EXTRA_POST_TYPE_SLUG = "post_type_slug"
40+
const val EXTRA_POST_TYPE_LABEL = "post_type_label"
41+
42+
fun createIntent(
43+
context: Context,
44+
site: SiteReference,
45+
postTypeSlug: String,
46+
postTypeLabel: String
47+
): Intent {
48+
return Intent(context, CptHierarchicalPostListActivity::class.java).apply {
49+
putExtra(BridgeConstants.EXTRA_SITE, site)
50+
putExtra(EXTRA_POST_TYPE_SLUG, postTypeSlug)
51+
putExtra(EXTRA_POST_TYPE_LABEL, postTypeLabel)
52+
}
53+
}
54+
}
55+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package org.wordpress.android.posttypes
2+
3+
import androidx.lifecycle.SavedStateHandle
4+
import androidx.lifecycle.ViewModel
5+
import dagger.hilt.android.lifecycle.HiltViewModel
6+
import kotlinx.coroutines.flow.MutableStateFlow
7+
import kotlinx.coroutines.flow.StateFlow
8+
import kotlinx.coroutines.flow.asStateFlow
9+
import org.wordpress.android.posttypes.bridge.BridgeConstants
10+
import org.wordpress.android.posttypes.bridge.SiteReference
11+
import javax.inject.Inject
12+
13+
data class CptHierarchicalPostItem(
14+
val id: Long,
15+
val title: String,
16+
val status: String,
17+
val indent: Int = 0
18+
)
19+
20+
data class CptHierarchicalPostListUiState(
21+
val postTypeLabel: String = "",
22+
val posts: List<CptHierarchicalPostItem> = emptyList(),
23+
val isLoading: Boolean = false
24+
)
25+
26+
@HiltViewModel
27+
class CptHierarchicalPostListViewModel @Inject constructor(
28+
savedStateHandle: SavedStateHandle
29+
) : ViewModel() {
30+
@Suppress("unused") // Will be used to fetch posts from wordpress-rs
31+
private val site: SiteReference? = savedStateHandle.get<SiteReference>(BridgeConstants.EXTRA_SITE)
32+
33+
@Suppress("unused") // Will be used to fetch posts from wordpress-rs
34+
private val postTypeSlug: String = savedStateHandle.get<String>(
35+
CptHierarchicalPostListActivity.EXTRA_POST_TYPE_SLUG
36+
) ?: ""
37+
38+
private val postTypeLabel: String = savedStateHandle.get<String>(
39+
CptHierarchicalPostListActivity.EXTRA_POST_TYPE_LABEL
40+
) ?: ""
41+
42+
private val _uiState = MutableStateFlow(
43+
CptHierarchicalPostListUiState(
44+
postTypeLabel = postTypeLabel,
45+
posts = generateMockHierarchy()
46+
)
47+
)
48+
val uiState: StateFlow<CptHierarchicalPostListUiState> = _uiState.asStateFlow()
49+
50+
@Suppress("MagicNumber") // Mock data - will be replaced with real data from wordpress-rs
51+
private fun generateMockHierarchy(): List<CptHierarchicalPostItem> {
52+
// Simulates a page hierarchy: Home > About > Team, Contact
53+
return listOf(
54+
CptHierarchicalPostItem(1, "Home", "Published", indent = 0),
55+
CptHierarchicalPostItem(2, "About", "Published", indent = 1),
56+
CptHierarchicalPostItem(3, "Team", "Published", indent = 2),
57+
CptHierarchicalPostItem(4, "Contact", "Published", indent = 1),
58+
CptHierarchicalPostItem(5, "Blog", "Draft", indent = 0)
59+
)
60+
}
61+
62+
@Suppress("UnusedParameter") // Will navigate to post editor with wordpress-rs integration
63+
fun onPostClick(post: CptHierarchicalPostItem) {
64+
// No-op: navigation will be implemented with wordpress-rs integration
65+
}
66+
}

libs/posttypes/src/main/java/org/wordpress/android/posttypes/CptPostTypesActivity.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,22 @@ class CptPostTypesActivity : AppCompatActivity(), CptActivity {
4646
private fun handleNavigation(action: CptNavigationAction) {
4747
when (action) {
4848
is CptNavigationAction.OpenPostTypeList -> {
49-
startActivity(
49+
val intent = if (action.hierarchical) {
50+
CptHierarchicalPostListActivity.createIntent(
51+
context = this,
52+
site = action.site,
53+
postTypeSlug = action.postTypeSlug,
54+
postTypeLabel = action.postTypeLabel
55+
)
56+
} else {
5057
CptFlatPostListActivity.createIntent(
5158
context = this,
5259
site = action.site,
5360
postTypeSlug = action.postTypeSlug,
5461
postTypeLabel = action.postTypeLabel
5562
)
56-
)
63+
}
64+
startActivity(intent)
5765
}
5866
}
5967
}

libs/posttypes/src/main/java/org/wordpress/android/posttypes/CptPostTypesViewModel.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import javax.inject.Inject
1515

1616
data class CptPostTypeItem(
1717
val slug: String,
18-
val label: String
18+
val label: String,
19+
val hierarchical: Boolean = false
1920
)
2021

2122
data class CptPostTypesUiState(
@@ -26,7 +27,8 @@ sealed class CptNavigationAction {
2627
data class OpenPostTypeList(
2728
val site: SiteReference,
2829
val postTypeSlug: String,
29-
val postTypeLabel: String
30+
val postTypeLabel: String,
31+
val hierarchical: Boolean
3032
) : CptNavigationAction()
3133
}
3234

@@ -39,8 +41,8 @@ class CptPostTypesViewModel @Inject constructor(
3941
private val _uiState = MutableStateFlow(
4042
CptPostTypesUiState(
4143
postTypes = listOf(
42-
CptPostTypeItem(slug = "post", label = "Posts"),
43-
CptPostTypeItem(slug = "page", label = "Pages")
44+
CptPostTypeItem(slug = "post", label = "Posts", hierarchical = false),
45+
CptPostTypeItem(slug = "page", label = "Pages", hierarchical = true)
4446
)
4547
)
4648
)
@@ -55,7 +57,8 @@ class CptPostTypesViewModel @Inject constructor(
5557
CptNavigationAction.OpenPostTypeList(
5658
site = it,
5759
postTypeSlug = postType.slug,
58-
postTypeLabel = postType.label
60+
postTypeLabel = postType.label,
61+
hierarchical = postType.hierarchical
5962
)
6063
)
6164
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package org.wordpress.android.posttypes.compose
2+
3+
import androidx.compose.foundation.clickable
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.Row
6+
import androidx.compose.foundation.layout.Spacer
7+
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.layout.fillMaxWidth
9+
import androidx.compose.foundation.layout.padding
10+
import androidx.compose.foundation.layout.width
11+
import androidx.compose.foundation.lazy.LazyColumn
12+
import androidx.compose.foundation.lazy.items
13+
import androidx.compose.material.icons.Icons
14+
import androidx.compose.material.icons.automirrored.filled.ArrowBack
15+
import androidx.compose.material3.ExperimentalMaterial3Api
16+
import androidx.compose.material3.HorizontalDivider
17+
import androidx.compose.material3.Icon
18+
import androidx.compose.material3.IconButton
19+
import androidx.compose.material3.MaterialTheme
20+
import androidx.compose.material3.Scaffold
21+
import androidx.compose.material3.Text
22+
import androidx.compose.material3.TopAppBar
23+
import androidx.compose.runtime.Composable
24+
import androidx.compose.ui.Alignment
25+
import androidx.compose.ui.Modifier
26+
import androidx.compose.ui.res.stringResource
27+
import androidx.compose.ui.text.style.TextOverflow
28+
import androidx.compose.ui.unit.dp
29+
import org.wordpress.android.posttypes.CptHierarchicalPostItem
30+
import org.wordpress.android.posttypes.CptHierarchicalPostListUiState
31+
import org.wordpress.android.posttypes.R
32+
33+
private const val INDENT_WIDTH_DP = 16
34+
35+
@OptIn(ExperimentalMaterial3Api::class)
36+
@Composable
37+
fun CptHierarchicalPostListScreen(
38+
uiState: CptHierarchicalPostListUiState,
39+
onBackClick: () -> Unit,
40+
onPostClick: (CptHierarchicalPostItem) -> Unit
41+
) {
42+
Scaffold(
43+
topBar = {
44+
TopAppBar(
45+
title = { Text(uiState.postTypeLabel) },
46+
navigationIcon = {
47+
IconButton(onClick = onBackClick) {
48+
Icon(
49+
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
50+
contentDescription = stringResource(R.string.cpt_back)
51+
)
52+
}
53+
}
54+
)
55+
}
56+
) { paddingValues ->
57+
LazyColumn(
58+
modifier = Modifier
59+
.fillMaxSize()
60+
.padding(paddingValues)
61+
) {
62+
items(uiState.posts) { post ->
63+
CptHierarchicalPostItemRow(
64+
post = post,
65+
onClick = { onPostClick(post) }
66+
)
67+
HorizontalDivider()
68+
}
69+
}
70+
}
71+
}
72+
73+
@Composable
74+
private fun CptHierarchicalPostItemRow(
75+
post: CptHierarchicalPostItem,
76+
onClick: () -> Unit
77+
) {
78+
Row(
79+
modifier = Modifier
80+
.fillMaxWidth()
81+
.clickable(onClick = onClick)
82+
.padding(horizontal = 16.dp, vertical = 12.dp),
83+
verticalAlignment = Alignment.CenterVertically
84+
) {
85+
// Indent based on hierarchy level
86+
if (post.indent > 0) {
87+
Spacer(modifier = Modifier.width((INDENT_WIDTH_DP * post.indent).dp))
88+
}
89+
Column(modifier = Modifier.weight(1f)) {
90+
Text(
91+
text = post.title,
92+
style = MaterialTheme.typography.titleMedium,
93+
maxLines = 1,
94+
overflow = TextOverflow.Ellipsis
95+
)
96+
Text(
97+
text = post.status,
98+
style = MaterialTheme.typography.labelSmall,
99+
color = MaterialTheme.colorScheme.primary,
100+
modifier = Modifier.padding(top = 4.dp)
101+
)
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)