From e8d120044aba192ccae7d46ce6dded02fb2925a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Smolarek?= <34063647+Razz4780@users.noreply.github.com> Date: Thu, 21 Dec 2023 20:43:52 +0100 Subject: [PATCH] Replaced waitlist registration action with mirrord for Teams action (#221) * Replaced action * Changelog entry * Changed button text * Added notifications about mirrord for Teams * Changelog entry * Changelog merged --- .../+mirrord-for-teams-action.changed.md | 2 + .../com/metalbear/mirrord/MirrordDropDown.kt | 10 +- .../metalbear/mirrord/MirrordExecManager.kt | 2 + .../mirrord/MirrordProjectService.kt | 2 + .../metalbear/mirrord/MirrordRunCounter.kt | 30 +++++ .../metalbear/mirrord/MirrordSettingsState.kt | 8 +- .../mirrord/MirrordWaitlistDialog.kt | 127 ------------------ 7 files changed, 47 insertions(+), 134 deletions(-) create mode 100644 changelog.d/+mirrord-for-teams-action.changed.md create mode 100644 modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordRunCounter.kt delete mode 100644 modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordWaitlistDialog.kt diff --git a/changelog.d/+mirrord-for-teams-action.changed.md b/changelog.d/+mirrord-for-teams-action.changed.md new file mode 100644 index 00000000..69860d26 --- /dev/null +++ b/changelog.d/+mirrord-for-teams-action.changed.md @@ -0,0 +1,2 @@ +Replaced waitlist registration action with mirrord for Teams actions. +Added occasional notifications about mirrord for Teams. \ No newline at end of file diff --git a/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordDropDown.kt b/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordDropDown.kt index a7fd0959..c0d60e4c 100644 --- a/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordDropDown.kt +++ b/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordDropDown.kt @@ -13,10 +13,11 @@ import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.util.indexing.* import com.intellij.util.io.EnumeratorStringDescriptor import com.intellij.util.io.KeyDescriptor -import java.util.Collections +import java.util.* import javax.swing.JComponent const val FEEDBACK_URL = "https://mirrord.dev/feedback" +const val MIRRORD_FOR_TEAMS_URL = "https://mirrord.dev/docs/teams/introduction/" fun VirtualFile.relativePath(project: Project): String { return calcRelativeToProjectPath(this, project, includeFilePath = true, keepModuleAlwaysOnTheLeft = true) @@ -105,10 +106,9 @@ class MirrordDropDown : ComboBoxAction(), DumbAware { } } - private class WaitlistSignupAction : AnAction("Join the waitlist") { + private class NavigateToMirrodForTeamsIntroAction : AnAction("Try it now") { override fun actionPerformed(e: AnActionEvent) { - val project = e.project ?: return - MirrordWaitlistDialog(project).show() + BrowserUtil.browse(MIRRORD_FOR_TEAMS_URL) } } @@ -130,7 +130,7 @@ class MirrordDropDown : ComboBoxAction(), DumbAware { add(SelectActiveConfigAction()) add(SettingsAction()) addSeparator("mirrord for Teams") - add(WaitlistSignupAction()) + add(NavigateToMirrodForTeamsIntroAction()) addSeparator("Feedback") add(FeedbackAction()) } diff --git a/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordExecManager.kt b/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordExecManager.kt index debd73d4..9df11c0a 100644 --- a/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordExecManager.kt +++ b/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordExecManager.kt @@ -159,6 +159,8 @@ class MirrordExecManager(private val service: MirrordProjectService) { null } + service.runCounter.bump(target?.startsWith("deploy") ?: false) + val executionInfo = mirrordApi.exec( cli, target, diff --git a/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordProjectService.kt b/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordProjectService.kt index 450ca065..49397b2c 100644 --- a/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordProjectService.kt +++ b/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordProjectService.kt @@ -17,6 +17,8 @@ class MirrordProjectService(val project: Project) : Disposable { val versionCheck: MirrordVersionCheck = MirrordVersionCheck(this) + val runCounter: MirrordRunCounter = MirrordRunCounter(this) + fun mirrordApi(environment: Map?): MirrordApi { return MirrordApi(this, environment) } diff --git a/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordRunCounter.kt b/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordRunCounter.kt new file mode 100644 index 00000000..a961dc9e --- /dev/null +++ b/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordRunCounter.kt @@ -0,0 +1,30 @@ +package com.metalbear.mirrord + +import com.intellij.ide.util.PropertiesComponent +import com.intellij.notification.NotificationType + +private const val NOTIFY_AFTER: Int = 100 +private const val NOTIFY_EVERY: Int = 30 +private const val COUNTER_KEY: String = "mirrordForTeamsCounter" + +private const val DEPLOYMENT_EXEC_MESSAGE: String = "When targeting multi-pod deployments, mirrord impersonates the first pod in the deployment. Support for multi-pod impersonation requires the mirrord operator, which is part of mirrord for Teams." +private const val REGULAR_EXEC_MESSAGE: String = "For more features of mirrord, including multi-pod impersonation, check out mirrord for Teams." +class MirrordRunCounter(private val service: MirrordProjectService) { + private fun showNotification(message: String) { + service.notifier.notification(message, NotificationType.INFORMATION).withLink("Try it now", MIRRORD_FOR_TEAMS_URL).withDontShowAgain(MirrordSettingsState.NotificationId.MIRRORD_FOR_TEAMS).fire() + } + fun bump(isDeploymentExec: Boolean) { + val pc = PropertiesComponent.getInstance() + + val previousRuns = pc.getInt(COUNTER_KEY, 0) + pc.setValue(COUNTER_KEY, (previousRuns + 1).toString()) + + if (isDeploymentExec) { + this.showNotification(DEPLOYMENT_EXEC_MESSAGE) + } else if (previousRuns >= NOTIFY_AFTER) { + if (previousRuns == NOTIFY_AFTER || (previousRuns - NOTIFY_AFTER) % NOTIFY_EVERY == 0) { + this.showNotification(REGULAR_EXEC_MESSAGE) + } + } + } +} diff --git a/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordSettingsState.kt b/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordSettingsState.kt index 8e5cc1c1..5433f991 100644 --- a/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordSettingsState.kt +++ b/modules/core/src/main/kotlin/com/metalbear/mirrord/MirrordSettingsState.kt @@ -1,7 +1,10 @@ package com.metalbear.mirrord import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.components.* +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.components.service @State(name = "MirrordSettingsState", storages = [Storage("mirrord.xml")]) open class MirrordSettingsState : PersistentStateComponent { @@ -29,7 +32,8 @@ open class MirrordSettingsState : PersistentStateComponent>): HttpRequest.Builder { - val boundary = UUID.randomUUID().toString() - val byteArrays = mutableListOf() - val separator = "--$boundary\r\nContent-Disposition: form-data; name=".toByteArray(StandardCharsets.UTF_8) - - data.forEach { - byteArrays.add(separator) - byteArrays.add("\"${it.first}\"\r\n\r\n${it.second}\r\n".toByteArray(StandardCharsets.UTF_8)) - } - - byteArrays.add("--$boundary--".toByteArray(StandardCharsets.UTF_8)) - - this - .header("Content-Type", "multipart/form-data;boundary=$boundary") - .POST(HttpRequest.BodyPublishers.ofByteArrays(byteArrays)) - - return this -} - -class MirrordWaitlistDialog(private val project: Project) { - /** - * Background task for making the signup request. - */ - private class SignupTask(project: Project, private val email: String) : Task.Backgroundable(project, "mirrord", true) { - private val service: MirrordProjectService = project.service() - - override fun run(indicator: ProgressIndicator) { - indicator.text = "making the signup request..." - - val client = HttpClient.newHttpClient() - val request = HttpRequest - .newBuilder(URI(SIGNUP_ENDPOINT)) - .timeout(Duration.ofSeconds(5)) - .postMultipartFormData(listOf(Pair("email", email))) - .build() - - val response = client.send(request, HttpResponse.BodyHandlers.ofString()) - if (response.statusCode() != 200) { - throw RuntimeException("invalid response status ${response.statusCode()}") - } - } - - override fun onThrowable(error: Throwable) { - MirrordLogger.logger.debug("waitlist signup failed", error) - service.notifier.notifyRichError("Failed to join the waitlist: ${error.message}") - } - - override fun onSuccess() { - service.notifier.notifySimple( - "Thank you for joining the waitlist for mirrord for Teams! We'll be in touch soon.", - NotificationType.INFORMATION - ) - } - } - - /** - * Display a simple dialog with email input. - * Waitlist signup is requested when the user presses the "Ok" button. - */ - fun show() { - val dialog = DialogBuilder() - - val textField = JTextField().apply { - val field = this - document.addDocumentListener(object : DocumentListener { - override fun insertUpdate(e: DocumentEvent) = doUpdate() - override fun removeUpdate(e: DocumentEvent) = doUpdate() - override fun changedUpdate(e: DocumentEvent) = doUpdate() - - private fun doUpdate() { - dialog.okActionEnabled(field.text.isNotEmpty()) - } - }) - } - - dialog.apply { - val panel = JPanel().apply { - layout = BoxLayout(this, BoxLayout.X_AXIS) - border = JBUI.Borders.empty(10, 5) - add(JLabel("Email Address:")) - add(Box.createRigidArea(Dimension(10, 0))) - add( - textField.apply { - minimumSize = Dimension(250, 50) - } - ) - } - setCenterPanel(panel) - setTitle("mirrord for Teams Waitlist Signup") - okActionEnabled(false) - } - - if (dialog.show() != DialogWrapper.OK_EXIT_CODE) { - return - } - - val email = textField.text - SignupTask(project, email).queue() - } -}