diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ce931c..aa4f065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ # CodeClocker Changelog -## 1.0.2 +## 1.0.4 - 2025-04-03 +### Added +- Validate API key input +- Improve onboarding UX + +## 1.0.2 - 2025-04-01 ### Added - Support IntelliJ Platform 2024.3.5 diff --git a/README.md b/README.md index 2e6c4f7..e45adcc 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ## What's CodeClocker -CodeClocker automatically tracks coding activity and visualizes it on web dashboard. +CodeClocker automatically tracks coding activity and visualizes it on [web dashboard on CodeClocker Hub](https://hub.codeclocker.com/). ![Demo](https://raw.githubusercontent.com/codeclocker/codeclocker-intellij-plugin/main/docs/img/demo.gif) diff --git a/gradle.properties b/gradle.properties index 88ed5f9..b8e4cb2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,10 +1,10 @@ # IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html pluginGroup = com.codeclocker -pluginName = CodeClocker - IntelliJ IDEA Activity Tracker +pluginName = CodeClocker pluginRepositoryUrl = https://github.com/codeclocker/codeclocker-intellij-plugin # SemVer format -> https://semver.org -pluginVersion = 1.0.2 +pluginVersion = 1.0.4 # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html pluginSinceBuild = 242 diff --git a/src/main/java/com/codeclocker/plugin/intellij/ListenerRegistrator.java b/src/main/java/com/codeclocker/plugin/intellij/ListenerRegistrator.java index 2035381..b2cfaf1 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/ListenerRegistrator.java +++ b/src/main/java/com/codeclocker/plugin/intellij/ListenerRegistrator.java @@ -2,10 +2,10 @@ import static java.awt.AWTEvent.FOCUS_EVENT_MASK; -import com.codeclocker.plugin.intellij.apikey.ApiKeyActivationCheckerTask; import com.codeclocker.plugin.intellij.apikey.ApiKeyPromptStartupActivity; import com.codeclocker.plugin.intellij.listeners.FocusListener; import com.codeclocker.plugin.intellij.reporting.DataReportingTask; +import com.codeclocker.plugin.intellij.subscription.SubscriptionStateCheckerTask; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.ProjectActivity; @@ -51,6 +51,6 @@ private static void startDataReportingTask() { } private static void startCheckingApiKeyStatus() { - ApplicationManager.getApplication().getService(ApiKeyActivationCheckerTask.class).schedule(); + ApplicationManager.getApplication().getService(SubscriptionStateCheckerTask.class).schedule(); } } diff --git a/src/main/java/com/codeclocker/plugin/intellij/apikey/ApiKeyInputValidator.java b/src/main/java/com/codeclocker/plugin/intellij/apikey/ApiKeyInputValidator.java new file mode 100644 index 0000000..381a6a0 --- /dev/null +++ b/src/main/java/com/codeclocker/plugin/intellij/apikey/ApiKeyInputValidator.java @@ -0,0 +1,21 @@ +package com.codeclocker.plugin.intellij.apikey; + +import com.intellij.openapi.ui.InputValidator; +import com.intellij.openapi.util.NlsSafe; + +public class ApiKeyInputValidator implements InputValidator { + + @Override + public boolean checkInput(@NlsSafe String s) { + return isValid(s); + } + + @Override + public boolean canClose(@NlsSafe String s) { + return isValid(s); + } + + private static boolean isValid(String apiKey) { + return apiKey != null && apiKey.startsWith("cc-") && apiKey.length() == 39; + } +} diff --git a/src/main/java/com/codeclocker/plugin/intellij/apikey/ApiKeyPromptStartupActivity.java b/src/main/java/com/codeclocker/plugin/intellij/apikey/ApiKeyPromptStartupActivity.java index 426a6d0..3f6525d 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/apikey/ApiKeyPromptStartupActivity.java +++ b/src/main/java/com/codeclocker/plugin/intellij/apikey/ApiKeyPromptStartupActivity.java @@ -1,23 +1,12 @@ package com.codeclocker.plugin.intellij.apikey; -import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.application.ApplicationManager; public class ApiKeyPromptStartupActivity { - private static final String FIRST_LAUNCH_PROPERTY = "com.codeclocker.first-launch"; - public static void showApiKeyDialog() { - PropertiesComponent properties = PropertiesComponent.getInstance(); - boolean prompt = properties.getBoolean("com.codeclocker.prompt-for-api-key", true); - - if (ApiKeyPersistence.getApiKey() == null && prompt) { - ApplicationManager.getApplication() - .invokeLater( - () -> { - EnterApiKeyAction.showAction(); - properties.setValue(FIRST_LAUNCH_PROPERTY, false); - }); + if (ApiKeyPersistence.getApiKey() == null) { + ApplicationManager.getApplication().invokeLater(EnterApiKeyAction::showAction); } } } diff --git a/src/main/java/com/codeclocker/plugin/intellij/apikey/EnterApiKeyAction.java b/src/main/java/com/codeclocker/plugin/intellij/apikey/EnterApiKeyAction.java index e0b6590..298ba60 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/apikey/EnterApiKeyAction.java +++ b/src/main/java/com/codeclocker/plugin/intellij/apikey/EnterApiKeyAction.java @@ -3,6 +3,7 @@ import static com.codeclocker.plugin.intellij.HubHost.HUB_UI_HOST; import static org.apache.commons.lang3.StringUtils.isBlank; +import com.codeclocker.plugin.intellij.subscription.SubscriptionStateCheckerTask; import com.intellij.ide.BrowserUtil; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -23,14 +24,14 @@ public static void showAction() { Messages.showYesNoCancelDialog( text, "Enter CodeClocker API Key", - "I Have API Key", "Get API Key", + "I Have API Key", "Cancel", Messages.getInformationIcon()); - if (result == Messages.YES) { + if (result == Messages.NO) { showApiKeyInputDialog(); - } else if (result == Messages.NO) { + } else if (result == Messages.YES) { BrowserUtil.browse(HUB_UI_HOST + "/api-key"); showApiKeyInputDialog(); } @@ -53,8 +54,12 @@ private static String getText() { private static void showApiKeyInputDialog() { String apiKey = Messages.showInputDialog( - "Enter your API key:", "Activate CodeClocker", Messages.getInformationIcon()); + "Enter your API key\n\nCopy from: hub.codeclocker.com/api-key", + "Activate CodeClocker", + Messages.getInformationIcon(), + null, + new ApiKeyInputValidator()); ApiKeyPersistence.persistApiKey(apiKey); - ApplicationManager.getApplication().getService(ApiKeyActivationCheckerTask.class).schedule(); + ApplicationManager.getApplication().getService(SubscriptionStateCheckerTask.class).schedule(); } } diff --git a/src/main/java/com/codeclocker/plugin/intellij/reporting/DataReportingTask.java b/src/main/java/com/codeclocker/plugin/intellij/reporting/DataReportingTask.java index 0c19494..ff635ff 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/reporting/DataReportingTask.java +++ b/src/main/java/com/codeclocker/plugin/intellij/reporting/DataReportingTask.java @@ -9,12 +9,12 @@ import static org.apache.commons.lang3.StringUtils.isBlank; import com.codeclocker.plugin.intellij.apikey.ApiKeyLifecycle; -import com.codeclocker.plugin.intellij.apikey.CheckApiKeyStateHttpClient; import com.codeclocker.plugin.intellij.config.ConfigProvider; import com.codeclocker.plugin.intellij.services.ChangesActivityTracker; import com.codeclocker.plugin.intellij.services.ChangesSample; import com.codeclocker.plugin.intellij.services.TimeSpentActivityTracker; import com.codeclocker.plugin.intellij.services.TimeSpentPerProjectSample; +import com.codeclocker.plugin.intellij.subscription.CheckSubscriptionStateHttpClient; import com.fasterxml.jackson.core.JsonProcessingException; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; @@ -28,7 +28,7 @@ public final class DataReportingTask implements Disposable { - private static final Logger LOG = Logger.getInstance(CheckApiKeyStateHttpClient.class); + private static final Logger LOG = Logger.getInstance(CheckSubscriptionStateHttpClient.class); private final int flushToServerFrequencySeconds; private final TimeSpentActivityTracker timeSpentActivityTracker; diff --git a/src/main/java/com/codeclocker/plugin/intellij/apikey/CheckApiKeyStateHttpClient.java b/src/main/java/com/codeclocker/plugin/intellij/subscription/CheckSubscriptionStateHttpClient.java similarity index 82% rename from src/main/java/com/codeclocker/plugin/intellij/apikey/CheckApiKeyStateHttpClient.java rename to src/main/java/com/codeclocker/plugin/intellij/subscription/CheckSubscriptionStateHttpClient.java index 783dfa2..d716519 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/apikey/CheckApiKeyStateHttpClient.java +++ b/src/main/java/com/codeclocker/plugin/intellij/subscription/CheckSubscriptionStateHttpClient.java @@ -1,4 +1,4 @@ -package com.codeclocker.plugin.intellij.apikey; +package com.codeclocker.plugin.intellij.subscription; import static com.codeclocker.plugin.intellij.HubHost.HUB_API_HOST; import static com.codeclocker.plugin.intellij.Timeouts.CONNECT_TIMEOUT; @@ -8,9 +8,9 @@ import com.intellij.util.io.HttpRequests; import org.jetbrains.annotations.Nullable; -public class CheckApiKeyStateHttpClient { +public class CheckSubscriptionStateHttpClient { - private static final Logger LOG = Logger.getInstance(CheckApiKeyStateHttpClient.class); + private static final Logger LOG = Logger.getInstance(CheckSubscriptionStateHttpClient.class); @Nullable public String check(String apiKey) { diff --git a/src/main/java/com/codeclocker/plugin/intellij/apikey/ApiKeyActivationCheckerTask.java b/src/main/java/com/codeclocker/plugin/intellij/subscription/SubscriptionStateCheckerTask.java similarity index 75% rename from src/main/java/com/codeclocker/plugin/intellij/apikey/ApiKeyActivationCheckerTask.java rename to src/main/java/com/codeclocker/plugin/intellij/subscription/SubscriptionStateCheckerTask.java index cbf65dc..336be3c 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/apikey/ApiKeyActivationCheckerTask.java +++ b/src/main/java/com/codeclocker/plugin/intellij/subscription/SubscriptionStateCheckerTask.java @@ -1,28 +1,29 @@ -package com.codeclocker.plugin.intellij.apikey; +package com.codeclocker.plugin.intellij.subscription; import static com.codeclocker.plugin.intellij.ScheduledExecutor.EXECUTOR; import static com.codeclocker.plugin.intellij.apikey.ApiKeyLifecycle.continueCollectingActivityData; import static java.util.concurrent.TimeUnit.SECONDS; import static org.apache.commons.lang3.StringUtils.isBlank; +import com.codeclocker.plugin.intellij.apikey.ApiKeyPersistence; import com.codeclocker.plugin.intellij.config.ConfigProvider; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import java.util.concurrent.ScheduledFuture; -public class ApiKeyActivationCheckerTask implements Disposable { +public class SubscriptionStateCheckerTask implements Disposable { - private static final Logger LOG = Logger.getInstance(ApiKeyActivationCheckerTask.class); + private static final Logger LOG = Logger.getInstance(SubscriptionStateCheckerTask.class); private final int checkApiKeyStatusFrequencySeconds; - private final CheckApiKeyStateHttpClient checkSubscriptionStateByApiKeyHttpClient; + private final CheckSubscriptionStateHttpClient checkSubscriptionStateByApiKeyHttpClient; private ScheduledFuture task; - public ApiKeyActivationCheckerTask() { + public SubscriptionStateCheckerTask() { this.checkSubscriptionStateByApiKeyHttpClient = - ApplicationManager.getApplication().getService(CheckApiKeyStateHttpClient.class); + ApplicationManager.getApplication().getService(CheckSubscriptionStateHttpClient.class); ConfigProvider configProvider = ApplicationManager.getApplication().getService(ConfigProvider.class); this.checkApiKeyStatusFrequencySeconds = configProvider.getCheckApiKeyStatusFrequencySeconds(); @@ -35,10 +36,7 @@ public void schedule() { this.task = EXECUTOR.scheduleWithFixedDelay( - this::checkApiKeyState, - checkApiKeyStatusFrequencySeconds, - checkApiKeyStatusFrequencySeconds, - SECONDS); + this::checkApiKeyState, 10, checkApiKeyStatusFrequencySeconds, SECONDS); } private void checkApiKeyState() { diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 0a5b5c9..70a38cf 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -15,13 +15,13 @@ serviceImplementation="com.codeclocker.plugin.intellij.services.TimeSpentActivityTracker"/> - + + serviceImplementation="com.codeclocker.plugin.intellij.subscription.SubscriptionStateCheckerTask"/>