Skip to content

Commit

Permalink
Merge remote-tracking branch 'rad/patches/e79cb5e5376b5d6e678c5945504…
Browse files Browse the repository at this point in the history
…b97741f0adc1d'
  • Loading branch information
JChrist committed Mar 27, 2024
2 parents d699658 + 7ecd921 commit 12980f3
Show file tree
Hide file tree
Showing 12 changed files with 496 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package network.radicle.jetbrains.radiclejetbrainsplugin.actions;

import com.intellij.icons.AllIcons;
import com.intellij.ide.plugins.newui.InstallButton;
import com.intellij.openapi.actionSystem.ActionUpdateThread;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.actions.IncrementalFindAction;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel;
import com.intellij.openapi.ui.ComponentContainer;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.serviceContainer.NonInjectable;
import com.intellij.ui.EditorSettingsProvider;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.components.panels.HorizontalLayout;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.util.ui.InlineIconButton;
import com.intellij.util.ui.JBDimension;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.JButtonAction;
import com.intellij.util.ui.UIUtil;
import net.miginfocom.layout.CC;
import net.miginfocom.layout.LC;
import net.miginfocom.swing.MigLayout;
import network.radicle.jetbrains.radiclejetbrainsplugin.RadicleBundle;
import network.radicle.jetbrains.radiclejetbrainsplugin.models.RadPatch;
import network.radicle.jetbrains.radiclejetbrainsplugin.patches.PatchComponentFactory;
import network.radicle.jetbrains.radiclejetbrainsplugin.services.RadicleProjectApi;
import org.jetbrains.annotations.NotNull;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class ReviewSubmitAction extends JButtonAction {
private RadPatch patch;
private JBPopup popup;
private EditorTextField editor;
private JButton approveButton;
private JButton rejectButton;
private InlineIconButton closeButton;
private ComponentContainer container;

public ReviewSubmitAction() {
super(RadicleBundle.message("submit"), "", null);
}

@NonInjectable
public ReviewSubmitAction(RadPatch patch) {
super(RadicleBundle.message("submit"), "", null);
this.patch = patch;
}

@Override
public @NotNull ActionUpdateThread getActionUpdateThread() {
return ActionUpdateThread.EDT;
}

@Override
public void update(@NotNull AnActionEvent e) {
var radPatch = e.getProject().getUserData(PatchComponentFactory.PATCH_DIFF);

Check warning on line 67 in src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/actions/ReviewSubmitAction.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Nullability and data flow problems

Method invocation `getUserData` may produce `NullPointerException`
if (radPatch == null) {
e.getPresentation().setEnabledAndVisible(false);
return;
}
patch = radPatch;
}

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
var parentComponent = e.getPresentation().getClientProperty(CustomComponentAction.COMPONENT_KEY);
if (parentComponent == null) {
return;
}
setUpPopup(parentComponent);
}

public void setUpPopup(JComponent parentComponent) {
var cancelActionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
popup.cancel();
}
};
container = createPopupComponent(cancelActionListener);
popup = JBPopupFactory.getInstance().createComponentPopupBuilder(container.getComponent(), container.getPreferredFocusableComponent())
.setFocusable(true)
.setRequestFocus(true)
.setResizable(true)
.createPopup();
popup.showUnderneathOf(parentComponent);
}

private void submitReview(RadPatch.Review.Verdict verdict, String summary) {
disableUI();
Runnable submitTask = () -> {
var api = patch.project.getService(RadicleProjectApi.class);
var res = api.addReview(verdict.getValue(), summary, patch);
var success = res != null;
ApplicationManager.getApplication().invokeLater(() -> {
if (!success) {
enableUI();
} else {
popup.cancel();
}
});
};
if (ApplicationManager.getApplication().isUnitTestMode()) {
submitTask.run();
} else {
ApplicationManager.getApplication().executeOnPooledThread(submitTask);
}
}

private void disableUI() {
editor.setEnabled(false);
approveButton.setEnabled(false);
rejectButton.setEnabled(false);
closeButton.setEnabled(false);
}

private void enableUI() {
editor.setEnabled(true);
approveButton.setEnabled(true);
rejectButton.setEnabled(true);
closeButton.setEnabled(true);
}

private ComponentContainer createPopupComponent(ActionListener cancelActionListener) {
var wrapper = new Wrapper();
wrapper.setOpaque(true);
wrapper.setBackground(JBUI.CurrentTheme.List.BACKGROUND);
wrapper.setPreferredSize(new JBDimension(500, 200));
editor = createEditor();
approveButton = new InstallButton(RadicleBundle.message("approve"), true) {
@Override
protected void setTextAndSize() { }
};
approveButton.addActionListener(e -> submitReview(RadPatch.Review.Verdict.ACCEPT, editor.getText()));

rejectButton = new JButton(RadicleBundle.message("requestChanges"));
rejectButton.addActionListener(e -> submitReview(RadPatch.Review.Verdict.REJECT, editor.getText()));
rejectButton.setOpaque(true);

closeButton = new InlineIconButton(AllIcons.Actions.Close, AllIcons.Actions.CloseHovered);
closeButton.setBorder(JBUI.Borders.empty(5));
closeButton.setActionListener(cancelActionListener);

var titleLabel = new JLabel(RadicleBundle.message("submitReview"));
var titlePAnel = new JPanel(new HorizontalLayout(0));
titlePAnel.setOpaque(false);
titlePAnel.add(titleLabel, HorizontalLayout.LEFT);
titlePAnel.add(closeButton, HorizontalLayout.RIGHT);

var buttonsPanel = new ScrollablePanel();
buttonsPanel.add(approveButton);
buttonsPanel.add(rejectButton);
var panel = new JPanel(new MigLayout(new LC().insets("12").fill().flowY().noGrid().hideMode(3)));
panel.setOpaque(false);
panel.add(titlePAnel, new CC().growX());
panel.add(editor, new CC().growX().growY());
panel.add(buttonsPanel, new CC());

wrapper.setContent(panel);
wrapper.repaint();
return new ComponentContainer() {
@Override
public @NotNull JComponent getComponent() {
return wrapper;
}

@Override
public JComponent getPreferredFocusableComponent() {
return editor;
}

@Override
public void dispose() { }
};
}

private EditorTextField createEditor() {
editor = new EditorTextField();
editor.setOneLineMode(false);
editor.putClientProperty(UIUtil.HIDE_EDITOR_FROM_DATA_CONTEXT_PROPERTY, true);
editor.setPlaceholder(RadicleBundle.message("review"));
var settingsProvider = new EditorSettingsProvider() {
@Override
public void customizeSettings(EditorEx myEditor) {
myEditor.getSettings().setUseSoftWraps(true);
myEditor.setVerticalScrollbarVisible(true);
myEditor.getScrollPane().setViewportBorder(JBUI.Borders.emptyLeft(4));
myEditor.putUserData(IncrementalFindAction.SEARCH_DISABLED, true);
}
};
editor.addSettingsProvider(settingsProvider);
return editor;
}

public ComponentContainer getContainer() {
return container;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import java.util.List;
import java.util.Map;

public class RadDiscussion {
public class RadDiscussion implements RadPatch.TimelineEvent {
public String id;
public RadAuthor author;
public String body;
Expand Down Expand Up @@ -40,6 +40,11 @@ public Reaction findReaction(String emojiUnicode) {
return reactions.stream().filter(r -> r.emoji().equals(emojiUnicode)).findFirst().orElse(null);
}

@Override
public Instant getTimestamp() {
return this.timestamp;
}

public static class Location {
public String path;
public String commit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.base.Strings;
import com.intellij.openapi.project.Project;
import git4idea.GitCommit;
Expand All @@ -15,7 +17,10 @@
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -95,6 +100,16 @@ public boolean isMerged() {
return this.state.status.equals(State.MERGED.status);
}

public String findRevisionId(String commentId) {
for (var revision : revisions) {
var found = revision.discussions.stream().anyMatch(d -> d.id.equals(commentId));
if (found) {
return revision.id;
}
}
return "";
}

@Override
public String toString() {
try {
Expand Down Expand Up @@ -131,14 +146,73 @@ public Revision findRevision(String revisionId) {

public record Revision(
String id, String description, String base, String oid, List<String> refs,
List<Merge> merges, Instant timestamp, List<RadDiscussion> discussions, List<Object> reviews, RadAuthor author) {
public RadDiscussion findDiscussion(String commentId) {
return discussions().stream().filter(disc -> disc.id.equals(commentId)).findFirst().orElse(null);
}
List<Merge> merges, Instant timestamp, List<RadDiscussion> discussions, List<Review> reviews, RadAuthor author) implements TimelineEvent {
@Override
public Instant getTimestamp() {
return timestamp;
}

public List<Review> getReviews() {
HashSet<Object> seen = new HashSet<>();
var myReviews = new ArrayList<>(reviews);
// Remove the duplicates reviews. We can only have 1 review per author per revision
myReviews.removeIf(e -> !seen.add(e.author.id));
return myReviews;
}

public RadDiscussion findDiscussion(String commentId) {
return discussions().stream().filter(disc -> disc.id.equals(commentId)).findFirst().orElse(null);
}
}

public record Merge(String node, String commit, Instant timestamp) { }

public record Review(String id, RadAuthor author, Verdict verdict, String summary,
List<RadDiscussion> comments, Instant timestamp) implements TimelineEvent {
@Override
public Instant getTimestamp() {
return timestamp;
}

@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum Verdict {
ACCEPT("accept"),
REJECT("reject");

private final String value;

Verdict(String value) {
this.value = value;
}

@JsonValue
public String getValue() {
return value;
}

@JsonCreator
public static Verdict forValues(String val) {
for (Verdict verdict : Verdict.values()) {
if (verdict.value.equals(val.toLowerCase())) {
return verdict;
}
}
return null;
}
}
}

@JsonIgnore
public List<TimelineEvent> getTimelineEvents() {
var list = new ArrayList<TimelineEvent>();
for (var revision : revisions) {
list.add(revision);
list.addAll(revision.getReviews());
list.addAll(revision.discussions);
}
list.sort(Comparator.comparing(TimelineEvent::getTimestamp));
return list;
}

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum State {
Expand All @@ -165,4 +239,8 @@ public static State forValues(@JsonProperty("status") String status) {
return null;
}
}

public interface TimelineEvent {
Instant getTimestamp();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public JComponent create() {
timelinePanel.setOpaque(false);
timelinePanel.add(header);
timelinePanel.add(descriptionWrapper);
revisionSection = componentsFactory.createRevisionSection();
revisionSection = componentsFactory.createTimeline();
timelinePanel.add(revisionSection);

var horizontalPanel = Utils.getHorizontalPanel(8);
Expand Down

0 comments on commit 12980f3

Please sign in to comment.