Skip to content
This repository was archived by the owner on Feb 4, 2025. It is now read-only.

Commit 4ee8985

Browse files
Merge branch 'refs/heads/trunk' into issue/12390-update-models-to-support-evergreen-campaigns
2 parents d0b5ede + 651ba46 commit 4ee8985

File tree

47 files changed

+988
-165
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+988
-165
lines changed

config/detekt/detekt.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ config:
77
complexity:
88
LongParameterList:
99
ignoreDefaultParameters: true
10-
ignoreAnnotated: ['Inject']
10+
ignoreAnnotated: ['Inject', 'Composable']
1111
TooManyFunctions:
1212
active: false
13+
LongMethod:
14+
ignoreAnnotated: ['Composable']
1315

1416
coroutines:
1517
GlobalCoroutineUsage:
@@ -22,6 +24,7 @@ style:
2224
MagicNumber:
2325
ignoreEnums: true
2426
ignorePropertyDeclaration: true
27+
ignoreAnnotated: ['Composable']
2528
SpacingBetweenPackageAndImports:
2629
active: true
2730
UnusedImports:
@@ -31,3 +34,7 @@ style:
3134
ForbiddenSuppress:
3235
active: true
3336
rules: ['MaximumLineLength']
37+
38+
naming:
39+
FunctionNaming:
40+
ignoreAnnotated: [ 'Composable' ]

