From ef891fef3d57d40de416611f78d8c3bcdee6e2b3 Mon Sep 17 00:00:00 2001 From: "Dmitry.Krasilschikov" Date: Thu, 10 Oct 2019 21:08:09 +0300 Subject: [PATCH] IDEA-223967 new update dialog: provide "What's New" notification that opens editor with HTML after IDE restart GitOrigin-RevId: 243fadd69f9e7efc1d25e26a6e4c7831f87214a9 --- .../internal/ShowUpdateInfoDialogAction.kt | 3 +- .../fileEditor/impl/HTMLEditorProvider.kt | 25 +++++++++++ .../openapi/fileEditor/impl/HTMLFileEditor.kt | 37 +++++++++++++++ .../updateSettings/impl/UpdateChecker.kt | 4 +- .../impl/UpdateCheckerComponent.java | 45 ++++++++++++++++++- .../updateSettings/impl/UpdateInfoDialog.java | 24 +++++++++- .../impl/WhatsNewFileIconProvider.java | 26 +++++++++++ .../src/messages/IdeBundle.properties | 3 ++ .../src/META-INF/LangExtensions.xml | 1 + .../src/META-INF/PlatformExtensions.xml | 1 + 10 files changed, 163 insertions(+), 6 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HTMLEditorProvider.kt create mode 100644 platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HTMLFileEditor.kt create mode 100644 platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/WhatsNewFileIconProvider.java diff --git a/platform/platform-impl/src/com/intellij/internal/ShowUpdateInfoDialogAction.kt b/platform/platform-impl/src/com/intellij/internal/ShowUpdateInfoDialogAction.kt index 45873ca11035..4932ad5cd29c 100644 --- a/platform/platform-impl/src/com/intellij/internal/ShowUpdateInfoDialogAction.kt +++ b/platform/platform-impl/src/com/intellij/internal/ShowUpdateInfoDialogAction.kt @@ -2,6 +2,7 @@ package com.intellij.internal import com.intellij.ide.util.BrowseFilesListener +import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.application.ApplicationInfo import com.intellij.openapi.application.ApplicationNamesInfo @@ -32,7 +33,7 @@ class ShowUpdateInfoDialogAction : DumbAwareAction() { val dialog = MyDialog(e.project) if (dialog.showAndGet()) { try { - UpdateChecker.testPlatformUpdate(dialog.updateXmlText(), dialog.patchFilePath(), dialog.forceUpdate()) + UpdateChecker.testPlatformUpdate(AnAction.getEventProject(e), dialog.updateXmlText (), dialog.patchFilePath(), dialog.forceUpdate()) } catch (ex: Exception) { Messages.showErrorDialog(e.project, "${ex.javaClass.name}: ${ex.message}", "Something Went Wrong") diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HTMLEditorProvider.kt b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HTMLEditorProvider.kt new file mode 100644 index 000000000000..d178957a4e98 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HTMLEditorProvider.kt @@ -0,0 +1,25 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.openapi.fileEditor.impl + +import com.intellij.openapi.fileEditor.FileEditor +import com.intellij.openapi.fileEditor.FileEditorPolicy +import com.intellij.openapi.fileEditor.FileEditorProvider +import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.openapi.util.KeyWithDefaultValue +import com.intellij.openapi.vfs.VirtualFile + +class HTMLEditorProvider : FileEditorProvider, DumbAware { + override fun createEditor(project: Project, file: VirtualFile): FileEditor = HTMLFileEditor(file) + + override fun accept(project: Project, file: VirtualFile) = file.getUserData(HTML_CONTENT_TYPE)!! + + override fun getEditorTypeId() = "html-editor" + + override fun getPolicy() = FileEditorPolicy.HIDE_DEFAULT_EDITOR + + companion object { + val HTML_CONTENT_TYPE: Key = KeyWithDefaultValue.create("HTML_CONTENT_TYPE", false) + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HTMLFileEditor.kt b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HTMLFileEditor.kt new file mode 100644 index 000000000000..80717f408933 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/HTMLFileEditor.kt @@ -0,0 +1,37 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.openapi.fileEditor.impl + +import com.intellij.openapi.fileEditor.FileEditor +import com.intellij.openapi.fileEditor.FileEditorLocation +import com.intellij.openapi.fileEditor.FileEditorState +import com.intellij.openapi.util.Key +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.ui.JBUI +import com.intellij.util.ui.UIUtil +import java.beans.PropertyChangeListener +import javax.swing.JComponent +import javax.swing.JEditorPane + +class HTMLFileEditor(private val vFile: VirtualFile) : FileEditor { + private val component: JComponent = + JEditorPane().also { + it.editorKit = UIUtil.getHTMLEditorKit() + it.text = VfsUtil.loadText(vFile) + it.border = JBUI.Borders.empty(8, 12) + it.isEditable = false + } + + override fun getComponent(): JComponent = component + override fun getPreferredFocusedComponent() = component + override fun getName() = "HTML Preview" + override fun setState(state: FileEditorState) {} + override fun isModified(): Boolean = false + override fun isValid(): Boolean = true + override fun addPropertyChangeListener(listener: PropertyChangeListener) {} + override fun getUserData(key: Key): T? = null + override fun putUserData(key: Key, value: T?) {} + override fun removePropertyChangeListener(listener: PropertyChangeListener) {} + override fun getCurrentLocation(): FileEditorLocation? = null + override fun dispose() {} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt index 1839f37a8573..4f6ad5f9a623 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt @@ -508,7 +508,7 @@ object UpdateChecker { } /** A helper method for manually testing platform updates (see [com.intellij.internal.ShowUpdateInfoDialogAction]). */ - fun testPlatformUpdate(updateInfoText: String, patchFilePath: String?, forceUpdate: Boolean) { + fun testPlatformUpdate(project: Project?, updateInfoText: String, patchFilePath: String?, forceUpdate: Boolean) { if (!ApplicationManager.getApplication().isInternal) { throw IllegalStateException() } @@ -533,7 +533,7 @@ object UpdateChecker { if (channel != null && newBuild != null) { val patchFile = if (patchFilePath != null) File(FileUtil.toSystemDependentName(patchFilePath)) else null - UpdateInfoDialog(channel, newBuild, patches, patchFile).show() + UpdateInfoDialog(project, channel, newBuild, patches, patchFile).show() } else { NoUpdatesDialog(true).show() diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateCheckerComponent.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateCheckerComponent.java index b06e7084c326..8efd33266916 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateCheckerComponent.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateCheckerComponent.java @@ -3,11 +3,16 @@ package com.intellij.openapi.updateSettings.impl; import com.intellij.ide.IdeBundle; import com.intellij.ide.util.PropertiesComponent; +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationAction; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.*; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManagerListener; @@ -15,11 +20,13 @@ import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.updateSettings.UpdateStrategyCustomization; import com.intellij.openapi.util.BuildNumber; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.concurrency.AppExecutorUtil; import com.intellij.util.concurrency.NonUrgentExecutor; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.text.DateFormatUtil; import org.jdom.JDOMException; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.io.File; @@ -38,6 +45,7 @@ public final class UpdateCheckerComponent implements Runnable { } static final String SELF_UPDATE_STARTED_FOR_BUILD_PROPERTY = "ide.self.update.started.for.build"; + @NonNls static final String UPDATE_WHATS_NEW_MESSAGE = "ide.update.whats.new.message"; private static final Logger LOG = Logger.getInstance(UpdateCheckerComponent.class); @@ -50,13 +58,22 @@ public final class UpdateCheckerComponent implements Runnable { Application app = ApplicationManager.getApplication(); if (!app.isCommandLine()) { NonUrgentExecutor.getInstance().execute(() -> { - checkIfPreviousUpdateFailed(); + boolean updateFailed = checkIfPreviousUpdateFailed(); updateDefaultChannel(); scheduleFirstCheck(); snapPackageNotification(); MessageBusConnection connection = app.getMessageBus().connect(); + if (!updateFailed) { + connection.subscribe(ProjectManager.TOPIC, new ProjectManagerListener() { + @Override + public void projectOpened(@NotNull Project project) { + StartupManager.getInstance(project).registerPostStartupActivity(() -> { showWhatsNewNotification(project); }); + } + }); + } + connection.subscribe(ProjectManager.TOPIC, new ProjectManagerListener() { @Override public void projectOpened(@NotNull Project project) { @@ -82,14 +99,38 @@ public final class UpdateCheckerComponent implements Runnable { if (future != null) future.cancel(false); } - private static void checkIfPreviousUpdateFailed() { + private static void showWhatsNewNotification(@NotNull Project project) { + PropertiesComponent properties = PropertiesComponent.getInstance(); + String updateHtmlMessage = properties.getValue(UPDATE_WHATS_NEW_MESSAGE); + if (updateHtmlMessage == null) { + LOG.warn("Cannot show what's new notification: no content found."); + return; + } + + String title = IdeBundle.message("update.whats.new.notification.title", ApplicationNamesInfo.getInstance().getFullProductName()); + UpdateChecker.NOTIFICATIONS.createNotification(title, null, null, NotificationType.INFORMATION, null) + .addAction(new NotificationAction(IdeBundle.message("update.whats.new.notification.action")) { + @Override + public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification notification) { + LightVirtualFile file = new LightVirtualFile(IdeBundle.message("update.whats.new.file.name", ApplicationInfo.getInstance().getFullVersion()), updateHtmlMessage); + file.putUserData(HTMLEditorProvider.Companion.getHTML_CONTENT_TYPE(), true); + FileEditorManager.getInstance(project).openFile(file, true); + IdeUpdateUsageTriggerCollector.trigger("update.whats.new"); + } + }); + properties.setValue(UPDATE_WHATS_NEW_MESSAGE, null); + } + + private static boolean checkIfPreviousUpdateFailed() { PropertiesComponent properties = PropertiesComponent.getInstance(); if (ApplicationInfo.getInstance().getBuild().asString().equals(properties.getValue(SELF_UPDATE_STARTED_FOR_BUILD_PROPERTY)) && new File(PathManager.getLogPath(), ERROR_LOG_FILE_NAME).length() > 0) { IdeUpdateUsageTriggerCollector.trigger("update.failed"); LOG.info("The previous IDE update failed"); + return false; } properties.setValue(SELF_UPDATE_STARTED_FOR_BUILD_PROPERTY, null); + return true; } private static void updateDefaultChannel() { diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java index 101435577984..bfd5e2b872a1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java @@ -11,15 +11,19 @@ import com.intellij.notification.NotificationType; import com.intellij.openapi.application.*; import com.intellij.openapi.application.impl.ApplicationImpl; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider; import com.intellij.openapi.progress.PerformInBackgroundOption; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.testFramework.LightVirtualFile; import com.intellij.ui.JBColor; import com.intellij.ui.LicensingFacade; import com.intellij.util.SystemProperties; @@ -54,6 +58,8 @@ class UpdateInfoDialog extends AbstractUpdateDialog { private final Pair myLicenseInfo; private final File myTestPatch; + private AbstractAction myWhatsNewAction; + UpdateInfoDialog(@NotNull UpdateChannel channel, @NotNull BuildInfo newBuild, @Nullable UpdateChain patches, @@ -77,7 +83,7 @@ class UpdateInfoDialog extends AbstractUpdateDialog { IdeUpdateUsageTriggerCollector.triggerUpdateDialog(myPatches, ApplicationManager.getApplication().isRestartCapable()); } - UpdateInfoDialog(UpdateChannel channel, BuildInfo newBuild, UpdateChain patches, @Nullable File patchFile) { + UpdateInfoDialog(@Nullable Project project, UpdateChannel channel, BuildInfo newBuild, UpdateChain patches, @Nullable File patchFile) { super(true); myUpdatedChannel = channel; myUpdatedPlugins = null; @@ -86,6 +92,18 @@ class UpdateInfoDialog extends AbstractUpdateDialog { myWriteProtected = false; myLicenseInfo = initLicensingInfo(myUpdatedChannel, myNewBuild); myTestPatch = patchFile; + if (project != null) { + myWhatsNewAction = new AbstractAction("What's new") { + @Override + public void actionPerformed(ActionEvent e) { + String title = IdeBundle.message("update.whats.new.file.name", ApplicationInfo.getInstance().getFullVersion()); + LightVirtualFile file = new LightVirtualFile(title, myNewBuild.getMessage()); + file.putUserData(HTMLEditorProvider.Companion.getHTML_CONTENT_TYPE(), true); + FileEditorManager.getInstance(project).openFile(file, true); + close(OK_EXIT_CODE); + } + }; + } init(); setTitle("[TEST] " + getTitle()); } @@ -175,6 +193,10 @@ class UpdateInfoDialog extends AbstractUpdateDialog { actions.add(updateButton); } + if (myWhatsNewAction != null) { + actions.add(myWhatsNewAction); + } + return actions.toArray(new Action[0]); } diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/WhatsNewFileIconProvider.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/WhatsNewFileIconProvider.java new file mode 100644 index 000000000000..4bb7bac7ec23 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/WhatsNewFileIconProvider.java @@ -0,0 +1,26 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.openapi.updateSettings.impl; + +import com.intellij.icons.AllIcons; +import com.intellij.ide.FileIconProvider; +import com.intellij.ide.IdeBundle; +import com.intellij.openapi.fileEditor.impl.HTMLEditorProvider; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Iconable; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public final class WhatsNewFileIconProvider implements FileIconProvider { + @Nullable + @Override + public Icon getIcon(@NotNull VirtualFile virtualFile, @Iconable.IconFlags int flags, @Nullable Project project) { + Boolean isHtml = virtualFile.getUserData(HTMLEditorProvider.Companion.getHTML_CONTENT_TYPE()); + if (isHtml != null && isHtml && virtualFile.getName().startsWith(IdeBundle.message("update.whats.new.file.name", ""))) { + return AllIcons.General.Information; + } + return null; + } +} \ No newline at end of file diff --git a/platform/platform-resources-en/src/messages/IdeBundle.properties b/platform/platform-resources-en/src/messages/IdeBundle.properties index 0a3417d6d200..c262704ccb16 100644 --- a/platform/platform-resources-en/src/messages/IdeBundle.properties +++ b/platform/platform-resources-en/src/messages/IdeBundle.properties @@ -1023,6 +1023,9 @@ updates.settings.current.version=Current version updates.settings.last.check=Last checked updates.settings.ignored=View/edit ignored updates updates.settings.ignored.title=Ignored Updates +update.whats.new.notification.title={0} Update Installed +update.whats.new.file.name=What''s New in {0} +update.whats.new.notification.action=What''s New update.plugin.host.url.message=Repository URL: update.no.update.hosts=No plugin repositories configured update.edit.plugin.host.title=Edit Repository diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index eabb00c6e177..2a838844d397 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -689,6 +689,7 @@ id="PlatformProjectConfigurator"/> + diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index c25f19020571..6764b1e66982 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -620,6 +620,7 @@ +