diff --git a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityBaseModule.kt b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityBaseModule.kt
index 04440909910ea632b9fc188346e687182bc2cb5d..1ea10c43e8175ef3f61985b1b933d6382da2e54a 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityBaseModule.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityBaseModule.kt
@@ -29,6 +29,7 @@ import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountViewModel
 import de.kuschku.quasseldroid.viewmodel.ChatViewModel
 import de.kuschku.quasseldroid.viewmodel.EditorViewModel
 import de.kuschku.quasseldroid.viewmodel.QuasselViewModel
+import de.kuschku.quasseldroid.viewmodel.QueryCreateViewModel
 
 @Module
 object ActivityBaseModule {
@@ -65,4 +66,10 @@ object ActivityBaseModule {
   @JvmStatic
   fun provideAccountViewModel(viewModelProvider: ViewModelProvider) =
     viewModelProvider[AccountViewModel::class.java]
+
+  @ActivityScope
+  @Provides
+  @JvmStatic
+  fun provideQueryCreateViewModel(viewModelProvider: ViewModelProvider) =
+    viewModelProvider[QueryCreateViewModel::class.java]
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
index 708d270603f7963fac12582efe41a0a771a9fa76..0b964f74867619125569554be096ae8f721b2311 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
@@ -171,14 +171,14 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     super.onNewIntent(intent)
     if (intent != null) {
       when {
-        intent.type == "text/plain"                                     -> {
+        intent.type == "text/plain"    -> {
           val text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT)
           if (text != null) {
             chatlineFragment?.replaceText(text)
             drawerLayout.closeDrawers()
           }
         }
-        intent.hasExtra(KEY_BUFFER_ID)                                  -> {
+        intent.hasExtra(KEY_BUFFER_ID) -> {
           chatViewModel.bufferId.onNext(BufferId(intent.getIntExtra(KEY_BUFFER_ID, -1)))
           chatViewModel.bufferOpened.onNext(Unit)
           if (intent.hasExtra(KEY_ACCOUNT_ID)) {
@@ -193,17 +193,19 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
             }
           }
         }
-        intent.hasExtra(KEY_AUTOCOMPLETE_TEXT)                          -> {
+        intent.hasExtra(KEY_AUTOCOMPLETE_TEXT)                            -> {
           chatlineFragment?.editorHelper?.appendText(
             intent.getStringExtra(KEY_AUTOCOMPLETE_TEXT),
             intent.getStringExtra(KEY_AUTOCOMPLETE_SUFFIX)
           )
           drawerLayout.closeDrawers()
         }
-        intent.hasExtra(KEY_NETWORK_ID) && intent.hasExtra(KEY_CHANNEL) -> {
+        intent.hasExtra(KEY_NETWORK_ID) && intent.hasExtra(KEY_CHANNEL)   -> {
           val networkId = NetworkId(intent.getIntExtra(KEY_NETWORK_ID, -1))
           val channel = intent.getStringExtra(KEY_CHANNEL)
 
+          val forceJoin = intent.getBooleanExtra(KEY_FORCE_JOIN, false)
+
           modelHelper.session.filter(Optional<ISession>::isPresent).firstElement().subscribe {
             it.orNull()?.also { session ->
               val info = session.bufferSyncer.find(
@@ -212,7 +214,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
                 type = Buffer_Type.of(Buffer_Type.ChannelBuffer)
               )
 
-              if (info != null) {
+              if (info != null && !forceJoin) {
                 ChatActivity.launch(this, bufferId = info.bufferId)
               } else {
                 modelHelper.allBuffers.map {
@@ -241,8 +243,51 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
             }
           }
         }
+        intent.hasExtra(KEY_NETWORK_ID) && intent.hasExtra(KEY_NICK_NAME) -> {
+          val networkId = NetworkId(intent.getIntExtra(KEY_NETWORK_ID, -1))
+          val channel = intent.getStringExtra(KEY_NICK_NAME)
+
+          val forceJoin = intent.getBooleanExtra(KEY_FORCE_JOIN, false)
+
+          modelHelper.session.filter(Optional<ISession>::isPresent).firstElement().subscribe {
+            it.orNull()?.also { session ->
+              val info = session.bufferSyncer.find(
+                bufferName = channel,
+                networkId = networkId,
+                type = Buffer_Type.of(Buffer_Type.QueryBuffer)
+              )
+
+              if (info != null && !forceJoin) {
+                ChatActivity.launch(this, bufferId = info.bufferId)
+              } else {
+                modelHelper.allBuffers.map {
+                  listOfNotNull(it.find {
+                    it.networkId == networkId &&
+                    it.bufferName == channel &&
+                    it.type.hasFlag(Buffer_Type.QueryBuffer)
+                  })
+                }.filter {
+                  it.isNotEmpty()
+                }.firstElement().toLiveData().observeForever {
+                  it?.firstOrNull()?.let { info ->
+                    ChatActivity.launch(this, bufferId = info.bufferId)
+                  }
+                }
+
+                session.bufferSyncer.find(
+                  networkId = networkId,
+                  type = Buffer_Type.of(Buffer_Type.StatusBuffer)
+                )?.let { statusInfo ->
+                  session.rpcHandler.sendInput(
+                    statusInfo, "/query $channel"
+                  )
+                }
+              }
+            }
+          }
+        }
         intent.scheme == "irc" ||
-        intent.scheme == "ircs"                                         -> {
+        intent.scheme == "ircs"                                           -> {
           val uri = intent.data
           if (uri != null) {
             val channelString = (uri.path.let { it ?: "" }.trimStart('/')) +
@@ -1097,6 +1142,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
     private const val KEY_ACCOUNT_ID = "account_id"
     private const val KEY_NETWORK_ID = "network_id"
     private const val KEY_CHANNEL = "channel"
+    private const val KEY_NICK_NAME = "nick_name"
+    private const val KEY_FORCE_JOIN = "force_join"
 
     // Instance state keys
     private const val KEY_OPEN_BUFFER = "open_buffer"
@@ -1111,18 +1158,22 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       autoCompleteText: CharSequence? = null,
       autoCompleteSuffix: String? = null,
       channel: String? = null,
+      nickName: String? = null,
       networkId: NetworkId? = null,
       bufferId: BufferId? = null,
-      accountId: Long? = null
+      accountId: Long? = null,
+      forceJoin: Boolean? = null
     ) = context.startActivity(
       intent(context,
              sharedText,
              autoCompleteText,
              autoCompleteSuffix,
              channel,
+             nickName,
              networkId,
              bufferId,
-             accountId)
+             accountId,
+             forceJoin)
     )
 
     fun intent(
@@ -1131,9 +1182,11 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       autoCompleteText: CharSequence? = null,
       autoCompleteSuffix: String? = null,
       channel: String? = null,
+      nickName: String? = null,
       networkId: NetworkId? = null,
       bufferId: BufferId? = null,
-      accountId: Long? = null
+      accountId: Long? = null,
+      forceJoin: Boolean? = null
     ) = Intent(context, ChatActivity::class.java).apply {
       if (sharedText != null) {
         type = "text/plain"
@@ -1154,6 +1207,12 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
       if (networkId != null && channel != null) {
         putExtra(KEY_NETWORK_ID, networkId.id)
         putExtra(KEY_CHANNEL, channel)
+      } else if (networkId != null && nickName != null) {
+        putExtra(KEY_NETWORK_ID, networkId.id)
+        putExtra(KEY_NICK_NAME, nickName)
+        if (forceJoin != null) {
+          putExtra(KEY_NICK_NAME, nickName)
+        }
       }
     }
   }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt
index e3baf6fb13f3bd4db350d46ec351db77589d915d..9b163bf382a4dc334faa3b35f0518a8c7659a631 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt
@@ -114,7 +114,7 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() {
       save.setText(R.string.label_saving)
       save.isEnabled = false
 
-      val networkId = NetworkId(network.selectedItemId.toInt())
+      val selectedNetworkId = NetworkId(network.selectedItemId.toInt())
       val channelName = name.text.toString().trim()
 
       val isInviteOnly = inviteOnly.isChecked
@@ -124,16 +124,17 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() {
 
       modelHelper.bufferSyncer.value?.orNull()?.let { bufferSyncer ->
         val existingBuffer = bufferSyncer.find(
-          networkId = networkId,
+          networkId = selectedNetworkId,
           type = Buffer_Type.of(Buffer_Type.ChannelBuffer),
           bufferName = channelName
         )
-        val existingChannel = modelHelper.networks.value?.get(networkId)?.ircChannel(channelName)
+        val existingChannel = modelHelper.networks.value?.get(selectedNetworkId)?.ircChannel(
+          channelName)
           .nullIf { it == IrcChannel.NULL }
         if (existingBuffer != null) {
           if (existingChannel == null) {
             bufferSyncer.find(
-              networkId = networkId,
+              networkId = selectedNetworkId,
               type = Buffer_Type.of(Buffer_Type.StatusBuffer)
             )?.let { statusBuffer ->
               modelHelper.session.value?.orNull()?.rpcHandler?.apply {
@@ -150,13 +151,13 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() {
           }
         } else {
           bufferSyncer.find(
-            networkId = networkId,
+            networkId = selectedNetworkId,
             type = Buffer_Type.of(Buffer_Type.StatusBuffer)
           )?.let { statusBuffer ->
             modelHelper.session.value?.orNull()?.rpcHandler?.apply {
               sendInput(statusBuffer, "/join $channelName")
               modelHelper.networks.switchMap {
-                it[networkId]?.liveIrcChannel(channelName)
+                it[selectedNetworkId]?.liveIrcChannel(channelName)
                 ?: Observable.empty()
               }.subscribe {
                 if (it.ircUsers().size <= 1) {
@@ -176,7 +177,7 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() {
                 activity?.let {
                   it.finish()
                   ChatActivity.launch(it,
-                                      networkId = networkId,
+                                      networkId = selectedNetworkId,
                                       channel = channelName
                   )
                 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt
index a9184d2689b890dd5cb6d09bfcc6cbf15388cc9d..36d589679fa653649db858b30d660a1e82d15bc8 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt
@@ -29,10 +29,8 @@ import androidx.appcompat.widget.AppCompatSpinner
 import androidx.lifecycle.Observer
 import butterknife.BindView
 import butterknife.ButterKnife
-import de.kuschku.libquassel.protocol.Buffer_Type
 import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.syncables.Network
-import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.ui.chat.ChatActivity
 import de.kuschku.quasseldroid.ui.chat.add.NetworkAdapter
@@ -92,23 +90,17 @@ class ChannelJoinFragment : ServiceBoundFragment() {
       join.setText(R.string.label_saving)
       join.isEnabled = false
 
-      val networkId = NetworkId(network.selectedItemId.toInt())
+      val selectedNetworkId = NetworkId(network.selectedItemId.toInt())
       val channelName = name.text.toString().trim()
 
-      modelHelper.bufferSyncer.value?.orNull()?.let { bufferSyncer ->
-        bufferSyncer.find(
-          networkId = networkId,
-          type = Buffer_Type.of(Buffer_Type.StatusBuffer)
-        )?.let { statusBuffer ->
-          modelHelper.session.value?.orNull()?.rpcHandler?.apply {
-            sendInput(statusBuffer, "/join $channelName")
-          }
-        }
-      }
-
       activity?.let {
         it.finish()
-        ChatActivity.launch(it, networkId = networkId, channel = channelName)
+        ChatActivity.launch(
+          it,
+          networkId = selectedNetworkId,
+          channel = channelName,
+          forceJoin = true
+        )
       }
     }
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt
index 6c8b37e5267236354c018f90a20863717683d47d..9244535cab8d0c3bed65778656bb241248e4825e 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt
@@ -19,21 +19,303 @@
 
 package de.kuschku.quasseldroid.ui.chat.add.query
 
+import android.graphics.Typeface
 import android.os.Bundle
+import android.text.Editable
+import android.text.SpannableString
+import android.text.TextWatcher
+import android.text.style.ForegroundColorSpan
+import android.text.style.StyleSpan
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.AdapterView
+import android.widget.Button
+import android.widget.EditText
+import androidx.appcompat.widget.AppCompatSpinner
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.DefaultItemAnimator
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import butterknife.BindView
 import butterknife.ButterKnife
+import com.bumptech.glide.Glide
+import com.bumptech.glide.ListPreloader
+import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader
+import com.bumptech.glide.util.FixedPreloadSizeProvider
+import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.quassel.syncables.IrcUser
+import de.kuschku.libquassel.quassel.syncables.Network
+import de.kuschku.libquassel.util.Optional
+import de.kuschku.libquassel.util.helpers.mapOrElse
+import de.kuschku.libquassel.util.helpers.mapSwitchMap
+import de.kuschku.libquassel.util.irc.IrcCaseMappers
+import de.kuschku.libquassel.util.irc.SenderColorUtil
+import de.kuschku.quasseldroid.GlideApp
 import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.settings.MessageSettings
+import de.kuschku.quasseldroid.ui.chat.ChatActivity
+import de.kuschku.quasseldroid.ui.chat.add.NetworkAdapter
+import de.kuschku.quasseldroid.ui.chat.add.NetworkItem
+import de.kuschku.quasseldroid.ui.chat.nicks.NickListAdapter
+import de.kuschku.quasseldroid.util.ColorContext
+import de.kuschku.quasseldroid.util.avatars.AvatarHelper
+import de.kuschku.quasseldroid.util.helper.combineLatest
+import de.kuschku.quasseldroid.util.helper.loadWithFallbacks
+import de.kuschku.quasseldroid.util.helper.styledAttributes
+import de.kuschku.quasseldroid.util.helper.toLiveData
+import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
 import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
+import de.kuschku.quasseldroid.viewmodel.data.Avatar
+import de.kuschku.quasseldroid.viewmodel.data.IrcUserItem
+import de.kuschku.quasseldroid.viewmodel.data.MatchMode
+import de.kuschku.quasseldroid.viewmodel.helper.EditorViewModelHelper
+import de.kuschku.quasseldroid.viewmodel.helper.QueryCreateViewModelHelper
+import io.reactivex.Observable
+import java.util.concurrent.TimeUnit
+import javax.inject.Inject
 
 class QueryCreateFragment : ServiceBoundFragment() {
+  @BindView(R.id.network)
+  lateinit var network: AppCompatSpinner
+
+  @BindView(R.id.name)
+  lateinit var name: EditText
+
+  @BindView(R.id.query)
+  lateinit var query: Button
+
+  @BindView(R.id.list)
+  lateinit var list: RecyclerView
+
+  @Inject
+  lateinit var ircFormatDeserializer: IrcFormatDeserializer
+
+  @Inject
+  lateinit var messageSettings: MessageSettings
+
+  @Inject
+  lateinit var modelHelper: QueryCreateViewModelHelper
 
   override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                             savedInstanceState: Bundle?): View? {
     val view = inflater.inflate(R.layout.add_query, container, false)
     ButterKnife.bind(this, view)
 
+    val networkId = NetworkId(arguments?.getInt("network_id", 0) ?: 0)
+
+    val networkAdapter = NetworkAdapter()
+    network.adapter = networkAdapter
+
+    network.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
+      override fun onNothingSelected(parent: AdapterView<*>?) {
+        modelHelper.queryCreate.networkId.onNext(NetworkId(0))
+      }
+
+      override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
+        modelHelper.queryCreate.networkId.onNext(networkAdapter.getItem(position).id)
+      }
+    }
+
+    var hasSetNetwork = false
+    modelHelper.networks.switchMap {
+      combineLatest(it.values.map(Network::liveNetworkInfo)).map {
+        it.map {
+          NetworkItem(it.networkId, it.networkName)
+        }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, NetworkItem::name))
+      }
+    }.toLiveData().observe(this, Observer {
+      if (it != null) {
+        networkAdapter.submitList(it)
+        if (!hasSetNetwork && networkId.isValidId() && it.isNotEmpty()) {
+          network.post {
+            val index = networkAdapter.indexOf(networkId)
+            if (index != null) {
+              network.setSelection(index)
+            }
+          }
+          hasSetNetwork = true
+        }
+      }
+    })
+
+    name.addTextChangedListener(object : TextWatcher {
+      override fun afterTextChanged(s: Editable?) {
+        modelHelper.queryCreate.nickName.onNext(s?.toString() ?: "")
+      }
+
+      override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
+      override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
+    })
+
+    val nickListAdapter = NickListAdapter(messageSettings, clickListener)
+    list.adapter = nickListAdapter
+    list.layoutManager = object : LinearLayoutManager(context) {
+      override fun supportsPredictiveItemAnimations() = false
+    }
+    list.itemAnimator = DefaultItemAnimator()
+
+    val senderColors = requireContext().theme.styledAttributes(
+      R.attr.senderColor0, R.attr.senderColor1, R.attr.senderColor2, R.attr.senderColor3,
+      R.attr.senderColor4, R.attr.senderColor5, R.attr.senderColor6, R.attr.senderColor7,
+      R.attr.senderColor8, R.attr.senderColor9, R.attr.senderColorA, R.attr.senderColorB,
+      R.attr.senderColorC, R.attr.senderColorD, R.attr.senderColorE, R.attr.senderColorF
+    ) {
+      IntArray(length()) {
+        getColor(it, 0)
+      }
+    }
+
+    val selfColor = requireContext().theme.styledAttributes(R.attr.colorForegroundSecondary) {
+      getColor(0, 0)
+    }
+
+    val colorContext = ColorContext(requireContext(), messageSettings)
+
+    val avatarSize = resources.getDimensionPixelSize(R.dimen.avatar_size)
+
+    val sizeProvider = FixedPreloadSizeProvider<List<Avatar>>(avatarSize, avatarSize)
+
+    val preloadModelProvider = object : ListPreloader.PreloadModelProvider<List<Avatar>> {
+      override fun getPreloadItems(position: Int) = listOfNotNull(
+        nickListAdapter[position]?.let { AvatarHelper.avatar(messageSettings, it) }
+      )
+
+      override fun getPreloadRequestBuilder(item: List<Avatar>) =
+        GlideApp.with(this@QueryCreateFragment).loadWithFallbacks(item)?.override(avatarSize)
+    }
+
+    val preloader = RecyclerViewPreloader(Glide.with(this), preloadModelProvider, sizeProvider, 10)
+
+    list.addOnScrollListener(preloader)
+
+    val nickData = combineLatest(modelHelper.networks, modelHelper.queryCreate.networkId)
+      .map { (networks, networkId) ->
+        Optional.ofNullable(networks[networkId])
+      }.mapSwitchMap {
+        it.liveIrcUsers()
+      }.mapOrElse(emptyList()).switchMap {
+        combineLatest<IrcUserItem>(
+          it.map<IrcUser, Observable<IrcUserItem>?> {
+            it.updates().map { user ->
+              IrcUserItem(
+                user.network().networkId(),
+                user.nick(),
+                "",
+                0,
+                user.realName(),
+                user.hostMask(),
+                user.isAway(),
+                user.network().isMyNick(user.nick()),
+                user.network().support("CASEMAPPING")
+              )
+            }
+          }
+        )
+      }
+
+    val searchedSortedNickData = combineLatest(modelHelper.queryCreate.nickName, nickData)
+      .map { (search, users) ->
+        users.filter {
+          it.nick.contains(search, ignoreCase = true)
+        }.map {
+          val matchMode = when {
+            it.nick.equals(search, ignoreCase = true)     -> MatchMode.EXACT
+            it.nick.startsWith(search, ignoreCase = true) -> MatchMode.START
+            else                                          -> MatchMode.CONTAINS
+          }
+
+          Pair(matchMode, it)
+        }.sortedBy { (_, user) ->
+          IrcCaseMappers.unicode.toLowerCaseNullable(user.nick)
+        }.sortedBy { (matchMode, _) ->
+          matchMode.priority
+        }.map { (_, user) ->
+          user
+        }
+      }
+
+    val nickDataThrottled = searchedSortedNickData
+      .distinctUntilChanged()
+      .throttleLast(100, TimeUnit.MILLISECONDS)
+
+    nickDataThrottled.map {
+      it.asSequence().map {
+        val nickName = it.nick
+        val senderColorIndex = SenderColorUtil.senderColor(nickName)
+        val rawInitial = nickName.trimStart(*EditorViewModelHelper.IGNORED_CHARS)
+                           .firstOrNull() ?: nickName.firstOrNull()
+        val initial = rawInitial?.toUpperCase().toString()
+        val useSelfColor = when (messageSettings.colorizeNicknames) {
+          MessageSettings.ColorizeNicknamesMode.ALL          -> false
+          MessageSettings.ColorizeNicknamesMode.ALL_BUT_MINE -> it.self
+          MessageSettings.ColorizeNicknamesMode.NONE         -> true
+        }
+        val senderColor = if (useSelfColor) selfColor else senderColors[senderColorIndex]
+
+        fun formatNick(nick: CharSequence): CharSequence {
+          val spannableString = SpannableString(nick)
+          spannableString.setSpan(
+            ForegroundColorSpan(senderColor),
+            0,
+            nick.length,
+            SpannableString.SPAN_INCLUSIVE_EXCLUSIVE
+          )
+          spannableString.setSpan(
+            StyleSpan(Typeface.BOLD),
+            0,
+            nick.length,
+            SpannableString.SPAN_INCLUSIVE_EXCLUSIVE
+          )
+          return spannableString
+        }
+        it.copy(
+          displayNick = formatNick(it.nick),
+          fallbackDrawable = colorContext.buildTextDrawable(initial, senderColor),
+          initial = initial,
+          modes = when (messageSettings.showPrefix) {
+            MessageSettings.ShowPrefixMode.ALL ->
+              it.modes
+            else                               ->
+              it.modes.substring(0, Math.min(it.modes.length, 1))
+          },
+          realname = ircFormatDeserializer.formatString(
+            it.realname.toString(), messageSettings.colorizeMirc
+          ),
+          avatarUrls = AvatarHelper.avatar(messageSettings, it, avatarSize)
+        )
+      }.sortedBy {
+        IrcCaseMappers[it.networkCasemapping].toLowerCase(it.nick.trimStart(*EditorViewModelHelper.IGNORED_CHARS))
+          .trimStart(*EditorViewModelHelper.IGNORED_CHARS)
+      }.sortedBy {
+        it.lowestMode
+      }.toList()
+    }.toLiveData().observe(this, Observer {
+      nickListAdapter.submitList(it)
+    })
+
+    query.setOnClickListener {
+      val selectedNetworkId = NetworkId(network.selectedItemId.toInt())
+      val nickName = name.text.toString().trim()
+
+      clickListener(selectedNetworkId, nickName)
+    }
+
     return view
   }
+
+  private val clickListener: ((NetworkId, String) -> Unit) = { selectedNetworkId, nickName ->
+    query.setText(R.string.label_saving)
+    query.isEnabled = false
+
+    activity?.let {
+      it.finish()
+      ChatActivity.launch(
+        it,
+        networkId = selectedNetworkId,
+        nickName = nickName,
+        forceJoin = true
+      )
+    }
+  }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
index 2b9874b35ccc348c311350771a76cd447c5e13ad..fafed3b75d596afaae5af7f6e994eaa8ac0383a2 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
@@ -582,17 +582,15 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
         .create()
     )
 
-    if (BuildConfig.DEBUG) {
-      fab.addActionItem(
-        SpeedDialActionItem.Builder(R.id.fab_query, R.drawable.ic_account)
-          .setFabBackgroundColor(fabBackground2)
-          .setFabImageTintColor(0xffffffffu.toInt())
-          .setLabel(R.string.label_query_medium)
-          .setLabelBackgroundColor(colorLabelBackground)
-          .setLabelColor(colorLabel)
-          .create()
-      )
-    }
+    fab.addActionItem(
+      SpeedDialActionItem.Builder(R.id.fab_query, R.drawable.ic_account)
+        .setFabBackgroundColor(fabBackground2)
+        .setFabImageTintColor(0xffffffffu.toInt())
+        .setLabel(R.string.label_query_medium)
+        .setLabelBackgroundColor(colorLabelBackground)
+        .setLabelColor(colorLabel)
+        .create()
+    )
 
     fab.setOnActionSelectedListener {
       val networkId = modelHelper.bufferData?.value?.network?.networkId()
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListAdapter.kt
index 5f26f1f30691c28baf5d29058e2235ce212e0507..81d95bc2e23fbfa70db878eb0ed96a7f007b0bf5 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListAdapter.kt
@@ -29,6 +29,7 @@ import androidx.recyclerview.widget.ListAdapter
 import androidx.recyclerview.widget.RecyclerView
 import butterknife.BindView
 import butterknife.ButterKnife
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.util.helpers.nullIf
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.settings.MessageSettings
@@ -41,7 +42,7 @@ import de.kuschku.quasseldroid.viewmodel.data.IrcUserItem
 
 class NickListAdapter(
   private val messageSettings: MessageSettings,
-  private val clickListener: ((String) -> Unit)? = null
+  private val clickListener: ((NetworkId, String) -> Unit)? = null
 ) : ListAdapter<IrcUserItem, NickListAdapter.NickViewHolder>(
   object : DiffUtil.ItemCallback<IrcUserItem>() {
     override fun areItemsTheSame(oldItem: IrcUserItem, newItem: IrcUserItem) =
@@ -84,7 +85,7 @@ class NickListAdapter(
 
   class NickViewHolder(
     itemView: View,
-    private val clickListener: ((String) -> Unit)? = null
+    private val clickListener: ((NetworkId, String) -> Unit)? = null
   ) : RecyclerView.ViewHolder(itemView) {
     @BindView(R.id.avatar)
     lateinit var avatar: ImageView
@@ -95,19 +96,19 @@ class NickListAdapter(
     @BindView(R.id.realname)
     lateinit var realname: TextView
 
-    var user: String? = null
+    var user: IrcUserItem? = null
 
     init {
       ButterKnife.bind(this, itemView)
       itemView.setOnClickListener {
         val nick = user
         if (nick != null)
-          clickListener?.invoke(nick)
+          clickListener?.invoke(nick.networkId, nick.nick)
       }
     }
 
     fun bind(data: IrcUserItem, messageSettings: MessageSettings) {
-      user = data.nick
+      user = data
 
       nick.text = SpanFormatter.format("%s%s", data.modes, data.displayNick ?: data.nick)
       realname.text = data.realname
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
index 997f840abb25755d2f4444bb22978151f943c2fe..2b81e90ff6519006fca863fded80ef24e0bc3697 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
@@ -38,6 +38,7 @@ import com.bumptech.glide.ListPreloader
 import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader
 import com.bumptech.glide.util.FixedPreloadSizeProvider
 import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.libquassel.util.irc.IrcCaseMappers
@@ -191,21 +192,19 @@ class NickListFragment : ServiceBoundFragment() {
     outState.putParcelable(KEY_STATE_LIST, nickList.layoutManager?.onSaveInstanceState())
   }
 
-  private val clickListener: ((String) -> Unit)? = { nick ->
+  private val clickListener: ((NetworkId, String) -> Unit) = { networkId, nick ->
     modelHelper.session.value?.orNull()?.bufferSyncer?.let { bufferSyncer ->
-      modelHelper.bufferData.value?.info?.let(BufferInfo::networkId)?.let { networkId ->
-        UserInfoActivity.launch(
-          requireContext(),
-          openBuffer = false,
-          bufferId = bufferSyncer.find(
-            bufferName = nick,
-            networkId = networkId,
-            type = Buffer_Type.of(Buffer_Type.QueryBuffer)
-          )?.let(BufferInfo::bufferId),
-          nick = nick,
-          networkId = networkId
-        )
-      }
+      UserInfoActivity.launch(
+        requireContext(),
+        openBuffer = false,
+        bufferId = bufferSyncer.find(
+          bufferName = nick,
+          networkId = networkId,
+          type = Buffer_Type.of(Buffer_Type.QueryBuffer)
+        )?.let(BufferInfo::bufferId),
+        nick = nick,
+        networkId = networkId
+      )
     }
   }
 
diff --git a/app/src/main/res/layout/add_join.xml b/app/src/main/res/layout/add_join.xml
index 6b039888cf7b61d9164697d41c54ba9f80fc8cee..abbdb7e7a92e37550ed9d4bcba903f6bcbeb4b73 100644
--- a/app/src/main/res/layout/add_join.xml
+++ b/app/src/main/res/layout/add_join.xml
@@ -30,39 +30,34 @@
     android:layout_height="0dip"
     android:layout_weight="1">
 
-    <androidx.core.widget.NestedScrollView
-      android:layout_width="match_parent"
-      android:layout_height="match_parent">
-
-      <LinearLayout style="@style/Widget.CoreSettings.Wrapper">
-
-        <de.kuschku.ui.spinner.MaterialSpinnerLayout
-          style="@style/Widget.CustomSpinnerLayout"
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:hint="@string/label_network">
+    <LinearLayout style="@style/Widget.CoreSettings.Wrapper">
 
-          <androidx.appcompat.widget.AppCompatSpinner
-            android:id="@+id/network"
-            style="@style/Widget.MaterialSpinner"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            tools:listitem="@layout/widget_spinner_item_material" />
-        </de.kuschku.ui.spinner.MaterialSpinnerLayout>
+      <de.kuschku.ui.spinner.MaterialSpinnerLayout
+        style="@style/Widget.CustomSpinnerLayout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:hint="@string/label_network">
 
-        <com.google.android.material.textfield.TextInputLayout
-          style="@style/Widget.CustomTextInput"
+        <androidx.appcompat.widget.AppCompatSpinner
+          android:id="@+id/network"
+          style="@style/Widget.MaterialSpinner"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
-          android:hint="@string/label_channel_name">
-
-          <com.google.android.material.textfield.TextInputEditText
-            android:id="@+id/name"
-            style="@style/Widget.CoreSettings.EditText"
-            tools:text="#trees" />
-        </com.google.android.material.textfield.TextInputLayout>
-      </LinearLayout>
-    </androidx.core.widget.NestedScrollView>
+          tools:listitem="@layout/widget_spinner_item_material" />
+      </de.kuschku.ui.spinner.MaterialSpinnerLayout>
+
+      <com.google.android.material.textfield.TextInputLayout
+        style="@style/Widget.CustomTextInput"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:hint="@string/label_channel_name">
+
+        <com.google.android.material.textfield.TextInputEditText
+          android:id="@+id/name"
+          style="@style/Widget.CoreSettings.EditText"
+          tools:text="#trees" />
+      </com.google.android.material.textfield.TextInputLayout>
+    </LinearLayout>
 
     <de.kuschku.quasseldroid.util.ui.view.ShadowView
       android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/add_query.xml b/app/src/main/res/layout/add_query.xml
index 15419319cb9314f4d75880509de63a2256c6c3ee..36a7193fa66ead803c16710ab136dee4706316f3 100644
--- a/app/src/main/res/layout/add_query.xml
+++ b/app/src/main/res/layout/add_query.xml
@@ -21,32 +21,76 @@
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
-  android:orientation="vertical"
-  android:padding="16dp">
+  android:orientation="vertical">
 
-  <de.kuschku.ui.spinner.MaterialSpinnerLayout
-    style="@style/Widget.CustomSpinnerLayout"
+  <FrameLayout
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:hint="@string/label_network">
+    android:layout_height="0dip"
+    android:layout_weight="1">
 
-    <androidx.appcompat.widget.AppCompatSpinner
-      android:id="@+id/network"
-      style="@style/Widget.MaterialSpinner"
+    <LinearLayout
       android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      tools:listitem="@layout/widget_spinner_item_material" />
-  </de.kuschku.ui.spinner.MaterialSpinnerLayout>
+      android:layout_height="match_parent"
+      android:orientation="vertical">
+
+      <LinearLayout style="@style/Widget.CoreSettings.Wrapper">
+
+        <de.kuschku.ui.spinner.MaterialSpinnerLayout
+          style="@style/Widget.CustomSpinnerLayout"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:hint="@string/label_network">
+
+          <androidx.appcompat.widget.AppCompatSpinner
+            android:id="@+id/network"
+            style="@style/Widget.MaterialSpinner"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            tools:listitem="@layout/widget_spinner_item_material" />
+        </de.kuschku.ui.spinner.MaterialSpinnerLayout>
+
+        <com.google.android.material.textfield.TextInputLayout
+          style="@style/Widget.CustomTextInput"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:hint="@string/label_nick">
 
-  <com.google.android.material.textfield.TextInputLayout
-    style="@style/Widget.CustomTextInput"
+          <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/name"
+            style="@style/Widget.CoreSettings.EditText"
+            tools:text="justJanne" />
+        </com.google.android.material.textfield.TextInputLayout>
+      </LinearLayout>
+
+      <de.kuschku.quasseldroid.util.ui.fastscroll.views.FastScrollRecyclerView
+        android:id="@+id/list"
+        style="@style/Widget.FastScroller"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        tools:listitem="@layout/widget_nick" />
+    </LinearLayout>
+
+    <de.kuschku.quasseldroid.util.ui.view.ShadowView
+      android:layout_width="match_parent"
+      android:layout_height="@dimen/shadow_height"
+      android:layout_gravity="bottom"
+      android:gravity="bottom" />
+  </FrameLayout>
+
+  <LinearLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:hint="@string/settings_aliasitem_name">
+    android:gravity="end"
+    android:paddingLeft="16dp"
+    android:paddingTop="4dp"
+    android:paddingRight="16dp"
+    android:paddingBottom="4dp">
 
-    <com.google.android.material.textfield.TextInputEditText
-      android:id="@+id/name"
-      style="@style/Widget.CoreSettings.EditText"
-      tools:text="back" />
-  </com.google.android.material.textfield.TextInputLayout>
+    <com.google.android.material.button.MaterialButton
+      android:id="@+id/query"
+      style="@style/Widget.Button.Colored"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:text="@string/label_query_medium" />
+  </LinearLayout>
 </LinearLayout>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1ad475ad628c724f1026e181863c9a8e425f029e..cdb820c65d9fe5b3d70ccfcd4b41bc15f64ce6c5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -88,6 +88,7 @@
   <string name="label_new_nick">New Nick</string>
   <string name="label_new_server">New Server</string>
   <string name="label_next">Next</string>
+  <string name="label_nick">Nick</string>
   <string name="label_nicklist">Nick List</string>
   <string name="label_no">No</string>
   <string name="label_no_away_message">No away message available</string>
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt
index f5ea7a7540bafa50dcc78f1dda7475173058900d..41a5a1d01568b6bdfba7009fd6a3b61d82bde2e8 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt
@@ -363,7 +363,7 @@ class Network constructor(
       }
       proxy.synchronize(ircUser)
       _ircUsers[nick] = ircUser
-      live_ircUsers.onNext(_ircUsers)
+      live_ircUsers.onNext(Unit)
       ircUser
     } else {
       user
@@ -376,7 +376,15 @@ class Network constructor(
   }.distinctUntilChanged()
 
   fun ircUsers() = _ircUsers.values.toList()
+  fun liveIrcUsers() = live_ircUsers.map {
+    ircUsers()
+  }
+
   fun ircUserCount(): UInt = _ircUsers.size.toUInt()
+  fun liveIrcUserCount() = live_ircUsers.map {
+    ircUserCount()
+  }
+
   fun newIrcChannel(channelName: String, initData: QVariantMap = emptyMap(),
                     index: Int? = null): IrcChannel =
     ircChannel(channelName).let { channel ->
@@ -389,7 +397,7 @@ class Network constructor(
         }
         proxy.synchronize(ircChannel)
         _ircChannels[caseMapper.toLowerCase(channelName)] = ircChannel
-        live_ircChannels.onNext(_ircChannels)
+        live_ircChannels.onNext(Unit)
         ircChannel
       } else {
         channel
@@ -404,7 +412,15 @@ class Network constructor(
   }.distinctUntilChanged()
 
   fun ircChannels() = _ircChannels.values.toList()
+  fun liveIrcChannels() = live_ircChannels.map {
+    ircChannels()
+  }
+
   fun ircChannelCount(): UInt = _ircChannels.size.toUInt()
+  fun liveIrcChannelCount() = live_ircChannels.map {
+    ircChannelCount()
+  }
+
   fun codecForServer(): String = _codecForServer
   fun codecForEncoding(): String = _codecForEncoding
   fun codecForDecoding(): String = _codecForDecoding
@@ -824,18 +840,18 @@ class Network constructor(
   fun removeChansAndUsers() {
     _ircUsers.clear()
     _ircChannels.clear()
-    live_ircChannels.onNext(_ircChannels)
-    live_ircUsers.onNext(_ircUsers)
+    live_ircChannels.onNext(Unit)
+    live_ircUsers.onNext(Unit)
   }
 
   fun removeIrcUser(user: IrcUser) {
     _ircUsers.remove(caseMapper.toLowerCase(user.nick()))
-    live_ircUsers.onNext(_ircUsers)
+    live_ircUsers.onNext(Unit)
   }
 
   fun removeIrcChannel(channel: IrcChannel) {
     _ircChannels.remove(caseMapper.toLowerCase(channel.name()))
-    live_ircChannels.onNext(_ircChannels)
+    live_ircChannels.onNext(Unit)
   }
 
   fun copy(): Network {
@@ -880,10 +896,10 @@ class Network constructor(
   private var _channelModes: Map<ChannelModeType, Set<Char>>? = null
   // stores all known nicks for the server
   private var _ircUsers: MutableMap<String, IrcUser> = mutableMapOf()
-  private val live_ircUsers = BehaviorSubject.createDefault(emptyMap<String, IrcUser>())
+  private val live_ircUsers = BehaviorSubject.createDefault(Unit)
   // stores all known channels
   private var _ircChannels: MutableMap<String, IrcChannel> = mutableMapOf()
-  private val live_ircChannels = BehaviorSubject.createDefault(emptyMap<String, IrcChannel>())
+  private val live_ircChannels = BehaviorSubject.createDefault(Unit)
   // stores results from RPL_ISUPPORT
   private var _supports: MutableMap<String, String?> = mutableMapOf()
     set (value) {
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QueryCreateViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QueryCreateViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dd6a0e214b04a363d754676741ed1b7a54d998d4
--- /dev/null
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QueryCreateViewModel.kt
@@ -0,0 +1,29 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.viewmodel
+
+import androidx.lifecycle.ViewModel
+import de.kuschku.libquassel.protocol.NetworkId
+import io.reactivex.subjects.BehaviorSubject
+
+class QueryCreateViewModel : ViewModel() {
+  val networkId = BehaviorSubject.createDefault(NetworkId(0))
+  val nickName = BehaviorSubject.createDefault("")
+}
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferProps.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferProps.kt
index fafd4901aeda344ef274453a5667b5ea04b1fc4f..a183bc95efcf6b938e362787c2d2c4468cba4755 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferProps.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/BufferProps.kt
@@ -41,14 +41,8 @@ data class BufferProps(
   val ircUser: IrcUser? = null,
   val avatarUrls: List<Avatar> = emptyList(),
   val fallbackDrawable: Drawable? = null,
-  val matchMode: BufferMatchMode = BufferMatchMode.EXACT
+  val matchMode: MatchMode = MatchMode.EXACT
 ) {
-  enum class BufferMatchMode(val priority: Int) {
-    EXACT(0),
-    START(1),
-    CONTAINS(2);
-  }
-
   override fun equals(other: Any?): Boolean {
     if (this === other) return true
     if (javaClass != other?.javaClass) return false
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt
index 1c69bc98cc8e1832982aea0d7555dfa5d55c42ba..581f8132196b9af61fea6b7dd38835e36adac8f8 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt
@@ -20,8 +20,10 @@
 package de.kuschku.quasseldroid.viewmodel.data
 
 import android.graphics.drawable.Drawable
+import de.kuschku.libquassel.protocol.NetworkId
 
 data class IrcUserItem(
+  val networkId: NetworkId,
   val nick: String,
   val modes: String,
   val lowestMode: Int,
@@ -34,40 +36,4 @@ data class IrcUserItem(
   val initial: String? = "",
   val fallbackDrawable: Drawable? = null,
   val displayNick: CharSequence? = null
-) {
-  override fun equals(other: Any?): Boolean {
-    if (this === other) return true
-    if (javaClass != other?.javaClass) return false
-
-    other as IrcUserItem
-
-    if (nick != other.nick) return false
-    if (modes != other.modes) return false
-    if (lowestMode != other.lowestMode) return false
-    if (realname != other.realname) return false
-    if (hostmask != other.hostmask) return false
-    if (away != other.away) return false
-    if (self != other.self) return false
-    if (networkCasemapping != other.networkCasemapping) return false
-    if (avatarUrls != other.avatarUrls) return false
-    if (initial != other.initial) return false
-    if (displayNick != other.displayNick) return false
-
-    return true
-  }
-
-  override fun hashCode(): Int {
-    var result = nick.hashCode()
-    result = 31 * result + modes.hashCode()
-    result = 31 * result + lowestMode
-    result = 31 * result + realname.hashCode()
-    result = 31 * result + hostmask.hashCode()
-    result = 31 * result + away.hashCode()
-    result = 31 * result + self.hashCode()
-    result = 31 * result + (networkCasemapping?.hashCode() ?: 0)
-    result = 31 * result + avatarUrls.hashCode()
-    result = 31 * result + (initial?.hashCode() ?: 0)
-    result = 31 * result + (displayNick?.hashCode() ?: 0)
-    return result
-  }
-}
+)
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/MatchMode.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/MatchMode.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c0c4afac35fae3a1441895ce891cbc2f6b0bd142
--- /dev/null
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/MatchMode.kt
@@ -0,0 +1,26 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.viewmodel.data
+
+enum class MatchMode(val priority: Int) {
+  EXACT(0),
+  START(1),
+  CONTAINS(2);
+}
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt
index de1ce86a4575f906a7609fe552c784de946a3dc5..48a72a1283952065c64df7a892ae8e3b8a0172ca 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt
@@ -156,6 +156,7 @@ open class ChatViewModelHelper @Inject constructor(
                     }.min() ?: prefixModes.size
 
                     IrcUserItem(
+                      bufferInfo.networkId,
                       user.nick(),
                       network.modesToPrefixes(userModes),
                       lowestMode,
@@ -282,12 +283,9 @@ open class ChatViewModelHelper @Inject constructor(
                         val name = info.bufferName?.trim() ?: ""
                         val search = bufferSearch.trim()
                         val matchMode = when {
-                          name.equals(search, ignoreCase = true)     ->
-                            BufferProps.BufferMatchMode.EXACT
-                          name.startsWith(search, ignoreCase = true) ->
-                            BufferProps.BufferMatchMode.START
-                          else                                       ->
-                            BufferProps.BufferMatchMode.CONTAINS
+                          name.equals(search, ignoreCase = true)     -> MatchMode.EXACT
+                          name.startsWith(search, ignoreCase = true) -> MatchMode.START
+                          else                                       -> MatchMode.CONTAINS
                         }
                         when (info.type.toInt()) {
                           BufferInfo.Type.QueryBuffer.toInt()   -> {
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/QueryCreateViewModelHelper.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/QueryCreateViewModelHelper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c32e4005b8d85f8b9015f9b453b52d5207bab9c2
--- /dev/null
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/QueryCreateViewModelHelper.kt
@@ -0,0 +1,29 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.viewmodel.helper
+
+import de.kuschku.quasseldroid.viewmodel.QuasselViewModel
+import de.kuschku.quasseldroid.viewmodel.QueryCreateViewModel
+import javax.inject.Inject
+
+open class QueryCreateViewModelHelper @Inject constructor(
+  val queryCreate: QueryCreateViewModel,
+  quassel: QuasselViewModel
+) : QuasselViewModelHelper(quassel)