example/build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ android {
6767
buildFeatures {
6868
buildConfig true
6969
viewBinding true
70+
compose true
71+
}
72+
73+
composeOptions {
74+
kotlinCompilerExtensionVersion = sharedLibs.versions.androidx.compose.compiler.get()
7075
}
7176

7277
sourceSets {
@@ -137,6 +142,10 @@ dependencies {
137142
testImplementation sharedLibs.assertj.core
138143
testImplementation sharedLibs.androidx.arch.core.testing
139144

145+
implementation platform(sharedLibs.androidx.compose.bom)
146+
implementation sharedLibs.androidx.compose.material
147+
implementation sharedLibs.androidx.compose.ui.tooling
148+
140149
androidTestImplementation sharedLibs.assertj.core
141150
androidTestImplementation sharedLibs.androidx.arch.core.testing
142151
androidTestCompileOnly sharedLibs.glassfish.javax.annotation

example/src/main/java/org/wordpress/android/fluxc/example/di/FragmentsModule.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import org.wordpress.android.fluxc.example.ui.customer.search.WooCustomersSearch
3131
import org.wordpress.android.fluxc.example.ui.gateways.WooGatewaysFragment
3232
import org.wordpress.android.fluxc.example.ui.helpsupport.WooHelpSupportFragment
3333
import org.wordpress.android.fluxc.example.ui.leaderboards.WooLeaderboardsFragment
34+
import org.wordpress.android.fluxc.example.ui.metadata.CustomFieldsFragment
3435
import org.wordpress.android.fluxc.example.ui.onboarding.WooOnboardingFragment
3536
import org.wordpress.android.fluxc.example.ui.orders.AddressEditDialogFragment
3637
import org.wordpress.android.fluxc.example.ui.orders.WooOrdersFragment
@@ -200,4 +201,7 @@ internal interface FragmentsModule {
200201

201202
@ContributesAndroidInjector
202203
fun provideWooAdminFragment(): WooAdminFragment
204+
205+
@ContributesAndroidInjector
206+
fun provideCustomFieldsFragment(): CustomFieldsFragment
203207
}
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
package org.wordpress.android.fluxc.example.ui.metadata
2+
3+
import android.os.Bundle
4+
import android.view.LayoutInflater
5+
import android.view.View
6+
import android.view.ViewGroup
7+
import androidx.compose.foundation.background
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.Spacer
12+
import androidx.compose.foundation.layout.fillMaxSize
13+
import androidx.compose.foundation.layout.fillMaxWidth
14+
import androidx.compose.foundation.layout.height
15+
import androidx.compose.foundation.layout.padding
16+
import androidx.compose.foundation.layout.wrapContentSize
17+
import androidx.compose.material.Button
18+
import androidx.compose.material.CircularProgressIndicator
19+
import androidx.compose.material.Icon
20+
import androidx.compose.material.IconButton
21+
import androidx.compose.material.MaterialTheme
22+
import androidx.compose.material.OutlinedTextField
23+
import androidx.compose.material.Text
24+
import androidx.compose.material.icons.Icons
25+
import androidx.compose.material.icons.filled.Delete
26+
import androidx.compose.material.icons.filled.Edit
27+
import androidx.compose.runtime.Composable
28+
import androidx.compose.runtime.getValue
29+
import androidx.compose.runtime.mutableStateOf
30+
import androidx.compose.runtime.remember
31+
import androidx.compose.runtime.setValue
32+
import androidx.compose.ui.Alignment
33+
import androidx.compose.ui.Modifier
34+
import androidx.compose.ui.platform.ComposeView
35+
import androidx.compose.ui.platform.ViewCompositionStrategy
36+
import androidx.compose.ui.text.font.FontWeight
37+
import androidx.compose.ui.text.style.TextOverflow
38+
import androidx.compose.ui.unit.dp
39+
import androidx.compose.ui.window.Dialog
40+
import androidx.lifecycle.lifecycleScope
41+
import dagger.android.support.DaggerFragment
42+
import org.wordpress.android.fluxc.example.ui.metadata.CustomFieldsViewModel.CustomFieldsState
43+
import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId
44+
import org.wordpress.android.fluxc.model.metadata.MetaDataParentItemType
45+
import org.wordpress.android.fluxc.model.metadata.WCMetaData
46+
import org.wordpress.android.fluxc.model.metadata.WCMetaDataValue
47+
import org.wordpress.android.fluxc.store.MetaDataStore
48+
import org.wordpress.android.fluxc.store.SiteStore
49+
import javax.inject.Inject
50+
51+
class CustomFieldsFragment : DaggerFragment() {
52+
companion object {
53+
private const val ARG_PARENT_ITEM_TYPE = "parentItemType"
54+
private const val ARG_PARENT_ITEM_ID = "parentItemId"
55+
private const val ARG_SITE_ID = "siteId"
56+
57+
fun newInstance(
58+
siteId: LocalId,
59+
parentItemId: Long,
60+
parentItemType: MetaDataParentItemType
61+
) = CustomFieldsFragment().apply {
62+
arguments = Bundle().apply {
63+
putInt(ARG_SITE_ID, siteId.value)
64+
putLong(ARG_PARENT_ITEM_ID, parentItemId)
65+
putString(ARG_PARENT_ITEM_TYPE, parentItemType.name)
66+
}
67+
}
68+
}
69+
70+
@Inject lateinit var metaDataStore: MetaDataStore
71+
72+
@Inject lateinit var siteStore: SiteStore
73+
74+
private val viewModel by lazy {
75+
CustomFieldsViewModel(
76+
coroutineScope = lifecycleScope,
77+
site = siteStore.getSiteByLocalId(requireArguments().getInt(ARG_SITE_ID))!!,
78+
parentItemId = requireArguments().getLong(ARG_PARENT_ITEM_ID),
79+
parentItemType = MetaDataParentItemType.valueOf(
80+
requireArguments().getString(ARG_PARENT_ITEM_TYPE)!!
81+
),
82+
metaDataStore = metaDataStore
83+
)
84+
}
85+
86+
override fun onCreateView(
87+
inflater: LayoutInflater,
88+
container: ViewGroup?,
89+
savedInstanceState: Bundle?
90+
): View {
91+
return ComposeView(requireContext()).apply {
92+
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
93+
94+
setContent {
95+
MaterialTheme {
96+
CustomFieldsScreen(viewModel.state)
97+
}
98+
}
99+
}
100+
}
101+
}
102+
103+
@Composable
104+
private fun CustomFieldsScreen(state: CustomFieldsState) {
105+
when (state) {
106+
CustomFieldsState.Loading -> CircularProgressIndicator(
107+
modifier = Modifier
108+
.fillMaxSize()
109+
.wrapContentSize()
110+
)
111+
112+
is CustomFieldsState.Error -> ErrorView(state)
113+
is CustomFieldsState.Loaded -> ContentView(state)
114+
}
115+
}
116+
117+
@Composable
118+
private fun ErrorView(state: CustomFieldsState.Error) {
119+
Column(
120+
horizontalAlignment = Alignment.CenterHorizontally,
121+
verticalArrangement = Arrangement.Center,
122+
modifier = Modifier.fillMaxSize()
123+
) {
124+
Text("An error occurred!")
125+
Text(state.message)
126+
Spacer(modifier = Modifier.height(16.dp))
127+
Button(state.onRetry) {
128+
Text("Retry")
129+
}
130+
}
131+
}
132+
133+
@Composable
134+
private fun ContentView(state: CustomFieldsState.Loaded) {
135+
var fieldBeingEdited by remember { mutableStateOf<WCMetaData?>(null) }
136+
137+
Column(
138+
horizontalAlignment = Alignment.CenterHorizontally,
139+
modifier = Modifier.padding(16.dp)
140+
) {
141+
Row(
142+
modifier = Modifier.align(Alignment.End),
143+
horizontalArrangement = Arrangement.spacedBy(8.dp)
144+
) {
145+
Button({ fieldBeingEdited = WCMetaData(id = 0L, "", "") }) {
146+
Text("Add")
147+
}
148+
Button(onClick = state.onSave, enabled = state.hasChanges) {
149+
Text("Save")
150+
}
151+
}
152+
153+
state.customFields.forEach { customField ->
154+
Row(
155+
verticalAlignment = Alignment.CenterVertically,
156+
horizontalArrangement = Arrangement.spacedBy(8.dp),
157+
modifier = Modifier.fillMaxWidth()
158+
) {
159+
Column {
160+
Text(
161+
text = customField.key,
162+
maxLines = 1,
163+
overflow = TextOverflow.Ellipsis,
164+
style = MaterialTheme.typography.subtitle1,
165+
fontWeight = FontWeight.Bold
166+
)
167+
168+
Text(
169+
text = customField.valueAsString,
170+
maxLines = 2,
171+
overflow = TextOverflow.Ellipsis,
172+
style = MaterialTheme.typography.body2
173+
)
174+
}
175+
Spacer(modifier = Modifier.weight(1f))
176+
IconButton({ fieldBeingEdited = customField }) {
177+
Icon(Icons.Default.Edit, contentDescription = "Edit")
178+
}
179+
IconButton({ state.onDelete(customField) }) {
180+
Icon(Icons.Default.Delete, contentDescription = "Delete")
181+
}
182+
}
183+
}
184+
}
185+
186+
if (fieldBeingEdited != null) {
187+
val field = fieldBeingEdited!!
188+
Dialog(onDismissRequest = { fieldBeingEdited = null }) {
189+
Column(
190+
modifier = Modifier
191+
.background(MaterialTheme.colors.surface)
192+
.padding(8.dp)
193+
) {
194+
Text("Edit field")
195+
Spacer(modifier = Modifier.height(16.dp))
196+
OutlinedTextField(
197+
label = { Text("Key") },
198+
value = field.key,
199+
onValueChange = {
200+
fieldBeingEdited = field.copy(key = it)
201+
},
202+
modifier = Modifier.fillMaxWidth()
203+
)
204+
OutlinedTextField(
205+
label = { Text("Value") },
206+
value = field.valueAsString,
207+
onValueChange = {
208+
fieldBeingEdited = field.copy(
209+
value = WCMetaDataValue.StringValue(it)
210+
)
211+
},
212+
modifier = Modifier.fillMaxWidth()
213+
)
214+
Button(
215+
onClick = {
216+
if (field.id == 0L) {
217+
state.onAdd(field)
218+
} else {
219+
state.onEdit(field)
220+
}
221+
fieldBeingEdited = null
222+
},
223+
modifier = Modifier.align(Alignment.End)
224+
) {
225+
Text("Done")
226+
}
227+
}
228+
}
229+
}
230+
}

0 commit comments

Comments
 (0)