Skip to content

Commit f7bc9f5

Browse files
committed
⚡️: use kotlinx.collections.immutable
1 parent 46717b2 commit f7bc9f5

6 files changed

Lines changed: 65 additions & 36 deletions

File tree

app/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,6 @@ dependencies {
7575
}
7676
implementation("com.tencent:mmkv:1.3.4")
7777
implementation(projects.hooks)
78+
79+
implementation(libs.kotlinx.collections.immutable)
7880
}

app/src/main/java/xyz/junerver/composehooks/example/UseReducerExample.kt

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import androidx.compose.runtime.setValue
2121
import androidx.compose.ui.Alignment
2222
import androidx.compose.ui.Modifier
2323
import androidx.compose.ui.unit.dp
24+
import kotlinx.collections.immutable.PersistentList
25+
import kotlinx.collections.immutable.mutate
26+
import kotlinx.collections.immutable.persistentListOf
27+
import kotlinx.collections.immutable.plus
2428
import xyz.junerver.compose.hooks.Middleware
2529
import xyz.junerver.compose.hooks.Reducer
2630
import xyz.junerver.compose.hooks.useGetState
@@ -112,7 +116,7 @@ fun TaskItem(task: Task, onChange: (Task) -> Unit, onDelete: (Int) -> Unit) {
112116

113117
if (isEditing) {
114118
TextField(
115-
modifier = Modifier.width(100.dp),
119+
modifier = Modifier.width(300.dp),
116120
value = text,
117121
onValueChange = { newText ->
118122
text = newText
@@ -143,7 +147,7 @@ fun AddTask(onAddTask: (String) -> Unit) {
143147

144148
Row {
145149
TextField(
146-
modifier = Modifier.width(100.dp),
150+
modifier = Modifier.width(300.dp),
147151
value = text,
148152
onValueChange = { newText ->
149153
text = newText
@@ -161,12 +165,21 @@ fun AddTask(onAddTask: (String) -> Unit) {
161165

162166
@Composable
163167
fun TaskApp() {
164-
val (tasks, dispatch) = useReducer<List<Task>, TaskAction>(
168+
val (tasks, dispatch) = useReducer<PersistentList<Task>, TaskAction>(
165169
{ prevState, action ->
166170
when (action) {
167171
is TaskAction.Added -> prevState + Task(nextId++, action.text, false)
168-
is TaskAction.Changed -> prevState.map { if (it.id == action.task.id) action.task else it }
169-
is TaskAction.Deleted -> prevState.filter { it.id != action.taskId }
172+
is TaskAction.Changed -> prevState.mutate { tasks ->
173+
tasks.indexOfFirst { it.id == action.task.id }
174+
.takeIf { it != -1 }
175+
?.let { index ->
176+
tasks[index] = action.task
177+
}
178+
}
179+
180+
is TaskAction.Deleted -> prevState.mutate { tasks ->
181+
tasks.removeIf { it.id == action.taskId }
182+
}
170183
}
171184
},
172185
initialTasks,
@@ -190,12 +203,16 @@ fun TaskApp() {
190203
Column {
191204
Text(text = "Day off in Kyoto", style = MaterialTheme.typography.titleLarge)
192205
AddTask(onAddTask = ::handleAddTask)
193-
TaskList(tasks = tasks, onChangeTask = ::handleChangeTask, onDeleteTask = ::handleDeleteTask)
206+
TaskList(
207+
tasks = tasks,
208+
onChangeTask = ::handleChangeTask,
209+
onDeleteTask = ::handleDeleteTask
210+
)
194211
}
195212
}
196213

197214
var nextId = 3
198-
val initialTasks = listOf(
215+
val initialTasks = persistentListOf(
199216
Task(id = 0, text = "Philosopher’s Path", done = true),
200217
Task(id = 1, text = "Visit the temple", done = false),
201218
Task(id = 2, text = "Drink matcha", done = false)

app/src/main/java/xyz/junerver/composehooks/example/UseReduxExample.kt

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import androidx.compose.foundation.layout.Spacer
66
import androidx.compose.foundation.layout.fillMaxWidth
77
import androidx.compose.foundation.layout.height
88
import androidx.compose.foundation.layout.padding
9-
import androidx.compose.material3.Divider
9+
import androidx.compose.material3.HorizontalDivider
1010
import androidx.compose.material3.OutlinedTextField
1111
import androidx.compose.material3.Surface
1212
import androidx.compose.material3.Text
@@ -16,6 +16,10 @@ import androidx.compose.ui.Modifier
1616
import androidx.compose.ui.unit.dp
1717
import kotlin.random.Random
1818
import kotlin.time.Duration.Companion.seconds
19+
import kotlinx.collections.immutable.PersistentList
20+
import kotlinx.collections.immutable.mutate
21+
import kotlinx.collections.immutable.persistentListOf
22+
import kotlinx.collections.immutable.plus
1923
import kotlinx.coroutines.CoroutineScope
2024
import kotlinx.coroutines.delay
2125
import kotlinx.coroutines.launch
@@ -37,14 +41,13 @@ sealed interface TodoAction
3741
data class AddTodo(val todo: Todo) : TodoAction
3842
data class DelTodo(val id: String) : TodoAction
3943

40-
val todoReducer: Reducer<List<Todo>, TodoAction> = { prevState: List<Todo>, action: TodoAction ->
44+
val todoReducer: Reducer<PersistentList<Todo>, TodoAction> = { prevState: PersistentList<Todo>, action: TodoAction ->
4145
when (action) {
42-
is AddTodo -> buildList {
43-
addAll(prevState)
44-
add(action.todo)
45-
}
46+
is AddTodo -> prevState + action.todo
4647

47-
is DelTodo -> prevState.filter { it.id != action.id }
48+
is DelTodo -> prevState.mutate { mutator ->
49+
mutator.removeIf { it.id == action.id }
50+
}
4851
}
4952
}
5053
val fetchReducer: Reducer<NetFetchResult<*>, NetFetchResult<*>> = { _, action ->
@@ -58,7 +61,7 @@ val fetchReducer: Reducer<NetFetchResult<*>, NetFetchResult<*>> = { _, action ->
5861
*/
5962
val simpleStore = createStore(arrayOf(logMiddleware())) {
6063
simpleReducer with SimpleData("default", 18)
61-
todoReducer with emptyList()
64+
todoReducer with persistentListOf()
6265
}
6366

6467
val fetchStore = createStore {
@@ -78,19 +81,19 @@ fun UseReduxExample() {
7881
.padding(20.dp)
7982
) {
8083
SimpleDataContainer()
81-
Divider(
84+
HorizontalDivider(
8285
modifier = Modifier
8386
.fillMaxWidth()
8487
.padding(top = 20.dp, bottom = 20.dp)
8588
)
8689
TodosListContainer()
87-
Divider(
90+
HorizontalDivider(
8891
modifier = Modifier
8992
.fillMaxWidth()
9093
.padding(top = 20.dp, bottom = 20.dp)
9194
)
9295
UseReduxFetch()
93-
Divider(
96+
HorizontalDivider(
9497
modifier = Modifier
9598
.fillMaxWidth()
9699
.padding(top = 20.dp, bottom = 20.dp)
@@ -116,7 +119,7 @@ fun TodoList() {
116119
* The corresponding state object saved in the store can be quickly
117120
* obtained through the [useSelector] function;
118121
*/
119-
val todos = useSelector<List<Todo>>()
122+
val todos = useSelector<PersistentList<Todo>>()
120123
Column {
121124
todos.map {
122125
TodoItem(item = it)

gradle/libs.versions.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ coreKtx = "1.13.1"
55
junit = "4.13.2"
66
junitVersion = "1.2.1"
77
espressoCore = "3.6.1"
8-
kotlinxDatetime = "0.6.0"
98
appcompat = "1.7.0"
109
material = "1.12.0"
1110
retrofitVersion = "2.11.0"
@@ -18,6 +17,8 @@ lifecycleRuntimeCompose = "2.8.4"
1817

1918
biometric = "1.2.0-alpha05"
2019

20+
kotlinxDatetime = "0.6.0"
21+
kotlinxCollectionsImmutable = "0.3.7"
2122

2223
[libraries]
2324
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -51,6 +52,7 @@ androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lif
5152
kotlin-bom = { group = "org.jetbrains.kotlin", name = "kotlin-bom", version.ref = "kotlin" }
5253
kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect" }
5354
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" }
55+
kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlinxCollectionsImmutable" }
5456

5557
ktx = "xyz.junerver.kotlin:ktx:0.1.6"
5658

hooks/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,5 @@ dependencies {
6464
implementation(libs.kotlin.reflect)
6565
api(libs.ktx)
6666
api(libs.kotlinx.datetime)
67+
implementation(libs.kotlinx.collections.immutable)
6768
}

hooks/src/main/kotlin/xyz/junerver/compose/hooks/useUndo.kt

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package xyz.junerver.compose.hooks
22

33
import androidx.compose.runtime.Composable
4+
import kotlinx.collections.immutable.PersistentList
5+
import kotlinx.collections.immutable.mutate
6+
import kotlinx.collections.immutable.persistentListOf
7+
import kotlinx.collections.immutable.plus
48
import xyz.junerver.kotlin.Tuple7
59
import xyz.junerver.kotlin.tuple
610

@@ -13,9 +17,9 @@ import xyz.junerver.kotlin.tuple
1317
*/
1418

1519
data class UndoState<T>(
16-
var past: List<T> = emptyList(),
20+
var past: PersistentList<T> = persistentListOf(),
1721
var present: T,
18-
var future: List<T> = emptyList(),
22+
var future: PersistentList<T> = persistentListOf(),
1923
)
2024

2125
private sealed interface UndoAction
@@ -30,41 +34,41 @@ private fun <T> undoReducer(preState: UndoState<T>, action: UndoAction): UndoSta
3034
return when (action) {
3135
Undo -> {
3236
if (past.isEmpty()) return preState
33-
val newPresent = past[past.size - 1]
34-
val newPast = past.dropLast(1)
3537
preState.copy(
36-
past = newPast,
37-
present = newPresent,
38-
future = listOf(present) + future
38+
past = past.mutate {
39+
it.removeLast()
40+
},
41+
present = past[past.size - 1],
42+
future = persistentListOf(present) + future
3943
)
4044
}
4145
Redo -> {
4246
if (future.isEmpty()) return preState
43-
val newPresent = future[0]
44-
val newFuture = future.drop(1)
4547
preState.copy(
46-
past = past + listOf(present),
47-
present = newPresent,
48-
future = newFuture
48+
past = past + present,
49+
present = future[0],
50+
future = future.mutate {
51+
it.removeFirst()
52+
}
4953
)
5054
}
5155

5256
is Set<*> -> {
5357
val (payload) = action as Set<T>
5458
if (present === payload) return preState
5559
preState.copy(
56-
past = past + listOf(present),
60+
past = past + present,
5761
present = payload!!,
58-
future = emptyList()
62+
future = persistentListOf()
5963
)
6064
}
6165

6266
is Reset<*> -> {
6367
val (payload) = action as Reset<T>
6468
preState.copy(
65-
past = emptyList(),
69+
past = persistentListOf(),
6670
present = payload!!,
67-
future = emptyList()
71+
future = persistentListOf()
6872
)
6973
}
7074
}

0 commit comments

Comments
 (0)