diff --git a/app/src/main/java/io/xxlabs/messenger/data/room/dao/ContactsDao.kt b/app/src/main/java/io/xxlabs/messenger/data/room/dao/ContactsDao.kt
index 22f51acbd00c9cba5c4c8d7ed5b21fd1d83197da..b3ab9ba44e9af59c52ccbf075551e030ea1efc19 100644
--- a/app/src/main/java/io/xxlabs/messenger/data/room/dao/ContactsDao.kt
+++ b/app/src/main/java/io/xxlabs/messenger/data/room/dao/ContactsDao.kt
@@ -43,15 +43,6 @@ interface ContactsDao {
     @Query("SELECT * FROM Contacts WHERE status = :status")
     fun getAllContactsWithStatusLive(status: Int): LiveData<List<ContactData>>
 
-    @Query("SELECT * FROM Contacts WHERE username LIKE :username ORDER BY username")
-    fun queryAllContactsUsername(username: String): Single<List<ContactData>>
-
-    @Query("SELECT * FROM Contacts WHERE email LIKE :email ORDER BY email")
-    fun queryAllContactsEmail(email: String): Single<List<ContactData>>
-
-    @Query("SELECT * FROM Contacts WHERE phone LIKE :phone ORDER BY phone")
-    fun queryAllContactsPhone(phone: String): Single<List<ContactData>>
-
     @Query("SELECT * FROM Contacts WHERE username = :username LIMIT 1")
     fun queryContactByUsername(username: String): Maybe<ContactData>
 
diff --git a/app/src/main/java/io/xxlabs/messenger/repository/DaoRepository.kt b/app/src/main/java/io/xxlabs/messenger/repository/DaoRepository.kt
index 984f129f6b37d6a96d2f39f503501e003cbd14ff..7974c3decce1e20d0f467d5ee64f67ed1bbebc9a 100644
--- a/app/src/main/java/io/xxlabs/messenger/repository/DaoRepository.kt
+++ b/app/src/main/java/io/xxlabs/messenger/repository/DaoRepository.kt
@@ -406,15 +406,6 @@ class DaoRepository @Inject constructor(
         }
     }
 
