Skip to content

Commit

Permalink
feat: implemented settings to disable editor push notification (redha…
Browse files Browse the repository at this point in the history
…t-developer#763)

Signed-off-by: Andre Dietisheim <[email protected]>
  • Loading branch information
adietish committed Oct 2, 2024
1 parent 9c69e50 commit 0c4f8e2
Show file tree
Hide file tree
Showing 8 changed files with 584 additions and 283 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,10 @@ import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.Key
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiDocumentManager
import com.intellij.util.messages.MessageBusConnection
import com.redhat.devtools.intellij.common.utils.MetadataClutter
import com.redhat.devtools.intellij.common.validation.KubernetesResourceInfo
import com.redhat.devtools.intellij.kubernetes.editor.notification.DeletedNotification
import com.redhat.devtools.intellij.kubernetes.editor.notification.ErrorNotification
import com.redhat.devtools.intellij.kubernetes.editor.notification.PullNotification
import com.redhat.devtools.intellij.kubernetes.editor.notification.PulledNotification
import com.redhat.devtools.intellij.kubernetes.editor.notification.PushNotification
import com.redhat.devtools.intellij.kubernetes.editor.notification.PushedNotification
import com.redhat.devtools.intellij.kubernetes.editor.notification.Notifications
import com.redhat.devtools.intellij.kubernetes.editor.util.getDocument
import com.redhat.devtools.intellij.kubernetes.editor.util.isKubernetesResource
import com.redhat.devtools.intellij.kubernetes.model.IResourceModel
Expand All @@ -39,6 +35,8 @@ import com.redhat.devtools.intellij.kubernetes.model.context.IActiveContext
import com.redhat.devtools.intellij.kubernetes.model.util.ResourceException
import com.redhat.devtools.intellij.kubernetes.model.util.toMessage
import com.redhat.devtools.intellij.kubernetes.model.util.toTitle
import com.redhat.devtools.intellij.kubernetes.settings.SettingsChangeListener
import com.redhat.devtools.intellij.kubernetes.settings.SettingsConfigurable
import io.fabric8.kubernetes.api.model.HasMetadata
import java.util.concurrent.CompletableFuture

Expand All @@ -57,18 +55,7 @@ open class ResourceEditor(
// for mocking purposes
private val createResourceFileForVirtual: (file: VirtualFile?) -> ResourceFile? =
ResourceFile.Factory::create,
// for mocking purposes
private val pushNotification: PushNotification = PushNotification(editor, project),
// for mocking purposes
private val pushedNotification: PushedNotification = PushedNotification(editor, project),
// for mocking purposes
private val pullNotification: PullNotification = PullNotification(editor, project),
// for mocking purposes
private val pulledNotification: PulledNotification = PulledNotification(editor, project),
// for mocking purposes
private val deletedNotification: DeletedNotification = DeletedNotification(editor, project),
// for mocking purposes
private val errorNotification: ErrorNotification = ErrorNotification(editor, project),
private val notifications: Notifications = Notifications(editor, project),
// for mocking purposes
private val getDocument: (FileEditor) -> Document? = ::getDocument,
// for mocking purposes
Expand All @@ -81,14 +68,18 @@ open class ResourceEditor(
// for mocking purposes
private val diff: ResourceDiff = ResourceDiff(project),
// for mocking purposes
protected val editorResources: EditorResources = EditorResources(resourceModel)
protected val editorResources: EditorResources = EditorResources(resourceModel),
private val connection: MessageBusConnection = ApplicationManager.getApplication().messageBus.connect()
) : Disposable {

init {
Disposer.register(editor, this)
editorResources.resourceChangedListener = onResourceChanged()
resourceModel.addListener(onNamespaceOrContextChanged())
runAsync { enableEditingNonProjectFile() }
runAsync {
enableEditingNonProjectFile()
connection.subscribe(SettingsChangeListener.CHANGED, onSettingsChanged())
}
}

companion object {
Expand Down Expand Up @@ -117,15 +108,15 @@ open class ResourceEditor(
if (editorResources.size == 1) {
// show notification for 1 resource
val editorResource = editorResources.firstOrNull() ?: return@runAsync
showNotification(editorResource)
notifications.show(editorResource)
} else if (editorResources.size > 1) {
// show notification for multiple resources
showNotification(editorResources)
notifications.show(editorResources)
}
} catch (e: Exception) {
runInUI {
hideNotifications()
errorNotification.show(
notifications.hideAll()
notifications.showError(
toTitle(e),
toMessage(e.cause)
)
Expand All @@ -134,108 +125,14 @@ open class ResourceEditor(
}
}

open protected fun updateDeleted(deleted: HasMetadata?) {
protected open fun updateDeleted(deleted: HasMetadata?) {
if (deleted == null) {
return
}
editorResources.setDeleted(deleted)
update()
}

private fun showNotification(editorResource: EditorResource) {
val state = editorResource.getState()
val resource = editorResource.getResource()
when (state) {
is Error ->
showErrorNotification(state.title, state.message)
is Pushed ->
showPushedNotification(listOf(editorResource))
is DeletedOnCluster ->
showDeletedNotification(resource)
/**
* avoid too many notifications, don't notify outdated
is Outdated ->
showPullNotification(resource)
*/
is Modified ->
showPushNotification(true, listOf(editorResource))

else ->
runInUI {
hideNotifications()
}
}
}

private fun showNotification(editorResources: Collection<EditorResource>) {
val toPush = editorResources.filter(FILTER_TO_PUSH)
if (toPush.isNotEmpty()) {
showPushNotification(false, toPush)
return
}
val inError = editorResources.filter(FILTER_ERROR)
if (inError.isNotEmpty()) {
showErrorNotification(inError)
} else {
runInUI {
hideNotifications()
}
}
}

private fun showErrorNotification(title: String, message: String?) {
runInUI {
hideNotifications()
errorNotification.show(title, message)
}
}

private fun showErrorNotification(editorResources: Collection<EditorResource>) {
val inError = editorResources.filter(FILTER_ERROR)
val toDisplay = inError.firstOrNull()?.getState() as? Error ?: return
showErrorNotification(toDisplay.title, toDisplay.message)
}

private fun showPushNotification(showPull: Boolean, editorResources: Collection<EditorResource>) {
runInUI {
// hide & show in the same UI thread runnable avoid flickering
hideNotifications()
pushNotification.show(showPull, editorResources)
}
}

private fun showPushedNotification(editorResources: Collection<EditorResource>) {
runInUI {
// hide & show in the same UI thread runnable avoid flickering
hideNotifications()
pushedNotification.show(editorResources)
}
}

private fun showPullNotification(resource: HasMetadata) {
runInUI {
hideNotifications()
pullNotification.show(resource)
}
}

private fun showDeletedNotification(resource: HasMetadata) {
runInUI {
// hide & show in the same UI thread runnable avoid flickering
hideNotifications()
deletedNotification.show(resource)
}
}

private fun hideNotifications() {
errorNotification.hide()
pullNotification.hide()
deletedNotification.hide()
pushNotification.hide()
pushedNotification.hide()
pulledNotification.hide()
}

/**
* Pulls the resource from the cluster and replaces the content of this editor.
* Does nothing if it doesn't exist.
Expand All @@ -248,7 +145,7 @@ open class ResourceEditor(
}
runInUI {
// hide before running pull. Pull may take quite some time on remote cluster
hideNotifications()
notifications.hideAll()
}
val resource = resources.firstOrNull() ?: return

Expand Down Expand Up @@ -293,7 +190,7 @@ open class ResourceEditor(
fun push(all: Boolean) {
runInUI {
// hide before running push. Push may take quite some time on remote cluster
hideNotifications()
notifications.hideAll()
}
runAsync {
if (all) {
Expand Down Expand Up @@ -370,7 +267,7 @@ open class ResourceEditor(
}
runInUI {
replaceDocument(resources)
hideNotifications()
notifications.hideAll()
}
}

Expand Down Expand Up @@ -431,6 +328,21 @@ open class ResourceEditor(
}
}

protected open fun onSettingsChanged(): SettingsChangeListener {
return object : SettingsChangeListener {
override fun changed(property: String, value: String?) {
if (value != null
&& SettingsConfigurable.EDITOR_NOTIFICATIONS_DISABLED == property) {
if (true == value.toBoolean()) {
notifications.hideAll()
} else {
update()
}
}
}
}
}

/**
* Closes this instance and cleans up references to it.
* - Removes the resource model listener,
Expand Down Expand Up @@ -496,5 +408,6 @@ open class ResourceEditor(
override fun dispose() {
resourceModel.removeListener(onNamespaceContextChanged)
editorResources.dispose()
connection.dispose()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*******************************************************************************
* Copyright (c) 2024 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package com.redhat.devtools.intellij.kubernetes.editor.notification

import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.project.Project
import com.redhat.devtools.intellij.kubernetes.editor.DeletedOnCluster
import com.redhat.devtools.intellij.kubernetes.editor.EditorResource
import com.redhat.devtools.intellij.kubernetes.editor.Error
import com.redhat.devtools.intellij.kubernetes.editor.FILTER_ERROR
import com.redhat.devtools.intellij.kubernetes.editor.FILTER_TO_PUSH
import com.redhat.devtools.intellij.kubernetes.editor.Modified
import com.redhat.devtools.intellij.kubernetes.editor.Pushed
import io.fabric8.kubernetes.api.model.HasMetadata

open class Notifications(
editor: FileEditor,
project: Project,
// for mocking purposes
private val pushNotification: PushNotification = PushNotification(editor, project),
// for mocking purposes
private val pushedNotification: PushedNotification = PushedNotification(editor, project),
// for mocking purposes
private val pullNotification: PullNotification = PullNotification(editor, project),
// for mocking purposes
private val pulledNotification: PulledNotification = PulledNotification(editor, project),
// for mocking purposes
private val deletedNotification: DeletedNotification = DeletedNotification(editor, project),
// for mocking purposes
private val errorNotification: ErrorNotification = ErrorNotification(editor, project),
) {

fun show(editorResource: EditorResource) {
val state = editorResource.getState()
val resource = editorResource.getResource()
when (state) {
is Error ->
showError(state.title, state.message)

is Pushed ->
showPushed(listOf(editorResource))

is DeletedOnCluster ->
showDeleted(resource)

/**
* avoid too many notifications, don't notify outdated
is Outdated ->
showPullNotification(resource)
*/

is Modified ->
showPush(true, listOf(editorResource))

else ->
hideAll()
}
}

fun show(editorResources: Collection<EditorResource>) {
val toPush = editorResources.filter(FILTER_TO_PUSH)
if (toPush.isNotEmpty()) {
showPush(false, toPush)
return
}
val inError = editorResources.filter(FILTER_ERROR)
if (inError.isNotEmpty()) {
showError(inError)
} else {
hideAll()
}
}

fun showError(title: String, message: String?) {
runInUI {
hideAll()
errorNotification.show(title, message)
}
}

fun showError(editorResources: Collection<EditorResource>) {
val inError = editorResources.filter(FILTER_ERROR)
val toDisplay = inError.firstOrNull()?.getState() as? Error ?: return
showError(toDisplay.title, toDisplay.message)
}

fun showPush(showPull: Boolean, editorResources: Collection<EditorResource>) {
runInUI {
// hide & show in the same UI thread runnable avoid flickering
hideAll()
pushNotification.show(showPull, editorResources)
}
}

fun showPushed(editorResources: Collection<EditorResource>) {
runInUI {
// hide & show in the same UI thread runnable avoid flickering
hideAll()
pushedNotification.show(editorResources)
}
}

fun showPull(resource: HasMetadata) {
runInUI {
hideAll()
pullNotification.show(resource)
}
}

fun showDeleted(resource: HasMetadata) {
runInUI {
// hide & show in the same UI thread runnable avoid flickering
hideAll()
deletedNotification.show(resource)
}
}

fun hideAll() {
runInUI {
errorNotification.hide()
pullNotification.hide()
deletedNotification.hide()
pushNotification.hide()
pushedNotification.hide()
pulledNotification.hide()
}
}

protected open fun runInUI(runnable: () -> Unit) {
if (ApplicationManager.getApplication().isDispatchThread) {
runnable.invoke()
} else {
ApplicationManager.getApplication().invokeLater(runnable)
}
}
}
Loading

0 comments on commit 0c4f8e2

Please sign in to comment.