├── examples ├── search │ ├── .gitignore │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── values │ │ │ │ │ ├── colors.xml │ │ │ │ │ ├── styles.xml │ │ │ │ │ └── strings.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── drawable │ │ │ │ │ ├── ic_baseline_add_24.xml │ │ │ │ │ ├── ic_baseline_search_24.xml │ │ │ │ │ └── ic_launcher_background.xml │ │ │ │ ├── layout │ │ │ │ │ ├── location_item.xml │ │ │ │ │ └── search_activity.xml │ │ │ │ └── drawable-v24 │ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── kotlin │ │ │ │ └── composablearchitecture │ │ │ │ │ └── example │ │ │ │ │ └── search │ │ │ │ │ ├── SearchApp.kt │ │ │ │ │ ├── LocalDateAdapter.kt │ │ │ │ │ ├── SearchActivity.kt │ │ │ │ │ ├── SearchAdapter.kt │ │ │ │ │ ├── ComposeSearchActivity.kt │ │ │ │ │ ├── WeatherClient.kt │ │ │ │ │ └── Search.kt │ │ │ └── AndroidManifest.xml │ │ ├── androidTest │ │ │ └── kotlin │ │ │ │ └── composablearchitecture │ │ │ │ └── example │ │ │ │ └── search │ │ │ │ └── ExampleInstrumentedTest.kt │ │ └── test │ │ │ └── kotlin │ │ │ └── composablearchitecture │ │ │ └── example │ │ │ └── search │ │ │ ├── Mocks.kt │ │ │ ├── LiveStoreTests.kt │ │ │ └── SearchTests.kt │ ├── proguard-rules.pro │ └── build.gradle.kts ├── todos │ ├── .gitignore │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── strings.xml │ │ │ │ │ ├── colors.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── drawable │ │ │ │ │ ├── ic_baseline_add_24.xml │ │ │ │ │ └── ic_launcher_background.xml │ │ │ │ ├── menu │ │ │ │ │ └── todos_menu.xml │ │ │ │ ├── layout │ │ │ │ │ ├── todos_activity.xml │ │ │ │ │ └── todo_item.xml │ │ │ │ └── drawable-v24 │ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── kotlin │ │ │ │ └── composablearchitecture │ │ │ │ │ └── example │ │ │ │ │ └── todos │ │ │ │ │ ├── TodosApp.kt │ │ │ │ │ ├── TodosActivity.kt │ │ │ │ │ ├── TodoAdapter.kt │ │ │ │ │ └── Todos.kt │ │ │ └── AndroidManifest.xml │ │ ├── androidTest │ │ │ └── kotlin │ │ │ │ └── composablearchitecture │ │ │ │ └── example │ │ │ │ └── todos │ │ │ │ └── ExampleInstrumentedTest.kt │ │ └── test │ │ │ └── kotlin │ │ │ └── composablearchitecture │ │ │ └── example │ │ │ └── todos │ │ │ ├── TodosSandbox.kt │ │ │ └── TodosTest.kt │ ├── build.gradle.kts │ └── proguard-rules.pro ├── case-studies │ ├── .gitignore │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── strings.xml │ │ │ │ │ ├── colors.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── drawable │ │ │ │ │ ├── circle.xml │ │ │ │ │ └── ic_launcher_background.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── layout │ │ │ │ │ └── main_activity.xml │ │ │ │ └── drawable-v24 │ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── kotlin │ │ │ │ └── composablearchitecture │ │ │ │ │ └── example │ │ │ │ │ └── casestudies │ │ │ │ │ ├── CaseStudiesApp.kt │ │ │ │ │ └── AnimationActivity.kt │ │ │ └── AndroidManifest.xml │ │ └── androidTest │ │ │ └── kotlin │ │ │ └── composablearchitecture │ │ │ └── example │ │ │ └── casestudies │ │ │ └── ExampleInstrumentedTest.kt │ ├── build.gradle.kts │ └── proguard-rules.pro └── tic-tac-toe │ ├── .gitignore │ ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── drawable │ │ │ │ ├── ic_baseline_add_24.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── layout │ │ │ │ └── tictactoe_activity.xml │ │ ├── kotlin │ │ │ └── composablearchitecture │ │ │ │ └── example │ │ │ │ └── tictactoe │ │ │ │ ├── Array+Helpers.kt │ │ │ │ ├── TicTacToeApp.kt │ │ │ │ ├── TicTacToeActivity.kt │ │ │ │ └── TicTacToe.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── kotlin │ │ │ └── composablearchitecture │ │ │ └── example │ │ │ └── tictactoe │ │ │ ├── ExampleUnitTest.kt │ │ │ └── TicTacToeTest.kt │ └── androidTest │ │ └── kotlin │ │ └── composablearchitecture │ │ └── example │ │ └── tictactoe │ │ └── ExampleInstrumentedTest.kt │ ├── build.gradle.kts │ └── proguard-rules.pro ├── composable-architecture-android ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ └── composablearchitecture │ │ └── android │ │ └── ScopedViewModel.kt └── build.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── composable-architecture ├── src │ ├── main │ │ └── kotlin │ │ │ └── composablearchitecture │ │ │ ├── Helpers.kt │ │ │ ├── Reducer+Debug.kt │ │ │ ├── ArrowOptics+Helpers.kt │ │ │ ├── Effect.kt │ │ │ ├── Effect+Cancellation.kt │ │ │ ├── Store.kt │ │ │ └── Reducer.kt │ └── test │ │ └── kotlin │ │ └── composablearchitecture │ │ └── sandbox │ │ ├── optional │ │ └── Sandbox.kt │ │ └── Sandbox.kt └── build.gradle.kts ├── settings.gradle.kts ├── composable-architecture-test ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── composablearchitecture │ └── test │ ├── TestExecutorService.kt │ └── TestStore.kt ├── gradle.properties ├── LICENSE ├── .gitignore ├── gradlew.bat ├── README.md └── gradlew /examples/search/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /examples/todos/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /examples/case-studies/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /composable-architecture-android/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Tic Tac Toe 3 | 4 | -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Case Studies 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /composable-architecture-android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/todos/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Todos 3 | new todo 4 | 5 | -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/todos/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/todos/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/search/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/search/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/search/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/todos/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/todos/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/search/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/search/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/todos/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/case-studies/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/case-studies/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/search/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/search/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/tic-tac-toe/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/tic-tac-toe/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/tic-tac-toe/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/todos/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/todos/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/todos/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/case-studies/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/case-studies/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/case-studies/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/search/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/search/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/search/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/tic-tac-toe/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/tic-tac-toe/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/todos/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/todos/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/tic-tac-toe/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/tic-tac-toe/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/case-studies/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/case-studies/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/case-studies/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/case-studies/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/tic-tac-toe/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/tic-tac-toe/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/tic-tac-toe/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/todos/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("shared-android") 3 | } 4 | 5 | android { 6 | defaultConfig { 7 | applicationId = "composablearchitecture.example.todos" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wearemakery/kotlin-composable-architecture/HEAD/examples/case-studies/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/tic-tac-toe/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("shared-android") 3 | } 4 | 5 | android { 6 | defaultConfig { 7 | applicationId = "composablearchitecture.example.tictactoe" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/kotlin/composablearchitecture/example/tictactoe/Array+Helpers.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.tictactoe 2 | 3 | inline fun Array>.copy() = map { it.clone() }.toTypedArray() 4 | -------------------------------------------------------------------------------- /composable-architecture/src/main/kotlin/composablearchitecture/Helpers.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture 2 | 3 | fun Iterable.update(index: Int, elem: E) = 4 | mapIndexed { i, existing -> if (i == index) elem else existing } 5 | -------------------------------------------------------------------------------- /examples/case-studies/src/main/kotlin/composablearchitecture/example/casestudies/CaseStudiesApp.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.casestudies 2 | 3 | import android.app.Application 4 | 5 | class CaseStudiesApp : Application() 6 | -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/drawable/circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /examples/search/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | 7 | -------------------------------------------------------------------------------- /examples/todos/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | 7 | -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | 7 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | #BBBBBB 7 | 8 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | include( 2 | ":composable-architecture", 3 | ":composable-architecture-android", 4 | ":composable-architecture-test", 5 | ":examples:case-studies", 6 | ":examples:search", 7 | ":examples:tic-tac-toe", 8 | ":examples:todos" 9 | ) 10 | 11 | rootProject.name = "composable-architecture" 12 | -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/search/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/todos/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/case-studies/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("shared-android") 3 | } 4 | 5 | android { 6 | defaultConfig { 7 | applicationId = "composablearchitecture.example.casestudies" 8 | } 9 | } 10 | 11 | dependencies { 12 | implementation("androidx.dynamicanimation:dynamicanimation:$androidxDynamicAnimationVersion") 13 | } 14 | -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/test/kotlin/composablearchitecture/example/tictactoe/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.tictactoe 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | class ExampleUnitTest { 7 | 8 | @Test 9 | fun `Example test`() { 10 | assertEquals(2 + 2, 4) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/search/src/main/res/drawable/ic_baseline_add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/todos/src/main/res/drawable/ic_baseline_add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/drawable/ic_baseline_add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /composable-architecture-test/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kotlin") 3 | } 4 | 5 | dependencies { 6 | implementation("io.arrow-kt:arrow-optics:$arrowVersion") 7 | implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") 8 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion") 9 | implementation(project(":composable-architecture")) 10 | } 11 | -------------------------------------------------------------------------------- /composable-architecture/src/main/kotlin/composablearchitecture/Reducer+Debug.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture 2 | 3 | @Suppress("unused") 4 | fun Reducer.debug(): Reducer = 5 | Reducer { state, action, environment -> 6 | val result = run(state, action, environment) 7 | println("state=${result.state}, action=$action") 8 | result 9 | } 10 | -------------------------------------------------------------------------------- /examples/todos/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/search/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/search/src/main/kotlin/composablearchitecture/example/search/SearchApp.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.search 2 | 3 | import android.app.Application 4 | import composablearchitecture.Store 5 | 6 | @Suppress("unused") 7 | class SearchApp : Application() { 8 | 9 | companion object { 10 | val store = Store( 11 | SearchState(), 12 | searchReducer, 13 | SearchEnvironment() 14 | ) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/search/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Search 3 | This application demonstrates live-searching with the Composable Architecture. As you type the\nevents are debounced for 300ms, and when you stop typing an API request is made to load\nlocations. Then tapping on a location will load weather. 4 | New York, San Francisco, … 5 | Weather API provided by MetaWeather.com 6 | 7 | -------------------------------------------------------------------------------- /examples/todos/src/main/res/menu/todos_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /composable-architecture/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kotlin") 3 | id("kotlin-kapt") 4 | } 5 | 6 | dependencies { 7 | implementation("io.arrow-kt:arrow-optics:$arrowVersion") 8 | implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") 9 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") 10 | kaptTest("io.arrow-kt:arrow-meta:$arrowVersion") 11 | testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion") 12 | testImplementation(project(":composable-architecture-test")) 13 | } 14 | -------------------------------------------------------------------------------- /examples/search/src/main/res/drawable/ic_baseline_search_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/search/src/main/kotlin/composablearchitecture/example/search/LocalDateAdapter.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.search 2 | 3 | import com.squareup.moshi.FromJson 4 | import com.squareup.moshi.ToJson 5 | import java.time.LocalDate 6 | import java.time.format.DateTimeFormatter 7 | 8 | object LocalDateAdapter { 9 | 10 | private val formatter = DateTimeFormatter.ISO_LOCAL_DATE 11 | 12 | @FromJson 13 | fun fromJson(json: String): LocalDate = LocalDate.parse(json, formatter) 14 | 15 | @ToJson 16 | fun toJson(date: LocalDate): String = formatter.format(date) 17 | } 18 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/kotlin/composablearchitecture/example/tictactoe/TicTacToeApp.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.tictactoe 2 | 3 | import android.app.Application 4 | import composablearchitecture.Store 5 | 6 | @Suppress("unused") 7 | class TicTacToeApp : Application() { 8 | 9 | companion object { 10 | val gameStore = Store( 11 | initialState = GameState(), 12 | reducer = gameReducer, 13 | environment = GameEnvironment 14 | ) 15 | } 16 | 17 | override fun onCreate() { 18 | super.onCreate() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /composable-architecture-android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | id("kotlin-android") 4 | } 5 | 6 | android { 7 | compileSdkVersion(androidCompileSdkVersion) 8 | sourceSets["main"].java.srcDir("src/main/kotlin") 9 | } 10 | 11 | dependencies { 12 | implementation("androidx.lifecycle:lifecycle-livedata-ktx:$androidxLifecycleVersion") 13 | implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$androidxLifecycleVersion") 14 | implementation("io.arrow-kt:arrow-optics:$arrowVersion") 15 | implementation(project(":composable-architecture")) 16 | } 17 | -------------------------------------------------------------------------------- /examples/todos/src/androidTest/kotlin/composablearchitecture/example/todos/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.todos 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | @RunWith(AndroidJUnit4::class) 10 | class ExampleInstrumentedTest { 11 | @Test 12 | fun useAppContext() { 13 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 14 | assertEquals("composablearchitecture.example.todos", appContext.packageName) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/todos/src/main/kotlin/composablearchitecture/example/todos/TodosApp.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.todos 2 | 3 | import android.app.Application 4 | import composablearchitecture.Store 5 | import java.util.UUID 6 | 7 | @Suppress("unused") 8 | class TodosApp : Application() { 9 | 10 | companion object { 11 | lateinit var store: Store 12 | } 13 | 14 | override fun onCreate() { 15 | super.onCreate() 16 | store = Store( 17 | initialState = AppState(), 18 | reducer = appReducer, 19 | environment = AppEnvironment(uuid = { UUID.randomUUID() }) 20 | ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/search/src/androidTest/kotlin/composablearchitecture/example/search/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.search 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | @RunWith(AndroidJUnit4::class) 10 | class ExampleInstrumentedTest { 11 | @Test 12 | fun useAppContext() { 13 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 14 | assertEquals("composablearchitecture.example.search", appContext.packageName) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/androidTest/kotlin/composablearchitecture/example/tictactoe/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.tictactoe 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | @RunWith(AndroidJUnit4::class) 10 | class ExampleInstrumentedTest { 11 | @Test 12 | fun useAppContext() { 13 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 14 | assertEquals("composablearchitecture.example.tictactoe", appContext.packageName) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/case-studies/src/androidTest/kotlin/composablearchitecture/example/casestudies/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.casestudies 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | @RunWith(AndroidJUnit4::class) 10 | class ExampleInstrumentedTest { 11 | @Test 12 | fun useAppContext() { 13 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 14 | assertEquals("composablearchitecture.example.casestudies", appContext.packageName) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/layout/main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/search/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /examples/todos/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /examples/todos/src/main/res/layout/todos_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/case-studies/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Gradle 2 | org.gradle.jvmargs=-Xmx2048m 3 | 4 | # Gradle Kotlin 5 | kotlin.code.style=official 6 | 7 | # Gradle Android 8 | android.useAndroidX=true 9 | 10 | # Versions 11 | androidToolsBuildVersion=7.0.0 12 | androidxActivityVersion=1.3.0 13 | androidxAppcompatVersion=1.3.1 14 | androidxConstraintLayoutVersion=2.0.4 15 | androidxCoreVersion=1.6.0 16 | androidxDynamicAnimationVersion=1.0.0 17 | androidxEspressoVersion=3.4.0 18 | androidxJunitVersion=1.1.3 19 | androidxLifecycleVersion=2.3.1 20 | androidxRecyclerviewVersion=1.2.1 21 | arrowVersion=0.13.2 22 | coroutinesVersion=1.5.1 23 | junitVersion=4.13.2 24 | kotlinComposeVersion=1.0.0 25 | kotlinVersion=1.5.10 26 | moshiVersion=1.12.0 27 | okhttpVersion=4.9.1 28 | retrofitVersion=2.9.0 29 | 30 | # Android 31 | androidCompileSdkVersion=30 32 | androidMinSdkVersion=28 33 | androidTargetSdkVersion=30 34 | -------------------------------------------------------------------------------- /examples/todos/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /examples/tic-tac-toe/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /composable-architecture-test/src/main/kotlin/composablearchitecture/test/TestExecutorService.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.test 2 | 3 | import java.util.concurrent.AbstractExecutorService 4 | import java.util.concurrent.TimeUnit 5 | 6 | class TestExecutorService : AbstractExecutorService() { 7 | 8 | @Volatile 9 | private var terminated: Boolean = false 10 | 11 | override fun isTerminated(): Boolean = terminated 12 | 13 | override fun execute(command: Runnable) { 14 | command.run() 15 | } 16 | 17 | override fun shutdown() { 18 | terminated = true 19 | } 20 | 21 | override fun shutdownNow(): MutableList = mutableListOf() 22 | 23 | override fun isShutdown(): Boolean = terminated 24 | 25 | override fun awaitTermination(timeout: Long, timeUnit: TimeUnit): Boolean { 26 | shutdown() 27 | return terminated 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/case-studies/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /composable-architecture/src/main/kotlin/composablearchitecture/ArrowOptics+Helpers.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture 2 | 3 | import arrow.core.Either 4 | import arrow.core.left 5 | import arrow.core.right 6 | import arrow.optics.Lens 7 | import arrow.optics.Optional 8 | import arrow.optics.dsl.index 9 | import arrow.optics.typeclasses.Index 10 | 11 | private fun listIndex(): Index, Int, A> = object : Index, Int, A> { 12 | override fun index(i: Int): Optional, A> = object : Optional, A> { 13 | override fun getOrModify(source: List): Either, A> = 14 | source.getOrNull(i)?.right() ?: source.left() 15 | 16 | override fun set(source: List, focus: A): List = 17 | source.update(i, focus) 18 | } 19 | } 20 | 21 | @Suppress("UNCHECKED_CAST") 22 | fun Lens.listIndex(i: Int): Optional where S : List { 23 | val index: Index = listIndex() as Index 24 | return index(index, i) 25 | } 26 | -------------------------------------------------------------------------------- /examples/search/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Makery, Kft. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/search/src/test/kotlin/composablearchitecture/example/search/Mocks.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.search 2 | 3 | import arrow.core.Either 4 | import arrow.core.right 5 | import java.time.LocalDate 6 | 7 | val mockLocations = listOf( 8 | Location(1, "Brooklyn"), 9 | Location(2, "Los Angeles"), 10 | Location(3, "San Francisco") 11 | ) 12 | 13 | val mockWeather = listOf( 14 | ConsolidatedWeather( 15 | LocalDate.now(), 16 | 90.0, 17 | 70.0, 18 | 80.0, 19 | "Clear" 20 | ), 21 | ConsolidatedWeather( 22 | LocalDate.now().plusDays(1), 23 | 70.0, 24 | 50.0, 25 | 60.0, 26 | "Rain" 27 | ), 28 | ConsolidatedWeather( 29 | LocalDate.now().plusDays(2), 30 | 100.0, 31 | 80.0, 32 | 90.0, 33 | "Cloudy" 34 | ) 35 | ) 36 | 37 | class MockWeatherClient : WeatherClient { 38 | 39 | override var searchLocation: suspend (String) -> Either> = { 40 | Either.catch { mockLocations } 41 | } 42 | 43 | override var weather: suspend (Int) -> Either = { 44 | Either.catch { LocationWeather(mockWeather, 1) } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/todos/src/test/kotlin/composablearchitecture/example/todos/TodosSandbox.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.todos 2 | 3 | import composablearchitecture.Store 4 | import kotlinx.coroutines.flow.collect 5 | import kotlinx.coroutines.launch 6 | import kotlinx.coroutines.runBlocking 7 | import kotlinx.coroutines.test.TestCoroutineDispatcher 8 | import java.util.UUID 9 | 10 | val todos = listOf( 11 | Todo(id = UUID.fromString("DEADBEEF-DEAD-BEEF-DEAD-BEEDDEADBEEF")) 12 | ) 13 | 14 | fun main() { 15 | runBlocking { 16 | val dispatcher = TestCoroutineDispatcher() 17 | 18 | val store = Store( 19 | initialState = AppState(todos = todos), 20 | reducer = appReducer, 21 | environment = AppEnvironment(uuid = { UUID.randomUUID() }), 22 | mainDispatcher = dispatcher 23 | ) 24 | 25 | val job = launch(dispatcher) { store.states.collect { println(it) } } 26 | 27 | store.send( 28 | AppAction.Todo( 29 | UUID.fromString("DEADBEEF-DEAD-BEEF-DEAD-BEEDDEADBEEF"), 30 | TodoAction.TextFieldChanged("Buy milk") 31 | ) 32 | ) 33 | 34 | job.cancel() 35 | println("✅") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/search/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("shared-android") 3 | } 4 | 5 | android { 6 | defaultConfig { 7 | applicationId = "composablearchitecture.example.search" 8 | } 9 | @Suppress("UnstableApiUsage") 10 | buildFeatures { 11 | compose = true 12 | aidl = false 13 | renderScript = false 14 | shaders = false 15 | } 16 | composeOptions { 17 | kotlinCompilerExtensionVersion = kotlinComposeVersion 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation("androidx.activity:activity-compose:$kotlinComposeVersion") 23 | implementation("androidx.compose.foundation:foundation:$kotlinComposeVersion") 24 | implementation("androidx.compose.material:material:$kotlinComposeVersion") 25 | implementation("androidx.compose.ui:ui-tooling:$kotlinComposeVersion") 26 | implementation("androidx.compose.ui:ui:$kotlinComposeVersion") 27 | implementation("com.squareup.okhttp3:okhttp:$okhttpVersion") 28 | implementation("com.squareup.retrofit2:converter-moshi:$retrofitVersion") 29 | implementation("com.squareup.retrofit2:retrofit:$retrofitVersion") 30 | } 31 | 32 | configurations.all { 33 | resolutionStrategy.force("com.squareup.okhttp3:okhttp:$okhttpVersion") 34 | } 35 | -------------------------------------------------------------------------------- /composable-architecture-android/src/main/kotlin/composablearchitecture/android/ScopedViewModel.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.android 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import arrow.optics.Lens 7 | import arrow.optics.Prism 8 | import composablearchitecture.Store 9 | import kotlinx.coroutines.Job 10 | import kotlinx.coroutines.flow.collect 11 | import kotlinx.coroutines.launch 12 | 13 | open class ScopedViewModel : ViewModel() { 14 | 15 | val state: MutableLiveData = MutableLiveData() 16 | 17 | protected lateinit var store: Store 18 | 19 | fun launch( 20 | globalStore: Store, 21 | lens: Lens, 22 | prism: Prism 23 | ): Job { 24 | store = globalStore.scope(lens, prism, viewModelScope) 25 | return viewModelScope.launch { 26 | store.states.collect { state.value = it } 27 | } 28 | } 29 | 30 | fun launch(globalStore: Store): Job { 31 | store = globalStore 32 | return viewModelScope.launch { 33 | store.states.collect { state.value = it } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | # release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/ 42 | 43 | # Keystore files 44 | # Uncomment the following lines if you do not want to check your keystore files in. 45 | #*.jks 46 | #*.keystore 47 | 48 | # External native build folder generated in Android Studio 2.2 and later 49 | .externalNativeBuild 50 | .cxx/ 51 | 52 | # Google Services (e.g. APIs or Firebase) 53 | # google-services.json 54 | 55 | # Freeline 56 | freeline.py 57 | freeline/ 58 | freeline_project_description.json 59 | 60 | # fastlane 61 | fastlane/report.xml 62 | fastlane/Preview.html 63 | fastlane/screenshots 64 | fastlane/test_output 65 | fastlane/readme.md 66 | 67 | # Version control 68 | vcs.xml 69 | 70 | # lint 71 | lint/intermediates/ 72 | lint/generated/ 73 | lint/outputs/ 74 | lint/tmp/ 75 | # lint/reports/ 76 | -------------------------------------------------------------------------------- /examples/todos/src/main/res/layout/todo_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 17 | 18 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /composable-architecture/src/main/kotlin/composablearchitecture/Effect.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | import kotlinx.coroutines.flow.FlowCollector 5 | import kotlinx.coroutines.flow.emptyFlow 6 | import kotlinx.coroutines.flow.flattenConcat 7 | import kotlinx.coroutines.flow.flattenMerge 8 | import kotlinx.coroutines.flow.flow 9 | import kotlinx.coroutines.flow.flowOf 10 | import kotlinx.coroutines.flow.map 11 | import kotlinx.coroutines.flow.toList 12 | 13 | class Effect(internal var flow: Flow) { 14 | 15 | companion object { 16 | operator fun invoke( 17 | block: suspend FlowCollector.() -> Unit 18 | ): Effect = Effect(flow(block)) 19 | 20 | fun none() = Effect(emptyFlow()) 21 | } 22 | 23 | fun map(transform: (Output) -> T): Effect = Effect(flow.map { transform(it) }) 24 | 25 | fun concatenate(vararg effects: Effect) { 26 | flow = flowOf(flow, *effects.map { it.flow }.toTypedArray()).flattenConcat() 27 | } 28 | 29 | fun merge(vararg effects: Effect) { 30 | flow = flowOf(flow, *effects.map { it.flow }.toTypedArray()).flattenMerge() 31 | } 32 | 33 | suspend fun sink(): List { 34 | val outputs = mutableListOf() 35 | flow.toList(outputs) 36 | return outputs 37 | } 38 | } 39 | 40 | fun State.withNoEffect(): Result = 41 | Result(this, Effect.none()) 42 | 43 | fun State.withEffect( 44 | block: suspend FlowCollector.() -> Unit 45 | ): Result = 46 | Result(this, Effect(flow(block))) 47 | -------------------------------------------------------------------------------- /examples/search/src/main/res/layout/location_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 18 | 19 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 40 | 41 | 44 | 45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /examples/search/src/test/kotlin/composablearchitecture/example/search/LiveStoreTests.kt: -------------------------------------------------------------------------------- 1 | package composablearchitecture.example.search 2 | 3 | import composablearchitecture.Store 4 | import composablearchitecture.test.TestExecutorService 5 | import kotlinx.coroutines.runBlocking 6 | import kotlinx.coroutines.test.TestCoroutineDispatcher 7 | import org.junit.Assert.assertEquals 8 | import org.junit.Assert.assertNotEquals 9 | import org.junit.Test 10 | 11 | class LiveStoreTests { 12 | 13 | @Test 14 | fun `Test store with live weather client`() { 15 | val dispatcher = TestCoroutineDispatcher() 16 | 17 | val environment = SearchEnvironment(weatherClient = LiveWeatherClient(TestExecutorService())) 18 | 19 | val store = Store( 20 | SearchState(), 21 | searchReducer, 22 | environment, 23 | mainDispatcher = dispatcher 24 | ) 25 | 26 | runBlocking { 27 | store.send(SearchAction.SearchQueryChanged("San")) 28 | 29 | dispatcher.advanceTimeBy(300L) 30 | 31 | store.send(SearchAction.LocationTapped(store.currentState.locations[0])) 32 | 33 | println("weather=${store.currentState.locationWeather}") 34 | assertNotEquals(store.currentState.locationWeather, null) 35 | 36 | store.send(SearchAction.SearchQueryChanged("Sa")) 37 | store.send(SearchAction.SearchQueryChanged("S")) 38 | store.send(SearchAction.SearchQueryChanged("")) 39 | 40 | dispatcher.advanceTimeBy(300L) 41 | 42 | assertEquals(store.currentState.locations, emptyList()) 43 | assertEquals(store.currentState.locationWeather, null) 44 | 45 | (environment.weatherClient as? LiveWeatherClient)?.shutdown() 46 | 47 | println("✅") 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/search/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | 31 | -------------------------------------------------------------------------------- /examples/todos/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | 31 | -------------------------------------------------------------------------------- /examples/case-studies/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | 31 | -------------------------------------------------------------------------------- /examples/search/src/main/res/layout/search_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 19 | 20 | 28 | 29 | 35 | 36 |