Skip to content

Commit eb7c3fa

Browse files
authored
CMM-802 create taxonomies data view (#22258)
* Updating library * Porting the terms fetching * Porting create * Porting delete term * porting update term * Update term fix * Creating the new isHierarchical cocal field * Parent fix * Fixing tests * detekt * Minor fix * Fixing tests * Adding the taxonomies menu view model * Adding show mechanism * Showing taxoniomies * Adding a LiveData * Call categories and tags screens * detekt and style * Adding tests * Creating dataview * Style adjustments * Adjustments and setHierarchical * Proper indentation * Handling parent and description in details * Sorting hierarchically * Adding tests * Title fix * Removing unused string * Using server query * Using server sorting * Handling indentation in the UI style layer
1 parent 191cff0 commit eb7c3fa

File tree

9 files changed

+678
-10
lines changed

9 files changed

+678
-10
lines changed

WordPress/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@
252252
android:name=".ui.accounts.applicationpassword.ApplicationPasswordsListActivity"
253253
android:label="@string/application_password_info_title"
254254
android:theme="@style/WordPress.NoActionBar" />
255+
<activity
256+
android:name=".ui.taxonomies.TermsDataViewActivity"
257+
android:label="@string/taxonomies_title"
258+
android:theme="@style/WordPress.NoActionBar" />
255259
<activity
256260
android:name=".ui.prefs.notifications.NotificationsSettingsActivity"
257261
android:configChanges="orientation|screenSize"

WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@
125125
import org.wordpress.android.ui.subscribers.SubscribersActivity;
126126
import org.wordpress.android.ui.suggestion.SuggestionActivity;
127127
import org.wordpress.android.ui.suggestion.SuggestionType;
128+
import org.wordpress.android.ui.taxonomies.TermsDataViewActivity;
128129
import org.wordpress.android.ui.themes.ThemeBrowserActivity;
129130
import org.wordpress.android.ui.utils.PreMigrationDeepLinkData;
130131
import org.wordpress.android.util.AppLog;
@@ -1899,4 +1900,10 @@ public static void openShareIntent(@NonNull Context context, @NonNull String lin
18991900
intent.putExtra(Intent.EXTRA_TITLE, title);
19001901
context.startActivity(Intent.createChooser(intent, context.getString(R.string.share_link)));
19011902
}
1903+
1904+
public static void showTermsList(@NonNull Context context, @NonNull String taxonomySlug,
1905+
@NonNull String taxonomyName, boolean isHierarchical) {
1906+
Intent intent = TermsDataViewActivity.Companion.getIntent(context, taxonomySlug, taxonomyName, isHierarchical);
1907+
context.startActivity(intent);
1908+
}
19021909
}

WordPress/src/main/java/org/wordpress/android/ui/dataview/DataViewItem.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package org.wordpress.android.ui.dataview
22

