Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Hack Week] ApiFaker: Home screen and integration with the app #13064

Open
wants to merge 15 commits into
base: task/api-faker-1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions WooCommerce/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ dependencies {
implementation(libs.google.play.services.wearable)

// Debug dependencies
debugImplementation project(":libs:apifaker")
debugImplementation(libs.facebook.flipper.main)
debugImplementation(libs.facebook.soloader)
debugImplementation(libs.facebook.flipper.network.plugin) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.woocommerce.android.apifaker

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.navigation.findNavController
import com.woocommerce.android.apifaker.ui.ApiFakerNavHost
import com.woocommerce.android.ui.base.BaseFragment
import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground
import com.woocommerce.android.ui.main.AppBarStatus

class ApiFakerHostFragment : BaseFragment() {
override val activityAppBarStatus: AppBarStatus
get() = AppBarStatus.Hidden

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)

setContent {
WooThemeWithBackground {
ApiFakerNavHost(
onExit = { findNavController().navigateUp() }
)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.woocommerce.android.R
import com.woocommerce.android.ui.base.BaseFragment
Expand Down Expand Up @@ -46,6 +47,12 @@ class DeveloperOptionsFragment : BaseFragment() {
selectedValue = event.selectedValue
)
}

is DeveloperOptionsViewModel.DeveloperOptionsEvents.OpenApiFaker -> {
findNavController().navigate(
DeveloperOptionsFragmentDirections.actionDeveloperOptionsFragmentToApiFaker()
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.woocommerce.android.viewmodel.MultiLiveEvent
import com.woocommerce.android.viewmodel.ScopedViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand Down Expand Up @@ -81,11 +82,25 @@ class DeveloperOptionsViewModel @Inject constructor(
)
}

private val apiFakerFlow = flowOf(
NonToggleableListItem(
icon = R.drawable.ic_globe,
iconTint = R.color.color_primary,
label = UiString.UiStringText("API Faker"),
key = UiString.UiStringText("API Faker"),
isEnabled = true,
onClick = {
triggerEvent(DeveloperOptionsEvents.OpenApiFaker)
}
)
)

val viewState = combine(
simulatedCardReaderFlow,
readerUpdateFrequencyFlow,
interacPaymentEnabledFlow,
savedPrivacySettingsOnDialogFlow
savedPrivacySettingsOnDialogFlow,
apiFakerFlow
) { items ->
DeveloperOptionsViewState(
rows = items.filterNotNull()
Expand Down Expand Up @@ -127,6 +142,8 @@ class DeveloperOptionsViewModel @Inject constructor(
val options: List<UpdateFrequencyUiModel>,
var selectedValue: UpdateFrequencyUiModel,
) : DeveloperOptionsEvents()

data object OpenApiFaker : DeveloperOptionsEvents()
}

data class DeveloperOptionsViewState(
Expand Down
11 changes: 10 additions & 1 deletion WooCommerce/src/main/res/navigation/nav_graph_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<include app:graph="@navigation/nav_graph_jetpack_install" />
<include app:graph="@navigation/nav_graph_domain_change" />
<include app:graph="@navigation/nav_graph_themes" />

<fragment
android:id="@+id/mainSettingsFragment"
android:name="com.woocommerce.android.ui.prefs.MainSettingsFragment"
Expand Down Expand Up @@ -169,7 +170,11 @@
<fragment
android:id="@+id/developerOptionsFragment"
android:name="com.woocommerce.android.ui.prefs.developer.DeveloperOptionsFragment"
android:label="DeveloperOptionsFragment" />
android:label="DeveloperOptionsFragment">
<action
android:id="@+id/action_developerOptionsFragment_to_ApiFaker"
app:destination="@id/apiFakerHostFragment" />
</fragment>
<fragment
android:id="@+id/accountSettingsFragment"
android:name="com.woocommerce.android.ui.prefs.account.AccountSettingsFragment"
Expand Down Expand Up @@ -199,4 +204,8 @@
android:id="@+id/pluginsFragment"
android:name="com.woocommerce.android.ui.prefs.plugins.PluginsFragment"
android:label="PluginsFragment" />
<fragment
android:id="@+id/apiFakerHostFragment"
android:name="com.woocommerce.android.apifaker.ApiFakerHostFragment"
android:label="ApiFakerHostFragment" />
</navigation>
1 change: 0 additions & 1 deletion WooCommerce/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2558,7 +2558,6 @@
<string name="enable_interac_payment" translatable="false">Enable Interac Payment</string>
<string name="enable_interac_key" translatable="false">Enable Interac Key</string>


<!--
WCToggleSingleOptionView
-->
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ androidx-hilt-navigation-fragment = { group = "androidx.hilt", name = "hilt-navi
androidx-hilt-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "androidx-hilt" }
androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "androidx-lifecycle" }
androidx-lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewmodel-savedstate = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-savedstate", version.ref = "androidx-lifecycle" }
androidx-navigation-common = { group = "androidx.navigation", name = "navigation-common", version.ref = "androidx-navigation" }
Expand Down
7 changes: 7 additions & 0 deletions libs/apifaker/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,15 @@ android {
}

dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.lifecycle.runtime.compose)

implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.material.main)
implementation(libs.androidx.compose.runtime.livedata)
implementation(libs.androidx.compose.ui.tooling.preview)
debugImplementation(libs.androidx.compose.ui.tooling.main)

implementation("${gradle.ext.fluxCBinaryPath}:${libs.versions.wordpress.fluxc.get()}") {
exclude group: "com.android.support"
Expand All @@ -64,6 +70,7 @@ dependencies {
ksp(libs.androidx.room.compiler)

implementation(libs.google.dagger.hilt.android.main)
implementation(libs.androidx.hilt.navigation.compose)
ksp(libs.google.dagger.hilt.compiler)

testImplementation(libs.junit)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.woocommerce.android.apifaker

import android.content.Context
import android.content.SharedPreferences
import com.woocommerce.android.apifaker.db.EndpointDao
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
import javax.inject.Singleton

private const val PREF_FILE_NAME = "api_faker"
private const val PREFERENCE_KEY = "api_faker_enabled"

@Singleton
internal class ApiFakerConfig @Inject constructor(
context: Context,
endpointDao: EndpointDao
) {
private val configScope = CoroutineScope(Dispatchers.Main)
private val preferences = context.getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE)

private val prefFlow = preferences.prefFlow(PREFERENCE_KEY, false)

val enabled = combine(
prefFlow,
endpointDao.observeEndpointsCount().map { it == 0 }
) { pref, isEmpty ->
pref && !isEmpty
}.stateIn(configScope, SharingStarted.Eagerly, false)

fun setStatus(enabled: Boolean) {
preferences.edit().putBoolean(PREFERENCE_KEY, enabled).apply()
}

private fun SharedPreferences.prefFlow(key: String, defaultValue: Boolean) = callbackFlow {
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, listenerKey ->
if (listenerKey == key) {
trySend(getBoolean(key, defaultValue))
}
}
registerOnSharedPreferenceChangeListener(listener)
trySend(getBoolean(key, defaultValue))
awaitClose { unregisterOnSharedPreferenceChangeListener(listener) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ internal interface EndpointDao {
fun observeEndpoints(): Flow<List<MockedEndpoint>>

@Query("Select COUNT(*) FROM Request")
suspend fun endpointsCount(): Int
fun observeEndpointsCount(): Flow<Int>

@Transaction
@Query(
Expand Down Expand Up @@ -51,6 +51,4 @@ internal interface EndpointDao {
val id = insertRequest(request)
insertResponse(response.copy(endpointId = id))
}

suspend fun isEmpty() = endpointsCount() == 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.woocommerce.android.apifaker.ui

import androidx.compose.runtime.Composable
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.woocommerce.android.apifaker.ui.Screen.EndpointDetails
import com.woocommerce.android.apifaker.ui.home.HomeScreen

@Composable
fun ApiFakerNavHost(
onExit: () -> Unit
) {
val navController = rememberNavController()

NavHost(
navController = navController,
startDestination = Screen.Home.route()
) {
composable(Screen.Home.route()) {
HomeScreen(viewModel = hiltViewModel(), navController = navController, onExit = onExit)
}
composable(
EndpointDetails.baseRoute,
arguments = listOf(
navArgument("endpointId") {
type = NavType.LongType
defaultValue = -1
}
)
) {
TODO()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.woocommerce.android.apifaker.ui

internal sealed class Screen(val baseRoute: String) {
object Home : Screen("home") {
fun route() = baseRoute
}

object EndpointDetails : Screen("/endpoint-details") {
fun route(endpointId: Long) = "$baseRoute/$endpointId"

fun routeForCreation() = baseRoute
}
}
Loading
Loading