Skip to content

Commit

Permalink
Support plugin update notification
Browse files Browse the repository at this point in the history
  • Loading branch information
willings committed Jun 29, 2019
1 parent 5b03fa0 commit 8ad42de
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/main/kotlin/browser/extension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,5 @@ external class ContextMenusOnClicked {

external class Runtime {
fun getURL(path: String): String
fun sendMessage(data: Json)
}
17 changes: 12 additions & 5 deletions src/main/kotlin/org/dicthub/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import org.dicthub.page.OptionsPage
import org.dicthub.page.SandboxPage
import org.dicthub.page.TranslationPage
import org.dicthub.page.WelcomePage
import org.dicthub.plugin.PluginIndex
import org.dicthub.plugin.PluginContentAdapter
import org.dicthub.plugin.PluginIndex
import org.dicthub.plugin.PluginOptionsAdapter
import org.dicthub.plugin.PluginUpdateChecker
import org.dicthub.util.AjaxHttpClient
import org.w3c.dom.COMPLETE
import org.w3c.dom.DocumentReadyState
Expand Down Expand Up @@ -49,8 +50,11 @@ private fun initPopupPage() {

extractQueryFromTabs().then {
loadUserPreference().then { userPreference ->
val translationPage = TranslationPage(userPreference, PluginContentAdapter(AjaxHttpClient, browserObj.storage.local),
pluginOptionsAdapter, langDetector, it.first, it.second, userPreference.primaryLang.code, false)
val pluginIndex = PluginIndex(AjaxHttpClient, userPreference.pluginRepository)
val pluginUpdateChecker = PluginUpdateChecker(pluginIndex, userPreference)
val pluginContentAdapter = PluginContentAdapter(AjaxHttpClient, browserObj.storage.local)
val translationPage = TranslationPage(userPreference, pluginContentAdapter, pluginOptionsAdapter,
pluginUpdateChecker, langDetector, it.first, it.second, userPreference.primaryLang.code, false)
translationPage.render()
}
}
Expand All @@ -64,8 +68,11 @@ private fun initOverlayPage() {

val queryContext = extractQueryFromUrl()
loadUserPreference().then { userPreference ->
val translationPage = TranslationPage(userPreference, PluginContentAdapter(AjaxHttpClient, browserObj.storage.local),
pluginOptionsAdapter, langDetector, queryContext.first, queryContext.second, userPreference.primaryLang.code, true)
val pluginIndex = PluginIndex(AjaxHttpClient, userPreference.pluginRepository)
val pluginUpdateChecker = PluginUpdateChecker(pluginIndex, userPreference)
val pluginContentAdapter = PluginContentAdapter(AjaxHttpClient, browserObj.storage.local)
val translationPage = TranslationPage(userPreference, pluginContentAdapter, pluginOptionsAdapter,
pluginUpdateChecker, langDetector, queryContext.first, queryContext.second, userPreference.primaryLang.code, true)
translationPage.render()
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/main/kotlin/org/dicthub/model/Preference.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ class UserPreference(val data: Json) {
saveUserPreference(this)
}

var autoUpdatePlugin: Boolean
inline get() = data["autoUpdatePlugin"]?.let { it as? Boolean } ?: true
inline set(value) {
data["autoUpdatePlugin"] = value
saveUserPreference(this)
}

var sendAnalysisInfo: Boolean
inline get() = data["sendAnalysisInfo"]?.let { it as? Boolean } ?: false
inline set(value) {
Expand Down
60 changes: 60 additions & 0 deletions src/main/kotlin/org/dicthub/page/TranslationPage.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.dicthub.page

import browserObj
import i18nMessage
import org.dicthub.lang.fromCode
import org.dicthub.model.*
Expand All @@ -17,24 +18,29 @@ import kotlinx.html.js.iframe
import kotlinx.html.stream.appendHTML
import org.dicthub.lang.Lang
import org.dicthub.lang.LangDetector
import org.dicthub.plugin.PluginUpdateChecker
import org.w3c.dom.HTMLIFrameElement
import org.w3c.dom.MessageEvent
import org.w3c.dom.events.Event
import kotlin.browser.document
import kotlin.browser.window
import kotlin.js.Date
import kotlin.js.Json
import kotlin.js.Promise
import kotlin.js.json

class TranslationPage(private val userPreference: UserPreference,
private val pluginContentAdapter: PluginContentAdapter,
private val pluginOptionsAdapter: PluginOptionsAdapter,
private val pluginUpdateChecker: PluginUpdateChecker,
private val langDetector: LangDetector,
private val queryText: String,
private val fromLangStr: String?,
private val toLangStr: String?,
private val inFrame: Boolean) {

private val PLUGIN_CHECK_INTERVAL = 24 * 3600 * 1000 // 1 day

private lateinit var sandbox: HTMLIFrameElement
private lateinit var queryContainer: QueryContainer
private lateinit var resultContainer: ResultContainer
Expand Down Expand Up @@ -139,6 +145,8 @@ class TranslationPage(private val userPreference: UserPreference,
queryContainer.render()

window.addEventListener("message", messageListener, false)

checkPluginStatus()
}

private fun sendQueryMessage(query: Query) {
Expand All @@ -152,6 +160,20 @@ class TranslationPage(private val userPreference: UserPreference,
?: "", emptyArray())
}

private fun sendPluginNotification(title: String, message: String) {
val notificationOptions = json(
"type" to "basic",
"title" to title,
"message" to message,
"iconUrl" to "ico/dicthub-48.png"
)

browserObj.runtime.sendMessage(json(
"cmd" to "plugin-notification",
"payload" to notificationOptions
))
}

private fun sendAnalysisInfo(query: Query) {
if (!userPreference.sendAnalysisInfo) {
return
Expand Down Expand Up @@ -181,6 +203,44 @@ class TranslationPage(private val userPreference: UserPreference,
}
return htmlContent.toString()
}

private fun checkPluginStatus() {

val lastPluginCheckTime = pluginUpdateChecker.getLastCheckTime()
if (Date().getTime() - lastPluginCheckTime < PLUGIN_CHECK_INTERVAL) {
return
}
pluginUpdateChecker.check().then { result ->
if (result.upgradablePlugins.isNotEmpty()) {
val upgradablePluginNames = result.upgradablePlugins.map { it.name }.joinToString("\n")
if (userPreference.autoUpdatePlugin) {
val pluginUpdates = result.upgradablePlugins.map { pluginContentAdapter.update(it) }.toTypedArray()
Promise.all(pluginUpdates).then {
// Update enabled plugins
val upgradedPluginIds = result.upgradablePlugins.map { it.id }.toSet()
val enabledPlugins = userPreference.enabledPlugins.filterNot { upgradedPluginIds.contains(it.id) }.toMutableSet().apply {
addAll(result.upgradablePlugins)
}
userPreference.enabledPlugins = enabledPlugins

val upgradedPluginsNames = result.upgradablePlugins.map { it.name }.joinToString("\n")
sendPluginNotification(i18nMessage("plugin_updated"), upgradedPluginsNames)
}.catch {
sendPluginNotification(i18nMessage("plugin_updates_available"), upgradablePluginNames)
}
} else {
sendPluginNotification(i18nMessage("plugin_updates_available"), upgradablePluginNames)
}
}

if (result.newPlugins.isNotEmpty()) {
val newPluginNames = result.newPlugins.map { it.name }.joinToString { "\n" }
sendPluginNotification(i18nMessage("new_plugins_available"), newPluginNames)
}
}.catch {
console.warn("Failed to check plugin updates", it)
}
}
}

private object NullLangDetector : LangDetector {
Expand Down
46 changes: 46 additions & 0 deletions src/main/kotlin/org/dicthub/plugin/PluginUpdateChecker.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.dicthub.plugin

import org.dicthub.model.PluginInfo
import org.dicthub.model.UserPreference
import org.w3c.dom.get
import kotlin.browser.localStorage
import kotlin.js.Date
import kotlin.js.Promise


data class PluginUpdateResult(
val newPlugins: List<PluginInfo>,
val upgradablePlugins: List<PluginInfo>
)


class PluginUpdateChecker(private val pluginIndex: PluginIndex, private val userPreference: UserPreference) {

private val LAST_CHECK_TIME = "lastCheckTime"
private val LAST_AVAILABLE_PLUGINS = "lastAvailablePlugins"

fun getLastCheckTime(): Long {
return localStorage[LAST_CHECK_TIME]?.toLong() ?: 0
}

fun check(): Promise<PluginUpdateResult> {

return Promise { resolve, reject ->
pluginIndex.load().then { latestPluginInfo ->

val lastPluginIds = localStorage.getItem(LAST_AVAILABLE_PLUGINS)?.split(",")?.toSet()

val newPlugins = latestPluginInfo.filterNot { lastPluginIds?.contains(it.id) ?: true }

val enabledPluginVersions = userPreference.enabledPlugins.map { it.id to it.version }.toMap()
val upgradablePlugins = latestPluginInfo.filter { enabledPluginVersions.containsKey(it.id) && enabledPluginVersions.get(it.id) != it.version}

val latestPluginIdStr = latestPluginInfo.map { it.id }.joinToString(",")
localStorage.setItem(LAST_AVAILABLE_PLUGINS, latestPluginIdStr)
localStorage.setItem(LAST_CHECK_TIME, Date().getTime().toString())

resolve(PluginUpdateResult(newPlugins = newPlugins, upgradablePlugins = upgradablePlugins))
}.catch(reject)
}
}
}
19 changes: 19 additions & 0 deletions src/main/kotlin/org/dicthub/view/options/PluginSettings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ class PluginSettings(private val parent: HTMLElement,
+i18nMessage("plugin_settings")
small { +"(${i18nMessage("drag_to_change_order")})" }
}
div(classes = CSS_SETTINGS_ROW) {
renderAutoPluginUpdate(this)
}
div(classes = "alert alert-danger") {
id = ID_PLUGIN_SETTINGS_INFO
role = "alert"
Expand All @@ -70,6 +73,22 @@ class PluginSettings(private val parent: HTMLElement,
pluginSettingsMessage = getElementById(ID_PLUGIN_SETTINGS_INFO)
}

private val renderAutoPluginUpdate: TagAppender = {
label(classes = CSS_SETTINGS_ROW_LABEL) {
+i18nMessage("auto_update_plugin")
}
div(classes = "$CSS_SETTINGS_ROW_CONTENT align-self-center") {
checkBoxInput {
var checked = userPreference.autoUpdatePlugin
this.checked = checked
onChangeFunction = {
checked = !checked
userPreference.autoUpdatePlugin = checked
}
}
}
}

private val renderPluginRepositorySettings: TagAppender = {
ul(classes = "list-group") {
userPreference.pluginRepository.forEach { repositoryUrl ->
Expand Down
1 change: 1 addition & 0 deletions src/main/static/chrome/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"storage",
"activeTab",
"tabs",
"notifications",
"contextMenus",
"\u003Call_urls>"
],
Expand Down
1 change: 1 addition & 0 deletions src/main/static/firefox/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"storage",
"activeTab",
"tabs",
"notifications",
"contextMenus",
"\u003Call_urls>"
],
Expand Down
27 changes: 20 additions & 7 deletions src/main/static/shared/js/backgroundContextMenus.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@

var MENU_ID = "WordhubQuery";
var MENU_ID = "DicthubQuery";

chrome.runtime.onInstalled.addListener(function (details) {

if (details.reason == 'install') {
chrome.tabs.create({'url': chrome.extension.getURL('welcome.html')}, function(tab) {});
chrome.tabs.create({ 'url': chrome.extension.getURL('welcome.html') }, function (tab) { });
} else if (details.reason == 'update') {
chrome.tabs.create({'url': 'https://dicthub.org/docs/getting-started/release/'}, function(tab) {});
chrome.tabs.create({ 'url': 'https://dicthub.org/docs/getting-started/release/' }, function (tab) { });
}

chrome.contextMenus.create({
"id": MENU_ID,
"title": "DictHub Query",
"contexts": ["selection"]
});
chrome.contextMenus.onClicked.addListener(function(info, tab) {
chrome.contextMenus.onClicked.addListener(function (info, tab) {
if (info.menuItemId === MENU_ID) {
chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {"action": "showContextMenuContainer"});
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { "action": "showContextMenuContainer" });
});
}
});
});
});


function handleNotification(message) {
if (message.cmd == "plugin-notification") {
chrome.notifications.create("dicthub-notification", message.payload, function (id) {
chrome.notifications.onClicked.addListener(function(id) {
chrome.tabs.create({ 'url': chrome.runtime.getURL('options.html') }, function (tab) { })
});
});
}
}

chrome.runtime.onMessage.addListener(handleNotification);

0 comments on commit 8ad42de

Please sign in to comment.