Skip to content

Commit

Permalink
DEV2-3931 fix chat first loading
Browse files Browse the repository at this point in the history
  • Loading branch information
yonip23 committed Oct 8, 2023
1 parent 64c2992 commit 4fcc83a
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 57 deletions.
60 changes: 32 additions & 28 deletions Common/src/main/java/com/tabnineCommon/chat/ChatBrowser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,35 @@ import java.awt.datatransfer.StringSelection
import java.nio.file.Paths
import java.util.concurrent.atomic.AtomicBoolean

class ChatBrowser(messagesRouter: ChatMessagesRouter, project: Project) {
class ChatBrowser private constructor(project: Project) {
var jbCefBrowser: JBCefBrowser
private val browserLoadedListeners = mutableMapOf<String, () -> Unit>()
private val isLoaded = AtomicBoolean(false)

companion object {
fun getInstance(project: Project): ChatBrowser? {
return project.getUserData(Consts.BROWSER_PROJECT_KEY)
}
}
private val chatAppStartupListeners = mutableMapOf<String, () -> Unit>()
private val isChatAppAlive = AtomicBoolean(false)

init {
this.jbCefBrowser = initializeBrowser(project, messagesRouter)
this.jbCefBrowser = initializeBrowser(project)
}

project.putUserData(Consts.BROWSER_PROJECT_KEY, this)
companion object {
fun getInstance(project: Project): ChatBrowser {
val existingBrowser = project.getUserData(Consts.BROWSER_PROJECT_KEY)
if (existingBrowser != null) {
return existingBrowser
}
val newBrowser = ChatBrowser(project)
project.putUserData(Consts.BROWSER_PROJECT_KEY, newBrowser)
return newBrowser
}
}

private fun initializeBrowser(
project: Project,
messagesRouter: ChatMessagesRouter
): JBCefBrowser {
val browser = createBrowser()
val postMessageListener = JBCefJSQuery.create(browser)
val copyCodeListener = JBCefJSQuery.create(browser)
postMessageListener.addHandler {
handleIncomingMessage(it, project, browser, messagesRouter)
handleIncomingMessage(it, project, browser)
return@addHandler null
}
copyCodeListener.addHandler {
Expand All @@ -55,20 +58,21 @@ class ChatBrowser(messagesRouter: ChatMessagesRouter, project: Project) {
cefLoadHandler(browser, postMessageListener, copyCodeListener),
browser.cefBrowser
)
loadChatOnto(browser)
loadChatOnto(browser, project)

return browser
}

fun isLoaded(): Boolean {
return isLoaded.get()
fun isChatAppAlive(): Boolean {
return isChatAppAlive.get()
}

fun registerBrowserLoadedListener(id: String, listener: () -> Unit) {
browserLoadedListeners[id] = listener
fun registerChatAppStartupListener(id: String, project: Project, listener: () -> Unit) {
Logger.getInstance(javaClass).debug("Registering chat app startup $id for project ${project.name}")
chatAppStartupListeners[id] = listener
}

private fun loadChatOnto(browser: JBCefBrowser) {
private fun loadChatOnto(browser: JBCefBrowser, project: Project) {
val devServerUrl = System.getenv("TABNINE_CHAT_DEV_SERVER_URL")

if (devServerUrl != null) {
Expand All @@ -77,7 +81,7 @@ class ChatBrowser(messagesRouter: ChatMessagesRouter, project: Project) {
return
}

Logger.getInstance(javaClass).info("Running Tabnine Chat")
Logger.getInstance(javaClass).info("Running Tabnine Chat for project ${project.name}")

try {
val destination = Paths.get(StaticConfig.getBaseDirectory().toString(), "chat")
Expand All @@ -93,10 +97,16 @@ class ChatBrowser(messagesRouter: ChatMessagesRouter, project: Project) {
it: String,
project: Project,
browser: JBCefBrowser,
messagesRouter: ChatMessagesRouter
) {
if (!isChatAppAlive.getAndSet(true)) {
Logger.getInstance(javaClass).debug("Chat app has started for project ${project.name}, invoking chat app startup listeners")
chatAppStartupListeners.forEach {
Logger.getInstance(javaClass).debug("Running chat app startup listener '${it.key}' on project ${project.name}")
it.value()
}
}
Logger.getInstance(javaClass).trace("Received message: $it")
val response = messagesRouter.handleRawMessage(it, project)
val response = ChatMessagesRouter.handleRawMessage(it, project)

Logger.getInstance(javaClass).trace("Sending response: $response")
browser.cefBrowser.executeJavaScript("window.postMessage($response, '*')", "", 0)
Expand Down Expand Up @@ -127,12 +137,6 @@ class ChatBrowser(messagesRouter: ChatMessagesRouter, project: Project) {
),
"", 0
)
browserLoadedListeners.forEach {
Logger.getInstance(javaClass).debug("Running browser loaded listener '${it.key}'")
val listener = it.value
listener()
}
isLoaded.set(true)
}
}

Expand Down
4 changes: 2 additions & 2 deletions Common/src/main/java/com/tabnineCommon/chat/ChatFrame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import javax.swing.JPanel
import javax.swing.SwingConstants.CENTER
import javax.swing.event.HyperlinkEvent

class ChatFrame(private val project: Project, private val messagesRouter: ChatMessagesRouter, private val binaryRequestFacade: BinaryRequestFacade) :
class ChatFrame(private val project: Project, private val binaryRequestFacade: BinaryRequestFacade) :
JPanel(true), Disposable {
private var capabilitiesFetched = false

Expand Down Expand Up @@ -97,7 +97,7 @@ class ChatFrame(private val project: Project, private val messagesRouter: ChatMe

private fun displayChat() {
val browser = try {
ChatBrowser(messagesRouter, project)
ChatBrowser.getInstance(project)
} catch (e: Exception) {
Logger.getInstance(javaClass).warn("Failed to create browser", e)
displayBrowserNotAvailable()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import com.tabnineCommon.general.DependencyContainer
data class ChatMessageRequest(val id: String, val command: String, val data: JsonElement? = null)
data class ChatMessageResponse(val id: String, val payload: Any? = null, val error: String? = null)

class ChatMessagesRouter {
object ChatMessagesRouter {
private val gson = DependencyContainer.instanceOfGson()
private val commandHandlers = mapOf<String, ChatMessageHandler<*, *>>(
"init" to InitHandler(gson),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ import com.intellij.openapi.wm.ToolWindowFactory
import com.tabnineCommon.general.DependencyContainer.instanceOfBinaryRequestFacade

class TabnineChatWebViewFactory : ToolWindowFactory, Disposable {

private var messagesRouter = ChatMessagesRouter()
private val binaryRequestFacade = instanceOfBinaryRequestFacade()

override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
val chatFrame = ChatFrame(project, messagesRouter, binaryRequestFacade)
val chatFrame = ChatFrame(project, binaryRequestFacade)
Disposer.register(toolWindow.disposable, chatFrame)
toolWindow.component.add(chatFrame)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,34 @@ import com.tabnineCommon.chat.Consts
import com.tabnineCommon.chat.actions.AskChatPayload
import com.tabnineCommon.chat.actions.TabnineActionRequest
import com.tabnineCommon.general.DependencyContainer
import org.jetbrains.concurrency.runAsync

object ChatActionCommunicator {
fun sendMessageToChat(project: Project, actionId: String, value: String) {
val browser = getBrowser(project) ?: return
val browser = ChatBrowser.getInstance(project)
val ourToolWindow = ToolWindowManager.getInstance(project)
.getToolWindow(Consts.CHAT_TOOL_WINDOW_ID) ?: return

if (browser.isLoaded()) {
if (browser.isChatAppAlive()) {
ourToolWindow.activate {
submitMessageToChat(project, value)
submitMessageToChat(browser, project, value)
}
} else {
browser.registerBrowserLoadedListener(actionId) {
runAsync {
Thread.sleep(1000)
submitMessageToChat(project, value)
}
browser.registerChatAppStartupListener(actionId, project) {
submitMessageToChat(browser, project, value)
}
ourToolWindow.activate(null)
}
}

private fun submitMessageToChat(project: Project, result: String) {
sendMessage(project, TabnineActionRequest("submit-message", AskChatPayload(result)))
private fun submitMessageToChat(browser: ChatBrowser, project: Project, result: String) {
sendMessage(project, TabnineActionRequest("submit-message", AskChatPayload(result)), browser)
}

private fun sendMessage(project: Project, message: TabnineActionRequest) {
val browser = getBrowser(project) ?: return
private fun sendMessage(project: Project, message: TabnineActionRequest, browser: ChatBrowser? = null) {
val chatBrowser = browser ?: ChatBrowser.getInstance(project)
val messageJson = DependencyContainer.instanceOfGson().toJson(message)

Logger.getInstance(javaClass).info("Sending message: $messageJson")
browser.jbCefBrowser.cefBrowser.executeJavaScript("window.postMessage($messageJson, '*')", "", 0)
}

private fun getBrowser(project: Project): ChatBrowser? {
val browser = ChatBrowser.getInstance(project)
if (browser == null) {
Logger.getInstance(javaClass).warn("Browser not found on project ${project.name}")
return null
}
return browser
chatBrowser.jbCefBrowser.cefBrowser.executeJavaScript("window.postMessage($messageJson, '*')", "", 0)
}
}

0 comments on commit 4fcc83a

Please sign in to comment.