mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-27 11:09:18 +03:00
Start reworking draft (simplify)
This commit is contained in:
parent
f030e098a8
commit
aa0520d47d
5 changed files with 65 additions and 132 deletions
|
@ -101,8 +101,11 @@ class RxRoom(private val room: Room) {
|
||||||
return room.getEventReadReceiptsLive(eventId).asObservable()
|
return room.getEventReadReceiptsLive(eventId).asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveDrafts(): Observable<List<UserDraft>> {
|
fun liveDraft(): Observable<Optional<UserDraft>> {
|
||||||
return room.getDraftsLive().asObservable()
|
return room.getDraftLive().asObservable()
|
||||||
|
.startWithCallable {
|
||||||
|
room.getDraft().toOptional()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveNotificationState(): Observable<RoomNotificationState> {
|
fun liveNotificationState(): Observable<RoomNotificationState> {
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.matrix.android.sdk.api.session.room.send
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
|
||||||
interface DraftService {
|
interface DraftService {
|
||||||
|
|
||||||
|
@ -34,8 +35,12 @@ interface DraftService {
|
||||||
fun deleteDraft(callback: MatrixCallback<Unit>): Cancelable
|
fun deleteDraft(callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current drafts if any, as a live data
|
* Return the current draft or null
|
||||||
* The draft list can contain one draft for {regular, reply, quote} and an arbitrary number of {edit} drafts
|
|
||||||
*/
|
*/
|
||||||
fun getDraftsLive(): LiveData<List<UserDraft>>
|
fun getDraft(): UserDraft?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current draft if any, as a live data
|
||||||
|
*/
|
||||||
|
fun getDraftLive(): LiveData<Optional<UserDraft>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.session.room.send.DraftService
|
import org.matrix.android.sdk.api.session.room.send.DraftService
|
||||||
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.task.launchToCallback
|
import org.matrix.android.sdk.internal.task.launchToCallback
|
||||||
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||||
|
@ -55,7 +56,11 @@ internal class DefaultDraftService @AssistedInject constructor(@Assisted private
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDraftsLive(): LiveData<List<UserDraft>> {
|
override fun getDraft(): UserDraft? {
|
||||||
|
return draftRepository.getDraft(roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDraftLive(): LiveData<Optional<UserDraft>> {
|
||||||
return draftRepository.getDraftsLive(roomId)
|
return draftRepository.getDraftsLive(roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,35 +20,61 @@ package org.matrix.android.sdk.internal.session.room.draft
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.Transformations
|
import androidx.lifecycle.Transformations
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.kotlin.createObject
|
||||||
import org.matrix.android.sdk.BuildConfig
|
import org.matrix.android.sdk.BuildConfig
|
||||||
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
import org.matrix.android.sdk.api.util.toOptional
|
||||||
import org.matrix.android.sdk.internal.database.mapper.DraftMapper
|
import org.matrix.android.sdk.internal.database.mapper.DraftMapper
|
||||||
import org.matrix.android.sdk.internal.database.model.DraftEntity
|
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.UserDraftsEntity
|
import org.matrix.android.sdk.internal.database.model.UserDraftsEntity
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.kotlin.createObject
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DraftRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy) {
|
internal class DraftRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy) {
|
||||||
|
|
||||||
suspend fun saveDraft(roomId: String, userDraft: UserDraft) {
|
suspend fun saveDraft(roomId: String, userDraft: UserDraft) {
|
||||||
monarchy.awaitTransaction {
|
monarchy.awaitTransaction {
|
||||||
saveDraft(it, userDraft, roomId)
|
saveDraftInDb(it, userDraft, roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteDraft(roomId: String) {
|
suspend fun deleteDraft(roomId: String) {
|
||||||
monarchy.awaitTransaction {
|
monarchy.awaitTransaction {
|
||||||
deleteDraft(it, roomId)
|
deleteDraftFromDb(it, roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteDraft(realm: Realm, roomId: String) {
|
fun getDraft(roomId: String): UserDraft? {
|
||||||
|
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||||
|
UserDraftsEntity.where(realm, roomId).findFirst()
|
||||||
|
?.userDrafts
|
||||||
|
?.firstOrNull()
|
||||||
|
?.let {
|
||||||
|
DraftMapper.map(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDraftsLive(roomId: String): LiveData<Optional<UserDraft>> {
|
||||||
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
|
{ UserDraftsEntity.where(it, roomId) },
|
||||||
|
{
|
||||||
|
it.userDrafts.map { draft ->
|
||||||
|
DraftMapper.map(draft)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return Transformations.map(liveData) {
|
||||||
|
it.firstOrNull()?.firstOrNull().toOptional()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteDraftFromDb(realm: Realm, roomId: String) {
|
||||||
UserDraftsEntity.where(realm, roomId).findFirst()?.let { userDraftsEntity ->
|
UserDraftsEntity.where(realm, roomId).findFirst()?.let { userDraftsEntity ->
|
||||||
if (userDraftsEntity.userDrafts.isNotEmpty()) {
|
if (userDraftsEntity.userDrafts.isNotEmpty()) {
|
||||||
userDraftsEntity.userDrafts.removeAt(userDraftsEntity.userDrafts.size - 1)
|
userDraftsEntity.userDrafts.removeAt(userDraftsEntity.userDrafts.size - 1)
|
||||||
|
@ -56,7 +82,7 @@ class DraftRepository @Inject constructor(@SessionDatabase private val monarchy:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveDraft(realm: Realm, draft: UserDraft, roomId: String) {
|
private fun saveDraftInDb(realm: Realm, draft: UserDraft, roomId: String) {
|
||||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||||
?: realm.createObject(roomId)
|
?: realm.createObject(roomId)
|
||||||
|
|
||||||
|
@ -68,62 +94,15 @@ class DraftRepository @Inject constructor(@SessionDatabase private val monarchy:
|
||||||
userDraftsEntity.let { userDraftEntity ->
|
userDraftsEntity.let { userDraftEntity ->
|
||||||
// Save only valid draft
|
// Save only valid draft
|
||||||
if (draft.isValid()) {
|
if (draft.isValid()) {
|
||||||
// Add a new draft or update the current one?
|
// Replace the current draft
|
||||||
val newDraft = DraftMapper.map(draft)
|
val newDraft = DraftMapper.map(draft)
|
||||||
|
|
||||||
// Is it an update of the top draft?
|
|
||||||
val topDraft = userDraftEntity.userDrafts.lastOrNull()
|
|
||||||
|
|
||||||
if (topDraft == null) {
|
|
||||||
Timber.d("Draft: create a new draft ${privacySafe(draft)}")
|
Timber.d("Draft: create a new draft ${privacySafe(draft)}")
|
||||||
|
userDraftEntity.userDrafts.clear()
|
||||||
userDraftEntity.userDrafts.add(newDraft)
|
userDraftEntity.userDrafts.add(newDraft)
|
||||||
} else if (topDraft.draftMode == DraftEntity.MODE_EDIT) {
|
|
||||||
// top draft is an edit
|
|
||||||
if (newDraft.draftMode == DraftEntity.MODE_EDIT) {
|
|
||||||
if (topDraft.linkedEventId == newDraft.linkedEventId) {
|
|
||||||
// Update the top draft
|
|
||||||
Timber.d("Draft: update the top edit draft ${privacySafe(draft)}")
|
|
||||||
topDraft.content = newDraft.content
|
|
||||||
} else {
|
|
||||||
// Check a previously EDIT draft with the same id
|
|
||||||
val existingEditDraftOfSameEvent = userDraftEntity.userDrafts.find {
|
|
||||||
it.draftMode == DraftEntity.MODE_EDIT && it.linkedEventId == newDraft.linkedEventId
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingEditDraftOfSameEvent != null) {
|
|
||||||
// Ignore the new text, restore what was typed before, by putting the draft to the top
|
|
||||||
Timber.d("Draft: restore a previously edit draft ${privacySafe(draft)}")
|
|
||||||
userDraftEntity.userDrafts.remove(existingEditDraftOfSameEvent)
|
|
||||||
userDraftEntity.userDrafts.add(existingEditDraftOfSameEvent)
|
|
||||||
} else {
|
|
||||||
Timber.d("Draft: add a new edit draft ${privacySafe(draft)}")
|
|
||||||
userDraftEntity.userDrafts.add(newDraft)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Add a new regular draft to the top
|
|
||||||
Timber.d("Draft: add a new draft ${privacySafe(draft)}")
|
|
||||||
userDraftEntity.userDrafts.add(newDraft)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Top draft is not an edit
|
|
||||||
if (newDraft.draftMode == DraftEntity.MODE_EDIT) {
|
|
||||||
Timber.d("Draft: create a new edit draft ${privacySafe(draft)}")
|
|
||||||
userDraftEntity.userDrafts.add(newDraft)
|
|
||||||
} else {
|
|
||||||
// Update the top draft
|
|
||||||
Timber.d("Draft: update the top draft ${privacySafe(draft)}")
|
|
||||||
topDraft.draftMode = newDraft.draftMode
|
|
||||||
topDraft.content = newDraft.content
|
|
||||||
topDraft.linkedEventId = newDraft.linkedEventId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// There is no draft to save, so the composer was clear
|
// There is no draft to save, so the composer was clear
|
||||||
Timber.d("Draft: delete a draft")
|
Timber.d("Draft: delete a draft")
|
||||||
|
|
||||||
val topDraft = userDraftEntity.userDrafts.lastOrNull()
|
val topDraft = userDraftEntity.userDrafts.lastOrNull()
|
||||||
|
|
||||||
if (topDraft == null) {
|
if (topDraft == null) {
|
||||||
Timber.d("Draft: nothing to do")
|
Timber.d("Draft: nothing to do")
|
||||||
} else {
|
} else {
|
||||||
|
@ -135,20 +114,6 @@ class DraftRepository @Inject constructor(@SessionDatabase private val monarchy:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDraftsLive(roomId: String): LiveData<List<UserDraft>> {
|
|
||||||
val liveData = monarchy.findAllMappedWithChanges(
|
|
||||||
{ UserDraftsEntity.where(it, roomId) },
|
|
||||||
{
|
|
||||||
it.userDrafts.map { draft ->
|
|
||||||
DraftMapper.map(draft)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return Transformations.map(liveData) {
|
|
||||||
it.firstOrNull().orEmpty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun privacySafe(o: Any): Any {
|
private fun privacySafe(o: Any): Any {
|
||||||
if (BuildConfig.LOG_PRIVATE_DATA) {
|
if (BuildConfig.LOG_PRIVATE_DATA) {
|
||||||
return o
|
return o
|
||||||
|
|
|
@ -741,6 +741,9 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun popDraft() {
|
private fun popDraft() {
|
||||||
|
setState {
|
||||||
|
copy(sendMode = SendMode.REGULAR(""))
|
||||||
|
}
|
||||||
room.deleteDraft(NoOpMatrixCallback())
|
room.deleteDraft(NoOpMatrixCallback())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,73 +918,25 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEditAction(action: RoomDetailAction.EnterEditMode) {
|
private fun handleEditAction(action: RoomDetailAction.EnterEditMode) {
|
||||||
saveCurrentDraft(action.text)
|
|
||||||
|
|
||||||
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
||||||
setState { copy(sendMode = SendMode.EDIT(timelineEvent, action.text)) }
|
setState { copy(sendMode = SendMode.EDIT(timelineEvent, timelineEvent.getTextEditableContent() ?: "")) }
|
||||||
timelineEvent.root.eventId?.let {
|
|
||||||
room.saveDraft(UserDraft.EDIT(it, timelineEvent.getTextEditableContent() ?: ""), NoOpMatrixCallback())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleQuoteAction(action: RoomDetailAction.EnterQuoteMode) {
|
private fun handleQuoteAction(action: RoomDetailAction.EnterQuoteMode) {
|
||||||
saveCurrentDraft(action.text)
|
|
||||||
|
|
||||||
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
||||||
setState { copy(sendMode = SendMode.QUOTE(timelineEvent, action.text)) }
|
setState { copy(sendMode = SendMode.QUOTE(timelineEvent, action.text)) }
|
||||||
withState { state ->
|
|
||||||
// Save a new draft and keep the previously entered text, if it was not an edit
|
|
||||||
timelineEvent.root.eventId?.let {
|
|
||||||
if (state.sendMode is SendMode.EDIT) {
|
|
||||||
room.saveDraft(UserDraft.QUOTE(it, ""), NoOpMatrixCallback())
|
|
||||||
} else {
|
|
||||||
room.saveDraft(UserDraft.QUOTE(it, action.text), NoOpMatrixCallback())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleReplyAction(action: RoomDetailAction.EnterReplyMode) {
|
private fun handleReplyAction(action: RoomDetailAction.EnterReplyMode) {
|
||||||
saveCurrentDraft(action.text)
|
|
||||||
|
|
||||||
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
||||||
setState { copy(sendMode = SendMode.REPLY(timelineEvent, action.text)) }
|
setState { copy(sendMode = SendMode.REPLY(timelineEvent, action.text)) }
|
||||||
withState { state ->
|
|
||||||
// Save a new draft and keep the previously entered text, if it was not an edit
|
|
||||||
timelineEvent.root.eventId?.let {
|
|
||||||
if (state.sendMode is SendMode.EDIT) {
|
|
||||||
room.saveDraft(UserDraft.REPLY(it, ""), NoOpMatrixCallback())
|
|
||||||
} else {
|
|
||||||
room.saveDraft(UserDraft.REPLY(it, action.text), NoOpMatrixCallback())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun saveCurrentDraft(draft: String) {
|
|
||||||
// Save the draft with the current text if any
|
|
||||||
withState {
|
|
||||||
if (draft.isNotBlank()) {
|
|
||||||
when (it.sendMode) {
|
|
||||||
is SendMode.REGULAR -> room.saveDraft(UserDraft.REGULAR(draft), NoOpMatrixCallback())
|
|
||||||
is SendMode.REPLY -> room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, draft), NoOpMatrixCallback())
|
|
||||||
is SendMode.QUOTE -> room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, draft), NoOpMatrixCallback())
|
|
||||||
is SendMode.EDIT -> room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, draft), NoOpMatrixCallback())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleExitSpecialMode(action: RoomDetailAction.ExitSpecialMode) = withState {
|
private fun handleExitSpecialMode(action: RoomDetailAction.ExitSpecialMode) = withState {
|
||||||
if (it.sendMode is SendMode.EDIT) {
|
|
||||||
room.deleteDraft(NoOpMatrixCallback())
|
room.deleteDraft(NoOpMatrixCallback())
|
||||||
} else {
|
|
||||||
// Save a new draft and keep the previously entered text
|
|
||||||
room.saveDraft(UserDraft.REGULAR(action.text), NoOpMatrixCallback())
|
|
||||||
}
|
|
||||||
setState { copy(sendMode = SendMode.REGULAR(action.text)) }
|
setState { copy(sendMode = SendMode.REGULAR(action.text)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue