mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 14:23:28 +07:00
(IJPL-173684, IJ-CR-152618) What's New: separate opening logic for web pages
#IJPL-173684 Fixed (cherry picked from commit 46ee26a3a82e70cee2d71dccba8536a058523d05) GitOrigin-RevId: 7309170a8ba2f309cd25128daf1b2842f0dd48a8
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a03adc096e
commit
c9078c27f3
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
package com.intellij.platform.whatsNew
|
package com.intellij.platform.whatsNew
|
||||||
|
|
||||||
import com.intellij.ide.DataManager
|
import com.intellij.ide.DataManager
|
||||||
@@ -9,25 +9,16 @@ import com.intellij.openapi.actionSystem.AnAction
|
|||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
import com.intellij.openapi.application.ApplicationNamesInfo
|
import com.intellij.openapi.application.ApplicationNamesInfo
|
||||||
import com.intellij.openapi.application.EDT
|
|
||||||
import com.intellij.openapi.application.writeIntentReadAction
|
|
||||||
import com.intellij.openapi.components.Service
|
import com.intellij.openapi.components.Service
|
||||||
import com.intellij.openapi.components.service
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.components.serviceAsync
|
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.diagnostic.runAndLogException
|
import com.intellij.openapi.diagnostic.runAndLogException
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
|
||||||
import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider.Companion.openEditor
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Disposer
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import com.intellij.platform.whatsNew.collectors.WhatsNewCounterUsageCollector
|
|
||||||
import com.intellij.platform.whatsNew.reaction.FUSReactionChecker
|
import com.intellij.platform.whatsNew.reaction.FUSReactionChecker
|
||||||
import com.intellij.platform.whatsNew.reaction.ReactionsPanel
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import com.intellij.ui.jcef.JBCefApp
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import com.intellij.util.application
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.launch
|
||||||
import org.jetbrains.concurrency.await
|
import org.jetbrains.concurrency.await
|
||||||
|
|
||||||
internal class WhatsNewAction : AnAction(), com.intellij.openapi.project.DumbAware {
|
internal class WhatsNewAction : AnAction(), com.intellij.openapi.project.DumbAware {
|
||||||
@@ -57,44 +48,13 @@ internal class WhatsNewAction : AnAction(), com.intellij.openapi.project.DumbAwa
|
|||||||
openWhatsNewPage(project, dataContext)
|
openWhatsNewPage(project, dataContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun openWhatsNewPage(project: Project, dataContext: DataContext?, byClient: Boolean = false) {
|
private suspend fun openWhatsNewPage(project: Project, dataContext: DataContext?, triggeredByUser: Boolean = false) {
|
||||||
if (JBCefApp.isSupported()) {
|
val content = contentAsync.await()
|
||||||
val content = contentAsync.await()
|
if (content != null && content.isAvailable()) {
|
||||||
if (content != null && content.isAvailable()) {
|
content.show(project, dataContext, triggeredByUser, reactionChecker)
|
||||||
openContent(project, content, dataContext, byClient)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LOG.warn("EapWhatsNew: can't be shown. Content not available")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG.warn("EapWhatsNew: can't be shown. JBCefApp isn't supported")
|
LOG.warn("EapWhatsNew: can't be shown. Content not available")
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun openContent(project: Project, whatsNewContent: WhatsNewContent, dataContext: DataContext?, byClient: Boolean) {
|
|
||||||
check(JBCefApp.isSupported()) { "JCEF is not supported on this system" }
|
|
||||||
val title = IdeBundle.message("update.whats.new", ApplicationNamesInfo.getInstance().fullProductName)
|
|
||||||
withContext(Dispatchers.EDT) {
|
|
||||||
LOG.info("Opening What's New in editor.")
|
|
||||||
val editor = writeIntentReadAction { openEditor(project, title, whatsNewContent.getRequest(dataContext)) }
|
|
||||||
editor?.let {
|
|
||||||
project.serviceAsync<FileEditorManager>().addTopComponent(it, ReactionsPanel.createPanel(PLACE, reactionChecker))
|
|
||||||
WhatsNewCounterUsageCollector.openedPerformed(project, byClient)
|
|
||||||
|
|
||||||
WhatsNewContentVersionChecker.saveLastShownContent(whatsNewContent)
|
|
||||||
|
|
||||||
val disposable = Disposer.newDisposable(project)
|
|
||||||
val busConnection = application.messageBus.connect(disposable)
|
|
||||||
busConnection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, object : FileEditorManagerListener {
|
|
||||||
override fun fileClosed(source: FileEditorManager, file: VirtualFile) {
|
|
||||||
if (it.file == file) {
|
|
||||||
WhatsNewCounterUsageCollector.closedPerformed(project)
|
|
||||||
Disposer.dispose(disposable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,37 +1,41 @@
|
|||||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
package com.intellij.platform.whatsNew
|
package com.intellij.platform.whatsNew
|
||||||
|
|
||||||
|
import com.intellij.ide.BrowserUtil
|
||||||
import com.intellij.ide.IdeBundle
|
import com.intellij.ide.IdeBundle
|
||||||
import com.intellij.l10n.LocalizationStateService
|
import com.intellij.l10n.LocalizationStateService
|
||||||
import com.intellij.openapi.actionSystem.*
|
import com.intellij.openapi.actionSystem.*
|
||||||
import com.intellij.openapi.application.ApplicationInfo
|
import com.intellij.openapi.application.*
|
||||||
import com.intellij.openapi.application.EDT
|
|
||||||
import com.intellij.openapi.components.service
|
import com.intellij.openapi.components.service
|
||||||
|
import com.intellij.openapi.components.serviceAsync
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.diagnostic.trace
|
import com.intellij.openapi.diagnostic.trace
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||||
import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider
|
import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider
|
||||||
|
import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider.Companion.openEditor
|
||||||
import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider.JsQueryHandler
|
import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider.JsQueryHandler
|
||||||
import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider.Request.Companion.html
|
import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider.Request.Companion.html
|
||||||
import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider.Request.Companion.url
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Disposer
|
||||||
import com.intellij.openapi.util.Version
|
import com.intellij.openapi.util.Version
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.platform.ide.customization.ExternalProductResourceUrls
|
import com.intellij.platform.ide.customization.ExternalProductResourceUrls
|
||||||
|
import com.intellij.platform.whatsNew.WhatsNewAction.Companion.PLACE
|
||||||
import com.intellij.platform.whatsNew.collectors.WhatsNewCounterUsageCollector
|
import com.intellij.platform.whatsNew.collectors.WhatsNewCounterUsageCollector
|
||||||
import com.intellij.util.Urls.newFromEncoded
|
import com.intellij.platform.whatsNew.reaction.FUSReactionChecker
|
||||||
|
import com.intellij.platform.whatsNew.reaction.ReactionsPanel
|
||||||
|
import com.intellij.ui.jcef.JBCefApp
|
||||||
|
import com.intellij.util.application
|
||||||
import com.intellij.util.io.DigestUtil
|
import com.intellij.util.io.DigestUtil
|
||||||
import com.intellij.util.ui.StartupUiUtil
|
import com.intellij.util.ui.StartupUiUtil
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.IOException
|
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
internal abstract class WhatsNewContent {
|
internal abstract class WhatsNewContent {
|
||||||
companion object {
|
companion object {
|
||||||
private val DataContext.project: Project?
|
|
||||||
get() = CommonDataKeys.PROJECT.getData(this)
|
|
||||||
|
|
||||||
suspend fun getWhatsNewContent(): WhatsNewContent? {
|
suspend fun getWhatsNewContent(): WhatsNewContent? {
|
||||||
return if (WhatsNewInVisionContentProvider.getInstance().isAvailable()) {
|
return if (WhatsNewInVisionContentProvider.getInstance().isAvailable()) {
|
||||||
WhatsNewVisionContent(WhatsNewInVisionContentProvider.getInstance().getContent().entities.first())
|
WhatsNewVisionContent(WhatsNewInVisionContentProvider.getInstance().getContent().entities.first())
|
||||||
@@ -41,7 +45,8 @@ internal abstract class WhatsNewContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Year and release have to be strings, because this is the ApplicationInfo.xml format. "release" might be a string like "2.1".
|
// Year and release have to be strings, because this is the ApplicationInfo.xml format.
|
||||||
|
// Remember that "release" might be a string like "2.1".
|
||||||
data class ContentVersion(val year: String, val release: String, val eap: Int?, val hash: String?) : Comparable<ContentVersion> {
|
data class ContentVersion(val year: String, val release: String, val eap: Int?, val hash: String?) : Comparable<ContentVersion> {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -88,92 +93,20 @@ internal abstract class WhatsNewContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun getRequest(dataContext: DataContext?): HTMLEditorProvider.Request
|
abstract suspend fun show(
|
||||||
abstract fun getActionWhiteList(): Set<String>
|
project: Project,
|
||||||
|
dataContext: DataContext?,
|
||||||
|
triggeredByUser: Boolean,
|
||||||
|
reactionChecker: FUSReactionChecker,
|
||||||
|
)
|
||||||
|
|
||||||
abstract fun getVersion(): ContentVersion?
|
abstract fun getVersion(): ContentVersion?
|
||||||
abstract suspend fun isAvailable(): Boolean
|
abstract suspend fun isAvailable(): Boolean
|
||||||
|
|
||||||
protected fun getHandler(dataContext: DataContext?): JsQueryHandler? {
|
|
||||||
dataContext ?: return null
|
|
||||||
|
|
||||||
return object : JsQueryHandler {
|
|
||||||
override suspend fun query(id: Long, request: String): String {
|
|
||||||
val contains = getActionWhiteList().contains(request)
|
|
||||||
if (!contains) {
|
|
||||||
logger.trace { "EapWhatsNew action $request not allowed" }
|
|
||||||
WhatsNewCounterUsageCollector.actionNotAllowed(dataContext.project, request)
|
|
||||||
return "false"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.isNotEmpty()) {
|
|
||||||
service<ActionManager>().getAction(request)?.let {
|
|
||||||
withContext(Dispatchers.EDT) {
|
|
||||||
it.actionPerformed(
|
|
||||||
AnActionEvent.createEvent(
|
|
||||||
it,
|
|
||||||
dataContext,
|
|
||||||
/*presentation =*/ null,
|
|
||||||
WhatsNewAction.PLACE,
|
|
||||||
ActionUiKind.NONE,
|
|
||||||
/*event =*/ null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
logger.trace { "EapWhatsNew action $request performed" }
|
|
||||||
WhatsNewCounterUsageCollector.actionPerformed(dataContext.project, request)
|
|
||||||
}
|
|
||||||
return "true"
|
|
||||||
} ?: run {
|
|
||||||
logger.trace { "EapWhatsNew action $request not found" }
|
|
||||||
WhatsNewCounterUsageCollector.actionNotFound(dataContext.project, request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "false"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class WhatsNewUrlContent(val url: String) : WhatsNewContent() {
|
internal class WhatsNewUrlContent(val url: String) : WhatsNewContent() {
|
||||||
companion object {
|
companion object {
|
||||||
val LOG = logger<WhatsNewUrlContent>()
|
val LOG = logger<WhatsNewUrlContent>()
|
||||||
private val actionWhiteList = mutableSetOf("SearchEverywhere", "ChangeLaf", "ChangeIdeScale",
|
|
||||||
"SettingsSyncOpenSettingsAction", "BuildWholeSolutionAction",
|
|
||||||
"GitLab.Open.Settings.Page",
|
|
||||||
"AIAssistant.ToolWindow.ShowOrFocus", "ChangeMainToolbarColor",
|
|
||||||
"ShowEapDiagram", "multilaunch.RunMultipleProjects",
|
|
||||||
"EfCore.Shared.OpenQuickEfCoreActionsAction",
|
|
||||||
"OpenNewTerminalEAP", "CollectionsVisualizerEAP", "ShowDebugMonitoringToolEAP",
|
|
||||||
"LearnMoreStickyScrollEAP", "NewRiderProject", "BlazorHotReloadEAP")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getRequest(dataContext: DataContext?): HTMLEditorProvider.Request {
|
|
||||||
val parameters = HashMap<String, String>()
|
|
||||||
parameters["var"] = "embed"
|
|
||||||
if (StartupUiUtil.isDarkTheme) {
|
|
||||||
parameters["theme"] = "dark"
|
|
||||||
}
|
|
||||||
parameters["lang"] = getCurrentLanguageTag()
|
|
||||||
val request = url(newFromEncoded(url).addParameters(parameters).toExternalForm())
|
|
||||||
try {
|
|
||||||
WhatsNewAction::class.java.getResourceAsStream("whatsNewTimeoutText.html").use { stream ->
|
|
||||||
if (stream != null) {
|
|
||||||
request.withTimeoutHtml(String(stream.readAllBytes(), StandardCharsets.UTF_8).replace("__THEME__",
|
|
||||||
if (StartupUiUtil.isDarkTheme) "theme-dark" else "")
|
|
||||||
.replace("__TITLE__", IdeBundle.message("whats.new.timeout.title"))
|
|
||||||
.replace("__MESSAGE__", IdeBundle.message("whats.new.timeout.message"))
|
|
||||||
.replace("__ACTION__", IdeBundle.message("whats.new.timeout.action", url)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e: IOException) {
|
|
||||||
LOG.error(e)
|
|
||||||
}
|
|
||||||
request.withQueryHandler(getHandler(dataContext))
|
|
||||||
return request
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getActionWhiteList(): Set<String> {
|
|
||||||
return actionWhiteList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getVersion(): ContentVersion? = null
|
override fun getVersion(): ContentVersion? = null
|
||||||
@@ -191,7 +124,7 @@ internal class WhatsNewUrlContent(val url: String) : WhatsNewContent() {
|
|||||||
connection.instanceFollowRedirects = false
|
connection.instanceFollowRedirects = false
|
||||||
|
|
||||||
connection.connect()
|
connection.connect()
|
||||||
if (connection.responseCode != 200) {
|
if (connection.responseCode >= 400) {
|
||||||
LOG.warn("WhatsNew page '$url' not available response code: ${connection.responseCode}")
|
LOG.warn("WhatsNew page '$url' not available response code: ${connection.responseCode}")
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -205,11 +138,15 @@ internal class WhatsNewUrlContent(val url: String) : WhatsNewContent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun show(project: Project, dataContext: DataContext?, triggeredByUser: Boolean, reactionChecker: FUSReactionChecker) {
|
||||||
|
BrowserUtil.browse(IdeUrlTrackingParametersProvider.getInstance().augmentUrl(url))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class WhatsNewVisionContent(page: WhatsNewInVisionContentProvider.Page) : WhatsNewContent() {
|
internal class WhatsNewVisionContent(page: WhatsNewInVisionContentProvider.Page) : WhatsNewContent() {
|
||||||
companion object {
|
companion object {
|
||||||
private const val THEME_KEY = "\$__VISION_PAGE_SETTINGS_THEME__\$"
|
private const val THEME_KEY = "\$__VISION_PAGE_SETTINGS_THEME__$"
|
||||||
private const val DARK_THEME = "dark"
|
private const val DARK_THEME = "dark"
|
||||||
private const val LIGHT_THEME = "light"
|
private const val LIGHT_THEME = "light"
|
||||||
|
|
||||||
@@ -233,16 +170,12 @@ internal class WhatsNewVisionContent(page: WhatsNewInVisionContentProvider.Page)
|
|||||||
contentHash = DigestUtil.sha1Hex(page.html)
|
contentHash = DigestUtil.sha1Hex(page.html)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRequest(dataContext: DataContext?): HTMLEditorProvider.Request {
|
private fun getRequest(dataContext: DataContext?): HTMLEditorProvider.Request {
|
||||||
val request = html(content)
|
val request = html(content)
|
||||||
request.withQueryHandler(getHandler(dataContext))
|
request.withQueryHandler(getHandler(dataContext))
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getActionWhiteList(): Set<String> {
|
|
||||||
return myActionWhiteList
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getVersion(): ContentVersion {
|
override fun getVersion(): ContentVersion {
|
||||||
val buildNumber = ApplicationInfo.getInstance().getBuild()
|
val buildNumber = ApplicationInfo.getInstance().getBuild()
|
||||||
return ContentVersion(
|
return ContentVersion(
|
||||||
@@ -256,6 +189,74 @@ internal class WhatsNewVisionContent(page: WhatsNewInVisionContentProvider.Page)
|
|||||||
override suspend fun isAvailable(): Boolean {
|
override suspend fun isAvailable(): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getHandler(dataContext: DataContext?): JsQueryHandler? {
|
||||||
|
dataContext ?: return null
|
||||||
|
|
||||||
|
return object : JsQueryHandler {
|
||||||
|
override suspend fun query(id: Long, request: String): String {
|
||||||
|
val contains = myActionWhiteList.contains(request)
|
||||||
|
if (!contains) {
|
||||||
|
logger.trace { "EapWhatsNew action $request not allowed" }
|
||||||
|
WhatsNewCounterUsageCollector.actionNotAllowed(dataContext.project, request)
|
||||||
|
return "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.isNotEmpty()) {
|
||||||
|
service<ActionManager>().getAction(request)?.let {
|
||||||
|
withContext(Dispatchers.EDT) {
|
||||||
|
it.actionPerformed(
|
||||||
|
AnActionEvent.createEvent(
|
||||||
|
it,
|
||||||
|
dataContext,
|
||||||
|
/*presentation =*/ null,
|
||||||
|
PLACE,
|
||||||
|
ActionUiKind.NONE,
|
||||||
|
/*event =*/ null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
logger.trace { "EapWhatsNew action $request performed" }
|
||||||
|
WhatsNewCounterUsageCollector.actionPerformed(dataContext.project, request)
|
||||||
|
}
|
||||||
|
return "true"
|
||||||
|
} ?: run {
|
||||||
|
logger.trace { "EapWhatsNew action $request not found" }
|
||||||
|
WhatsNewCounterUsageCollector.actionNotFound(dataContext.project, request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "false"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun show(project: Project, dataContext: DataContext?, triggeredByUser: Boolean, reactionChecker: FUSReactionChecker) {
|
||||||
|
if (!JBCefApp.isSupported()) {
|
||||||
|
logger.error("EapWhatsNew: can't be shown. JBCefApp isn't supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
val title = IdeBundle.message("update.whats.new", ApplicationNamesInfo.getInstance().fullProductName)
|
||||||
|
withContext(Dispatchers.EDT) {
|
||||||
|
logger.info("Opening What's New in editor.")
|
||||||
|
val editor = writeIntentReadAction { openEditor(project, title, getRequest(dataContext)) }
|
||||||
|
editor?.let {
|
||||||
|
project.serviceAsync<FileEditorManager>().addTopComponent(it, ReactionsPanel.createPanel(PLACE, reactionChecker))
|
||||||
|
WhatsNewCounterUsageCollector.openedPerformed(project, triggeredByUser)
|
||||||
|
|
||||||
|
WhatsNewContentVersionChecker.saveLastShownContent(this@WhatsNewVisionContent)
|
||||||
|
|
||||||
|
val disposable = Disposer.newDisposable(project)
|
||||||
|
val busConnection = application.messageBus.connect(disposable)
|
||||||
|
busConnection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, object : FileEditorManagerListener {
|
||||||
|
override fun fileClosed(source: FileEditorManager, file: VirtualFile) {
|
||||||
|
if (it.file == file) {
|
||||||
|
WhatsNewCounterUsageCollector.closedPerformed(project)
|
||||||
|
Disposer.dispose(disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCurrentLanguageTag(): String {
|
fun getCurrentLanguageTag(): String {
|
||||||
@@ -265,4 +266,7 @@ fun getCurrentLanguageTag(): String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val DataContext.project: Project?
|
||||||
|
get() = CommonDataKeys.PROJECT.getData(this)
|
||||||
|
|
||||||
private val logger = logger<WhatsNewContent>()
|
private val logger = logger<WhatsNewContent>()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
package com.intellij.platform.whatsNew
|
package com.intellij.platform.whatsNew
|
||||||
|
|
||||||
import com.intellij.ide.util.PropertiesComponent
|
import com.intellij.ide.util.PropertiesComponent
|
||||||
@@ -12,6 +12,11 @@ internal class WhatsNewContentVersionChecker {
|
|||||||
|
|
||||||
|
|
||||||
fun isNeedToShowContent(whatsNewContent: WhatsNewContent): Boolean {
|
fun isNeedToShowContent(whatsNewContent: WhatsNewContent): Boolean {
|
||||||
|
val newVersion = whatsNewContent.getVersion() ?: run {
|
||||||
|
LOG.info("What's New content provider returns null version. What's New will be ignored.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
val savedVersionInfo = PropertiesComponent.getInstance().getValue(LAST_SHOWN_EAP_VERSION_PROP) ?: run {
|
val savedVersionInfo = PropertiesComponent.getInstance().getValue(LAST_SHOWN_EAP_VERSION_PROP) ?: run {
|
||||||
LOG.info("$LAST_SHOWN_EAP_VERSION_PROP is not defined. Will show What's New.")
|
LOG.info("$LAST_SHOWN_EAP_VERSION_PROP is not defined. Will show What's New.")
|
||||||
return true
|
return true
|
||||||
@@ -21,11 +26,6 @@ internal class WhatsNewContentVersionChecker {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
val newVersion = whatsNewContent.getVersion() ?: run {
|
|
||||||
LOG.warn("What's New content provider returns null version. What's New will be ignored.")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
val result = shouldShowWhatsNew(savedVersion, newVersion)
|
val result = shouldShowWhatsNew(savedVersion, newVersion)
|
||||||
LOG.info("Comparing versions $newVersion and $savedVersion: $result.")
|
LOG.info("Comparing versions $newVersion and $savedVersion: $result.")
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
package com.intellij.platform.whatsNew
|
package com.intellij.platform.whatsNew
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.platform.whatsNew.reaction.FUSReactionChecker
|
||||||
import com.intellij.testFramework.junit5.TestApplication
|
import com.intellij.testFramework.junit5.TestApplication
|
||||||
import com.intellij.testFramework.junit5.fixture.projectFixture
|
import com.intellij.testFramework.junit5.fixture.projectFixture
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
@@ -17,8 +18,9 @@ class OnStartCheckServiceTest {
|
|||||||
private val project = projectFixture()
|
private val project = projectFixture()
|
||||||
|
|
||||||
private val mockContent = object : WhatsNewContent() {
|
private val mockContent = object : WhatsNewContent() {
|
||||||
override fun getRequest(dataContext: DataContext?) = error("Mock object, do not call")
|
override suspend fun show(project: Project, dataContext: DataContext?, triggeredByUser: Boolean, reactionChecker: FUSReactionChecker) {
|
||||||
override fun getActionWhiteList() = error("Mock object, do not call")
|
error("Mock object, do not call")
|
||||||
|
}
|
||||||
override fun getVersion() = error("Mock object, do not call")
|
override fun getVersion() = error("Mock object, do not call")
|
||||||
override suspend fun isAvailable() = error("Mock object, do not call")
|
override suspend fun isAvailable() = error("Mock object, do not call")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user