Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new documentation website #124

Merged
merged 10 commits into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ jobs:
java-version: 22
cache: 'gradle'

- uses: actions/setup-node@v4
name: Set up Node
with:
node-version: 23
cache: npm
cache-dependency-path: './docs/package-lock.json'

- name: Validate gradle wrapper
uses: gradle/actions/wrapper-validation@v4

Expand All @@ -51,11 +58,17 @@ jobs:
- name: Run detekt
run: ./gradlew detektAll

- name: Build
run: ./gradlew assemble --stacktrace --no-configuration-cache

- name: Unit tests
run: ./gradlew allTests --stacktrace --no-configuration-cache

- name: Install docs deps
run: cd docs && npm ci

- name: Test build website
run: cd docs && npm run build

- name: Build
run: ./gradlew assemble --stacktrace --no-configuration-cache

- name: Verify IDE plugin
run: ./gradlew debugger:ideplugin:verifyPlugin
19 changes: 14 additions & 5 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,31 +48,40 @@ jobs:
restore-keys: |
${{ runner.os }}-konan-

- uses: actions/setup-node@v4
with:
node-version: 23
cache: npm
cache-dependency-path: './docs/package-lock.json'

- name: Update docs/README.md
run: cp ./README.md ./docs/README.md
run: cp ./README.md ./docs/docs/README.md

- name: Build website
run: cd docs && npm run build

- name: Generate docs
run: ./gradlew dokkaGenerate

- name: Make javadoc dir
run: mkdir -p ./docs/javadocs
run: mkdir -p ./docs/build/javadocs

- name: Move docs to the parent docs dir
run: cp -r ./build/dokka/html/ ./docs/javadocs/
run: cp -r ./build/dokka/html/ ./docs/build/javadocs/

- name: Create sample app distributable
run: ./gradlew wasmJsBrowserDistribution --no-configuration-cache

- name: Move assembled sample app to docs folder
run: cp -r ./sample/build/dist/wasmJs/productionExecutable/ ./docs/sample/
run: cp -r ./sample/build/dist/wasmJs/productionExecutable/ ./docs/build/sample/

- name: Setup Pages
uses: actions/configure-pages@v5

- name: Upload pages
uses: actions/upload-pages-artifact@v3
with:
path: './docs/'
path: './docs/build/'

- name: Deploy to GitHub Pages
id: deployment
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ hs_err_pid*
/.idea/artifacts/**
!gradle/gradle-wrapper.jar
# built and copied by dokka plugin
./docs/javadocs/**
docs/javadocs/**
# yarn.lock location & other
/kotlin-js-store/**
/.idea/appInsightsSettings.xml
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![](docs/images/banner.png)
![](docs/static/banner.png)

[![CI](https://github.com/respawn-app/FlowMVI/actions/workflows/ci.yml/badge.svg)](https://github.com/respawn-app/FlowMVI/actions/workflows/ci.yml)
![License](https://img.shields.io/github/license/respawn-app/flowMVI)
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/dokkaDocumentation.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dokka {
moduleVersion = project.version.toString()
pluginsConfiguration.html {
footerMessage = "© ${Config.vendorName}"
customAssets.from(rootDir.resolve("docs/images/icon-512-maskable.png"))
customAssets.from(rootDir.resolve("docs/static/icon-512-maskable.png"))
homepageLink = Config.url
}
dokkaPublications.configureEach {
Expand Down
22 changes: 22 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Dependencies
/node_modules

# Production
/build

# Generated files
.docusaurus
.cache-loader

# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

**/.kotlin/**
3 changes: 0 additions & 3 deletions docs/README.md

This file was deleted.

16 changes: 0 additions & 16 deletions docs/_navbar.md

This file was deleted.

5 changes: 5 additions & 0 deletions docs/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# FlowMVI

### Stub readme file, do not edit!

Will be replaced with project readme on CI
6 changes: 6 additions & 0 deletions docs/docs/integrations/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"position": 3,
"label": "Integrations",
"collapsible": true,
"collapsed": false
}
21 changes: 17 additions & 4 deletions docs/integrations/android.md → docs/docs/integrations/android.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
sidebar_position: 2
sidebar_label: Android
---

# Learn how to use FlowMVI with Android

There are multiple options on how to organize your code when working with Android.
Expand Down Expand Up @@ -47,11 +52,15 @@ class CounterViewModel(
Prefer to extend `ImmutableContainer` as that will hide the `intent` function from outside code, otherwise you'll leak
the `PipelineContext` of the store to subscribers.

?> The upside of this approach is that it's easier to implement and use some platform-specific features
:::info

The upside of this approach is that it's easier to implement and use some platform-specific features
like `savedState` (you can still use them for KMP though)
The downside is that you completely lose KMP compatibility. If you have plans to make your ViewModels multiplatform,
The downside is that you lose KMP compatibility. If you have plans to make your ViewModels multiplatform,
it is advised to use the delegated approach instead, which is only slightly more verbose.

:::

### Delegated ViewModels

A slightly more advanced approach would be to avoid subclassing ViewModels altogether.
Expand All @@ -62,7 +71,7 @@ First, wrap your store in a simple class. You don't have to implement `Container
class CounterContainer(
private val repo: CounterRepo,
) : Container<CounterState, CounterIntent, CounterAction> {

override val store = store(Loading) {
/* ... as before ... */
}
Expand Down Expand Up @@ -93,11 +102,15 @@ val appModule = module {
}
```

!> Qualifiers are needed because you'll have many `StoreViewModels` that differ only by type of the container. Due to
:::warning[On type-safety]

Qualifiers are needed because you'll have many `StoreViewModels` that differ only by type of the container. Due to
type erasure, you must inject the VM by specifying a fully-qualified type
e.g. `StoreViewModel<CounterState, CounterIntent, CounterAction>`, or it will be replaced with `Store<*, *, *>` and the
DI framework will fail, likely in runtime.

:::

This is a more robust and multiplatform friendly approach that is slightly more boilerplatish but does not require you
to subclass ViewModels. This example is also demonstrated in the sample app.

Expand Down
21 changes: 15 additions & 6 deletions docs/compose.md → docs/docs/integrations/compose.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Compose and Lifecycle Integration
---
sidebar_position: 1
sidebar_label: Compose
---

# Compose Integration

## Step 1: Add Dependencies

Expand Down Expand Up @@ -52,10 +57,14 @@ how to set compose compiler configuration globally and/or in gradle conventions.

## Step 3: Subscribe to Stores

!> Compose does not play well with MVVM+ style because of the instability of the `LambdaIntent` and `ViewModel` classes.
:::warning

Compose does not play well with MVVM+ style because of the instability of the `LambdaIntent` and `ViewModel` classes.
It is discouraged to use Lambda intents with Compose as that will not only leak the context of the store but
also degrade performance, also forcing you to pass tons of function references as parameters.

:::

Subscribing to a store is as simple as calling `subscribe()`

```kotlin
Expand Down Expand Up @@ -92,7 +101,7 @@ launch new coroutines that will parallelize your flow (e.g. for snackbars).

A best practice is to make your state handling (UI redraw composable) a pure function and extract it to a separate
Composable such as `ScreenContent(state: ScreenState)` to keep your `*Screen` function clean, as shown below.
It will also enable smart-casting by the compiler make UI tests super easy. If you want to send `MVIIntent`s from a
It will also enable smart-casting by the compiler make UI tests super easy. If you want to send `MVIIntent`s from a
nested composable, just use `IntentReceiver` as a context or pass a function reference:

```kotlin
Expand All @@ -110,7 +119,7 @@ private fun IntentReceiver<CounterIntent>.CounterScreenContent(state: CounterSta
```

