Skip to content
Snippets Groups Projects
Verified Commit 3628e8cd authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Implement Emoji Autocompletion and Replacement

parent de3934ce
Branches
Tags
No related merge requests found
Showing
with 6435 additions and 73 deletions
This diff is collapsed.
......@@ -27,7 +27,8 @@ data class AutoCompleteSettings(
val prefix: Boolean = true,
val nicks: Boolean = true,
val buffers: Boolean = true,
val aliases: Boolean = true
val aliases: Boolean = true,
val emoji: Boolean = true
) {
companion object {
val DEFAULT = AutoCompleteSettings()
......
......@@ -37,7 +37,8 @@ data class MessageSettings(
val showGravatarAvatars: Boolean = false,
val showMatrixAvatars: Boolean = false,
val largerEmoji: Boolean = false,
val highlightOwnMessages: Boolean = false
val highlightOwnMessages: Boolean = false,
val replaceEmoji: Boolean = true
) {
enum class ColorizeNicknamesMode {
......
......@@ -132,6 +132,10 @@ object Settings {
highlightOwnMessages = getBoolean(
context.getString(R.string.preference_highlight_own_messages_key),
MessageSettings.DEFAULT.highlightOwnMessages
),
replaceEmoji = getBoolean(
context.getString(R.string.preference_replace_emoji_key),
MessageSettings.DEFAULT.replaceEmoji
)
)
}
......@@ -216,6 +220,10 @@ object Settings {
aliases = getBoolean(
context.getString(R.string.preference_autocomplete_aliases_key),
AutoCompleteSettings.DEFAULT.aliases
),
emoji = getBoolean(
context.getString(R.string.preference_autocomplete_emoji_key),
AutoCompleteSettings.DEFAULT.emoji
)
)
}
......
......@@ -80,6 +80,11 @@ class AutoCompleteAdapter @Inject constructor(
.inflate(R.layout.widget_alias, parent, false),
clickListener = clickListener
)
VIEWTYPE_EMOJI -> AutoCompleteViewHolder.EmojiViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.widget_emoji, parent, false),
clickListener = clickListener
)
else -> throw IllegalArgumentException(
"Invoked with wrong item type"
)
......@@ -92,6 +97,7 @@ class AutoCompleteAdapter @Inject constructor(
when {
it is AutoCompleteItem.ChannelItem -> VIEWTYPE_CHANNEL
it is AutoCompleteItem.AliasItem -> VIEWTYPE_ALIAS
it is AutoCompleteItem.EmojiItem -> VIEWTYPE_EMOJI
it is AutoCompleteItem.UserItem && it.away -> VIEWTYPE_NICK_AWAY
else -> VIEWTYPE_NICK_ACTIVE
}
......@@ -105,6 +111,8 @@ class AutoCompleteAdapter @Inject constructor(
this.bindImpl(data, messageSettings)
data is AutoCompleteItem.AliasItem && this is AliasViewHolder ->
this.bindImpl(data, messageSettings)
data is AutoCompleteItem.EmojiItem && this is EmojiViewHolder ->
this.bindImpl(data, messageSettings)
else ->
throw IllegalArgumentException("Invoked with wrong item type")
}
......@@ -209,6 +217,35 @@ class AutoCompleteAdapter @Inject constructor(
expansion.text = data.expansion
}
}
class EmojiViewHolder(
itemView: View,
private val clickListener: ((String, String) -> Unit)? = null
) : AutoCompleteViewHolder(itemView) {
@BindView(R.id.emoji)
lateinit var emoji: TextView
@BindView(R.id.shortCode)
lateinit var shortCode: TextView
var value: AutoCompleteItem? = null
init {
ButterKnife.bind(this, itemView)
itemView.setOnClickListener {
val value = value
if (value != null)
clickListener?.invoke(value.name, value.suffix)
}
}
fun bindImpl(data: AutoCompleteItem.EmojiItem, messageSettings: MessageSettings) {
value = data
emoji.text = data.replacement
shortCode.text = data.shortCodes.joinToString(", ")
}
}
}
companion object {
......@@ -216,5 +253,6 @@ class AutoCompleteAdapter @Inject constructor(
const val VIEWTYPE_NICK_ACTIVE = 1
const val VIEWTYPE_NICK_AWAY = 2
const val VIEWTYPE_ALIAS = 3
const val VIEWTYPE_EMOJI = 4
}
}
......@@ -42,6 +42,7 @@ import de.kuschku.quasseldroid.settings.AutoCompleteSettings
import de.kuschku.quasseldroid.settings.MessageSettings
import de.kuschku.quasseldroid.util.ColorContext
import de.kuschku.quasseldroid.util.avatars.AvatarHelper
import de.kuschku.quasseldroid.util.emoji.EmojiData
import de.kuschku.quasseldroid.util.helper.styledAttributes
import de.kuschku.quasseldroid.util.helper.toLiveData
import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
......@@ -92,12 +93,14 @@ class AutoCompleteHelper(
(autoCompleteSettings.auto && query.length >= 3) ||
(autoCompleteSettings.prefix && autoCompleteSettings.nicks && query.startsWith('@')) ||
(autoCompleteSettings.prefix && autoCompleteSettings.buffers && query.startsWith('#')) ||
(autoCompleteSettings.prefix && autoCompleteSettings.aliases && query.startsWith('/'))
(autoCompleteSettings.prefix && autoCompleteSettings.aliases && query.startsWith('/')) ||
(autoCompleteSettings.prefix && autoCompleteSettings.emoji && query.startsWith(':'))
val list = if (shouldShowResults) it?.second.orEmpty() else emptyList()
val data = list.filter {
it is AutoCompleteItem.AliasItem && autoCompleteSettings.aliases ||
it is AutoCompleteItem.UserItem && autoCompleteSettings.nicks ||
it is AutoCompleteItem.ChannelItem && autoCompleteSettings.buffers
it is AutoCompleteItem.ChannelItem && autoCompleteSettings.buffers ||
it is AutoCompleteItem.EmojiItem && autoCompleteSettings.emoji
}.map {
when (it) {
is AutoCompleteItem.UserItem -> {
......@@ -184,15 +187,25 @@ class AutoCompleteHelper(
network.ircChannel(bufferInfo.bufferName) ?: IrcChannel.NULL
} else IrcChannel.NULL
val users = ircChannel.ircUsers()
fun processResults(list: List<AutoCompleteItem>) = list.filter {
it.name.trimStart(*IGNORED_CHARS)
fun filterStart(name: String): Boolean {
return name.trimStart(*IGNORED_CHARS)
.startsWith(
lastWord.first.trimStart(*IGNORED_CHARS),
ignoreCase = true
)
}.sorted()
}
fun filter(name: String): Boolean {
return name.trim(*IGNORED_CHARS)
.contains(
lastWord.first.trim(*IGNORED_CHARS),
ignoreCase = true
)
}
fun getAliases() = aliases.map {
fun getAliases() = aliases.filter {
filterStart(it.name ?: "")
}.map {
AutoCompleteItem.AliasItem(
"/${it.name}",
it.expansion
......@@ -200,6 +213,8 @@ class AutoCompleteHelper(
}
fun getBuffers() = infos.filter {
filterStart(it.bufferName ?: "")
}.filter {
it.type.toInt() == Buffer_Type.ChannelBuffer.toInt()
}.mapNotNull { info ->
networks[info.networkId]?.let { info to it }
......@@ -230,7 +245,9 @@ class AutoCompleteHelper(
emptySet()
}
fun getNicks() = getUsers().map { user ->
fun getNicks() = getUsers().filter {
filterStart(it.nick())
}.map { user ->
val userModes = ircChannel.userModes(user)
val prefixModes = network.prefixModes()
......@@ -249,12 +266,19 @@ class AutoCompleteHelper(
)
}
when (lastWord.first.firstOrNull()) {
'/' -> processResults(getAliases())
'@' -> processResults(getNicks())
'#' -> processResults(getBuffers())
else -> processResults(getNicks())
fun getEmojis() = EmojiData.processedEmojiMap.filter {
it.shortCodes.any {
it.contains(lastWord.first.trim(':'))
}
}
when (lastWord.first.firstOrNull()) {
'/' -> getAliases()
'@' -> getNicks()
'#' -> getBuffers()
':' -> getEmojis()
else -> getNicks()
}.sorted()
} else {
emptyList()
}
......@@ -269,7 +293,8 @@ class AutoCompleteHelper(
}?.filter {
it is AutoCompleteItem.AliasItem && autoCompleteSettings.aliases ||
it is AutoCompleteItem.UserItem && autoCompleteSettings.nicks ||
it is AutoCompleteItem.ChannelItem && autoCompleteSettings.buffers
it is AutoCompleteItem.ChannelItem && autoCompleteSettings.buffers ||
it is AutoCompleteItem.EmojiItem && autoCompleteSettings.emoji
}.orEmpty()
if (previous != null && originalWord.first == previous.originalWord && originalWord.second.start == previous.range.start) {
......
......@@ -39,6 +39,7 @@ import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.settings.AppearanceSettings
import de.kuschku.quasseldroid.settings.AutoCompleteSettings
import de.kuschku.quasseldroid.settings.MessageSettings
import de.kuschku.quasseldroid.util.emoji.EmojiData
import de.kuschku.quasseldroid.util.helper.*
import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer
......@@ -168,8 +169,12 @@ class ChatlineFragment : ServiceBoundFragment() {
}
fun send() {
if (chatline.safeText.isNotEmpty()) {
val lines = chatline.safeText.lineSequence().map {
val safeText =
if (messageSettings.replaceEmoji) EmojiData.replaceShortCodes(chatline.safeText)
else chatline.safeText
if (safeText.isNotEmpty()) {
val lines = safeText.lineSequence().map {
SpannableString(it).apply {
for (span in getSpans(0, length, Any::class.java)) {
if (getSpanFlags(span) and Spanned.SPAN_COMPOSING != 0) {
......
......@@ -76,6 +76,8 @@
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/handle"
android:visibility="gone"
tools:visibility="visible"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
......
<?xml version="1.0" encoding="utf-8"?><!--
Quasseldroid - Quassel client for Android
Copyright (c) 2019 Janne Mareike 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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/backgroundMenuItem"
android:minHeight="48dp"
android:paddingLeft="16dp"
android:paddingTop="8dp"
android:paddingRight="16dp"
android:paddingBottom="8dp">
<TextView
android:id="@+id/emoji"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical|start"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:fontFamily="sans-serif-medium"
android:gravity="center_vertical|start"
android:singleLine="true"
android:textColor="?attr/colorTextPrimary"
android:textSize="24dp"
tools:ignore="SpUsage"
tools:text="♥" />
<TextView
android:id="@+id/shortCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:singleLine="true"
android:textColor="?attr/colorTextSecondary"
android:textSize="12sp"
tools:text=":heart:" />
</LinearLayout>
......@@ -256,6 +256,10 @@
<string name="preference_highlight_own_messages_title">Highlight own messages</string>
<string name="preference_highlight_own_messages_summary">Shows your own messages more prominently</string>
<string name="preference_replace_emoji_key" translatable="false">replace_emoji</string>
<string name="preference_replace_emoji_title">Replace Emoji Shortcodes</string>
<string name="preference_replace_emoji_summary">Automatically replaces shortcodes such as :+1: or :like: with emoji</string>
<string name="preference_autocomplete_title">Autocomplete</string>
......@@ -288,6 +292,9 @@
<string name="preference_autocomplete_aliases_key" translatable="false">autocomplete_aliases</string>
<string name="preference_autocomplete_aliases_title">Autocomplete Commands</string>
<string name="preference_autocomplete_emoji_key" translatable="false">autocomplete_emoji</string>
<string name="preference_autocomplete_emoji_title">Autocomplete Emoji</string>
<string name="preference_backlog_title">Backlog</string>
......
......@@ -239,6 +239,11 @@
android:key="@string/preference_highlight_own_messages_key"
android:summary="@string/preference_highlight_own_messages_summary"
android:title="@string/preference_highlight_own_messages_title" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/preference_replace_emoji_key"
android:summary="@string/preference_replace_emoji_summary"
android:title="@string/preference_replace_emoji_title" />
</PreferenceCategory>
<PreferenceCategory android:layout="@layout/widget_preference_divider" />
......@@ -289,6 +294,11 @@
android:defaultValue="true"
android:key="@string/preference_autocomplete_aliases_key"
android:title="@string/preference_autocomplete_aliases_title" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/preference_autocomplete_emoji_key"
android:title="@string/preference_autocomplete_emoji_title" />
</PreferenceCategory>
<PreferenceCategory android:layout="@layout/widget_preference_divider" />
......
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Mareike 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.testutil
object StringEscaper {
private fun escape(text: String): String {
val stringBuilder = StringBuilder()
escape(stringBuilder, text)
return stringBuilder.toString()
}
private fun escape(stringBuilder: StringBuilder, text: String) {
for (char in text) {
escape(stringBuilder, char)
}
}
private fun escape(stringBuilder: StringBuilder, text: Char) {
if (text > '\u007f') {
// write \udddd
stringBuilder.append("\\u")
val hex = StringBuffer(Integer.toHexString(text.toInt()))
hex.reverse()
val length = 4 - hex.length
for (j in 0 until length) {
hex.append('0')
}
for (j in 0..3) {
stringBuilder.append(hex[3 - j])
}
} else {
stringBuilder.append(Character.toString(text))
}
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Mareike 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.util.emoji
import android.text.SpannableStringBuilder
import de.kuschku.quasseldroid.QuasseldroidTest
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@Config(application = QuasseldroidTest::class)
@RunWith(RobolectricTestRunner::class)
class EmojiDataTest {
@Test
fun replaceShortCodes() {
assertEquals("\ud83d\udc4d", replaceShortCodes(":like:"))
assertEquals("this\ud83d\udc4disa\ud83d\udc1e\ud83d\udc4dtest",
replaceShortCodes("this:like:isa:beetle::+1:test"))
}
companion object {
private fun replaceShortCodes(text: String): String =
EmojiData.replaceShortCodes(SpannableStringBuilder(text)).toString()
}
}
......@@ -20,6 +20,8 @@
package de.kuschku.quasseldroid.util.emoji
import android.os.Build
import android.text.Editable
import de.kuschku.quasseldroid.viewmodel.data.AutoCompleteItem
object EmojiData {
val rawEmojiMap = mapOf(
......@@ -1637,7 +1639,7 @@ object EmojiData {
"secret" to "\u3299\uFE0F"
)
val emojiMap = rawEmojiMap + mapOf(
val emojiReplacementMap = mapOf(
// Aliases imported from IRCCloud and Slack
"like" to rawEmojiMap["+1"],
"doge" to rawEmojiMap["dog"],
......@@ -1646,20 +1648,9 @@ object EmojiData {
"party_popper" to rawEmojiMap["tada"],
"shock" to rawEmojiMap["scream"],
"atom" to rawEmojiMap["atom_symbol"],
"<3" to rawEmojiMap["heart"],
"</3" to rawEmojiMap["broken_heart"],
")" to rawEmojiMap["smiley"],
"')" to rawEmojiMap["smiley"],
"-)" to rawEmojiMap["disappointed"],
"(" to rawEmojiMap["cry"],
"_(" to rawEmojiMap["sob"],
"loudly_crying_face" to rawEmojiMap["sob"],
"sad_tears" to rawEmojiMap["sob"],
"bawl" to rawEmojiMap["sob"],
";)" to rawEmojiMap["wink"],
";p" to rawEmojiMap["stuck_out_tongue_winking_eye"],
"simple_smile" to ":)",
"slightly_smiling_face" to ":)",
"ufo" to rawEmojiMap["flying_saucer"],
"throwing_up" to rawEmojiMap["face_with_open_mouth_vomiting"],
"being_sick" to rawEmojiMap["face_with_open_mouth_vomiting"],
......@@ -1699,6 +1690,24 @@ object EmojiData {
"steam_train" to rawEmojiMap["steam_locomotive"]
)
val emojiAsciiMap = mapOf(
"<3" to rawEmojiMap["heart"],
"</3" to rawEmojiMap["broken_heart"],
")" to rawEmojiMap["smiley"],
"')" to rawEmojiMap["smiley"],
"-)" to rawEmojiMap["disappointed"],
"(" to rawEmojiMap["cry"],
"_(" to rawEmojiMap["sob"],
";)" to rawEmojiMap["wink"],
";p" to rawEmojiMap["stuck_out_tongue_winking_eye"]
)
val processedEmojiMap = (rawEmojiMap + emojiReplacementMap).toList()
.groupBy(Pair<String, String?>::second, Pair<String, String?>::first)
.map { (replacement, shortCodes) ->
AutoCompleteItem.EmojiItem(shortCodes.sorted(), replacement ?: "")
}
val conversionMap = mapOf(
"\u0030\u20E3" to "\uDBBA\uDC37", // ZERO
"\u0031\u20E3" to "\uDBBA\uDC2E", // ONE
......@@ -1741,5 +1750,22 @@ object EmojiData {
)
} else emptyMap()
val emojis = EmojiData.conversionMap.values.toSet() + EmojiData.rawEmojiMap.values.toSet() + "\u200d" + "\ufe0f"
val emojis = conversionMap.values.toSet() + rawEmojiMap.values.toSet() + "\u200d" + "\ufe0f"
fun replaceShortCodes(source: Editable): Editable {
var result = source
for (emoji in processedEmojiMap) {
for (rawShortCode in emoji.shortCodes) {
val shortCode = ":$rawShortCode:"
var index: Int
while (true) {
index = result.indexOf(shortCode)
if (index == -1) break
result = result.replace(index, index + shortCode.length, emoji.replacement)
}
}
}
return result
}
}
......@@ -131,4 +131,27 @@ sealed class AutoCompleteItem(open val name: String, val suffix: String, private
return result
}
}
data class EmojiItem(
val shortCodes: List<String>,
val replacement: String
) : AutoCompleteItem(replacement, " ", 3) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as EmojiItem
if (shortCodes != other.shortCodes) return false
if (replacement != other.replacement) return false
return true
}
override fun hashCode(): Int {
var result = shortCodes.hashCode()
result = 31 * result + replacement.hashCode()
return result
}
}
}
......@@ -32,6 +32,7 @@ import de.kuschku.libquassel.util.helper.combineLatest
import de.kuschku.libquassel.util.helper.mapNullable
import de.kuschku.libquassel.util.helper.nullIf
import de.kuschku.libquassel.util.helper.safeSwitchMap
import de.kuschku.quasseldroid.util.emoji.EmojiData
import de.kuschku.quasseldroid.viewmodel.ChatViewModel
import de.kuschku.quasseldroid.viewmodel.EditorViewModel
import de.kuschku.quasseldroid.viewmodel.QuasselViewModel
......@@ -47,14 +48,13 @@ open class EditorViewModelHelper @Inject constructor(
quassel: QuasselViewModel
) : ChatViewModelHelper(chat, quassel) {
val rawAutoCompleteData: Observable<Triple<Optional<ISession>, BufferId, Pair<String, IntRange>>> =
combineLatest(connectedSession, chat.bufferId, editor.lastWord)
.safeSwitchMap { (sessionOptional, id, lastWordWrapper) ->
lastWordWrapper
.distinctUntilChanged()
.map { lastWord ->
Triple(sessionOptional, id, lastWord)
}
}
combineLatest(
connectedSession,
chat.bufferId,
editor.lastWord.safeSwitchMap {
it
}.distinctUntilChanged()
)
val autoCompleteData: Observable<Pair<String, List<AutoCompleteItem>>> = rawAutoCompleteData
.distinctUntilChanged()
......@@ -72,31 +72,43 @@ open class EditorViewModelHelper @Inject constructor(
network.ircChannel(bufferInfo.bufferName) ?: IrcChannel.NULL
} else IrcChannel.NULL
ircChannel.liveIrcUsers().safeSwitchMap { users ->
fun processResults(results: List<Observable<out AutoCompleteItem>>) =
combineLatest<AutoCompleteItem>(results)
.map { list ->
val filtered = list.filter {
it.name.trimStart(*IGNORED_CHARS)
fun filterStart(name: String): Boolean {
return name.trimStart(*IGNORED_CHARS)
.startsWith(
lastWord.first.trimStart(*IGNORED_CHARS),
ignoreCase = true
)
}
fun filter(name: String): Boolean {
return name.trim(*IGNORED_CHARS)
.contains(
lastWord.first.trim(*IGNORED_CHARS),
ignoreCase = true
)
}
fun processResults(results: List<Observable<out AutoCompleteItem>>) =
combineLatest<AutoCompleteItem>(results)
.map { list ->
Pair(
lastWord.first,
filtered.sorted()
list.sorted()
)
}
fun getAliases() = aliases.map {
fun getAliases() = aliases.filter {
filterStart(it.name ?: "")
}.map {
Observable.just(AutoCompleteItem.AliasItem(
"/${it.name}",
it.expansion
))
}
fun getBuffers() = infos.values
.filter {
fun getBuffers() = infos.values.filter {
filterStart(it.bufferName ?: "")
}.filter {
it.type.toInt() == Buffer_Type.ChannelBuffer.toInt()
}.mapNotNull { info ->
networks[info.networkId]?.let { info to it }
......@@ -129,7 +141,9 @@ open class EditorViewModelHelper @Inject constructor(
emptySet()
}
fun getNicks() = getUsers().map<IrcUser, Observable<AutoCompleteItem.UserItem>> {
fun getNicks() = getUsers().filter {
filterStart(it.nick())
}.map<IrcUser, Observable<AutoCompleteItem.UserItem>> {
it.updates().map { user ->
val userModes = ircChannel.userModes(user)
val prefixModes = network.prefixModes()
......@@ -150,12 +164,21 @@ open class EditorViewModelHelper @Inject constructor(
}
}
when (lastWord.first.firstOrNull()) {
'/' -> processResults(getAliases())
'@' -> processResults(getNicks())
'#' -> processResults(getBuffers())
else -> processResults(getAliases() + getNicks() + getBuffers())
fun getEmojis() = EmojiData.processedEmojiMap.filter {
it.shortCodes.any {
it.contains(lastWord.first.trim(':'))
}
}.map {
Observable.just(it)
}
processResults(when (lastWord.first.firstOrNull()) {
'/' -> getAliases()
'@' -> getNicks()
'#' -> getBuffers()
':' -> getEmojis()
else -> getAliases() + getNicks() + getBuffers() + getEmojis()
})
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment