Skip to content

Commit c7dd2d5

Browse files
authored
Merge pull request #3132 from element-hq/feature/fga/draft_in_memory_when_editing
Draft : add volatile storage when moving to edit mode.
2 parents 3fb0c06 + 008ba4f commit c7dd2d5

File tree

10 files changed

+436
-124
lines changed

10 files changed

+436
-124
lines changed

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/draft/ComposerDraftService.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ import io.element.android.libraries.matrix.api.core.RoomId
2020
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
2121

2222
interface ComposerDraftService {
23-
suspend fun loadDraft(roomId: RoomId): ComposerDraft?
24-
suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?)
23+
suspend fun loadDraft(roomId: RoomId, isVolatile: Boolean): ComposerDraft?
24+
suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?, isVolatile: Boolean)
2525
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2024 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.messages.impl.draft
18+
19+
import io.element.android.libraries.matrix.api.core.RoomId
20+
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
21+
22+
interface ComposerDraftStore {
23+
suspend fun loadDraft(roomId: RoomId): ComposerDraft?
24+
suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?)
25+
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/draft/DefaultComposerDraftService.kt

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,44 +18,28 @@ package io.element.android.features.messages.impl.draft
1818

1919
import com.squareup.anvil.annotations.ContributesBinding
2020
import io.element.android.libraries.di.RoomScope
21-
import io.element.android.libraries.matrix.api.MatrixClient
2221
import io.element.android.libraries.matrix.api.core.RoomId
2322
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
24-
import timber.log.Timber
2523
import javax.inject.Inject
2624

2725
@ContributesBinding(RoomScope::class)
2826
class DefaultComposerDraftService @Inject constructor(
29-
private val client: MatrixClient,
27+
private val volatileComposerDraftStore: VolatileComposerDraftStore,
28+
private val matrixComposerDraftStore: MatrixComposerDraftStore,
3029
) : ComposerDraftService {
31-
override suspend fun loadDraft(roomId: RoomId): ComposerDraft? {
32-
return client.getRoom(roomId)?.use { room ->
33-
room.loadComposerDraft()
34-
.onFailure {
35-
Timber.e(it, "Failed to load composer draft for room $roomId")
36-
}
37-
.onSuccess { draft ->
38-
room.clearComposerDraft()
39-
Timber.d("Loaded composer draft for room $roomId : $draft")
40-
}
41-
.getOrNull()
42-
}
30+
override suspend fun loadDraft(roomId: RoomId, isVolatile: Boolean): ComposerDraft? {
31+
return getStore(isVolatile).loadDraft(roomId)
32+
}
33+
34+
override suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?, isVolatile: Boolean) {
35+
getStore(isVolatile).updateDraft(roomId, draft)
4336
}
4437

45-
override suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?) {
46-
client.getRoom(roomId)?.use { room ->
47-
val updateDraftResult = if (draft == null) {
48-
room.clearComposerDraft()
49-
} else {
50-
room.saveComposerDraft(draft)
51-
}
52-
updateDraftResult
53-
.onFailure {
54-
Timber.e(it, "Failed to update composer draft for room $roomId")
55-
}
56-
.onSuccess {
57-
Timber.d("Updated composer draft for room $roomId")
58-
}
38+
private fun getStore(isVolatile: Boolean): ComposerDraftStore {
39+
return if (isVolatile) {
40+
volatileComposerDraftStore
41+
} else {
42+
matrixComposerDraftStore
5943
}
6044
}
6145
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (c) 2024 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.messages.impl.draft
18+
19+
import io.element.android.libraries.matrix.api.MatrixClient
20+
import io.element.android.libraries.matrix.api.core.RoomId
21+
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
22+
import timber.log.Timber
23+
import javax.inject.Inject
24+
25+
/**
26+
* A draft store that persists drafts in the room state.
27+
* It can be used to store drafts that should be persisted across app restarts.
28+
*/
29+
class MatrixComposerDraftStore @Inject constructor(
30+
private val client: MatrixClient,
31+
) : ComposerDraftStore {
32+
override suspend fun loadDraft(roomId: RoomId): ComposerDraft? {
33+
return client.getRoom(roomId)?.use { room ->
34+
room.loadComposerDraft()
35+
.onFailure {
36+
Timber.e(it, "Failed to load composer draft for room $roomId")
37+
}
38+
.onSuccess { draft ->
39+
room.clearComposerDraft()
40+
Timber.d("Loaded composer draft for room $roomId : $draft")
41+
}
42+
.getOrNull()
43+
}
44+
}
45+
46+
override suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?) {
47+
client.getRoom(roomId)?.use { room ->
48+
val updateDraftResult = if (draft == null) {
49+
room.clearComposerDraft()
50+
} else {
51+
room.saveComposerDraft(draft)
52+
}
53+
updateDraftResult
54+
.onFailure {
55+
Timber.e(it, "Failed to update composer draft for room $roomId")
56+
}
57+
.onSuccess {
58+
Timber.d("Updated composer draft for room $roomId")
59+
}
60+
}
61+
}
62+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2024 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.messages.impl.draft
18+
19+
import io.element.android.libraries.matrix.api.core.RoomId
20+
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
21+
import javax.inject.Inject
22+
23+
/**
24+
* A volatile draft store that keeps drafts in memory only.
25+
* It can be used to store drafts that should not be persisted across app restarts.
26+
* Currently it's used to store draft message when moving to edit mode.
27+
*/
28+
class VolatileComposerDraftStore @Inject constructor() : ComposerDraftStore {
29+
private val drafts: MutableMap<RoomId, ComposerDraft> = mutableMapOf()
30+
31+
override suspend fun loadDraft(roomId: RoomId): ComposerDraft? {
32+
// Remove the draft from the map when it is loaded
33+
return drafts.remove(roomId)
34+
}
35+
36+
override suspend fun updateDraft(roomId: RoomId, draft: ComposerDraft?) {
37+
if (draft == null) {
38+
drafts.remove(roomId)
39+
} else {
40+
drafts[roomId] = draft
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)