3+
import androidx.compose.ui.unit.Dp
4+
import androidx.compose.ui.unit.dp
5+
36
/**
47
* Represents a basic model for data to be displayed in a [DataViewItemCard]. Note that [data] is
58
* optional but is intended to store the actual data object associated with the item..
@@ -11,5 +14,6 @@ data class DataViewItem(
1114
val fields: List<DataViewItemField>,
1215
// Avoid adding the last field to the end of the card and follow regular alignment instead
1316
val skipEndPositioning: Boolean = false,
14-
val data: Any? = null
17+
val data: Any? = null,
18+
val indentation: Dp = 0.dp // Used to indent items
1519
)

WordPress/src/main/java/org/wordpress/android/ui/dataview/DataViewItemCard.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ fun DataViewItemCard(
4949
) {
5050
Row(
5151
modifier = Modifier
52-
.padding(16.dp)
52+
.padding(start = 16.dp + item.indentation, end = 16.dp, top = 16.dp , bottom = 16.dp)
5353
.fillMaxWidth(),
5454
) {
5555
item.image?.let { image ->

WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,14 +1143,8 @@ private void showTaxonomies(List<TaxonomyTypeDetailsWithEditContext> taxonomies)
11431143
pref.setTitle(taxonomy.getName());
11441144
pref.setKey(taxonomy.getSlug());
11451145
pref.setOnPreferenceClickListener(preference -> {
1146-
// TODO: Create generic taxonomies DataView and call it from here
1147-
// We are not accepting the taxonomy name as a parameter yet
1148-
// So Categories and Tags are still hardcoded
1149-
if ("category".equals(taxonomy.getSlug())) {
1150-
ActivityLauncher.showCategoriesList(getActivity(), mSite);
1151-
} else if ("post_tag".equals(taxonomy.getSlug())) {
1152-
SiteSettingsTagListActivity.showTagList(getActivity(), mSite);
1153-
}
1146+
ActivityLauncher.showTermsList(getActivity(), taxonomy.getSlug(), taxonomy.getName(),
1147+
taxonomy.getHierarchical());
11541148
return false;
11551149
}
11561150
);
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
package org.wordpress.android.ui.taxonomies
2+
3+
import android.content.Context
4+
import android.content.Intent
5+
import android.os.Build
6+
import android.os.Bundle
7+
import androidx.activity.viewModels
8+
import androidx.compose.foundation.layout.Arrangement
9+
import androidx.compose.foundation.layout.Column
10+
import androidx.compose.foundation.layout.Row
11+
import androidx.compose.foundation.layout.fillMaxSize
12+
import androidx.compose.foundation.layout.fillMaxWidth
13+
import androidx.compose.foundation.layout.padding
14+
import androidx.compose.foundation.rememberScrollState
15+
import androidx.compose.foundation.verticalScroll
16+
import androidx.compose.material.icons.Icons
17+
import androidx.compose.material.icons.automirrored.filled.ArrowBack
18+
import androidx.compose.material3.Card
19+
import androidx.compose.material3.CardDefaults
20+
import androidx.compose.material3.ExperimentalMaterial3Api
21+
import androidx.compose.material3.Icon
22+
import androidx.compose.material3.IconButton
23+
import androidx.compose.material3.MaterialTheme
24+
import androidx.compose.material3.Scaffold
25+
import androidx.compose.material3.Text
26+
import androidx.compose.material3.TopAppBar
27+
import androidx.compose.runtime.Composable
28+
import androidx.compose.runtime.collectAsState
29+
import androidx.compose.runtime.mutableStateOf
30+
import androidx.compose.runtime.remember
31+
import androidx.compose.ui.Alignment
32+
import androidx.compose.ui.Modifier
33+
import androidx.compose.ui.platform.ComposeView
34+
import androidx.compose.ui.platform.ViewCompositionStrategy
35+
import androidx.compose.ui.res.stringResource
36+
import androidx.compose.ui.text.font.FontWeight
37+
import androidx.compose.ui.unit.dp
38+
import androidx.navigation.NavHostController
39+
import androidx.navigation.compose.NavHost
40+
import androidx.navigation.compose.composable
41+
import androidx.navigation.compose.rememberNavController
42+
import dagger.hilt.android.AndroidEntryPoint
43+
import org.wordpress.android.R
44+
import org.wordpress.android.fluxc.utils.AppLogWrapper
45+
import org.wordpress.android.ui.compose.theme.AppThemeM3
46+
import org.wordpress.android.ui.dataview.DataViewScreen
47+
import org.wordpress.android.ui.main.BaseAppCompatActivity
48+
import org.wordpress.android.util.AppLog
49+
import uniffi.wp_api.AnyTermWithEditContext
50+
import javax.inject.Inject
51+
52+
@AndroidEntryPoint
53+
class TermsDataViewActivity : BaseAppCompatActivity() {
54+
@Inject
55+
lateinit var appLogWrapper: AppLogWrapper
56+
57+
private val viewModel by viewModels<TermsViewModel>()
58+
59+
private lateinit var composeView: ComposeView
60+
private lateinit var navController: NavHostController
61+
62+
override fun onCreate(savedInstanceState: Bundle?) {
63+
super.onCreate(savedInstanceState)
64+
65+
val taxonomySlug = intent.getStringExtra(TAXONOMY_SLUG)
66+
val isHierarchical = intent.getBooleanExtra(IS_HIERARCHICAL, false)
67+
val taxonomyName = intent.getStringExtra(TAXONOMY_NAME) ?: ""
68+
if (taxonomySlug == null) {
69+
appLogWrapper.e(AppLog.T.API, "Error: No taxonomy selected")
70+
finish()
71+
return
72+
}
73+
74+
viewModel.initialize(taxonomySlug, isHierarchical)
75+
76+
composeView = ComposeView(this)
77+
setContentView(
78+
composeView.apply {
79+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
80+
this.isForceDarkAllowed = false
81+
}
82+
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
83+
setContent {
84+
NavigableContent(taxonomyName)
85+
}
86+
}
87+
)
88+
}
89+
90+
private enum class TermScreen {
91+
List,
92+
Detail
93+
}
94+
95+
@OptIn(ExperimentalMaterial3Api::class)
96+
@Composable
97+
private fun NavigableContent(taxonomyName: String) {
98+
navController = rememberNavController()
99+
val listTitle = taxonomyName
100+
val titleState = remember { mutableStateOf(listTitle) }
101+
102+
AppThemeM3 {
103+
Scaffold(
104+
topBar = {
105+
TopAppBar(
106+
title = { Text(titleState.value) },
107+
navigationIcon = {
108+
IconButton(onClick = {
109+
if (navController.previousBackStackEntry != null) {
110+
navController.navigateUp()
111+
} else {
112+
finish()
113+
}
114+
}) {
115+
Icon(Icons.AutoMirrored.Filled.ArrowBack, stringResource(R.string.back))
116+
}
117+
}
118+
)
119+
},
120+
) { contentPadding ->
121+
NavHost(
122+
navController = navController,
123+
startDestination = TermScreen.List.name
124+
) {
125+
composable(route = TermScreen.List.name) {
126+
titleState.value = listTitle
127+
ShowListScreen(
128+
navController,
129+
modifier = Modifier.padding(contentPadding)
130+
)
131+
}
132+
133+
composable(route = TermScreen.Detail.name) {
134+
navController.previousBackStackEntry?.savedStateHandle?.let { handle ->
135+
val termId = handle.get<Long>(KEY_TERM_ID)
136+
if (termId != null) {
137+
viewModel.getTerm(termId)?.let { term ->
138+
titleState.value = term.name
139+
ShowTermDetailScreen(
140+
allTerms = viewModel.getAllTerms(),
141+
term = term,
142+
modifier = Modifier.padding(contentPadding)
143+
)
144+
}
145+
}
146+
}
147+
}
148+
}
149+
}
150+
}
151+
}
152+
153+
@Composable
154+
private fun ShowListScreen(
155+
navController: NavHostController,
156+
modifier: Modifier
157+
) {
158+
DataViewScreen(
159+
uiState = viewModel.uiState.collectAsState(),
160+
supportedFilters = viewModel.getSupportedFilters(),
161+
supportedSorts = viewModel.getSupportedSorts(),
162+
onRefresh = {
163+
viewModel.onRefreshData()
164+
},
165+
onFetchMore = {
166+
viewModel.onFetchMoreData()
167+
},
168+
onSearchQueryChange = { query ->
169+
viewModel.onSearchQueryChange(query)
170+
},
171+
onItemClick = { item ->
172+
viewModel.onItemClick(item)
173+
(item.data as? AnyTermWithEditContext)?.let { term ->
174+
navController.currentBackStackEntry?.savedStateHandle?.set(
175+
key = KEY_TERM_ID,
176+
value = term.id
177+
)
178+
navController.navigate(route = TermScreen.Detail.name)
179+
}
180+
},
181+
onFilterClick = { filter ->
182+
viewModel.onFilterClick(filter)
183+
},
184+
onSortClick = { sort ->
185+
viewModel.onSortClick(sort)
186+
},
187+
onSortOrderClick = { order ->
188+
viewModel.onSortOrderClick(order)
189+
},
190+
emptyView = viewModel.emptyView,
191+
modifier = modifier
192+
)
193+
}
194+
195+
@Composable
196+
private fun ShowTermDetailScreen(
197+
allTerms: List<AnyTermWithEditContext>,
198+
term: AnyTermWithEditContext,
199+
modifier: Modifier
200+
) {
201+
Column(
202+
modifier = modifier
203+
.fillMaxSize()
204+
.padding(16.dp)
205+
.verticalScroll(rememberScrollState()),
206+
verticalArrangement = Arrangement.spacedBy(16.dp)
207+
) {
208+
TermDetailsCard(allTerms, term)
209+
}
210+
}
211+
212+
@Composable
213+
private fun TermDetailsCard(allTerms: List<AnyTermWithEditContext>, term: AnyTermWithEditContext) {
214+
Card(
215+
modifier = Modifier.fillMaxWidth(),
216+
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
217+
colors = CardDefaults.cardColors(
218+
containerColor = MaterialTheme.colorScheme.surface
219+
)
220+
) {
221+
Column(
222+
modifier = Modifier
223+
.fillMaxWidth()
224+
.padding(16.dp),
225+
verticalArrangement = Arrangement.spacedBy(12.dp)
226+
) {
227+
DetailRow(
228+
label = stringResource(R.string.term_name_label),
229+
value = term.name
230+
)
231+
232+
DetailRow(
233+
label = stringResource(R.string.term_slug_label),
234+
value = term.slug
235+
)
236+
237+
DetailRow(
238+
label = stringResource(R.string.term_description_label),
239+
value = term.description
240+
)
241+
242+
DetailRow(
243+
label = stringResource(R.string.term_count_label),
244+
value = term.count.toString()
245+
)
246+
247+
term.parent?.let { parentId ->
248+
val parentName = allTerms.firstOrNull { it.id == parentId }?.name
249+
parentName?.let {
250+
DetailRow(
251+
label = stringResource(R.string.term_parent_label),
252+
value = parentName
253+
)
254+
}
255+
}
256+
}
257+
}
258+
}
259+
260+
@Composable
261+
private fun DetailRow(
262+
label: String,
263+
value: String
264+
) {
265+
Row(
266+
modifier = Modifier.fillMaxWidth(),
267+
verticalAlignment = Alignment.Top
268+
) {
269+
Text(
270+
text = label,
271+
style = MaterialTheme.typography.bodyMedium,
272+
fontWeight = FontWeight.SemiBold,
273+
color = MaterialTheme.colorScheme.onSurfaceVariant,
274+
modifier = Modifier.weight(0.3f)
275+
)
276+
277+
Text(
278+
text = value,
279+
style = MaterialTheme.typography.bodyMedium,
280+
color = MaterialTheme.colorScheme.onSurface,
281+
modifier = Modifier.weight(0.7f)
282+
)
283+
}
284+
}
285+
286+
companion object {
287+
private const val TAXONOMY_SLUG = "taxonomy_slug"
288+
private const val IS_HIERARCHICAL = "is_hierarchical"
289+
private const val TAXONOMY_NAME = "taxonomy_name"
290+
private const val KEY_TERM_ID = "termId"
291+
292+
fun getIntent(context: Context, taxonomySlug: String, taxonomyName: String, isHierarchical: Boolean): Intent =
293+
Intent(context, TermsDataViewActivity::class.java).apply {
294+
putExtra(TAXONOMY_SLUG, taxonomySlug)
295+
putExtra(TAXONOMY_NAME, taxonomyName)
296+
putExtra(IS_HIERARCHICAL, isHierarchical)
297+
}
298+
}
299+
}

0 commit comments

Comments
 (0)