mirror of
https://github.com/oxen-io/session-android.git
synced 2023-12-14 02:53:01 +01:00
fix: migrate some dependencies and functionality out of VM into repository to remove content resolver and context dependecy so tests pass again
This commit is contained in:
parent
162debdcf1
commit
637dc80d48
5 changed files with 68 additions and 34 deletions
|
@ -79,7 +79,6 @@ import org.session.libsession.messaging.messages.signal.OutgoingTextMessage
|
||||||
import org.session.libsession.messaging.messages.visible.Reaction
|
import org.session.libsession.messaging.messages.visible.Reaction
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApi.Capability
|
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
|
||||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
||||||
|
@ -250,7 +249,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||||
}
|
}
|
||||||
} ?: finish()
|
} ?: finish()
|
||||||
}
|
}
|
||||||
viewModelFactory.create(applicationContext, threadId, MessagingModuleConfiguration.shared.getUserED25519KeyPair(), contentResolver)
|
viewModelFactory.create(threadId, MessagingModuleConfiguration.shared.getUserED25519KeyPair())
|
||||||
}
|
}
|
||||||
private var actionMode: ActionMode? = null
|
private var actionMode: ActionMode? = null
|
||||||
private var unreadCount = 0
|
private var unreadCount = 0
|
||||||
|
@ -309,8 +308,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||||
handleSwipeToReply(message)
|
handleSwipeToReply(message)
|
||||||
},
|
},
|
||||||
onItemLongPress = { message, position, view ->
|
onItemLongPress = { message, position, view ->
|
||||||
if (!isMessageRequestThread() &&
|
if (!viewModel.isMessageRequestThread &&
|
||||||
(viewModel.openGroup == null || Capability.REACTIONS.name.lowercase() in viewModel.serverCapabilities)
|
viewModel.canReactToMessages
|
||||||
) {
|
) {
|
||||||
showEmojiPicker(message, view)
|
showEmojiPicker(message, view)
|
||||||
} else {
|
} else {
|
||||||
|
@ -762,7 +761,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
||||||
val recipient = viewModel.recipient ?: return false
|
val recipient = viewModel.recipient ?: return false
|
||||||
if (!isMessageRequestThread()) {
|
if (!viewModel.isMessageRequestThread) {
|
||||||
ConversationMenuHelper.onPrepareOptionsMenu(
|
ConversationMenuHelper.onPrepareOptionsMenu(
|
||||||
menu,
|
menu,
|
||||||
menuInflater,
|
menuInflater,
|
||||||
|
@ -848,12 +847,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isMessageRequestThread(): Boolean {
|
|
||||||
val recipient = viewModel.recipient ?: return false
|
|
||||||
if (recipient.isLocalNumber) return false
|
|
||||||
return !recipient.isGroupRecipient && !recipient.isApproved
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isOutgoingMessageRequestThread(): Boolean {
|
private fun isOutgoingMessageRequestThread(): Boolean {
|
||||||
val recipient = viewModel.recipient ?: return false
|
val recipient = viewModel.recipient ?: return false
|
||||||
return !recipient.isGroupRecipient &&
|
return !recipient.isGroupRecipient &&
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
package org.thoughtcrime.securesms.conversation.v2
|
package org.thoughtcrime.securesms.conversation.v2
|
||||||
|
|
||||||
import android.content.ContentResolver
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.cash.copper.flow.observeQuery
|
|
||||||
import com.goterl.lazysodium.utils.KeyPair
|
import com.goterl.lazysodium.utils.KeyPair
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
@ -19,22 +16,17 @@ import org.session.libsession.messaging.open_groups.OpenGroup
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.utilities.SessionId
|
import org.session.libsession.messaging.utilities.SessionId
|
||||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
import org.session.libsession.utilities.Address
|
|
||||||
import org.session.libsession.utilities.GroupUtil
|
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.utilities.IdPrefix
|
import org.session.libsignal.utilities.IdPrefix
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders
|
|
||||||
import org.thoughtcrime.securesms.database.Storage
|
import org.thoughtcrime.securesms.database.Storage
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.repository.ConversationRepository
|
import org.thoughtcrime.securesms.repository.ConversationRepository
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
class ConversationViewModel(
|
class ConversationViewModel(
|
||||||
private val context: Context,
|
|
||||||
val threadId: Long,
|
val threadId: Long,
|
||||||
val edKeyPair: KeyPair?,
|
val edKeyPair: KeyPair?,
|
||||||
private val contentResolver: ContentResolver,
|
|
||||||
private val repository: ConversationRepository,
|
private val repository: ConversationRepository,
|
||||||
private val storage: Storage
|
private val storage: Storage
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
@ -55,11 +47,7 @@ class ConversationViewModel(
|
||||||
get() = _recipient.value?.let { recipient ->
|
get() = _recipient.value?.let { recipient ->
|
||||||
when {
|
when {
|
||||||
recipient.isOpenGroupOutboxRecipient -> recipient
|
recipient.isOpenGroupOutboxRecipient -> recipient
|
||||||
recipient.isOpenGroupInboxRecipient -> Recipient.from(
|
recipient.isOpenGroupInboxRecipient -> repository.maybeGetBlindedRecipient(recipient)
|
||||||
context,
|
|
||||||
Address.fromSerialized(GroupUtil.getDecodedOpenGroupInboxSessionId(recipient.address.serialize())),
|
|
||||||
false
|
|
||||||
)
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,12 +67,22 @@ class ConversationViewModel(
|
||||||
?.let { SessionId(IdPrefix.BLINDED, it) }?.hexString
|
?.let { SessionId(IdPrefix.BLINDED, it) }?.hexString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isMessageRequestThread : Boolean
|
||||||
|
get() {
|
||||||
|
val recipient = recipient ?: return false
|
||||||
|
return !recipient.isLocalNumber && !recipient.isGroupRecipient && !recipient.isApproved
|
||||||
|
}
|
||||||
|
|
||||||
|
val canReactToMessages: Boolean
|
||||||
|
// allow reactions if the open group is null (normal conversations) or the open group's capabilities include reactions
|
||||||
|
get() = (openGroup == null || OpenGroupApi.Capability.REACTIONS.name.lowercase() in serverCapabilities)
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
contentResolver.observeQuery(DatabaseContentProviders.Conversation.getUriForThread(threadId))
|
repository.recipientUpdateFlow(threadId)
|
||||||
.collect {
|
.collect { recipient ->
|
||||||
val recipientExists = storage.getRecipientForThread(threadId) != null
|
if (recipient == null && _uiState.value.conversationExists) {
|
||||||
if (!recipientExists && _uiState.value.conversationExists) {
|
|
||||||
_uiState.update { it.copy(conversationExists = false) }
|
_uiState.update { it.copy(conversationExists = false) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,21 +220,19 @@ class ConversationViewModel(
|
||||||
|
|
||||||
@dagger.assisted.AssistedFactory
|
@dagger.assisted.AssistedFactory
|
||||||
interface AssistedFactory {
|
interface AssistedFactory {
|
||||||
fun create(context: Context, threadId: Long, edKeyPair: KeyPair?, contentResolver: ContentResolver): Factory
|
fun create(threadId: Long, edKeyPair: KeyPair?): Factory
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
class Factory @AssistedInject constructor(
|
class Factory @AssistedInject constructor(
|
||||||
@Assisted private val threadId: Long,
|
@Assisted private val threadId: Long,
|
||||||
@Assisted private val edKeyPair: KeyPair?,
|
@Assisted private val edKeyPair: KeyPair?,
|
||||||
@Assisted private val contentResolver: ContentResolver,
|
|
||||||
@Assisted private val context: Context,
|
|
||||||
private val repository: ConversationRepository,
|
private val repository: ConversationRepository,
|
||||||
private val storage: Storage
|
private val storage: Storage
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
|
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
return ConversationViewModel(context, threadId, edKeyPair, contentResolver, repository, storage) as T
|
return ConversationViewModel(threadId, edKeyPair, repository, storage) as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.thoughtcrime.securesms.dependencies
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
object ContentModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun providesContentResolver(@ApplicationContext context: Context) =context.contentResolver
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,11 @@
|
||||||
package org.thoughtcrime.securesms.repository
|
package org.thoughtcrime.securesms.repository
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.content.Context
|
||||||
|
import app.cash.copper.flow.observeQuery
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import org.session.libsession.database.MessageDataProvider
|
import org.session.libsession.database.MessageDataProvider
|
||||||
import org.session.libsession.messaging.messages.Destination
|
import org.session.libsession.messaging.messages.Destination
|
||||||
import org.session.libsession.messaging.messages.control.MessageRequestResponse
|
import org.session.libsession.messaging.messages.control.MessageRequestResponse
|
||||||
|
@ -15,6 +21,7 @@ import org.session.libsession.utilities.GroupUtil
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.utilities.toHexString
|
import org.session.libsignal.utilities.toHexString
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseContentProviders
|
||||||
import org.thoughtcrime.securesms.database.DraftDatabase
|
import org.thoughtcrime.securesms.database.DraftDatabase
|
||||||
import org.thoughtcrime.securesms.database.LokiMessageDatabase
|
import org.thoughtcrime.securesms.database.LokiMessageDatabase
|
||||||
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
||||||
|
@ -35,6 +42,8 @@ import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
interface ConversationRepository {
|
interface ConversationRepository {
|
||||||
fun maybeGetRecipientForThreadId(threadId: Long): Recipient?
|
fun maybeGetRecipientForThreadId(threadId: Long): Recipient?
|
||||||
|
fun maybeGetBlindedRecipient(recipient: Recipient): Recipient?
|
||||||
|
fun recipientUpdateFlow(threadId: Long): Flow<Recipient?>
|
||||||
fun saveDraft(threadId: Long, text: String)
|
fun saveDraft(threadId: Long, text: String)
|
||||||
fun getDraft(threadId: Long): String?
|
fun getDraft(threadId: Long): String?
|
||||||
fun clearDrafts(threadId: Long)
|
fun clearDrafts(threadId: Long)
|
||||||
|
@ -75,6 +84,7 @@ interface ConversationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultConversationRepository @Inject constructor(
|
class DefaultConversationRepository @Inject constructor(
|
||||||
|
@ApplicationContext private val context: Context,
|
||||||
private val textSecurePreferences: TextSecurePreferences,
|
private val textSecurePreferences: TextSecurePreferences,
|
||||||
private val messageDataProvider: MessageDataProvider,
|
private val messageDataProvider: MessageDataProvider,
|
||||||
private val threadDb: ThreadDatabase,
|
private val threadDb: ThreadDatabase,
|
||||||
|
@ -87,13 +97,29 @@ class DefaultConversationRepository @Inject constructor(
|
||||||
private val storage: Storage,
|
private val storage: Storage,
|
||||||
private val lokiMessageDb: LokiMessageDatabase,
|
private val lokiMessageDb: LokiMessageDatabase,
|
||||||
private val sessionJobDb: SessionJobDatabase,
|
private val sessionJobDb: SessionJobDatabase,
|
||||||
private val configFactory: ConfigFactory
|
private val configFactory: ConfigFactory,
|
||||||
|
private val contentResolver: ContentResolver,
|
||||||
) : ConversationRepository {
|
) : ConversationRepository {
|
||||||
|
|
||||||
override fun maybeGetRecipientForThreadId(threadId: Long): Recipient? {
|
override fun maybeGetRecipientForThreadId(threadId: Long): Recipient? {
|
||||||
return threadDb.getRecipientForThreadId(threadId)
|
return threadDb.getRecipientForThreadId(threadId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun maybeGetBlindedRecipient(recipient: Recipient): Recipient? {
|
||||||
|
if (!recipient.isOpenGroupInboxRecipient) return null
|
||||||
|
return Recipient.from(
|
||||||
|
context,
|
||||||
|
Address.fromSerialized(GroupUtil.getDecodedOpenGroupInboxSessionId(recipient.address.serialize())),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun recipientUpdateFlow(threadId: Long): Flow<Recipient?> {
|
||||||
|
return contentResolver.observeQuery(DatabaseContentProviders.Conversation.getUriForThread(threadId)).map {
|
||||||
|
maybeGetRecipientForThreadId(threadId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun saveDraft(threadId: Long, text: String) {
|
override fun saveDraft(threadId: Long, text: String) {
|
||||||
if (text.isEmpty()) return
|
if (text.isEmpty()) return
|
||||||
val drafts = DraftDatabase.Drafts()
|
val drafts = DraftDatabase.Drafts()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.thoughtcrime.securesms.conversation.v2
|
package org.thoughtcrime.securesms.conversation.v2
|
||||||
|
|
||||||
import com.goterl.lazysodium.utils.KeyPair
|
import com.goterl.lazysodium.utils.KeyPair
|
||||||
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import org.hamcrest.CoreMatchers.endsWith
|
import org.hamcrest.CoreMatchers.endsWith
|
||||||
import org.hamcrest.CoreMatchers.equalTo
|
import org.hamcrest.CoreMatchers.equalTo
|
||||||
|
@ -30,13 +31,14 @@ class ConversationViewModelTest: BaseViewModelTest() {
|
||||||
private lateinit var recipient: Recipient
|
private lateinit var recipient: Recipient
|
||||||
|
|
||||||
private val viewModel: ConversationViewModel by lazy {
|
private val viewModel: ConversationViewModel by lazy {
|
||||||
ConversationViewModel(threadId, edKeyPair, mock(), repository, storage)
|
ConversationViewModel(threadId, edKeyPair, repository, storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
recipient = mock(Recipient::class.java)
|
recipient = mock(Recipient::class.java)
|
||||||
whenever(repository.maybeGetRecipientForThreadId(anyLong())).thenReturn(recipient)
|
whenever(repository.maybeGetRecipientForThreadId(anyLong())).thenReturn(recipient)
|
||||||
|
whenever(repository.recipientUpdateFlow(anyLong())).thenReturn(emptyFlow())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue