Skip to content

Commit

Permalink
feat: Copy to clipboard
Browse files Browse the repository at this point in the history
Add a button to the patch and issue panel so that users can easily copy patch or issue ID

Signed-off-by: stelios maurommatakis <steliosmavr@cytech.gr>
  • Loading branch information
Stelios123 committed May 14, 2024
1 parent cec2c7f commit 8d6ac83
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.intellij.ui.OnePixelSplitter;
import com.intellij.ui.components.JBTextField;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.ui.scale.JBUIScale;
import com.intellij.ui.tabs.TabInfo;
import com.intellij.ui.tabs.impl.SingleHeightTabs;
import com.intellij.util.ui.JBUI;
Expand Down Expand Up @@ -95,8 +96,13 @@ private JComponent getIssueInfo() {
var issueTitle = getLabelPanel(RadicleBundle.message("title", Strings.nullToEmpty(issue.title)));
detailsSection.add(issueTitle, new CC().gapBottom(String.valueOf(UI.scale(4))));

var issueId = getLabelPanel(RadicleBundle.message("issueId", Strings.nullToEmpty(issue.id)));
detailsSection.add(issueId, new CC().gapBottom(String.valueOf(UI.scale(4))));
var issueId = getLabelPanel(RadicleBundle.message("issueId", Strings.nullToEmpty(Utils.formatId(issue.id))));

final var issueIdAndCopyButton = new NonOpaquePanel(new MigLayout(new LC().insets("0").gridGap("0", "0").noGrid()));
issueIdAndCopyButton.add(issueId, new CC().gapRight(String.valueOf(JBUIScale.scale(4))));
issueIdAndCopyButton.add(new Utils.CopyButton(issue.id), new CC().gapRight(String.valueOf(JBUIScale.scale(4))));

detailsSection.add(issueIdAndCopyButton, new CC().gapBottom(String.valueOf(UI.scale(4))));

var issueAuthor = getLabelPanel(RadicleBundle.message("issueAuthor", Strings.nullToEmpty(issue.author.generateLabelText())));
detailsSection.add(issueAuthor, new CC().gapBottom(String.valueOf(UI.scale(4))));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ private SimpleAsyncChangesBrowser getChangesBrowser() {
@Nullable

Check warning on line 101 in src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/patches/PatchComponentFactory.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Nullability and data flow problems

@nullable method 'getCurrentName' always returns a non-null value

Check warning on line 101 in src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/patches/PatchComponentFactory.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Nullability and data flow problems

@nullable method 'getCurrentName' always returns a non-null value
@Override
protected String getCurrentName() {

Check failure on line 103 in src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/patches/PatchComponentFactory.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

Overridden method 'getCurrentName()' is declared in 'com.intellij.openapi.vcs.changes.EditorTabPreviewBase' scheduled for removal in version 242.10180.13
return myPatch != null ? RadicleBundle.message("patch.diff.label", Utils.formatPatchId(myPatch.id)) :
return myPatch != null ? RadicleBundle.message("patch.diff.label", Utils.formatId(myPatch.id)) :
RadicleBundle.message("changes");
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public Cell(int index, RadPatch patch) {
infoPanel.setForeground(JBColor.GRAY);
infoPanel.setOpaque(false);

var patchId = new JLabel(Utils.formatPatchId(patch.id));
var patchId = new JLabel(Utils.formatId(patch.id));
patchId.setForeground(JBColor.GRAY);
infoPanel.add(patchId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;

import static network.radicle.jetbrains.radiclejetbrainsplugin.toolwindow.Utils.formatPatchId;
import static network.radicle.jetbrains.radiclejetbrainsplugin.toolwindow.Utils.formatId;

public class PatchProposalPanel {
private static final Logger logger = Logger.getInstance(PatchProposalPanel.class);
Expand Down Expand Up @@ -95,7 +95,7 @@ private void listenForRepoChanges() {
return;
}
var currentBranchName = patch.repo.getCurrentBranch().getName();
checkoutBtn.setEnabled(!currentBranchName.contains(formatPatchId(patch.id)));
checkoutBtn.setEnabled(!currentBranchName.contains(formatId(patch.id)));
});
}

Expand Down Expand Up @@ -198,7 +198,7 @@ private JComponent descriptionComponent() {
public void actionPerformed(ActionEvent e) {
checkoutBtn.setEnabled(false);
ApplicationManager.getApplication().executeOnPooledThread(() -> {
var formattedPatchId = formatPatchId(patch.id);
var formattedPatchId = formatId(patch.id);
var countDown = new CountDownLatch(1);
UpdateBackgroundTask ubt = new UpdateBackgroundTask(patch.project, RadicleBundle.message("checkingOut", "patch/" + formattedPatchId),
countDown);
Expand All @@ -222,7 +222,7 @@ public void actionPerformed(ActionEvent e) {
checkoutBtn = new JButton(checkoutAction);
checkoutBtn.setText(RadicleBundle.message("checkout"));
var currentBranch = patch.repo.getCurrentBranch();
if (currentBranch != null && currentBranch.getName().contains(formatPatchId(patch.id))) {
if (currentBranch != null && currentBranch.getName().contains(formatId(patch.id))) {
checkoutBtn.setEnabled(false);
}
nonOpaquePanel.add(checkoutBtn, new CC().gapBottom(String.valueOf(UI.scale(8))));
Expand All @@ -249,11 +249,15 @@ private JComponent titleComponent() {
final var idPane = new BaseHtmlEditorPane();

Check failure on line 249 in src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/patches/PatchProposalPanel.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'com.intellij.collaboration.ui.codereview.BaseHtmlEditorPane' is scheduled for removal in version 242.10180.13

Check failure on line 249 in src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/patches/PatchProposalPanel.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'BaseHtmlEditorPane()' is declared in 'com.intellij.collaboration.ui.codereview.BaseHtmlEditorPane' scheduled for removal in version 242.10180.13

Check warning on line 249 in src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/patches/PatchProposalPanel.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'com.intellij.collaboration.ui.codereview.BaseHtmlEditorPane' is declared in unstable package 'com.intellij.collaboration.ui.codereview' marked with @ApiStatus.Experimental
idPane.setFont(titlePane.getFont().deriveFont((float) (titlePane.getFont().getSize() * 0.8)));
idPane.setForeground(JBColor.GRAY);
idPane.setBody(patch.id);
idPane.setBody(Utils.formatId(patch.id));

Check warning on line 252 in src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/patches/PatchProposalPanel.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Deprecated API usage

'setBody(java.lang.String)' is deprecated

Check failure on line 252 in src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/patches/PatchProposalPanel.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'setBody(java.lang.String)' is declared in 'com.intellij.collaboration.ui.codereview.BaseHtmlEditorPane' scheduled for removal in version 242.10180.13

Check warning on line 252 in src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/patches/PatchProposalPanel.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Deprecated API usage

'setBody(java.lang.String)' is deprecated

final var patchIdAndCopyButton = new NonOpaquePanel(new MigLayout(new LC().insets("0").gridGap("0", "0").noGrid()));
patchIdAndCopyButton.add(idPane, new CC().gapRight(String.valueOf(JBUIScale.scale(4))));
patchIdAndCopyButton.add(new Utils.CopyButton(patch.id), new CC().gapRight(String.valueOf(JBUIScale.scale(4))));

final var nonOpaquePanel = new NonOpaquePanel(new MigLayout(new LC().insets("0").gridGap("0", "0").fill().flowY()));
nonOpaquePanel.add(iconAndTitlePane, new CC().gapBottom(String.valueOf(UI.scale(8))));
nonOpaquePanel.add(idPane, new CC());
nonOpaquePanel.add(patchIdAndCopyButton, new CC().gapBottom(String.valueOf(UI.scale(8))));
return nonOpaquePanel;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import com.google.common.base.Strings;
import com.intellij.collaboration.ui.SingleValueModel;
import com.intellij.collaboration.ui.codereview.CodeReviewChatItemUIUtil;
import com.intellij.ide.ClipboardSynchronizer;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.project.Project;
import com.intellij.ui.components.labels.LinkLabel;
import com.intellij.ui.components.labels.LinkListener;
Expand All @@ -19,10 +23,15 @@

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.Action;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.util.List;

public class Utils {
Expand Down Expand Up @@ -93,8 +102,11 @@ public static JPanel descriptionPanel(MarkDownEditorPaneFactory editorPane, Proj
return (JPanel) b.build();

Check failure on line 102 in src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/toolwindow/Utils.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unstable API Usage

'build()' is declared in 'com.intellij.collaboration.ui.codereview.CodeReviewChatItemUIUtil' scheduled for removal in version 242.10180.13
}

public static String formatPatchId(String patchId) {
return patchId.substring(0, 6);
public static String formatId(String id) {
if (id.length() < 6) {
return id;
}
return id.substring(0, 6);
}

public static String formatDid(String did) {
Expand Down Expand Up @@ -136,4 +148,28 @@ public void replace(FilterBypass fb, int offset, int length, String text, Attrib
}
}
}

public static class CopyButton extends JButton {
public CopyButton(String contents) {
setAction(getCopyAction(contents));
}

private Action getCopyAction(String contents) {
ActionManager actionManager = ActionManager.getInstance();
if (actionManager == null) {
return null;
}
AnAction action = actionManager.getAction(IdeActions.ACTION_COPY);
if (action == null) {
return null;
}
return new AbstractAction("", action.getTemplatePresentation().getIcon()) {
@Override
public void actionPerformed(ActionEvent e) {
StringSelection content = new StringSelection(contents);
ClipboardSynchronizer.getInstance().setContent(content, content);
}
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.intellij.collaboration.ui.SingleValueModel;
import com.intellij.ide.ClipboardSynchronizer;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager;
import com.intellij.openapi.ui.popup.JBPopupFactory;
Expand Down Expand Up @@ -55,6 +56,8 @@
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.HierarchyEvent;
import java.io.IOException;
Expand Down Expand Up @@ -225,7 +228,7 @@ public void testIssueInfo() {
var issueCreatedLabel = UIUtil.findComponentOfType((JPanel) allPanels[6], JLabel.class);

assertThat(titleLabel.getText()).isEqualTo(RadicleBundle.message("title", Strings.nullToEmpty(issue.title)));
assertThat(issueIdLabel.getText()).isEqualTo(RadicleBundle.message("issueId", Strings.nullToEmpty(issue.id)));
assertThat(issueIdLabel.getText()).isEqualTo(RadicleBundle.message("issueId", Strings.nullToEmpty(Utils.formatId(issue.id))));
assertThat(issueAuthorLabel.getText()).isEqualTo(RadicleBundle.message("issueAuthor", Strings.nullToEmpty(issue.author.id)));
assertThat(issueTagLabel.getText()).isEqualTo(RadicleBundle.message("issueLabels", String.join(",", issue.labels)));
assertThat(issueAssigneeLabel.getText()).isEqualTo(RadicleBundle.message("issueAssignees",
Expand Down Expand Up @@ -385,6 +388,17 @@ public void createNewIssueTest() throws InterruptedException {
assertThat(res.get("assignees")).usingRecursiveAssertion().isEqualTo(List.of(getTestProjects().get(0).delegates.get(0).id));
}

@Test
public void testCopyButton() throws IOException, UnsupportedFlavorException {
var panel = issueTabController.getIssueJPanel();
var ef = UIUtil.findComponentOfType(panel, OnePixelSplitter.class);
var copyButton = UIUtil.findComponentOfType(ef.getFirstComponent(), Utils.CopyButton.class);
copyButton.doClick();
var contents = ClipboardSynchronizer.getInstance().getContents();
var issueId = (String) contents.getTransferData(DataFlavor.stringFlavor);
assertThat(issueId).isEqualTo(issue.id);
}

@Test
public void changeStateTest() throws InterruptedException {
var issuePanel = issueTabController.getIssuePanel();
Expand Down Expand Up @@ -797,7 +811,7 @@ public void testComment() throws InterruptedException {
prBtn.doClick();
executeUiTasks();
var res = response.poll(5, TimeUnit.SECONDS);
assertThat(res.get("id")).isEqualTo(issue.id);
assertThat(res.get("id")).isEqualTo(issue.discussion.get(1).id);
assertThat(res.get("type")).isEqualTo("comment.edit");
assertThat(res.get("body")).isEqualTo(editedComment);
}
Expand Down Expand Up @@ -848,7 +862,7 @@ private RadIssue createIssue() {
var secondDiscussion = createDiscussion("321", "321", commentDescription, embeds);
discussions.add(firstDiscussion);
discussions.add(secondDiscussion);
var myIssue = new RadIssue("321", new RadAuthor(AUTHOR), "My Issue",
var myIssue = new RadIssue(UUID.randomUUID().toString(), new RadAuthor(AUTHOR), "My Issue",
RadIssue.State.OPEN, List.of(new RadAuthor("did:key:test"), new RadAuthor("did:key:assignee2")), List.of("tag1", "tag2"), discussions);
myIssue.project = getProject();
myIssue.projectId = UUID.randomUUID().toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.intellij.diff.requests.SimpleDiffRequest;
import com.intellij.diff.tools.util.side.TwosideTextDiffViewer;
import com.intellij.diff.util.Side;
import com.intellij.ide.ClipboardSynchronizer;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
Expand Down Expand Up @@ -82,6 +83,8 @@
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.nio.file.Paths;
Expand Down Expand Up @@ -575,6 +578,25 @@ public void testChangeTitle() throws InterruptedException {
assertThat(not.getTitle()).isEqualTo(RadicleBundle.message("patchTitleError"));
}

@Test
public void testCopyButton() throws IOException, UnsupportedFlavorException {
var patchProposalPanel = new PatchProposalPanel(patchTabController, new SingleValueModel<>(patch)) {
@Override
public void refreshVcs() {

}
};
var panel = patchProposalPanel.createViewPatchProposalPanel();
var ef = UIUtil.findComponentOfType(panel, OnePixelSplitter.class);
var myPanel = ef.getFirstComponent();
var mainPanel = (JPanel) myPanel.getComponents()[0];
var copyButton = UIUtil.findComponentOfType(mainPanel, Utils.CopyButton.class);
copyButton.doClick();
var contents = ClipboardSynchronizer.getInstance().getContents();
var patchId = (String) contents.getTransferData(DataFlavor.stringFlavor);
assertThat(patchId).isEqualTo(patch.id);
}

@Test
public void testCheckoutButton() throws InterruptedException {
var patchProposalPanel = new PatchProposalPanel(patchTabController, new SingleValueModel<>(patch)) {
Expand Down Expand Up @@ -991,7 +1013,8 @@ private RadPatch createPatch() {
var secondDiscussion = createDiscussion("321", "321", secondComment + txtEmbedMarkDown + imgEmbedMarkDown, List.of(txtEmbed, imgEmbed));
var firstRev = createRevision("testRevision1", "testRevision1", firstCommit, firstDiscussion);
var secondRev = createRevision("testRevision2", "testRevision1", secondCommit, secondDiscussion);
var myPatch = new RadPatch("c5df12", new RadProject(UUID.randomUUID().toString(), "test", "test", "main", List.of()), new RadAuthor(AUTHOR),
var myPatch = new RadPatch(UUID.randomUUID().toString(), new RadProject(UUID.randomUUID().toString(),
"test", "test", "main", List.of()), new RadAuthor(AUTHOR),
"testPatch", new RadAuthor(AUTHOR), "testTarget", List.of("tag1", "tag2"), RadPatch.State.OPEN, List.of(firstRev, secondRev));
myPatch.project = getProject();
myPatch.repo = firstRepo;
Expand Down

0 comments on commit 8d6ac83

Please sign in to comment.