Commit 530a49e9 authored by Leonardo Aramaki's avatar Leonardo Aramaki

Set command list completion restrained to the line start offset

parent f3224435
...@@ -11,7 +11,8 @@ import chat.rocket.android.widget.autocompletion.model.SuggestionModel ...@@ -11,7 +11,8 @@ import chat.rocket.android.widget.autocompletion.model.SuggestionModel
import chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder import chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder
import chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter import chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter
class CommandSuggestionsAdapter : SuggestionsAdapter<CommandSuggestionsViewHolder>("/", UNLIMITED_RESULT_COUNT) { class CommandSuggestionsAdapter : SuggestionsAdapter<CommandSuggestionsViewHolder>(token = "/",
constraint = CONSTRAINT_BOUND_TO_START, threshold = UNLIMITED_RESULT_COUNT) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommandSuggestionsViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommandSuggestionsViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.suggestion_command_item, parent, val view = LayoutInflater.from(parent.context).inflate(R.layout.suggestion_command_item, parent,
......
...@@ -6,10 +6,10 @@ import chat.rocket.android.chatroom.adapter.AutoCompleteType ...@@ -6,10 +6,10 @@ import chat.rocket.android.chatroom.adapter.AutoCompleteType
import chat.rocket.android.chatroom.adapter.PEOPLE import chat.rocket.android.chatroom.adapter.PEOPLE
import chat.rocket.android.chatroom.adapter.ROOMS import chat.rocket.android.chatroom.adapter.ROOMS
import chat.rocket.android.chatroom.domain.UriInteractor import chat.rocket.android.chatroom.domain.UriInteractor
import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel
import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
import chat.rocket.android.chatroom.viewmodel.ViewModelMapper import chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel
import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel
import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.UrlHelper import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
...@@ -203,25 +203,25 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -203,25 +203,25 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
val roomType = roomTypeOf(chatRoomType!!) val roomType = roomTypeOf(chatRoomType!!)
messagesRepository.getByRoomId(chatRoomId!!) messagesRepository.getByRoomId(chatRoomId!!)
.sortedByDescending { it.timestamp }.firstOrNull()?.let { lastMessage -> .sortedByDescending { it.timestamp }.firstOrNull()?.let { lastMessage ->
val instant = Instant.ofEpochMilli(lastMessage.timestamp) val instant = Instant.ofEpochMilli(lastMessage.timestamp)
val messages = client.history(chatRoomId!!, roomType, count = 50, val messages = client.history(chatRoomId!!, roomType, count = 50,
oldest = instant.toString()) oldest = instant.toString())
Timber.d("History: $messages") Timber.d("History: $messages")
if (messages.result.isNotEmpty()) { if (messages.result.isNotEmpty()) {
val models = mapper.map(messages.result) val models = mapper.map(messages.result)
messagesRepository.saveAll(messages.result) messagesRepository.saveAll(messages.result)
launchUI(strategy) { launchUI(strategy) {
view.showNewMessage(models) view.showNewMessage(models)
}
if (messages.result.size == 50) {
// we loade at least count messages, try one more to fetch more messages
loadMissingMessages()
}
}
} }
if (messages.result.size == 50) {
// we loade at least count messages, try one more to fetch more messages
loadMissingMessages()
}
}
}
} }
} }
} }
...@@ -489,16 +489,12 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -489,16 +489,12 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
view.showReactionsPopup(messageId) view.showReactionsPopup(messageId)
} }
/** fun loadCommands() {
* Loads the list of available commands.
*/
fun loadCommands(query: String = "") {
launchUI(strategy) { launchUI(strategy) {
try { try {
//TODO: cache the commands //TODO: cache the commands
val commands = client.commands(0, 100).result val commands = client.commands(0, 100).result
view.populateCommandSuggestions(commands.map { view.populateCommandSuggestions(commands.map {
println(it)
CommandSuggestionViewModel(it.command, it.description ?: "", listOf(it.command)) CommandSuggestionViewModel(it.command, it.description ?: "", listOf(it.command))
}) })
} catch (ex: RocketChatException) { } catch (ex: RocketChatException) {
......
...@@ -494,10 +494,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -494,10 +494,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
private fun setupSuggestionsView() { private fun setupSuggestionsView() {
suggestions_view.anchor(text_message) suggestions_view.anchorTo(text_message)
.registerTokenAdapter(PeopleSuggestionsAdapter()) .addTokenAdapter(PeopleSuggestionsAdapter())
.registerTokenAdapter(CommandSuggestionsAdapter()) .addTokenAdapter(CommandSuggestionsAdapter())
.registerTokenAdapter(RoomSuggestionsAdapter()) .addTokenAdapter(RoomSuggestionsAdapter())
.addSuggestionProviderAction("@") { query -> .addSuggestionProviderAction("@") { query ->
if (query.isNotEmpty()) { if (query.isNotEmpty()) {
presenter.spotlight(query, PEOPLE, true) presenter.spotlight(query, PEOPLE, true)
...@@ -508,8 +508,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -508,8 +508,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter.loadChatRooms() presenter.loadChatRooms()
} }
} }
.addSuggestionProviderAction("/") { query -> .addSuggestionProviderAction("/") { _ ->
presenter.loadCommands(query) presenter.loadCommands()
} }
presenter.loadCommands() presenter.loadCommands()
......
...@@ -7,9 +7,18 @@ import chat.rocket.android.widget.autocompletion.strategy.regex.StringMatchingCo ...@@ -7,9 +7,18 @@ import chat.rocket.android.widget.autocompletion.strategy.regex.StringMatchingCo
import java.lang.reflect.Type import java.lang.reflect.Type
import kotlin.properties.Delegates import kotlin.properties.Delegates
abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>(val token: String, threshold: Int = MAX_RESULT_COUNT) : RecyclerView.Adapter<VH>() { abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>(
val token: String,
val constraint: Int = CONSTRAINT_UNBOUND,
threshold: Int = MAX_RESULT_COUNT) : RecyclerView.Adapter<VH>() {
companion object { companion object {
// Any number of results.
const val UNLIMITED_RESULT_COUNT = -1 const val UNLIMITED_RESULT_COUNT = -1
// Trigger suggestions only if on the line start.
const val CONSTRAINT_BOUND_TO_START = 0
// Trigger suggestions from anywhere.
const val CONSTRAINT_UNBOUND = 1
// Maximum number of results to display by default.
private const val MAX_RESULT_COUNT = 5 private const val MAX_RESULT_COUNT = 5
} }
private var itemType: Type? = null private var itemType: Type? = null
......
...@@ -21,6 +21,7 @@ import android.widget.EditText ...@@ -21,6 +21,7 @@ import android.widget.EditText
import android.widget.FrameLayout import android.widget.FrameLayout
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.widget.autocompletion.model.SuggestionModel import chat.rocket.android.widget.autocompletion.model.SuggestionModel
import chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter.Companion.CONSTRAINT_BOUND_TO_START
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
...@@ -77,6 +78,10 @@ class SuggestionsView : FrameLayout, TextWatcher { ...@@ -77,6 +78,10 @@ class SuggestionsView : FrameLayout, TextWatcher {
val new = s.subSequence(start, start + count).toString() val new = s.subSequence(start, start + count).toString()
if (adaptersByToken.containsKey(new)) { if (adaptersByToken.containsKey(new)) {
val constraint = adapter(new).constraint
if (constraint == CONSTRAINT_BOUND_TO_START && start != 0) {
return
}
swapAdapter(getAdapterForToken(new)!!) swapAdapter(getAdapterForToken(new)!!)
completionStartIndex.compareAndSet(NO_STATE_INDEX, start + 1) completionStartIndex.compareAndSet(NO_STATE_INDEX, start + 1)
editor?.let { editor?.let {
...@@ -125,14 +130,14 @@ class SuggestionsView : FrameLayout, TextWatcher { ...@@ -125,14 +130,14 @@ class SuggestionsView : FrameLayout, TextWatcher {
private fun getAdapterForToken(token: String): SuggestionsAdapter<*>? = adaptersByToken.get(token) private fun getAdapterForToken(token: String): SuggestionsAdapter<*>? = adaptersByToken.get(token)
fun anchor(editText: EditText): SuggestionsView { fun anchorTo(editText: EditText): SuggestionsView {
editText.removeTextChangedListener(this) editText.removeTextChangedListener(this)
editText.addTextChangedListener(this) editText.addTextChangedListener(this)
editor = WeakReference(editText) editor = WeakReference(editText)
return this return this
} }
fun registerTokenAdapter(adapter: SuggestionsAdapter<*>): SuggestionsView { fun addTokenAdapter(adapter: SuggestionsAdapter<*>): SuggestionsView {
adaptersByToken.getOrPut(adapter.token, { adapter }) adaptersByToken.getOrPut(adapter.token, { adapter })
return this return this
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment