Skip to content
Merged
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
6 changes: 6 additions & 0 deletions apps/teacher/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ dependencies {
/* Kotlin */
implementation Libs.KOTLIN_STD_LIB

/* Mobius */
implementation Libs.MOBIUS_CORE
testImplementation Libs.MOBIUS_TEST
implementation Libs.MOBIUS_ANDROID
implementation Libs.MOBIUS_EXTRAS

/* Crashlytics */
implementation Libs.FIREBASE_CORE
implementation(Libs.CRASHLYTICS) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2019 - present Instructure, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.instructure.teacher.mobius.common

import com.spotify.mobius.functions.Consumer
import java.util.*

class ConsumerQueueWrapper<T> : Consumer<T> {

private val queuedEvents = LinkedList<T>()

private var wrappedConsumer: Consumer<T>? = null

fun attach(consumer: Consumer<T>) {
wrappedConsumer = consumer
while (queuedEvents.isNotEmpty()) consumer.accept(queuedEvents.poll())
}

fun detach() {
wrappedConsumer = null
}

override fun accept(event: T) {
if (wrappedConsumer == null) {
queuedEvents.add(event)
} else {
wrappedConsumer?.accept(event)
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2019 - present Instructure, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.instructure.teacher.mobius.common

import com.spotify.mobius.Connection
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren
import kotlin.coroutines.CoroutineContext

abstract class CoroutineConnection<T> : Connection<T>, CoroutineScope {

private val job = SupervisorJob()

override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main

fun cancelCoroutine() {
coroutineContext.cancelChildren()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2019 - present Instructure, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.instructure.teacher.mobius.common

import com.spotify.mobius.EventSource
import com.spotify.mobius.disposables.Disposable
import com.spotify.mobius.functions.Consumer
import java.util.*


sealed class GlobalEvent {
data class TestEvent(val testValue: Int) : GlobalEvent()
}

object GlobalEvents {

val subscribers: MutableSet<GlobalEventSource<*>> = Collections.newSetFromMap(WeakHashMap<GlobalEventSource<*>, Boolean>())

fun post(event: GlobalEvent) {
publish(event)
}

private fun publish(event: GlobalEvent) {
synchronized(subscribers) {
subscribers.forEach { it.postEvent(event) }
}
}

fun subscribe(eventSource: GlobalEventSource<*>) {
synchronized(subscribers) { subscribers += eventSource }
}

fun unsubscribe(eventSource: GlobalEventSource<*>) {
synchronized(subscribers) { subscribers -= eventSource }
}
}

interface GlobalEventMapper<E> {
fun mapGlobalEvent(event: GlobalEvent): E? = null
}

class GlobalEventSource<E>(private val mapper: GlobalEventMapper<E>) : EventSource<E> {

private val queuedEvents = LinkedList<E>()

private var consumer: Consumer<E>? = null

override fun subscribe(eventConsumer: Consumer<E>): Disposable {
consumer = eventConsumer
GlobalEvents.subscribe(this)
while (queuedEvents.isNotEmpty()) {
consumer?.accept(queuedEvents.poll())
}
return Disposable { consumer = null }
}

fun dispose() {
consumer = null
GlobalEvents.unsubscribe(this)
}

fun postEvent(event: GlobalEvent) {
mapper.mapGlobalEvent(event)?.let { localEvent ->
if (consumer == null) {
queuedEvents.add(localEvent)
} else {
consumer?.accept(localEvent)
}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2019 - present Instructure, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.instructure.teacher.mobius.common

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

class LateInit<T>(val onInit: (T) -> T) : ReadWriteProperty<Any?, T> {
private var value: T? = null

override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw UninitializedPropertyAccessException()
}

override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = if (this.value == null) onInit(value) else value
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2019 - present Instructure, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.instructure.teacher.mobius.common

import android.content.Context
import com.spotify.mobius.Connectable
import com.spotify.mobius.Connection
import kotlin.reflect.KFunction2

fun <I, J, O> Connectable<I, O>.contraMap(
mapper: KFunction2<J, Context, I>,
context: Context
): Connectable<J, O> {
return Connectable { output ->
val delegateConnection = connect(output)
object : Connection<J> {
var lastValue: I? = null

override fun accept(value: J) {
val mappedValue: I = mapper(value, context)
// Only push value if it has changed (prevents duplicate renders)
if (mappedValue != lastValue) {
lastValue = mappedValue
delegateConnection.accept(mappedValue)
}
}

override fun dispose() = delegateConnection.dispose()
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2019 - present Instructure, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.instructure.teacher.mobius.common.modules

import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.ModuleItem
import com.instructure.canvasapi2.utils.DataResult

sealed class ModulesListEvent {
object PullToRefresh : ModulesListEvent()
data class ModuleClicked(val moduleItem: ModuleItem) : ModulesListEvent()
data class DataLoaded(val modulesResult: DataResult<ModuleItem>) : ModulesListEvent()
}

sealed class ModulesListEffect {
data class ShowModuleDetailView(val moduleItem: ModuleItem) : ModulesListEffect()
data class LoadData(val canvasContext: CanvasContext, val forceNetwork: Boolean) : ModulesListEffect()
}

data class ModulesListModel(
val course: CanvasContext,
val isLoading: Boolean = false,
val modulesResult: DataResult<ModuleItem>? = null
)
Loading