https://kotlinlang.org logo
Join the conversationJoin Slack
Channels
100daysofcode
100daysofkotlin
100daysofkotlin-2021
advent-of-code
aem
ai
alexa
algeria
algolialibraries
amsterdam
android
android-architecture
android-databinding
android-studio
androidgithubprojects
androidthings
androidx
androidx-xprocessing
anime
anko
announcements
apollo-kotlin
appintro
arabic
argentina
arkenv
arksemdevteam
armenia
arrow
arrow-contributors
arrow-meta
ass
atlanta
atm17
atrium
austin
australia
austria
awesome-kotlin
ballast
bangladesh
barcelona
bayarea
bazel
beepiz-libraries
belgium
berlin
big-data
books
boston
brazil
brikk
budapest
build
build-tools
bulgaria
bydgoszcz
cambodia
canada
carrat
carrat-dev
carrat-feed
chicago
chile
china
chucker
cincinnati-user-group
cli
clikt
cloudfoundry
cn
cobalt
code-coverage
codeforces
codemash-precompiler
codereview
codingame
codingconventions
coimbatore
collaborations
colombia
colorado
communities
competitive-programming
competitivecoding
compiler
compose
compose-android
compose-desktop
compose-hiring
compose-ios
compose-mp
compose-ui-showcase
compose-wear
compose-web
connect-audit-events
corda
cork
coroutines
couchbase
coursera
croatia
cryptography
cscenter-course-2016
cucumber-bdd
cyprus
czech
dagger
data2viz
databinding
datascience
dckotlin
debugging
decompose
decouple
denmark
deprecated
detekt
detekt-hint
dev-core
dfw
docs-revamped
dokka
domain-driven-design
doodle
dsl
dublin
dutch
eap
eclipse
ecuador
edinburgh
education
effective-kotlin
effectivekotlin
emacs
embedded-kotlin
estatik
event21-community-content
events
exposed
failgood
fb-internal-demo
feed
firebase
flow
fluid-libraries
forkhandles
forum
fosdem
fp-in-kotlin
framework-elide
freenode
french
fritz2
fuchsia
functional
funktionale
gamedev
ge-kotlin
general-advice
georgia
geospatial
german-lang
getting-started
github-workflows-kt
glance
godot-kotlin
google-io
gradle
graphic
graphkool
graphql
graphql-kotlin
graviton-browser
greece
grpc
gsoc
gui
hackathons
hacktoberfest
hamburg
hamkrest
helios
helsinki
hexagon
hibernate
hikari-cp
hire-me
hiring
hongkong
hoplite
http4k
hungary
hyderabad
image-processing
india
indonesia
inkremental
intellij
intellij-plugins
intellij-tricks
internships
introduce-yourself
io
ios
iran
israel
istanbulcoders
italian
jackson-kotlin
jadx
japanese
jasync-sql
java-to-kotlin-refactoring
javadevelopers
javafx
javalin
javascript
jdbi
jhipster-kotlin
jobsworldwide
jpa
jshdq
juul-libraries
jvm-ir-backend-feedback
jxadapter
k2-early-adopters
kaal
kafka
kakao
kalasim
kapt
karachi
karg
karlsruhe
kash_shell
kaskade
kbuild
kdbc
kgen-doc-tools
kgraphql
kinta
klaxon
klock
kloudformation
kmdc
kmm-español
kmongo
knbt
knote
koalaql
koans
kobalt
kobweb
kodein
kodex
kohesive
koin
koin-dev
komapper
kondor-json
kong
kontent
kontributors
korau
korean
korge
korim
korio
korlibs
korte
kotest
kotest-contributors
kotless
kotlick
kotlin-asia
kotlin-beam
kotlin-by-example
kotlin-csv
kotlin-data-storage
kotlin-foundation
kotlin-fuel
kotlin-in-action
kotlin-inject
kotlin-latam
kotlin-logging
kotlin-multiplatform-contest
kotlin-mumbai
kotlin-native
kotlin-pakistan
kotlin-plugin
kotlin-pune
kotlin-roadmap
kotlin-samples
kotlin-sap
kotlin-serbia
kotlin-spark
kotlin-szeged
kotlin-website
kotlinacademy
kotlinbot
kotlinconf
kotlindl
kotlinforbeginners
kotlingforbeginners
kotlinlondon
kotlinmad
kotlinprogrammers
kotlinsu
kotlintest
kotlintest-devs
kotlintlv
kotlinultimatechallenge
kotlinx-datetime
kotlinx-files
kotlinx-html
kotrix
kotson
kovenant
kprompt
kraph
krawler
kroto-plus
ksp
ktcc
ktfmt
ktlint
ktor
ktp
kubed
kug-leads
kug-torino
kvision
kweb
lambdaworld_cadiz
lanark
language-evolution
language-proposals
latvia
leakcanary
leedskotlinusergroup
lets-have-fun
libgdx
libkgd
library-development
linkeddata
lithuania
london
losangeles
lottie
love
lychee
macedonia
machinelearningbawas
madrid
malaysia
mathematics
meetkotlin
memes
meta
metro-detroit
mexico
miami
micronaut
minnesota
minutest
mirror
mockk
moko
moldova
monsterpuzzle
montreal
moonbean
morocco
motionlayout
mpapt
mu
multiplatform
mumbai
munich
mvikotlin
mvrx
myndocs-oauth2-server
naming
navigation-architecture-component
nepal
new-mexico
new-zealand
newname
nigeria
nodejs
norway
npm-publish
nyc
oceania
ohio-kotlin-users
oldenburg
oolong
opensource
orbit-mvi
osgi
otpisani
package-search
pakistan
panamá
pattern-matching
pbandk
pdx
peru
philippines
phoenix
pinoy
pocketgitclient
polish
popkorn
portugal
practical-functional-programming
proguard
prozis-android-backup
pyhsikal
python
python-contributors
quasar
random
re
react
reaktive
realm
realworldkotlin
reductor
reduks
redux
redux-kotlin
refactoring-to-kotlin
reflect
refreshversions
reports
result
rethink
revolver
rhein-main
rocksdb
romania
room
rpi-pico
rsocket
russian
russian_feed
russian-kotlinasfirst
rx
rxjava
san-diego
science
scotland
scrcast
scrimage
script
scripting
seattle
serialization
server
sg-user-group
singapore
skia-wasm-interop-temp
skrape-it
slovak
snake
sofl-user-group
southafrica
spacemacs
spain
spanish
speaking
spek
spin
splitties
spotify-mobius
spring
spring-security
squarelibraries
stackoverflow
stacks
stayhungrystayfoolish
stdlib
stlouis
strife-discord-lib
strikt
students
stuttgart
sudan
swagger-gradle-codegen
swarm
sweden
swing
swiss-user-group
switzerland
talking-kotlin
tallinn
tampa
teamcity
tegal
tempe
tensorflow
terminal
test
testing
testtestest
texas
tgbotapi
thailand
tornadofx
touchlab-tools
training
tricity-kotlin-user-group
trójmiasto
truth
tunisia
turkey
turkiye
twitter-feed
uae
udacityindia
uk
ukrainian
uniflow
unkonf
uruguay
utah
uuid
vancouver
vankotlin
vertx
videos
vienna
vietnam
vim
vkug
vuejs
web-mpp
webassembly
webrtc
wimix_sentry
wwdc
zircon
Powered by Linen
flow
  • e

    expensivebelly

    07/21/2021, 11:17 AM
    Is there an equivalent of
    Observable.concatArrayEager(...)
    in Flow?
    n
    • 2
    • 3
  • a

    alexfacciorusso

    07/26/2021, 8:45 AM
    Hey all! I have been thinkering on this problem for a bit, and I think there’s something I might be missing. I have a data class, MyThing1, which has an ID in it. data class
    MyThing1(val id: String)
    I have another data class which we don’t care about its content, but let’s call it
    MyThing2
    I have a Flow, that returns a list of MyThing1
    fun myEmitter1(): Flow<List<MyThing1>>
    I have another Flow that takes in input an ID and returns a flow of objects, so
    fun myEmitter2(id: String): Flow<MyThing2>
    For each MyThing1 received from the flow, I want to combine it with the latest MyThing2 that gets emitted by
    myEmitter2
    and return a
    Flow<List<CombinedThing>>
    given the CombinedThing is
    data class CombinedThing(val myThing1: MyThing1, val myThing2: MyThing2)
    . Any thought for doing it efficiently?
    n
    • 2
    • 1
  • a

    Arpan Sarkar

    07/31/2021, 8:18 PM
    This class which basically does validation on input fields. Using
    LiveData
    to how can i convert the
    MediatorLiveData
    part to
    StateFlow
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MediatorLiveData
    import androidx.lifecycle.MutableLiveData
    
    abstract class InputFrom {
        private val _isFromValid = MediatorLiveData<Boolean>()
        val isFromValid: LiveData<Boolean>
            get() = _isFromValid
    
        abstract val fields: List<LiveInputField<*>>
    
        fun init() {
            fields.forEach { inputField ->
                _isFromValid.addSource(inputField.value) { changedValue ->
                    inputField.validate(changedValue)
                    _isFromValid.value = fields.all {
                        it.isValid
                    }
                }
            }
        }
    
        inner class LiveInputField<T>(
            private val errorMessage: String? = null,
            private val predicate: (T?) -> Boolean
        ) {
    
            val value = MutableLiveData<T>()
    
            private val _error = MutableLiveData<String>()
            val errorText: LiveData<String>
                get() = _error
    
            var isValid: Boolean = false
    
            fun validate(value: Any?) {
                @Suppress("UNCHECKED_CAST")
                return if (predicate(value as? T)) {
                    _error.value = null
                    isValid = true
                } else {
                    _error.value = errorMessage
                    isValid = false
                }
            }
        }
    }
    Example Usages:
    class LoginFrom : InputFrom() {
    
        val username =
            LiveInputField<String>("At least 4 characters, only letters, numbers, ., _ allowed.") {
                !it.isNullOrBlank() &&
                    Pattern.compile("[a-zA-Z0-9_.]{4,}")
                        .matcher(it)
                        .matches()
            }
    
        val password =
            LiveInputField<String>(
                "8 characters or more, at least one number, one uppercase, one lowercase."
            ) {
                !it.isNullOrBlank()/* &&
              Pattern.compile("((?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,})")
                  .matcher(it)
                  .matches()*/
            }
    
        override val fields: List<LiveInputField<*>>
            get() = listOf(
                username,
                password
            )
    }
    🧵 1
    n
    • 2
    • 1
  • j

    Justin Tullgren

    08/02/2021, 1:15 PM
    Hi, can anyone help me figure out how "cancel" an action inside a flow based on incoming values? Ex:
    root flow: OperationStart ------ AnotherOperation ------ OperationCancel -------- AnotherBOperation
    next flow: OperationStart, perform long running action while !OperationCancel
    n
    • 2
    • 15
  • m

    Marcin Wisniowski

    08/11/2021, 10:12 PM
    Hello, I'm looking for a combination of
    debounce()
    and
    distinctUntilChanged()
    . I want to debounce elements if they are the same. So
    A B B C D
    in quick succession will become
    A B C D
    . I can't just use
    distinctUntilChanged()
    , because I want to allow sending the same element twice, as long as it's not in quick succession. How would I achieve that?
    f
    • 2
    • 1
  • c

    CLOVIS

    08/22/2021, 2:48 PM
    What is the correct way to unsubscribe from a Flow?
    val flow = myFlow()
    flow.collect {
      if (it is Success) {
        // I found the value I was waiting for,
        // stop collecting values and go back to executing the function
      }
    }
    d
    e
    • 3
    • 7
  • m

    Marek Kubiczek

    08/23/2021, 3:46 PM
    Considering following code snippet
    viewLifecycleOwner.lifecycleScope.launch {
      val result = someFlow.first()
      doSomethingInUI(result)
    }
    where
    doSomethingInUI
    is not a
    suspend
    function. What will happen if scope is destroyed/cancelled while there is ongoing operation
    someFlow.first()
    (eg. database read). Is
    first()
    cancellable? Will it exit the launch block gracefully or do I need to always call
    ensureActive()
    before a call to non suspended function in such case? The same stands for
    someFlow.onEach {
      doSomnethinginUI(it)
    }.launchIn(viewLifecycleOwner.lifecycleScope)
    Is there any guarantee
    onEach
    won’t be called after scope is destroyed/cancelled?
    e
    • 2
    • 10
  • c

    Chris Fillmore

    08/25/2021, 6:24 PM
    Any advice on how to approach this problem? I want to use
    MutableStateFlow.compareAndSet()
    , but with a `sealed interface`:
    sealed interface State {
      object NotConnected : State
      class Connected(val connection: SomeConnection) : State
    }
    
    val stateFlow = MutableStateFlow<State>(State.NotConnected)
    ...
    // Obviously doesn't work, can't use State.Connected in this way
    stateFlow.compareAndSet(State.Connected, State.NotConnected)
    • 1
    • 1
  • d

    darkmoon_uk

    08/26/2021, 7:55 AM
    I'm sure there used to be a
    .filterNotNull()
    Flow
    operator
    🤔 where did it go? Edit: IDE autocomplete fail. Explicitly importing it worked.
    w
    • 2
    • 1
  • s

    Semyon Zadorozhnyi

    08/26/2021, 12:44 PM
    Hi all! I’m trying to implement so kind of event bus repository using SharedFlow Long story short: System can send events to TriggerRepository via sendEvent method Any component in the system should have an ability to observe trigger. Trigger should fire when there is sequence of events in specific order emmited. I’m struggling with transformation step where I should somehow collect sequence of events and call the trigger. Code snippet and example in thread. Any form of help is appreciated 🙂
    i
    n
    • 3
    • 4
  • c

    Chris Fillmore

    08/30/2021, 5:32 PM
    Question: Is there a variant of
    MutableStateFlow.compareAndSet(expect, update)
    where
    update
    is evaluated lazily?
    e
    • 2
    • 3
  • a

    Alex

    08/31/2021, 10:39 AM
    Why can't we
    .map
    a
    StateFlow
    and have it still be a
    StateFlow
    ? Why the conversion to a regular
    Flow
    ?
    c
    c
    • 3
    • 4
  • c

    Chris Fillmore

    09/01/2021, 1:06 AM
    Has anyone observed any performance impact from accessing
    StateFlow.value
    very often? For example, every 15-30 ms. I realize I could just keep a reference, but I worry about it getting stale. Thanks for any insight
    w
    • 2
    • 1
  • a

    Alexander Black

    09/02/2021, 12:29 AM
    Hey all… I keep wrestling with this general problem… and I’m hoping someone can point me in the right direction. I think it’s just my lack of understanding. If I do something like this:
    val scope = MainScope() + Job()
    val scope2 = MainScope() + Job()
    
    
    val someEmitter = MutableStateFlow("")
    
    fun test3()= runBlocking {
        scope.launch {
            someEmitter.emit("1")
            someEmitter.emit("2")
            someEmitter.emit("3")
        }
        scope2.launch {
            someEmitter.collect {
                println(it)
            }
        }
    }
    why does scope2 collect operation get blocked by scope? do they have to be on different threads? Am I missing something? I’ve gotten this type of thing to work before, but only by using another thread as the collector.
    l
    w
    r
    • 4
    • 10
  • m

    Marc Knaup

    09/10/2021, 6:25 PM
    For 
    Flow.buffer()
     there’s 
    BufferOverflow
     
    .SUSPEND
    , 
    .DROP_OLDEST
     and 
    .DROP_LATEST
    . Is there a way to fail if the buffer is full and stop the collector?
    s
    • 2
    • 1
  • s

    Simon Lin

    09/14/2021, 3:47 AM
    Are those correct?
    Instead of MutableLiveData with default value to MutableStateFlow
    Instead of MutableLiveData without default value to MutableSharedFlow
    Instead of SingleLiveEvent to Channel.receiveAsFlow
    s
    • 2
    • 1
  • m

    Max Kachinkin

    09/15/2021, 5:07 PM
    Hello everyone! 👋 Is there some best practice for using flow to get only one item? Like alternative Single in Rx, when I guaranteed want to get single value and not ready to process stream.
    w
    p
    • 3
    • 9
  • e

    edenman

    09/17/2021, 2:10 AM
    is there an equivalent to
    flowOn
    that takes a
    CoroutineScope
    ? i basically want a way to have a
    flow { }
    block that runs inside an existing scope
    e
    • 2
    • 5
  • c

    Chris Fillmore

    09/21/2021, 3:28 PM
    Is there something like this
    pipe
    function (which I just made up) available in the coroutines library? (Sends emissions from one
    Flow
    to another
    FlowCollector
    ) :
    suspend fun <T, U> Flow<T>.pipe(destination: FlowCollector<U>, transform: (T) -> U) {
      collect { 
        destination.emit(transform(it))
      }
    }
    It feels so obvious but I’ve been looking at the available extension functions and haven’t spotted it. Thanks!
    d
    • 2
    • 3
  • h

    Hank

    09/24/2021, 2:03 AM
    Hi guys, can anyone tell me what's the difference between sharedflow and statedflow? I read the post on Android Developer but still didn't get it.
    b
    t
    • 3
    • 3
  • s

    Simon Lin

    09/29/2021, 3:59 AM
    How can I use
    CoroutineExceptionHandler
    in flow? for example
    val flow = flow { emit("data") }
        .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
    b
    • 2
    • 1
  • c

    Chris Fillmore

    10/04/2021, 9:00 PM
    Am I doing the right thing with these nested state flows? For example:
    data class MyDomainObject(
      val id: String,
      val enabled: StateFlow<Boolean>
    )
    
    val myDomainObjects: StateFlow<List<MyDomainObject> = /* This data comes from somewhere */
    
    // I would like a list of objects which have `enabled.value == true`, and
    // I would like this to be updated any time any `enabled` state changes
    val enabledObjects: Flow<List<MyDomainObject> = myDomainObjects.transform { objects ->
      combine(*objects.map { it.enabled }.toTypedArray()) { enabledStates ->
        emit(objects.filterIndexed { index, _ -> enabledStates[index] })
      }
    }
    The implementation seems slightly wrong, the way I’m using
    index
    to emit objects which are enabled. But I don’t see another way to do this. Any advice? Thanks in advance.
    e
    • 2
    • 7
  • m

    Mohamed Ibrahim

    10/07/2021, 1:38 PM
    if I have this code
    private fun SearchScreenEvent.eventToUsecase(): Flow<SearchState> {
        return when (this) {
            is SearchClicked -> searchUsecase(this.query)
            is SearchQueryChanged ->
                flowOf(this.query)
                    .debounce(5000)
                    .flatMapConcat { searchUsecase(this.query) }
        }
    }
    why debounce is not working here
    m
    e
    • 3
    • 8
  • m

    Matti MK

    10/07/2021, 1:45 PM
    private val networkResponse = onContinueClicked.filter { it }.zip(
            combine(
                email.filter { it.count() >= MIN_INPUT_LENGTH },
                password.filter { it.count() >= MIN_INPUT_LENGTH })
            { e, p -> Pair(e, p) }
        ) { _, cred ->
            val (user, pass) = cred
            repository.login(username = user, password = pass)
        }
    I’m playing around with flows: aim is to do a simple login network request.
    email
    and
    password
    are
    MutableStateFlow
    , this works fine except I’d like to emit a
    Loading
    value before the second to last line. The
    login
    call does not return a flow. I think I’m missing the use of the correct operator here in place of
    zip
    or I should have my repository return a flow (and
    startWith
    a given state in the repo).
    • 1
    • 1
  • p

    parth

    10/13/2021, 2:44 PM
    hey all — i have a pretty common construction where i’m in an Android ViewModel and need to observe a Flow, do some stuff, and then push the result into a 
    MutableStateFlow
    . Looks something like this:
    fun whatever() { 
    	viewModelScope.launch { 
    		inputFlow
    			.map {...}
    			.collect { value -> 
    				mutableStateFlow.value = value
    			}
    	}	
    }
    (Assume that I need to push other values into the 
    MSF
     outside this 
    collect
     block) One issue I’ve run into with the above construction is that when the observer of the
    MSF
    cancels collection (e.g. the Fragment observing the ViewModel falls off the screen), it doesn’t cancel this intermediate collection…this makes absolute sense — that cancellation signal is not being propagated upstream! So digging into the
    stateIn
    sources, I’ve come up with what I think is a way to push the cancellation “upstream” — can y’all give thoughts/comments/suggestions on the following approach?
    fun <T> Flow<T>.stateInto(downstream: MutableStateFlow<T>, scope: CoroutineScope) {
        val upstream = this
        scope.launch {
            downstream.subscriptionCount
                .mapLatest { count -> //this is a simplified version of [StartedWhileSubscribed#command]
                    if (count > 0) true
                    else {
                        delay(800.milliseconds)
                        false
                    }
                }
                .dropWhile { active -> !active }
                .distinctUntilChanged()
                .collectLatest { active ->
                    when (active) {
                        true -> upstream.collect(downstream) //will be cancelled upon new emission
                        false -> Unit /*just cancel and do nothing*/ 
                    }
                }
        }
    }
    d
    n
    • 3
    • 14
  • d

    dephinera

    10/25/2021, 9:25 AM
    Instead of LiveData, I'm using StateFlow in my ViewModel to emit values to the UI. I realized that StateFlow doesn't emit equal values. How do you approach that? What if you want to emit multiple times the same value to show some message pop-up/toast/smth?
    b
    j
    k
    • 4
    • 16
  • a

    althaf

    11/01/2021, 2:45 PM
    Hi i'm having a hard time converting this RxJS code to Kotlin flow Some how in java script after the buffer we are getting the stream events as array , however in flow i'm not receiving it has an array of events. Is this because buffer() behaves differently from bufferWhen in RxJS ?
    var button = document.querySelector('.button');
    var label = document.querySelector('h4');
    var clickStream = Rx.Observable.fromEvent(button, 'click');
    var doubleClickStream = clickStream
      
    .bufferWhen(() => clickStream.debounceTime(250))
      
    .map(arr => arr.length)
      
    .filter(len => len === 2);
    doubleClickStream.subscribe(event => {
      
    label.textContent = 'double click';
    });
    doubleClickStream
      
    .delay(1000)
      
    .subscribe(suggestion => {
        
    label.textContent = '-';
      
    });
    n
    • 2
    • 2
  • d

    Diego

    11/03/2021, 8:18 PM
    Based on a list of ids I need to retrieve a list of users with detailed informaiton of each user. Below a simplification of my models and API;
    data class UserDTO(val id: String)
        data class PetDTO(val name: String)
        
        interface Api {
          suspend fun users(): List<UserDTO>
          suspend fun pets(userId: String): List<PetDTO>
        }
        
        data class User(
          val id: String,
          val pets: List<Pet>
        )
        
        data class Pet(val name: String)
        
        class UsersRepository(api: Api) {
          val users: Flow<List<User>> = TODO()
        }
    In RxJava I would do something like this:
    val users: Observable<List<User>> = api.users()
          .flatMapIterable { it }
          .concatMap { userDto ->
            api.pets(userDto.id)
              .map { petsListDto ->
                User(userDto.id, petsListDto.map { Pet(it.name) })
              }
          }.toList()
    How can I implement
    UsersRepository
    and return the list of `User`s using Kotlin Flow?
    n
    g
    • 3
    • 11
  • d

    Daniel Ryan

    11/08/2021, 12:30 PM
    Newbie question: when querying a database using a reactive driver, what are the advantages and differences in behaviour when returning a flow from a repository function, as opposed to returning a list from a suspend function?
    k
    • 2
    • 5
  • d

    darkmoon_uk

    11/09/2021, 2:02 AM
    I noticed during testing that within a
    callbackFlow { ... }
    if we throw a plain
    Exception
    this halts the VM immediately, and is not caught as an in-
    Flow
    `.catch`able error, while throwing a
    RuntimeException
    is `.catch`able. While this isn't so surprising given the conventional relationship between
    Exception
    and
    RuntimeException
    , I can't find this documented in respect to Flows - should it be? Is it?
    e
    • 2
    • 3
Powered by Linen
Title
d

darkmoon_uk

11/09/2021, 2:02 AM
I noticed during testing that within a
callbackFlow { ... }
if we throw a plain
Exception
this halts the VM immediately, and is not caught as an in-
Flow
`.catch`able error, while throwing a
RuntimeException
is `.catch`able. While this isn't so surprising given the conventional relationship between
Exception
and
RuntimeException
, I can't find this documented in respect to Flows - should it be? Is it?
e

ephemient

11/09/2021, 2:17 AM
on playground, the ordering of send vs catch seems non-deterministic but catch always seems to work: https://pl.kotl.in/kihB7LrBS
d

darkmoon_uk

11/09/2021, 2:46 AM
Thanks @ephemient, nice use of playground too 👍
The difference must in our code, this helps.
View count: 7