-    fun connectionsUsernameSearch(username: String): Single<List<ContactData>> =
-        contactsDao.queryAllContactsUsername(username)
-
-    fun connectionsEmailSearch(email: String): Single<List<ContactData>> =
-        contactsDao.queryAllContactsEmail(email)
-
-    fun connectionsPhoneSearch(phone: String): Single<List<ContactData>> =
-        contactsDao.queryAllContactsPhone(phone)
-
     companion object {
         @Volatile
         private var instance: DaoRepository? = null
diff --git a/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestItem.kt b/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestItem.kt
index 2a6dd4b1278f82558afb7683b64857b84fc3d9f9..ecac901ecaab0c59ba878fa4cd6dc179d474aef4 100644
--- a/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestItem.kt
+++ b/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestItem.kt
@@ -21,7 +21,7 @@ sealed class RequestItem(val request: Request) : ItemThumbnail {
     abstract val subtitle: String?
     abstract val details: String?
 
-    val actionLabel: String? =
+    open val actionLabel: String? =
         when (request.requestStatus) {
             VERIFYING -> appContext().getString(R.string.request_item_action_verifying)
             VERIFICATION_FAIL -> appContext().getString(R.string.request_item_action_failed_verification)
@@ -30,7 +30,7 @@ sealed class RequestItem(val request: Request) : ItemThumbnail {
             else -> null
         }
 
-    val actionIcon: Int? =
+    open val actionIcon: Int? =
         when (request.requestStatus) {
             VERIFICATION_FAIL -> R.drawable.ic_info_outline_24dp
             SEND_FAIL, SENT -> R.drawable.ic_retry
@@ -38,7 +38,7 @@ sealed class RequestItem(val request: Request) : ItemThumbnail {
             else -> null
         }
 
-    val actionIconColor: Int? =
+    open val actionIconColor: Int? =
         when (request.requestStatus) {
             VERIFICATION_FAIL -> R.color.accent_danger
             SEND_FAIL, SENT-> R.color.brand_default
@@ -47,7 +47,7 @@ sealed class RequestItem(val request: Request) : ItemThumbnail {
         }
 
     @IdRes
-    val actionTextStyle: Int? =
+    open val actionTextStyle: Int? =
         when (request.requestStatus) {
             VERIFYING -> R.style.request_item_verifying
             VERIFICATION_FAIL -> R.style.request_item_error
@@ -76,6 +76,27 @@ private fun ContactRequest.getContactInfo(): String? =
         }
     }
 
+data class ContactRequestSearchResultItem(
+    val contactRequest: ContactRequest,
+    val photo: Bitmap? = null,
+    val statusText: String = "Request pending",
+    val statusTextColor: Int = R.color.neutral_weak,
+    val actionVisible: Boolean = true
+) : RequestItem(contactRequest) {
+    override val subtitle: String = statusText
+    override val details: String? = null
+    override val itemPhoto: Bitmap? = photo
+    override val itemInitials: String = contactRequest.model.initials
+    override val itemIconRes: Int? = null
+
+    // Request search results always have the "SENT" UI even if it's failed.
+    // Instead, the failed status is described in the statusText property.
+    override val actionLabel: String = if (actionVisible) appContext().getString(R.string.request_item_action_retry) else ""
+    override val actionIcon: Int = R.drawable.ic_retry
+    override val actionIconColor: Int = R.color.brand_default
+    override val actionTextStyle: Int = R.style.request_item_retry
+}
+
 data class GroupInviteItem(
     val invite: GroupInvitation,
 //    val membersList: List<MemberItem>,
diff --git a/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestItemViewHolder.kt b/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestItemViewHolder.kt
index 443f00a3872d701f1866de7bb074f9e8d2b022f0..c6fcb9191c8692f73adb18d2ac220236f29b98bc 100644
--- a/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestItemViewHolder.kt
+++ b/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestItemViewHolder.kt
@@ -9,6 +9,7 @@ import io.xxlabs.messenger.R
 import io.xxlabs.messenger.databinding.ListItemEmptyPlaceholderBinding
 import io.xxlabs.messenger.databinding.ListItemHiddenRequestsToggleBinding
 import io.xxlabs.messenger.databinding.ListItemRequestBinding
+import io.xxlabs.messenger.databinding.ListItemRequestSearchResultBinding
 import io.xxlabs.messenger.databinding.ListItemSectionDividerBinding
 import timber.log.Timber
 import java.io.InvalidObjectException
@@ -39,6 +40,31 @@ class RequestViewHolder(
     }
 }
 
+/**
+ * For Connection Requests being shown as search results on the Search screen.
+ */
+class RequestSearchResultViewHolder(
+    private val binding: ListItemRequestSearchResultBinding
+) : RequestItemViewHolder(binding.root) {
+
+    override fun onBind(ui: RequestItem, listener: RequestItemListener) {
+        binding.ui = ui as ContactRequestSearchResultItem
+        binding.listener = listener
+        listener.markAsSeen(ui)
+    }
+
+    companion object {
+        fun create(parent: ViewGroup): RequestSearchResultViewHolder {
+            val binding = ListItemRequestSearchResultBinding.inflate(
+                LayoutInflater.from(parent.context),
+                parent,
+                false
+            )
+            return RequestSearchResultViewHolder(binding)
+        }
+    }
+}
+
 class ConnectionViewHolder(
     private val binding: ListItemRequestBinding
 ) : RequestItemViewHolder(binding.root) {
diff --git a/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestsAdapter.kt b/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestsAdapter.kt
index 6a97f17dea98ff998c64920dfa041848697b35cf..abfdd264a78328e32c8a2056548625cb47abbdf9 100644
--- a/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestsAdapter.kt
+++ b/app/src/main/java/io/xxlabs/messenger/requests/ui/list/adapter/RequestsAdapter.kt
@@ -11,8 +11,9 @@ class RequestsAdapter(
 
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RequestItemViewHolder {
         return when (ViewType.from(viewType)) {
-            CONNECTION, SEARCH -> ConnectionViewHolder.create(parent)
+            CONNECTION, UD_SEARCH_RESULT -> ConnectionViewHolder.create(parent)
             REQUEST, INVITE -> RequestViewHolder.create(parent)
+            REQUEST_SEARCH_RESULT -> RequestSearchResultViewHolder.create(parent)
             PLACEHOLDER -> Placeholder.create(parent)
             SWITCH -> HiddenRequestToggle.create(parent)
             DIVIDER -> ConnectionsSectionDivider.create(parent)
@@ -35,8 +36,9 @@ class RequestsAdapter(
                 is EmptyPlaceholderItem -> PLACEHOLDER.value
                 is HiddenRequestToggleItem -> SWITCH.value
                 is AcceptedConnectionItem -> CONNECTION.value
-                is SearchResultItem -> SEARCH.value
+                is SearchResultItem -> UD_SEARCH_RESULT.value
                 is ConnectionsDividerItem -> DIVIDER.value
+                is ContactRequestSearchResultItem -> REQUEST_SEARCH_RESULT.value
                 else -> OTHER.value
             }
             status + model
@@ -49,9 +51,10 @@ class RequestsAdapter(
         PLACEHOLDER(300),
         SWITCH(400),
         CONNECTION(500),
-        SEARCH(600),
+        UD_SEARCH_RESULT(600),
         DIVIDER(700),
-        OTHER(800);
+        REQUEST_SEARCH_RESULT(800),
+        OTHER(900);
 
         companion object {
             fun from(value: Int): ViewType {
diff --git a/app/src/main/java/io/xxlabs/messenger/search/UserSearchViewModel.kt b/app/src/main/java/io/xxlabs/messenger/search/UserSearchViewModel.kt
index 56bf1d68c87df8401bdcb92b48e66f4b6994ce01..730d2159d629368963eb9deaeda62e28b8559e1a 100644
--- a/app/src/main/java/io/xxlabs/messenger/search/UserSearchViewModel.kt
+++ b/app/src/main/java/io/xxlabs/messenger/search/UserSearchViewModel.kt
@@ -1,12 +1,17 @@
 package io.xxlabs.messenger.search
 
 import android.graphics.Bitmap
-import android.text.*
+import android.text.Editable
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.Spanned
 import android.text.style.ForegroundColorSpan
 import androidx.lifecycle.*
+import bindings.Fact
 import io.xxlabs.messenger.R
 import io.xxlabs.messenger.bindings.wrapper.contact.ContactWrapperBase
 import io.xxlabs.messenger.data.data.Country
+import io.xxlabs.messenger.data.datatype.ContactRequestState
 import io.xxlabs.messenger.data.datatype.FactType
 import io.xxlabs.messenger.data.datatype.RequestStatus
 import io.xxlabs.messenger.data.room.model.ContactData
@@ -25,7 +30,8 @@ import io.xxlabs.messenger.ui.dialog.info.createInfoDialog
 import io.xxlabs.messenger.ui.dialog.info.createTwoButtonDialogUi
 import io.xxlabs.messenger.ui.main.countrycode.CountrySelectionListener
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import timber.log.Timber
@@ -291,26 +297,33 @@ class UserSearchViewModel @Inject constructor(
 
         _udSearchUi.value = searchRunningState
         viewModelScope.launch {
-            /*  TODO: When the username matches a connections' nickname, search UD too.
-                Two users can have the same displayName when
-                one username matches the other nickname.
-            */
             clearPreviousResults(resultsEmitter)
-            searchConnections(factQuery).run {
-                if (isNotEmpty()) {
-                    resultsEmitter.emitResults(this)
-                } else {
-                    null
-                }
-            } ?: searchUd(factQuery)?.run {
-                resultsEmitter.emitResults(listOf(this))
-            } ?: resultsEmitter.emitResults(noResultsFor(factQuery))
+            val udResult = searchUd(factQuery)
+            val requestResults = searchRequests(factQuery)
+            val connectionResults = searchConnections(factQuery)
+
+            val remoteResults = udResult?.let {
+                listOf(udResult) + requestResults
+            } ?: requestResults
+
+            val sortedResults = if (remoteResults.isEmpty()) {
+                connectionResults.ifEmpty { noResultsFor(factQuery) }
+            } else {
+                if (connectionResults.isEmpty()) remoteResults
+                else remoteResults + listOf(ConnectionsDividerItem()) + connectionResults
+            }
+
+            resultsEmitter.emitResults(sortedResults)
         }
     }
 
     private fun isValidQuery(factQuery: FactQuery): Boolean {
         // Prevent users from searching (and possibly requesting) themselves.
-        return !preferences.userData.contains(factQuery.fact, true)
+        return with (factQuery.fact) {
+            this != repo.getStoredUsername()
+                    && this != repo.getStoredEmail()
+                    && this !=  repo.getStoredPhone()
+        }
     }
 
     private fun noResultsFor(factQuery: FactQuery): List<RequestItem> =
@@ -330,59 +343,109 @@ class UserSearchViewModel @Inject constructor(
         _udSearchUi.value = searchCompleteState
     }
 
-    private suspend fun searchConnections(factQuery: FactQuery): List<RequestItem> {
-        return when (factQuery.type) {
-            FactType.USERNAME -> {
-                daoRepo.connectionsUsernameSearch(factQuery.fact)
-                    .value()
-                    .asAcceptedConnections()
-            }
-            FactType.EMAIL -> {
-                daoRepo.connectionsEmailSearch(factQuery.fact)
-                    .value()
-                    .asAcceptedConnections()
-            }
-            FactType.PHONE -> {
-                daoRepo.connectionsPhoneSearch(factQuery.fact)
-                    .value()
-                    .asAcceptedConnections()
+    private suspend fun savedUsers(): List<ContactData> =
+        daoRepo.getAllContacts().value()
+
+    private fun ContactData.isConnection(): Boolean =
+        RequestStatus.from(status) == RequestStatus.ACCEPTED
+
+    private fun ContactData.isRequest(): Boolean = !isConnection()
+
+    private suspend fun searchConnections(factQuery: FactQuery): List<RequestItem> =
+        withContext(Dispatchers.IO) {
+            when (factQuery.type) {
+                FactType.USERNAME -> {
+                    savedUsers().filter {
+                        it.isConnection() && it.displayName.contains(factQuery.fact)
+                    }.asConnectionsSearchResult()
+                }
+                FactType.EMAIL -> {
+                    savedUsers().filter {
+                        it.isConnection() && it.email.contains(factQuery.fact)
+                    }.asConnectionsSearchResult()
+                }
+                FactType.PHONE -> {
+                    savedUsers().filter {
+                        it.isConnection() && it.phone.contains(factQuery.fact)
+                    }.asConnectionsSearchResult()
+                }
+                else -> listOf()
             }
-            else -> listOf()
         }
-    }
 
-    private suspend fun List<ContactData>.asAcceptedConnections(): List<RequestItem> {
-        val requests = filter {
-            it.status != RequestStatus.ACCEPTED.value
-        }.toSet()
-        // Separate into a sublist of accepted connections and requests.
-        val contacts = this - requests
-
-        // Wrap the requests as SearchResultItems for UI layer.
-        val requestItems = requests.map {
-            SearchResultItem(
-                ContactRequestData(it),
-                resolveBitmap(it.photo)
-            )
-        }.toMutableList()
+    private suspend fun searchRequests(factQuery: FactQuery): List<RequestItem> =
+        withContext(Dispatchers.IO) {
+            when (factQuery.type) {
+                FactType.USERNAME -> {
+                    savedUsers().filter {
+                        it.isRequest() && it.displayName.contains(factQuery.fact)
+                    }.asRequestsSearchResult()
+                }
+                FactType.EMAIL -> {
+                    savedUsers().filter {
+                        it.isRequest() && it.email.contains(factQuery.fact)
+                    }.asRequestsSearchResult()
+                }
+                FactType.PHONE -> {
+                    savedUsers().filter {
+                        it.isRequest() && it.phone.contains(factQuery.fact)
+                    }.asRequestsSearchResult()
+                }
+                else -> listOf()
+            }
+        }
 
-        // Wrap the connections as AcceptedConnectionItems for UI layer.
-        val contactItems = contacts.map {
+    private suspend fun List<ContactData>.asConnectionsSearchResult(): List<RequestItem> =
+        map {
             val requestData = ContactRequestData(it)
             AcceptedConnectionItem(
                 requestData,
                 resolveBitmap(it.photo)
             )
         }
-        val localResults = if (contacts.isNotEmpty()) {
-            requestItems + ConnectionsDividerItem() + contactItems
-        } else {
-            requestItems
+
+    private suspend fun List<ContactData>.asRequestsSearchResult(): List<RequestItem> =
+        map {
+            ContactRequestSearchResultItem(
+                contactRequest = ContactRequestData(it),
+                photo = resolveBitmap(it.photo),
+                statusText = it.statusText(),
+                statusTextColor = it.statusTextColor(),
+                actionVisible= it.actionVisible()
+            )
+        }
+
+    private fun ContactData.statusText(): String {
+        return when (RequestStatus.from(status)) {
+            RequestStatus.SENT,
+            RequestStatus.VERIFIED,
+            RequestStatus.RESET_SENT -> "Request pending"
+
+            RequestStatus.SEND_FAIL,
+            RequestStatus.CONFIRM_FAIL,
+            RequestStatus.VERIFICATION_FAIL,
+            RequestStatus.RESET_FAIL -> "Request failed"
+
+            else -> ""
+        }
+    }
+
+    private fun ContactData.statusTextColor(): Int {
+        return when (RequestStatus.from(status)) {
+            RequestStatus.SEND_FAIL,
+            RequestStatus.CONFIRM_FAIL,
+            RequestStatus.VERIFICATION_FAIL,
+            RequestStatus.RESET_FAIL ->  R.color.accent_danger
+
+            else -> R.color.neutral_weak
+        }
+    }
+
+    private fun ContactData.actionVisible(): Boolean {
+        return when (RequestStatus.from(status)) {
+            RequestStatus.VERIFIED -> false
+            else -> true
         }
-        Timber.d("RequestItems: ${requestItems.size} entries")
-        Timber.d("ContactItems: ${contactItems.size} entries")
-        Timber.d("LocalResults: ${localResults.size} entries")
-        return localResults
     }
 
     private suspend fun resolveBitmap(data: ByteArray?): Bitmap? = withContext(Dispatchers.IO) {
@@ -394,7 +457,9 @@ class UserSearchViewModel @Inject constructor(
             val udResult = repo.searchUd(factQuery.fact, factQuery.type).value()
             udResult.second?.let { // Error message
                 if (it.isNotEmpty()) {
-                    showToast(it)
+                    if (!it.contains("no results found", true)) {
+                        showToast(it)
+                    }
                     noResultPlaceholder(factQuery)
                 } else { // Search result
                     udResult.first?.asSearchResult() ?: noResultPlaceholder(factQuery)
diff --git a/app/src/main/res/layout/list_item_request_search_result.xml b/app/src/main/res/layout/list_item_request_search_result.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6dcf6c880a13c4dc0eab50d1f8818468c0295fa9
--- /dev/null
+++ b/app/src/main/res/layout/list_item_request_search_result.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+        <import type="io.xxlabs.messenger.ui.main.chat.DrawablePosition"/>
+        <variable
+            name="ui"
+            type="io.xxlabs.messenger.requests.ui.list.adapter.ContactRequestSearchResultItem" />
+        <variable
+            name="listener"
+            type="io.xxlabs.messenger.requests.ui.list.adapter.RequestItemListener" />
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/requestItemLayout"
+        android:padding="16dp"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:onClick="@{() -> listener.onItemClicked(ui)}" >
+
+        <include
+            android:id="@+id/requestProfilePhoto"
+            layout="@layout/component_item_thumbnail"
+            android:layout_width="@dimen/spacing_42"
+            android:layout_height="@dimen/spacing_42"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:ui="@{ui}" />
+
+        <TextView
+            android:id="@+id/requestTitle"
+            style="@style/request_item_header"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="4dp"
+            android:text="@{ui.title}"
+            app:layout_constraintBottom_toTopOf="@+id/requestSubtitle"
+            app:layout_constraintEnd_toStartOf="@id/requestAction"
+            app:layout_constraintHorizontal_bias="0.0"
+            app:layout_constraintStart_toEndOf="@id/requestProfilePhoto"
+            app:layout_constraintTop_toTopOf="@id/requestProfilePhoto"
+            tools:text="bartender007" />
+
+        <TextView
+            android:id="@+id/requestSubtitle"
+            style="@style/request_item_subheader"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{ui.subtitle}"
+            android:visibility="@{ui.subtitle}"
+            android:textColor="@{ui.statusTextColor}"
+            app:layout_constraintEnd_toStartOf="@id/requestAction"
+            app:layout_constraintHorizontal_bias="0.0"
+            app:layout_constraintStart_toEndOf="@id/requestProfilePhoto"
+            app:layout_constraintTop_toBottomOf="@id/requestTitle"
+            tools:text="Request pending"
+            tools:textColor="@color/neutral_weak"/>
+
+        <io.xxlabs.messenger.support.view.SingleClickTextView
+            android:id="@+id/requestAction"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{ui.actionLabel}"
+            android:padding="12dp"
+            android:layout_marginHorizontal="0dp"
+            android:drawablePadding="4dp"
+            android:onClick="@{() -> listener.onActionClicked(ui)}"
+            android:visibility="@{ui.actionLabel}"
+            android:singleLine="false"
+            android:gravity="center_vertical|end"
+            app:actionIcon="@{ui.actionIcon}"
+            app:iconColor="@{ui.actionIconColor}"
+            app:iconPosition="@{DrawablePosition.START}"
+            app:customStyle="@{ui.actionTextStyle}"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            tools:drawableLeft="@drawable/ic_retry"
+            tools:text="Resend"
+            style="@style/request_item_subheader"/>
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>
\ No newline at end of file