Now this function cannot be called outside of the required store's area of responsibility.
You can also subclass your `Intent` class by target state to make it impossible at compilation time to send an intent
You can also subclass your `Intent` class by target state to make it impossible at compilation time to send an intent
for an incorrect state:

```kotlin
Expand All @@ -120,7 +129,7 @@ sealed interface CounterIntent: MVIIntent {
sealed interface LoadingIntent : MVIIntent
}

// then, use
// then, use
IntentReceiver<DisplayingCounterIntent>.DisplayingCounterContent()
```

Expand All @@ -130,7 +139,7 @@ When you have defined your `*Content` function, you will get a composable that c
That composable will not need DI, Local Providers from compose, or anything else for that matter, to draw itself.
But there's a catch: It has an `IntentReceiver<I>` as a parameter. To deal with this, there is an `EmptyReceiver`
composable. EmptyReceiver does nothing when an intent is sent, which is exactly what we want for previews and UI tests.
We can now define our `PreviewParameterProvider` and the Preview composable.
We can now define our `PreviewParameterProvider` and the Preview composable.
You won't need the `EmptyReceiver` if you pass the `intent` callback manually.

```kotlin
Expand Down
18 changes: 15 additions & 3 deletions docs/integrations/essenty.md → docs/docs/integrations/essenty.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
sidebar_position: 3
---

# Essenty Integration

The library integrates with Essenty (Decompose) to support lifecycle and retaining store instances across configuration
Expand All @@ -7,7 +11,7 @@ changes. The integration supports all the artifacts that Essenty supports.
# Includes retained stores and coroutine scopes
flowmvi-essenty = { module = "pro.respawn.flowmvi:essenty", version.ref = "flowmvi" }
# Includes lifecycle support for store subscription
flowmvi-essenty-compose = { module = "pro.respawn.flowmvi:essenty-compose", version.ref = "flowmvi" }
flowmvi-essenty-compose = { module = "pro.respawn.flowmvi:essenty-compose", version.ref = "flowmvi" }
```

## Retaining Stores
Expand Down Expand Up @@ -58,7 +62,9 @@ retainedStore(

Pass `null` to the scope to not start the store upon creation. In this case, you'll have to start the store yourself.

!> Caveat: If you do not use the factory DSL and instead build a store that is retained, it will capture everything you
:::warning [Caveat:]

If you do not use the factory DSL and instead build a store that is retained, it will capture everything you
pass into the `builder` closure. This means that any parameters or outside properties you use in the builder will be
captured **and retained** as well. This is the same caveat that you have to be aware of when
using [Retained Components](https://arkivanov.github.io/Decompose/component/instance-retaining/#retained-components-since-v210-alpha-03).
Expand All @@ -67,6 +73,8 @@ normally using a `store` builder. However, the store will be recreated and relau
If you are using retained components already, you can opt-in to the warning annotation issues by the library using a
compiler flag or just not use retained stores.

:::

## Retaining Coroutine Scopes

By default, a store is launched using a `retainedScope`. As the name says, it's retained across configuration changes
Expand Down Expand Up @@ -117,9 +125,13 @@ By default, the store will unsubscribe when:
* The composable goes out of the composition
* The lifecycle reaches the `STOPPED` state, such as when the UI is no longer visible, but is still composed.

?> The requirements for setting up lifecycle correctly are the same as in
:::tip

The requirements for setting up lifecycle correctly are the same as in
the [Decompose docs](https://arkivanov.github.io/Decompose/component/lifecycle/).

:::

If you want another approach, you can provide the lifecycle via a `CompositionLocal`:

```kotlin
Expand Down
5 changes: 5 additions & 0 deletions docs/CONTRIBUTING.md → docs/docs/misc/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
sidebar_position: 2
sidebar_label: Contribution guide
---

# Contributing

* To build the project, you will need the following in your local.properties:
Expand Down
Loading