├── .github └── workflows │ ├── documentation.yml │ ├── lint_and_tests.yml │ ├── publish_release.yml │ └── publish_snapshot.yml ├── .gitignore ├── .nojekyll ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── androidApp ├── build.gradle.kts ├── debug.keystore ├── sampledata │ ├── comments.json │ └── posts.json └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── kotlin │ └── com │ │ └── onegravity │ │ └── bloc │ │ ├── Application.kt │ │ ├── books │ │ └── BooksActivity.kt │ │ ├── calculator │ │ ├── CalculatorActivity.kt │ │ ├── CalculatorActivityNoVM.kt │ │ └── CalculatorViewModel.kt │ │ ├── counter │ │ ├── CounterActivity.kt │ │ ├── CounterActivityCompose.kt │ │ ├── CounterReduxActivity.kt │ │ ├── CounterReduxViewModel.kt │ │ └── CounterSimpleViewModel.kt │ │ ├── menu │ │ ├── MainActivity.kt │ │ └── MainViewModel.kt │ │ ├── menuCompose │ │ ├── MainActivityCompose.kt │ │ ├── Mapping.kt │ │ ├── MenuEntries.kt │ │ └── RootUi.kt │ │ ├── posts │ │ ├── PostCommentItem.kt │ │ ├── PostFragment.kt │ │ ├── PostViewModel.kt │ │ ├── PostsActivity.kt │ │ ├── PostsFragment.kt │ │ ├── PostsItem.kt │ │ ├── PostsViewModel.kt │ │ ├── SeparatorDecoration.kt │ │ └── ShadowScrollBehavior.kt │ │ ├── postsCompose │ │ ├── Error.kt │ │ ├── Post.kt │ │ ├── PostItem.kt │ │ ├── PostPane.kt │ │ ├── Posts.kt │ │ ├── PostsActivity.kt │ │ ├── PostsPane.kt │ │ └── RootUi.kt │ │ ├── todo │ │ ├── ToDoInput.kt │ │ ├── ToDoListHeader.kt │ │ ├── ToDoListTasks.kt │ │ ├── ToDoUi.kt │ │ └── TodoActivity.kt │ │ └── util │ │ ├── Color.kt │ │ ├── ComposeAppTheme.kt │ │ ├── Shape.kt │ │ └── Type.kt │ └── res │ ├── layout-land │ ├── activity_calculator.xml │ └── activity_calculator_no_vm.xml │ ├── layout │ ├── activity_books.xml │ ├── activity_calculator.xml │ ├── activity_calculator_no_vm.xml │ ├── activity_counter_redux.xml │ ├── activity_counter_simple.xml │ ├── activity_main.xml │ ├── activity_posts.xml │ ├── part_books_content.xml │ ├── part_books_empty.xml │ ├── part_books_error.xml │ ├── part_books_loading.xml │ ├── post_comment_list_item.xml │ ├── post_details_fragment.xml │ ├── post_list_fragment.xml │ └── post_list_item.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ └── ic_launcher_foreground.png │ ├── mipmap-mdpi │ └── ic_launcher_foreground.png │ ├── mipmap-xhdpi │ └── ic_launcher_foreground.png │ ├── mipmap-xxhdpi │ └── ic_launcher_foreground.png │ ├── mipmap-xxxhdpi │ └── ic_launcher_foreground.png │ ├── navigation │ └── nav_graph.xml │ └── values │ ├── colors.xml │ ├── colors_orbit.xml │ ├── dimens.xml │ ├── dimens_orbit.xml │ ├── ic_launcher_background.xml │ ├── strings.xml │ ├── strings_orbit.xml │ ├── styles_orbit.xml │ └── themes.xml ├── bloc-compose ├── .gitignore ├── build.gradle.kts ├── gradle.properties └── src │ └── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ └── com │ └── onegravity │ └── bloc │ └── compose │ ├── ComposableLifecycle.kt │ ├── SideEffectExtensions.kt │ └── StateExtensions.kt ├── bloc-core ├── build.gradle.kts ├── gradle.properties └── src │ ├── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── com │ │ └── onegravity │ │ └── bloc │ │ ├── AndroidExtensions.kt │ │ ├── BlocContextBuilder.kt │ │ ├── BlocViewModel.kt │ │ ├── ComponentLazy.kt │ │ └── utils │ │ ├── FragmentViewBindingDelegate.kt │ │ └── Logger.kt │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── onegravity │ │ └── bloc │ │ ├── Bloc.kt │ │ ├── BlocBuilderDsl.kt │ │ ├── BlocContext.kt │ │ ├── BlocDsl.kt │ │ ├── BlocObservable.kt │ │ ├── BlocObservableOwner.kt │ │ ├── BlocOwner.kt │ │ ├── Sink.kt │ │ ├── StateStream.kt │ │ ├── internal │ │ ├── BlocContextImpl.kt │ │ ├── BlocExtension.kt │ │ ├── BlocImpl.kt │ │ ├── CoroutineHelper.kt │ │ ├── InitializeProcessor.kt │ │ ├── ReduceProcessor.kt │ │ ├── ReducerContainer.kt │ │ ├── ThunkProcessor.kt │ │ ├── Utils.kt │ │ ├── builder │ │ │ ├── BlocBuilder.kt │ │ │ ├── MatcherReducer.kt │ │ │ └── MatcherThunk.kt │ │ ├── fsm │ │ │ ├── Graph.kt │ │ │ ├── GraphBuilder.kt │ │ │ ├── Matcher.kt │ │ │ ├── StateDefinitionBuilder.kt │ │ │ ├── StateMachine.kt │ │ │ └── Transition.kt │ │ └── lifecycle │ │ │ ├── BlocLifecycle.kt │ │ │ ├── BlocLifecycleImpl.kt │ │ │ ├── Callbacks.kt │ │ │ ├── LifecycleEvent.kt │ │ │ ├── LifecycleExtensions.kt │ │ │ ├── LifecycleSideEffect.kt │ │ │ ├── LifecycleState.kt │ │ │ ├── LifecycleStateMachine.kt │ │ │ └── LifecycleTransition.kt │ │ ├── state │ │ ├── BlocState.kt │ │ ├── BlocStateBase.kt │ │ ├── BlocStateBuilder.kt │ │ ├── BlocStateBuilderImpl.kt │ │ ├── BlocStateDsl.kt │ │ ├── DefaultBlocState.kt │ │ ├── Extensions.kt │ │ ├── SimpleBlocStateBuilder.kt │ │ └── SimpleBlocStateBuilderImpl.kt │ │ └── utils │ │ ├── Contexts.kt │ │ ├── Definitions.kt │ │ ├── Effect.kt │ │ ├── Extensions.kt │ │ ├── JobConfig.kt │ │ ├── LoggerUtils.kt │ │ ├── MutableBehaviorFlow.kt │ │ ├── MutableStateStream.kt │ │ └── Subscriptions.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── onegravity │ │ └── bloc │ │ ├── Utils.kt │ │ └── internal │ │ ├── BaseTestClass.kt │ │ ├── BlocInitializerExecutionTests.kt │ │ ├── BlocLifecycleTests.kt │ │ ├── BlocReducerExecutionTests.kt │ │ ├── BlocSideEffectTests.kt │ │ ├── BlocThunkExecutionTests.kt │ │ └── HelperObjects.kt │ └── iosMain │ └── kotlin │ └── com │ └── onegravity │ └── bloc │ └── utils │ └── Logger.kt ├── bloc-redux ├── build.gradle.kts ├── gradle.properties └── src │ ├── androidMain │ └── AndroidManifest.xml │ └── commonMain │ └── kotlin │ └── com │ └── onegravity │ └── bloc │ └── redux │ ├── Extensions.kt │ ├── ReduxBlocState.kt │ ├── ReduxBlocStateBuilder.kt │ ├── ReduxBlocStateBuilderImpl.kt │ ├── ReduxBlocStateDsl.kt │ └── select │ ├── AbstractSelector.kt │ ├── InputField.kt │ ├── Memoizer.kt │ ├── Selector.kt │ ├── SelectorBuilder.kt │ ├── SelectorInput.kt │ ├── SelectorSubscriberBuilder.kt │ └── Selectors.kt ├── bloc-samples ├── build.gradle.kts └── src │ ├── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── com │ │ └── onegravity │ │ └── bloc │ │ └── util │ │ └── Driver.kt │ ├── commonMain │ ├── kotlin │ │ └── com │ │ │ └── onegravity │ │ │ └── bloc │ │ │ ├── sample │ │ │ ├── MainMenu.kt │ │ │ ├── MainMenuCompose.kt │ │ │ ├── books │ │ │ │ ├── BookStore.kt │ │ │ │ ├── BooksRepository.kt │ │ │ │ ├── BooksRepositoryImpl.kt │ │ │ │ ├── BooksState.kt │ │ │ │ ├── BooksUseCase.kt │ │ │ │ ├── BooksUseCaseImpl.kt │ │ │ │ ├── BooksUseCaseImplRedux.kt │ │ │ │ └── BooksUseCaseImplSimple.kt │ │ │ ├── calculator │ │ │ │ ├── ActionEnum.kt │ │ │ │ ├── Calculator.kt │ │ │ │ ├── CalculatorAction.kt │ │ │ │ ├── CalculatorEnums.kt │ │ │ │ ├── CalculatorState.kt │ │ │ │ ├── Operator.kt │ │ │ │ └── Register.kt │ │ │ ├── counter │ │ │ │ ├── CounterStore.kt │ │ │ │ ├── ReduxCounter.kt │ │ │ │ └── SimpleCounter.kt │ │ │ ├── posts │ │ │ │ ├── bloc │ │ │ │ │ ├── Definitions.kt │ │ │ │ │ ├── Post.kt │ │ │ │ │ ├── PostState.kt │ │ │ │ │ ├── Posts.kt │ │ │ │ │ └── PostsState.kt │ │ │ │ ├── compose │ │ │ │ │ ├── PostState.kt │ │ │ │ │ ├── PostsComponent.kt │ │ │ │ │ ├── PostsComponentIOS.kt │ │ │ │ │ ├── PostsComponentImpl.kt │ │ │ │ │ ├── PostsRootState.kt │ │ │ │ │ └── PostsState.kt │ │ │ │ ├── data │ │ │ │ │ ├── PostDataRepository.kt │ │ │ │ │ └── posts │ │ │ │ │ │ ├── model │ │ │ │ │ │ ├── Address.kt │ │ │ │ │ │ ├── CommentData.kt │ │ │ │ │ │ ├── Company.kt │ │ │ │ │ │ ├── Geo.kt │ │ │ │ │ │ ├── PostData.kt │ │ │ │ │ │ └── UserData.kt │ │ │ │ │ │ └── network │ │ │ │ │ │ ├── AvatarUrlGenerator.kt │ │ │ │ │ │ ├── PostNetworkDataSource.kt │ │ │ │ │ │ └── PostNetworkDataSourceImpl.kt │ │ │ │ └── domain │ │ │ │ │ └── repositories │ │ │ │ │ ├── Post.kt │ │ │ │ │ ├── PostComment.kt │ │ │ │ │ └── PostRepository.kt │ │ │ └── todo │ │ │ │ ├── FilterStatus.kt │ │ │ │ ├── PersistingToDoState.kt │ │ │ │ ├── ToDo.kt │ │ │ │ ├── ToDoAction.kt │ │ │ │ ├── ToDoBloc.kt │ │ │ │ ├── ToDoDao.kt │ │ │ │ └── ToDoState.kt │ │ │ └── util │ │ │ ├── CommonModule.kt │ │ │ ├── DBModule.kt │ │ │ ├── Driver.kt │ │ │ └── Koin.kt │ └── sqldelight │ │ └── com │ │ └── onegravity │ │ └── bloc │ │ └── todo │ │ └── Todo.sq │ └── iosMain │ └── kotlin │ └── com │ └── onegravity │ └── bloc │ └── util │ └── Driver.kt ├── build.gradle.kts ├── buildSrc ├── .gitignore ├── build.gradle.kts ├── gradle.properties ├── gradle │ └── libs.versions.toml ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── bloc-android-base.gradle.kts │ └── bloc-publish.gradle.kts ├── config └── detekt │ └── detekt.yml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── iosApp ├── iosApp.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── iosApp.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── Emanuel.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── xcschemes │ │ └── xcschememanagement.plist └── iosApp │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── CounterView.swift │ ├── Info.plist │ ├── Localizable.strings │ ├── MainMenuView.swift │ ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json │ ├── calculator │ ├── CalculatorButtonStyle.swift │ ├── CalculatorView.swift │ └── KeypadView.swift │ ├── iOSApp.swift │ ├── posts │ ├── CommentsView.swift │ ├── PostDetailView.swift │ ├── PostItemView.swift │ ├── PostListView.swift │ ├── PostObservable.swift │ └── PostView.swift │ └── utils │ ├── ActivityIndicator.swift │ ├── BlocComponent.swift │ ├── BlocHolder.swift │ ├── BlocObserver.swift │ ├── InterfaceOrientation.swift │ └── NavigationLazyView.swift ├── settings.gradle.kts ├── todo.txt └── website ├── .gitignore ├── README.md ├── babel.config.js ├── docs ├── architecture │ ├── architecture.md │ ├── bloc │ │ ├── bloc.md │ │ ├── bloc_builder.md │ │ ├── bloc_context.md │ │ ├── initializer.md │ │ ├── launcher.md │ │ ├── lifecycle.md │ │ ├── reducer.md │ │ └── thunk.md │ ├── blocowner │ │ ├── bloc_observable.md │ │ └── bloc_owner.md │ └── blocstate │ │ ├── bloc_state.md │ │ └── bloc_state_builder.md ├── examples │ ├── books.md │ ├── calculator.md │ ├── counter.md │ ├── examples.md │ ├── posts.md │ └── todo.md ├── extensions │ ├── android │ │ ├── bloc_context.md │ │ ├── compose.md │ │ ├── data_binding.md │ │ ├── live_data.md │ │ └── subscription.md │ ├── ios │ │ ├── bloc_component.md │ │ ├── bloc_holder.md │ │ ├── bloc_observer.md │ │ └── overview.md │ ├── overview.md │ └── redux │ │ ├── motivation.md │ │ ├── redux_bloc_state.md │ │ └── setup.md └── getting_started │ ├── examples.md │ ├── overview.md │ └── setup.md ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src ├── components │ └── HomepageFeatures │ │ ├── index.js │ │ └── styles.module.css ├── css │ └── custom.css └── pages │ ├── index.js │ ├── index.module.css │ └── markdown-page.md ├── static ├── .nojekyll └── img │ ├── Bloc Architecture - Bloc Details.svg │ ├── Bloc Architecture - Bloc Overview.svg │ ├── Bloc Architecture - Bloc Redux.svg │ ├── Bloc Architecture - External Lifecycle.svg │ ├── Bloc Architecture - Internal Lifecycle.svg │ ├── Bloc Architecture - ReduxBlocState.svg │ ├── calculator.png │ ├── composable.svg │ ├── docusaurus.png │ ├── download.png │ ├── favicon.ico │ ├── logo.png │ ├── posts.png │ ├── predictable.svg │ ├── reducer_mvvm_style.png │ ├── reducer_redux_style.png │ └── simplicity.svg └── yarn.lock /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Create documentation (Dokka / Docusaurus) 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - documentation 8 | - master 9 | 10 | jobs: 11 | publish: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | 17 | - uses: actions/setup-java@v3 18 | with: 19 | distribution: 'temurin' # See 'Supported distributions' for available options 20 | java-version: '11' 21 | 22 | - name: Create Dokka documentation 23 | run: ./gradlew dokkaHtmlMultiModule 24 | 25 | - uses: actions/setup-node@v3 26 | with: 27 | node-version: 16.x 28 | cache: yarn 29 | cache-dependency-path: website/yarn.lock 30 | 31 | - name: Install dependencies 32 | run: | 33 | cd website 34 | yarn install --frozen-lockfile 35 | 36 | - name: Build website 37 | run: | 38 | cd website 39 | yarn build 40 | 41 | # Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus 42 | - name: Deploy to GitHub Pages 43 | uses: peaceiris/actions-gh-pages@v3 44 | with: 45 | github_token: ${{ secrets.GITHUB_TOKEN }} 46 | # Build output to publish to the `gh-pages` branch: 47 | publish_dir: ./website/build/ 48 | -------------------------------------------------------------------------------- /.github/workflows/lint_and_tests.yml: -------------------------------------------------------------------------------- 1 | name: Run lint and tests 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - feature/** 8 | 9 | jobs: 10 | lint: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - uses: actions/setup-java@v3.3.0 17 | with: 18 | distribution: 'temurin' # See 'Supported distributions' for available options 19 | java-version: '11' 20 | 21 | - name: Run Lint 22 | run: ./gradlew lint 23 | 24 | detekt: 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | 30 | - uses: actions/setup-java@v3.3.0 31 | with: 32 | distribution: 'temurin' # See 'Supported distributions' for available options 33 | java-version: '11' 34 | 35 | - name: Run Detekt 36 | run: ./gradlew detekt 37 | 38 | tests: 39 | runs-on: ubuntu-latest 40 | 41 | steps: 42 | - uses: actions/checkout@v3 43 | 44 | - uses: actions/setup-java@v3.3.0 45 | with: 46 | distribution: 'temurin' # See 'Supported distributions' for available options 47 | java-version: '11' 48 | 49 | - name: Run Tests 50 | run: ./gradlew test 51 | 52 | - name: Test results Debug 53 | uses: actions/upload-artifact@v2 54 | with: 55 | name: debug-unit-tests-results 56 | path: bloc-core/build/reports/tests/testDebugUnitTest/ 57 | 58 | - name: Test results Release 59 | uses: actions/upload-artifact@v2 60 | with: 61 | name: release-unit-tests-results 62 | path: bloc-core/build/reports/tests/testReleaseUnitTest/ 63 | 64 | -------------------------------------------------------------------------------- /.github/workflows/publish_release.yml: -------------------------------------------------------------------------------- 1 | name: Publish release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | env: 9 | SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_NEXUS_USERNAME }} 10 | SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} 11 | GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} 12 | GPG_SIGNING_PASSWORD: ${{ secrets.GPG_SIGNING_PASSWORD }} 13 | 14 | jobs: 15 | publish: 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: [macos-latest] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - uses: actions/setup-java@v3.3.0 25 | with: 26 | distribution: 'temurin' # See 'Supported distributions' for available options 27 | java-version: '11' 28 | 29 | - name: Publish Release 30 | run: ./gradlew -Dorg.gradle.parallel=false publish -PPUBLISH_AS_SNAPSHOT=false 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea 5 | .DS_Store 6 | /build 7 | */build 8 | /captures 9 | .externalNativeBuild 10 | .cxx 11 | local.properties 12 | lint.xml 13 | 14 | ## User settings 15 | **/xcuserdata/** 16 | 17 | /website/.vscode/** 18 | 19 | ## Xcode 8 and earlier 20 | *.xcscmblueprint 21 | *.xccheckout -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1gravity/Kotlin-Bloc/a911759197a447baca7acbb88a9cbe5b3f5f010d/.nojekyll -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "jira-plugin.workingProject": "", 3 | "java.configuration.updateBuildConfiguration": "interactive", 4 | "cSpell.words": [ 5 | "Essenty", 6 | "Essenty's", 7 | "Jetpack", 8 | "Multiplatform", 9 | "MVVM", 10 | "onegravity", 11 | "upsert", 12 | "viewmodel" 13 | ] 14 | } -------------------------------------------------------------------------------- /androidApp/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1gravity/Kotlin-Bloc/a911759197a447baca7acbb88a9cbe5b3f5f010d/androidApp/debug.keystore -------------------------------------------------------------------------------- /androidApp/sampledata/comments.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "name": "id labore ex et quam laborum", 5 | "email": "Eliseo@gardner.biz", 6 | "body": "laudantium enim quasi est quidem magnam voluptate ipsam eos\ntempora quo necessitatibus\ndolor quam autem quasi\nreiciendis et nam sapiente accusantium" 7 | }, 8 | { 9 | "name": "quo vero reiciendis velit similique earum", 10 | "email": "Jayne_Kuhic@sydney.com", 11 | "body": "est natus enim nihil est dolore omnis voluptatem numquam\net omnis occaecati quod ullam at\nvoluptatem error expedita pariatur\nnihil sint nostrum voluptatem reiciendis et" 12 | }, 13 | { 14 | "name": "odio adipisci rerum aut animi", 15 | "email": "Nikita@garfield.biz", 16 | "body": "quia molestiae reprehenderit quasi aspernatur\naut expedita occaecati aliquam eveniet laudantium\nomnis quibusdam delectus saepe quia accusamus maiores nam est\ncum et ducimus et vero voluptates excepturi deleniti ratione" 17 | }, 18 | { 19 | "name": "alias odio sit", 20 | "email": "Lew@alysha.tv", 21 | "body": "non et atque\noccaecati deserunt quas accusantium unde odit nobis qui voluptatem\nquia voluptas consequuntur itaque dolor\net qui rerum deleniti ut occaecati" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /androidApp/sampledata/posts.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "post_username": "Leanne Graham", 5 | "post_title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", 6 | "post_comments": "10 comments", 7 | "post_body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" 8 | }, 9 | { 10 | "post_username": "Ervin Howell", 11 | "post_title": "qui est esse", 12 | "post_comments": "1 comment", 13 | "post_body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla" 14 | }, 15 | { 16 | "post_username": "Clementine Bauch", 17 | "post_title": "ea molestias quasi exercitationem repellat qui ipsa sit aut", 18 | "post_comments": "0 comments", 19 | "post_body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut" 20 | }, 21 | { 22 | "post_username": "Patricia Lebsack", 23 | "post_title": "eum et est occaecati", 24 | "post_comments": "5 comments", 25 | "post_body": "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /androidApp/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1gravity/Kotlin-Bloc/a911759197a447baca7acbb88a9cbe5b3f5f010d/androidApp/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/Application.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc 2 | 3 | import androidx.multidex.MultiDexApplication 4 | import com.onegravity.bloc.util.initKoin 5 | import org.koin.android.ext.koin.androidContext 6 | import org.koin.android.ext.koin.androidLogger 7 | import org.koin.core.logger.Level 8 | import org.koin.dsl.module 9 | 10 | class Application : MultiDexApplication() { 11 | 12 | override fun onCreate() { 13 | super.onCreate() 14 | 15 | initKoin { 16 | androidContext(this@Application) 17 | androidLogger(Level.ERROR) 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/calculator/CalculatorActivity.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.calculator 2 | 3 | import android.os.Bundle 4 | import androidx.activity.viewModels 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.onegravity.bloc.R 7 | import com.onegravity.bloc.bind 8 | import com.onegravity.bloc.databinding.ActivityCalculatorBinding 9 | 10 | class CalculatorActivity : AppCompatActivity() { 11 | 12 | private val viewModel: CalculatorViewModel by viewModels() 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | 17 | bind(R.layout.activity_calculator) { it.viewmodel = viewModel } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/calculator/CalculatorActivityNoVM.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.calculator 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.onegravity.bloc.R 6 | import com.onegravity.bloc.bind 7 | import com.onegravity.bloc.databinding.ActivityCalculatorNoVmBinding 8 | import com.onegravity.bloc.getOrCreate 9 | import com.onegravity.bloc.sample.calculator.ActionEnum 10 | import com.onegravity.bloc.toLiveData 11 | import com.onegravity.bloc.sample.calculator.blocEnum as calculatorBloc 12 | 13 | class CalculatorActivityNoVM : AppCompatActivity() { 14 | 15 | val bloc by getOrCreate { calculatorBloc(it) } 16 | 17 | // this needs to be lazy because the bloc won't be initialized before onCreate is called 18 | val state by lazy { toLiveData(bloc) } 19 | 20 | override fun onCreate(savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | 23 | bind(R.layout.activity_calculator_no_vm) { 24 | it.activity = this 25 | } 26 | } 27 | 28 | fun button(action: ActionEnum) { 29 | bloc.send(action) 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/calculator/CalculatorViewModel.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("WildcardImport") 2 | 3 | package com.onegravity.bloc.calculator 4 | 5 | import androidx.lifecycle.ViewModel 6 | import com.onegravity.bloc.R 7 | import com.onegravity.bloc.blocContext 8 | import com.onegravity.bloc.sample.calculator.CalculatorAction.* 9 | import com.onegravity.bloc.sample.calculator.bloc 10 | import com.onegravity.bloc.toLiveData 11 | 12 | @Suppress("MagicNumber") 13 | class CalculatorViewModel : ViewModel() { 14 | 15 | private val bloc = bloc(blocContext()) 16 | 17 | val state = toLiveData(bloc) 18 | 19 | private val view2Action = mapOf( 20 | R.id.button_clear to Clear, 21 | R.id.button_add to Add, 22 | R.id.button_subtract to Subtract, 23 | R.id.button_multiply to Multiply, 24 | R.id.button_divide to Divide, 25 | R.id.button_plus_minus to PlusMinus, 26 | R.id.button_percentage to Percentage, 27 | R.id.button_period to Period, 28 | R.id.button_equals to Equals, 29 | R.id.button_0 to Digit(0), 30 | R.id.button_1 to Digit(1), 31 | R.id.button_2 to Digit(2), 32 | R.id.button_3 to Digit(3), 33 | R.id.button_4 to Digit(4), 34 | R.id.button_5 to Digit(5), 35 | R.id.button_6 to Digit(6), 36 | R.id.button_7 to Digit(7), 37 | R.id.button_8 to Digit(8), 38 | R.id.button_9 to Digit(9) 39 | ) 40 | 41 | fun button(id: Int) { 42 | view2Action[id]?.let { bloc.send(it) } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/counter/CounterActivity.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.counter 2 | 3 | import android.os.Bundle 4 | import androidx.activity.viewModels 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.onegravity.bloc.R 7 | import com.onegravity.bloc.bind 8 | import com.onegravity.bloc.databinding.ActivityCounterSimpleBinding 9 | 10 | class CounterActivity : AppCompatActivity() { 11 | 12 | private val viewModel: CounterSimpleViewModel by viewModels() 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | bind(R.layout.activity_counter_simple) { 17 | it.viewmodel = viewModel 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/counter/CounterReduxActivity.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.counter 2 | 3 | import android.os.Bundle 4 | import androidx.activity.viewModels 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.onegravity.bloc.R 7 | import com.onegravity.bloc.bind 8 | import com.onegravity.bloc.databinding.ActivityCounterReduxBinding 9 | 10 | class CounterReduxActivity : AppCompatActivity() { 11 | 12 | private val viewModel: CounterReduxViewModel by viewModels() 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | bind(R.layout.activity_counter_redux) { 17 | it.viewmodel = viewModel 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/counter/CounterReduxViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.counter 2 | 3 | import androidx.lifecycle.ViewModel 4 | import com.onegravity.bloc.blocContext 5 | import com.onegravity.bloc.sample.counter.ReduxCounter.Decrement 6 | import com.onegravity.bloc.sample.counter.ReduxCounter.Increment 7 | import com.onegravity.bloc.sample.counter.ReduxCounter.bloc 8 | import com.onegravity.bloc.toLiveData 9 | 10 | class CounterReduxViewModel : ViewModel() { 11 | 12 | // once we migrate to Kotlin 1.6.20 we can use multiple receivers and this will be more concise 13 | private val bloc = bloc(blocContext()) 14 | 15 | val state = toLiveData(bloc) 16 | 17 | fun increment() { 18 | bloc.send(Increment(1)) 19 | } 20 | 21 | fun decrement() { 22 | bloc.send(Decrement(1)) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/counter/CounterSimpleViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.counter 2 | 3 | import androidx.lifecycle.ViewModel 4 | import com.onegravity.bloc.blocContext 5 | import com.onegravity.bloc.sample.counter.SimpleCounter.Decrement 6 | import com.onegravity.bloc.sample.counter.SimpleCounter.Increment 7 | import com.onegravity.bloc.sample.counter.SimpleCounter.bloc 8 | import com.onegravity.bloc.toLiveData 9 | import org.koin.core.component.KoinComponent 10 | 11 | class CounterSimpleViewModel : ViewModel(), KoinComponent { 12 | 13 | private val bloc = bloc(blocContext()) 14 | 15 | val state = toLiveData(bloc) 16 | 17 | fun increment() { 18 | bloc.send(Increment(1)) 19 | } 20 | 21 | fun decrement() { 22 | bloc.send(Decrement(1)) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/menu/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.menu 2 | 3 | import androidx.lifecycle.ViewModel 4 | import com.onegravity.bloc.BlocObservableOwner 5 | import com.onegravity.bloc.blocContext 6 | import com.onegravity.bloc.sample.MainMenu.ActionState 7 | import com.onegravity.bloc.sample.MainMenu.bloc 8 | import com.onegravity.bloc.toObservable 9 | 10 | class MainViewModel : 11 | ViewModel(), 12 | BlocObservableOwner { 13 | 14 | private val bloc = bloc(blocContext()) 15 | 16 | override val observable = bloc.toObservable() 17 | 18 | fun onClick(action: ActionState) { 19 | bloc.send(action) 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/menuCompose/MainActivityCompose.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.menuCompose 2 | 3 | import android.os.Bundle 4 | import androidx.activity.compose.setContent 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.onegravity.bloc.getOrCreate 7 | import com.onegravity.bloc.sample.MainMenuCompose.bloc 8 | import com.onegravity.bloc.util.ComposeAppTheme 9 | 10 | class MainActivityCompose : AppCompatActivity() { 11 | 12 | private val bloc by getOrCreate { bloc(it) } 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | 17 | setContent { 18 | ComposeAppTheme { 19 | RootUi(bloc) 20 | } 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/menuCompose/Mapping.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("WildcardImport") 2 | 3 | package com.onegravity.bloc.menuCompose 4 | 5 | import com.onegravity.bloc.R 6 | import com.onegravity.bloc.books.BooksActivity 7 | import com.onegravity.bloc.calculator.CalculatorActivity 8 | import com.onegravity.bloc.calculator.CalculatorActivityNoVM 9 | import com.onegravity.bloc.counter.CounterActivity 10 | import com.onegravity.bloc.counter.CounterActivityCompose 11 | import com.onegravity.bloc.counter.CounterReduxActivity 12 | import com.onegravity.bloc.menu.MainActivity 13 | import com.onegravity.bloc.posts.PostsActivity 14 | import com.onegravity.bloc.sample.MainMenuCompose.MenuEntry.* 15 | import com.onegravity.bloc.todo.TodoActivity 16 | 17 | val menuItem2Text = mapOf( 18 | MainMenu to R.string.main_menu, 19 | Counter to R.string.main_menu_counter, 20 | CounterCompose to R.string.main_menu_counter_compose, 21 | CounterRedux to R.string.main_menu_counter_redux, 22 | Books to R.string.main_menu_books, 23 | Calculator to R.string.main_menu_calculator, 24 | CalculatorNoViewModel to R.string.main_menu_calculator_no_vm, 25 | Posts to R.string.main_menu_posts, 26 | PostsCompose to R.string.main_menu_posts_compose, 27 | ToDo to R.string.main_menu_todo, 28 | ) 29 | 30 | val menuItem2Activity = mapOf( 31 | MainMenu to MainActivity::class.java, 32 | Counter to CounterActivity::class.java, 33 | CounterCompose to CounterActivityCompose::class.java, 34 | CounterRedux to CounterReduxActivity::class.java, 35 | Books to BooksActivity::class.java, 36 | Calculator to CalculatorActivity::class.java, 37 | CalculatorNoViewModel to CalculatorActivityNoVM::class.java, 38 | Posts to PostsActivity::class.java, 39 | PostsCompose to com.onegravity.bloc.postsCompose.PostsActivity::class.java, 40 | ToDo to TodoActivity::class.java 41 | ) 42 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/menuCompose/RootUi.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.menuCompose 2 | 3 | import androidx.compose.foundation.layout.BoxWithConstraints 4 | import androidx.compose.foundation.layout.fillMaxHeight 5 | import androidx.compose.foundation.layout.fillMaxWidth 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.tooling.preview.Preview 9 | import com.onegravity.bloc.compose.previewBlocContext 10 | import com.onegravity.bloc.sample.MainMenuCompose 11 | import com.onegravity.bloc.sample.MenuBloc 12 | 13 | @Composable 14 | @Suppress("FunctionNaming", "FunctionName") 15 | fun RootUi(bloc: MenuBloc) { 16 | BoxWithConstraints( 17 | Modifier 18 | .fillMaxWidth() 19 | .fillMaxHeight() 20 | ) { 21 | MenuEntries(bloc, 22 | Modifier 23 | .fillMaxWidth() 24 | .fillMaxHeight()) 25 | } 26 | } 27 | 28 | @Preview 29 | @Composable 30 | @Suppress("FunctionNaming", "FunctionName") 31 | fun RootUiPreview() { 32 | val bloc = MainMenuCompose.bloc(previewBlocContext()) 33 | RootUi(bloc) 34 | } 35 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/posts/PostViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.posts 2 | 3 | import androidx.lifecycle.ViewModel 4 | import com.onegravity.bloc.BlocObservableOwner 5 | import com.onegravity.bloc.blocContext 6 | import com.onegravity.bloc.sample.posts.bloc.Post 7 | import com.onegravity.bloc.sample.posts.bloc.PostState 8 | import com.onegravity.bloc.sample.posts.domain.repositories.PostRepository 9 | import com.onegravity.bloc.toObservable 10 | import com.onegravity.bloc.sample.posts.domain.repositories.Post as PostData 11 | 12 | /** 13 | * Not used any more but we keep it around for illustration purposes 14 | */ 15 | class PostViewModel( 16 | repository: PostRepository, 17 | post: PostData 18 | ) : ViewModel(), BlocObservableOwner { 19 | 20 | private val bloc = Post.bloc(blocContext(), repository) 21 | .also { it.send(Post.Action.Load(post.id)) } 22 | 23 | override val observable = bloc.toObservable() 24 | 25 | } 26 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/posts/PostsActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Mikołaj Leszczyński & Appmattus Limited 3 | * Copyright 2020 Babylon Partners Limited 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * File modified by Mikołaj Leszczyński & Appmattus Limited 18 | * See: https://github.com/orbit-mvi/orbit-mvi/compare/c5b8b3f2b83b5972ba2ad98f73f75086a89653d3...main 19 | */ 20 | 21 | package com.onegravity.bloc.posts 22 | 23 | import android.os.Bundle 24 | import androidx.appcompat.app.AppCompatActivity 25 | import androidx.navigation.findNavController 26 | import com.onegravity.bloc.R 27 | 28 | class PostsActivity : AppCompatActivity() { 29 | 30 | private val navController by lazy { findNavController(R.id.nav_host_fragment) } 31 | 32 | override fun onCreate(savedInstanceState: Bundle?) { 33 | super.onCreate(savedInstanceState) 34 | setContentView(R.layout.activity_posts) 35 | } 36 | 37 | override fun onSupportNavigateUp() = navController.navigateUp() 38 | 39 | } 40 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/postsCompose/Error.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.postsCompose 2 | 3 | import androidx.compose.foundation.layout.padding 4 | import androidx.compose.material.Button 5 | import androidx.compose.material.Snackbar 6 | import androidx.compose.material.Text 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.res.stringResource 10 | import androidx.compose.ui.unit.dp 11 | import com.onegravity.bloc.R 12 | 13 | @Composable 14 | @Suppress("FunctionNaming", "FunctionName") 15 | fun Error(retry: () -> Unit, error: Throwable) { 16 | Snackbar( 17 | action = { Button(retry) { Text(stringResource(id = R.string.posts_compose_retry)) } }, 18 | modifier = Modifier.padding(8.dp) 19 | ) { 20 | Text(stringResource(id = R.string.posts_compose_error, error.message ?: "")) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/postsCompose/PostPane.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.postsCompose 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.material.CircularProgressIndicator 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.getValue 8 | import androidx.compose.ui.Alignment 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.graphics.Color 11 | import co.touchlab.kermit.Logger 12 | import com.github.michaelbull.result.mapBoth 13 | import com.onegravity.bloc.compose.observeState 14 | import com.onegravity.bloc.sample.posts.compose.PostsComponent 15 | 16 | @Composable 17 | @Suppress("FunctionNaming", "FunctionName") 18 | internal fun PostPane( 19 | component: PostsComponent, 20 | modifier: Modifier 21 | ) { 22 | val state by component.observeState() 23 | 24 | when (state.postState.loadingId != null) { 25 | true -> Box(modifier = modifier.background(Color.Transparent)) { 26 | CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) 27 | } 28 | else -> state.postState.post?.mapBoth( 29 | { post -> Post(post, modifier) }, 30 | { error -> Error({ Logger.e("$error") }, error) } 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/postsCompose/Posts.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.postsCompose 2 | 3 | import androidx.compose.foundation.lazy.LazyColumn 4 | import androidx.compose.foundation.lazy.items 5 | import androidx.compose.material.Divider 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import com.onegravity.bloc.sample.posts.domain.repositories.Post 9 | 10 | @Composable 11 | @Suppress("FunctionNaming", "FunctionName") 12 | internal fun Posts( 13 | posts: List, 14 | selectedPost: Int?, 15 | modifier: Modifier = Modifier, 16 | onClicked: (post: Post) -> Unit 17 | ) { 18 | LazyColumn(modifier = modifier) { 19 | items(posts) { post -> 20 | PostItem(post, post.id == selectedPost, onClicked) 21 | Divider() 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/postsCompose/PostsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.postsCompose 2 | 3 | import android.os.Bundle 4 | import androidx.activity.compose.setContent 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.onegravity.bloc.getOrCreate 7 | import com.onegravity.bloc.sample.posts.compose.PostsComponent 8 | import com.onegravity.bloc.util.ComposeAppTheme 9 | import org.koin.android.ext.android.get 10 | import org.koin.core.parameter.parametersOf 11 | 12 | class PostsActivity : AppCompatActivity() { 13 | 14 | // Using getOrCreate we can create any "component" that takes the ActivityBlocContext as 15 | // parameter 16 | // a component can be a simple Bloc: 17 | // val bloc by getOrCreate { bloc(it, 1) } 18 | // it can also be any other object that wraps around a bloc like: 19 | // val component by getOrCreate { PostsComponentImpl(it) } 20 | // Note: a ViewModel is created under the hood to have the "correct" lifecycle 21 | // (not the Activity lifecycle) 22 | 23 | private val component: PostsComponent by getOrCreate { 24 | get(parameters = { parametersOf(it) }) 25 | } 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | 30 | setContent { 31 | ComposeAppTheme { 32 | RootUi(component) 33 | } 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/postsCompose/PostsPane.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.postsCompose 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.material.CircularProgressIndicator 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.getValue 8 | import androidx.compose.ui.Alignment 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.graphics.Color 11 | import co.touchlab.kermit.Logger 12 | import com.github.michaelbull.result.mapBoth 13 | import com.onegravity.bloc.compose.observeState 14 | import com.onegravity.bloc.sample.posts.compose.PostsComponent 15 | 16 | @Composable 17 | @Suppress("FunctionNaming", "FunctionName") 18 | internal fun PostsPane( 19 | component: PostsComponent, 20 | modifier: Modifier 21 | ) { 22 | val state by component.observeState() 23 | 24 | when (state.postsState.loading) { 25 | true -> Box(modifier = modifier.background(Color.Transparent)) { 26 | CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) 27 | } 28 | else -> state.postsState.posts.mapBoth( 29 | { posts -> 30 | Posts(posts, state.selectedPost(), modifier) { post -> 31 | component.onSelected(post) 32 | } 33 | }, 34 | { error -> Error({ Logger.e("$error") }, error) } 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/todo/ToDoInput.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("WildcardImport") 2 | 3 | package com.onegravity.bloc.todo 4 | 5 | import androidx.compose.foundation.layout.fillMaxWidth 6 | import androidx.compose.foundation.text.KeyboardActions 7 | import androidx.compose.foundation.text.KeyboardOptions 8 | import androidx.compose.material.Text 9 | import androidx.compose.material.TextField 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.runtime.getValue 12 | import androidx.compose.runtime.mutableStateOf 13 | import androidx.compose.runtime.saveable.rememberSaveable 14 | import androidx.compose.runtime.setValue 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.graphics.Color 17 | import androidx.compose.ui.res.stringResource 18 | import androidx.compose.ui.text.TextStyle 19 | import androidx.compose.ui.text.font.FontWeight 20 | import androidx.compose.ui.text.input.ImeAction 21 | import com.onegravity.bloc.Bloc 22 | import com.onegravity.bloc.R 23 | import com.onegravity.bloc.sample.todo.AddToDo 24 | import com.onegravity.bloc.sample.todo.ToDoAction 25 | import com.onegravity.bloc.sample.todo.ToDoState 26 | 27 | @Composable 28 | @Suppress("FunctionNaming", "FunctionName") 29 | fun ToDoInput(bloc: Bloc) { 30 | var text: String by rememberSaveable { mutableStateOf("") } 31 | 32 | TextField( 33 | value = text, 34 | label = { Text(stringResource(R.string.todo_description)) }, 35 | maxLines = 1, 36 | singleLine = true, 37 | modifier = Modifier.fillMaxWidth(), 38 | textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold), 39 | onValueChange = { text = it }, 40 | keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), 41 | keyboardActions = KeyboardActions( 42 | onDone = { 43 | bloc.send(AddToDo(text)) 44 | text = "" 45 | }, 46 | ) 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/todo/ToDoUi.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("WildcardImport") 2 | 3 | package com.onegravity.bloc.todo 4 | 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.Spacer 7 | import androidx.compose.foundation.layout.fillMaxHeight 8 | import androidx.compose.foundation.layout.fillMaxWidth 9 | import androidx.compose.foundation.layout.height 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.unit.dp 14 | import com.onegravity.bloc.Bloc 15 | import com.onegravity.bloc.sample.todo.ToDoAction 16 | import com.onegravity.bloc.sample.todo.ToDoState 17 | 18 | @Composable 19 | @Suppress("FunctionNaming", "FunctionName") 20 | fun ToDoUi(bloc: Bloc) { 21 | Column( 22 | Modifier 23 | .fillMaxWidth() 24 | .fillMaxHeight() 25 | .padding(16.dp)) { 26 | ToDoInput(bloc) 27 | 28 | Spacer(Modifier.height(24.0.dp)) 29 | 30 | ToDoListHeader(bloc) 31 | 32 | ToDoListTasks(bloc) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/todo/TodoActivity.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.todo 2 | 3 | import android.os.Bundle 4 | import androidx.activity.compose.setContent 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.onegravity.bloc.getOrCreate 7 | import com.onegravity.bloc.sample.todo.toDoBloc 8 | import com.onegravity.bloc.util.ComposeAppTheme 9 | import org.koin.android.ext.android.get 10 | 11 | class TodoActivity : AppCompatActivity() { 12 | 13 | private val bloc by getOrCreate { toDoBloc(it, get()) } 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | 18 | setContent { 19 | ComposeAppTheme { 20 | ToDoUi(bloc) 21 | } 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/util/Color.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("MagicNumber") 2 | 3 | package com.onegravity.bloc.util 4 | 5 | import androidx.compose.material.darkColors 6 | import androidx.compose.material.lightColors 7 | import androidx.compose.ui.graphics.Color 8 | 9 | val primaryColor = Color(0xFF283593) 10 | val primaryColorLight = Color(0xFF5f5fc4) 11 | val secondaryColor = Color(0xFF1b5e20) 12 | val secondaryColorLight = Color(0xFF4c8c4a) 13 | 14 | val darkColorPalette = darkColors( 15 | primary = primaryColor, 16 | primaryVariant = primaryColorLight, 17 | secondary = secondaryColor 18 | ) 19 | 20 | val lightColorPalette = lightColors( 21 | primary = primaryColorLight, 22 | primaryVariant = primaryColorLight, 23 | secondary = secondaryColorLight 24 | ) 25 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/util/ComposeAppTheme.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.util 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.runtime.Composable 6 | 7 | @Composable 8 | @Suppress("FunctionNaming", "FunctionName") 9 | fun ComposeAppTheme( 10 | darkTheme: Boolean = isSystemInDarkTheme(), 11 | content: @Composable () -> Unit 12 | ) { 13 | val colors = if (darkTheme) darkColorPalette else lightColorPalette 14 | 15 | MaterialTheme( 16 | colors = colors, 17 | typography = typography, 18 | shapes = shapes, 19 | content = content 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/util/Shape.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.util 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(4.dp), 10 | large = RoundedCornerShape(0.dp) 11 | ) 12 | -------------------------------------------------------------------------------- /androidApp/src/main/kotlin/com/onegravity/bloc/util/Type.kt: -------------------------------------------------------------------------------- 1 | package com.onegravity.bloc.util 2 | 3 | import androidx.compose.material.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val typography = Typography( 11 | body1 = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp 15 | ) 16 | ) 17 | -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/activity_books.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 18 | 19 | 23 | 24 | 28 | 29 | 33 | 34 | 35 | 36 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/activity_posts.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 27 | -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/part_books_empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 |