Skip to content

Commit

Permalink
prototype on how ui state/controller could work
Browse files Browse the repository at this point in the history
  • Loading branch information
yigit committed Sep 20, 2020
1 parent e773021 commit 5bd4e27
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.dropbox.android.sample.ui.uistate

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.dropbox.android.external.store4.Fetcher
import com.dropbox.android.external.store4.StoreBuilder
import com.dropbox.android.external.store4.StoreRequest
import com.dropbox.android.external.store4.UIController
import com.dropbox.android.sample.R
import kotlinx.android.synthetic.main.ui_state.buttonRefresh
import kotlinx.android.synthetic.main.ui_state.loadingInfo
import kotlinx.android.synthetic.main.ui_state.streamValue
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow


class UIStateFragment : Fragment() {
val store = StoreBuilder.from<Int, Int>(
fetcher = Fetcher.ofFlow {key:Int ->
flow {
var latest = key
while(true) {
delay(2000)
emit(latest)
latest += 2

}
}
}
).build()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.ui_state, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val controller = UIController(
store = store,
request = StoreRequest.fresh(3)
)
buttonRefresh.setOnClickListener {
controller.refresh()
}
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
controller.state.collect {
streamValue.text = "${it.data?.dataOrNull()}"
loadingInfo.text = "loading: ${it.isLoading()}"
}
}
}
}
20 changes: 20 additions & 0 deletions app/src/main/res/layout/ui_state.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<Button
android:id="@+id/buttonRefresh"
android:text="refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/streamValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/loadingInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

</LinearLayout>
4 changes: 4 additions & 0 deletions app/src/main/res/menu/bottom_navigation_menu.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@
android:id="@+id/streamFragment"
android:icon="@drawable/ic_distribution"
android:title="@string/bottom_navigation_menu_title_stream" />
<item
android:id="@+id/uiStateFragment"
android:icon="@drawable/ic_distribution"
android:title="@string/bottom_navigation_menu_title_uistate" />
</menu>
6 changes: 5 additions & 1 deletion app/src/main/res/navigation/main_nav_graph.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_nav_graph"
app:startDestination="@id/redditFragment">
app:startDestination="@id/uiStateFragment">

<fragment
android:id="@+id/redditFragment"
Expand All @@ -16,4 +16,8 @@
android:id="@+id/streamFragment"
android:name="com.dropbox.android.sample.ui.stream.StreamFragment"
android:label="StreamFragment" />
<fragment
android:id="@+id/uiStateFragment"
android:name="com.dropbox.android.sample.ui.uistate.UIStateFragment"
android:label="UIStateFragment" />
</navigation>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
<string name="bottom_navigation_menu_title_reddit">Reddit</string>
<string name="bottom_navigation_menu_title_stream">Stream</string>
<string name="bottom_navigation_menu_title_room">Room</string>
<string name="bottom_navigation_menu_title_uistate">UIState</string>
</resources>
71 changes: 71 additions & 0 deletions store/src/main/java/com/dropbox/android/external/store4/UIState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.dropbox.android.external.store4

import kotlinx.coroutines.channels.ConflatedBroadcastChannel
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.fold
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest

data class UIState<T>(
val loading: StoreResponse.Loading? = null,
val data: StoreResponse.Data<T>? = null,
val error: StoreResponse.Error? = null
) {
fun isLoading() = loading != null
internal fun combine(response: StoreResponse<T>) : UIState<T> {
return when(response) {
is StoreResponse.Loading -> {
copy(
loading = response,
error = null
)
}
is StoreResponse.Data -> {
copy(
loading = null,
data = response,
error = null
)
}
is StoreResponse.Error -> {
copy(
error = response,
loading = null
)
}
is StoreResponse.NoNewData -> {
copy(
loading = null
)
}
}
}
}

class UIController<Key : Any, Output : Any>(
val store: Store<Key, Output>,
val request: StoreRequest<Key>
) {
// use mutable state flow instead when we can
private val refreshTrigger = ConflatedBroadcastChannel<Unit>().also {
it.offer(Unit)
}


fun refresh() = refreshTrigger.offer(Unit)
val state = flow<UIState<Output>> {
var prevState : UIState<Output> = UIState()
emitAll(refreshTrigger.asFlow().flatMapLatest {
store.stream(request)
}.map {
prevState.combine(it).also {
prevState = it
}
prevState
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.dropbox.android.external.store4.impl

/**
* Internal helper class to help with fallback logic from Fetcher to local data
*/
internal class FallbackController {

}

0 comments on commit 5bd4e27

Please sign in to comment.