diff --git a/scm-ui/ui-components/.storybook/config.js b/scm-ui/ui-components/.storybook/config.js index 5b9ed3d3a2..1033a7a805 100644 --- a/scm-ui/ui-components/.storybook/config.js +++ b/scm-ui/ui-components/.storybook/config.js @@ -4,6 +4,8 @@ import { addDecorator, configure } from "@storybook/react"; import { withI18next } from "storybook-addon-i18next"; import "!style-loader!css-loader!sass-loader!../../ui-styles/src/scm.scss"; +import React, { ReactNode } from "react"; +import { MemoryRouter } from "react-router-dom"; i18n.use(initReactI18next).init({ whitelist: ["en", "de", "es"], @@ -28,4 +30,7 @@ addDecorator( }) ); +const RoutingDecorator = (story) => {story()}; +addDecorator(RoutingDecorator); + configure(require.context("../src", true, /\.stories\.tsx?$/), module); diff --git a/scm-ui/ui-components/package.json b/scm-ui/ui-components/package.json index 9bf3c8381f..1e3648dbab 100644 --- a/scm-ui/ui-components/package.json +++ b/scm-ui/ui-components/package.json @@ -50,13 +50,14 @@ "event-source-polyfill": "^1.0.9", "query-string": "5", "react": "^16.8.6", - "react-diff-view": "^1.8.1", + "react-diff-view": "^2.4.1", "react-dom": "^16.8.6", "react-i18next": "^10.13.1", "react-markdown": "^4.0.6", "react-router-dom": "^5.1.2", "react-select": "^2.1.2", - "react-syntax-highlighter": "^11.0.2" + "react-syntax-highlighter": "^11.0.2", + "gitdiff-parser": "^0.1.2" }, "babel": { "presets": [ diff --git a/scm-ui/ui-components/src/MarkdownView.stories.tsx b/scm-ui/ui-components/src/MarkdownView.stories.tsx index a7e71aac2b..22a478f1b2 100644 --- a/scm-ui/ui-components/src/MarkdownView.stories.tsx +++ b/scm-ui/ui-components/src/MarkdownView.stories.tsx @@ -2,7 +2,6 @@ import React from "react"; import { storiesOf } from "@storybook/react"; import MarkdownView from "./MarkdownView"; import styled from "styled-components"; -import { MemoryRouter } from "react-router-dom"; import TestPage from "./__resources__/test-page.md"; import MarkdownWithoutLang from "./__resources__/markdown-without-lang.md"; @@ -12,7 +11,6 @@ const Spacing = styled.div` `; storiesOf("MarkdownView", module) - .addDecorator(story => {story()}) .add("Default", () => ( diff --git a/scm-ui/ui-components/src/__resources__/Diff.hunks.ts b/scm-ui/ui-components/src/__resources__/Diff.hunks.ts new file mode 100644 index 0000000000..86acfbff0a --- /dev/null +++ b/scm-ui/ui-components/src/__resources__/Diff.hunks.ts @@ -0,0 +1,36 @@ +export default `diff --git a/src/main/java/com/cloudogu/scm/review/pullrequest/service/DefaultPullRequestService.java b/src/main/java/com/cloudogu/scm/review/pullrequest/service/DefaultPullRequestService.java +index 17c35f6..cdf70f1 100644 +--- a/src/main/java/com/cloudogu/scm/review/pullrequest/service/DefaultPullRequestService.java ++++ b/src/main/java/com/cloudogu/scm/review/pullrequest/service/DefaultPullRequestService.java +@@ -25,6 +25,8 @@ import java.util.Optional; + import java.util.Set; + import java.util.stream.Collectors; + ++import static com.cloudogu.scm.review.pullrequest.service.PullRequestApprovalEvent.ApprovalCause.APPROVAL_REMOVED; ++import static com.cloudogu.scm.review.pullrequest.service.PullRequestApprovalEvent.ApprovalCause.APPROVED; + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.MERGED; + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.OPEN; + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.REJECTED; +@@ -200,6 +202,7 @@ public class DefaultPullRequestService implements PullRequestService { + PullRequest pullRequest = getPullRequestFromStore(repository, pullRequestId); + pullRequest.addApprover(user.getId()); + getStore(repository).update(pullRequest); ++ eventBus.post(new PullRequestApprovalEvent(repository, pullRequest, APPROVED)); + } + + @Override +@@ -211,8 +214,12 @@ public class DefaultPullRequestService implements PullRequestService { + approver.stream() + .filter(recipient -> user.getId().equals(recipient)) + .findFirst() +- .ifPresent(pullRequest::removeApprover); +- getStore(repository).update(pullRequest); ++ .ifPresent( ++ approval -> { ++ pullRequest.removeApprover(approval); ++ getStore(repository).update(pullRequest); ++ eventBus.post(new PullRequestApprovalEvent(repository, pullRequest, APPROVAL_REMOVED)); ++ }); + } + + @Override`; diff --git a/scm-ui/ui-components/src/__resources__/Diff.simple.ts b/scm-ui/ui-components/src/__resources__/Diff.simple.ts new file mode 100644 index 0000000000..a08aa7cdb0 --- /dev/null +++ b/scm-ui/ui-components/src/__resources__/Diff.simple.ts @@ -0,0 +1,148 @@ +export default `diff --git a/src/main/java/com/cloudogu/scm/review/events/EventListener.java b/src/main/java/com/cloudogu/scm/review/events/EventListener.java +index f889f9c..95e3b10 100644 +--- a/src/main/java/com/cloudogu/scm/review/events/EventListener.java ++++ b/src/main/java/com/cloudogu/scm/review/events/EventListener.java +@@ -1,20 +1,12 @@ + package com.cloudogu.scm.review.events; + +-import com.cloudogu.scm.review.comment.service.BasicComment; +-import com.cloudogu.scm.review.comment.service.BasicCommentEvent; +-import com.cloudogu.scm.review.comment.service.CommentEvent; +-import com.cloudogu.scm.review.comment.service.ReplyEvent; + import com.cloudogu.scm.review.pullrequest.service.BasicPullRequestEvent; + import com.cloudogu.scm.review.pullrequest.service.PullRequest; +-import com.cloudogu.scm.review.pullrequest.service.PullRequestEvent; + import com.github.legman.Subscribe; +-import lombok.Data; + import org.apache.shiro.SecurityUtils; + import org.apache.shiro.subject.PrincipalCollection; + import org.apache.shiro.subject.Subject; + import sonia.scm.EagerSingleton; +-import sonia.scm.HandlerEventType; +-import sonia.scm.event.HandlerEvent; + import sonia.scm.plugin.Extension; + import sonia.scm.repository.Repository; + import sonia.scm.security.SessionId; +diff --git a/src/main/js/ChangeNotification.tsx b/src/main/js/ChangeNotification.tsx +index f6d61a9..5f371e4 100644 +--- a/src/main/js/ChangeNotification.tsx ++++ b/src/main/js/ChangeNotification.tsx +@@ -2,6 +2,7 @@ import React, { FC, useEffect, useState } from "react"; + import { Link } from "@scm-manager/ui-types"; + import { apiClient, Toast, ToastButtons, ToastButton } from "@scm-manager/ui-components"; + import { PullRequest } from "./types/PullRequest"; ++import { useTranslation } from "react-i18next"; + + type HandlerProps = { + url: string; +@@ -15,14 +16,19 @@ const EventNotificationHandler: FC = ({ url, reload }) => { + pullRequest: setEvent + }); + }, [url]); ++ const { t } = useTranslation("plugins"); + if (event) { + return ( +- +-

The underlying Pull-Request has changed. Press reload to see the changes.

+-

Warning: Non saved modification will be lost.

++ ++

{t("scm-review-plugin.changeNotification.description")}

++

{t("scm-review-plugin.changeNotification.modificationWarning")}

+ +- Reload +- setEvent(undefined)}>Ignore ++ ++ {t("scm-review-plugin.changeNotification.buttons.reload")} ++ ++ setEvent(undefined)}> ++ {t("scm-review-plugin.changeNotification.buttons.ignore")} ++ + +
+ ); +diff --git a/src/main/resources/locales/de/plugins.json b/src/main/resources/locales/de/plugins.json +index 80f84a1..2c63ab3 100644 +--- a/src/main/resources/locales/de/plugins.json ++++ b/src/main/resources/locales/de/plugins.json +@@ -181,6 +181,15 @@ + "titleClickable": "Der Kommentar bezieht sich auf eine ältere Version des Source- oder Target-Branches. Klicken Sie hier, um den ursprünglichen Kontext zu sehen." + } + } ++ }, ++ "changeNotification": { ++ "title": "Neue Änderungen", ++ "description": "An diesem Pull Request wurden Änderungen vorgenommen. Laden Sie die Seite neu um diese anzuzeigen.", ++ "modificationWarning": "Warnung: Nicht gespeicherte Eingaben gehen verloren.", ++ "buttons": { ++ "reload": "Neu laden", ++ "ignore": "Ignorieren" ++ } + } + }, + "permissions": { +diff --git a/src/main/resources/locales/en/plugins.json b/src/main/resources/locales/en/plugins.json +index d020fcd..e3c1630 100644 +--- a/src/main/resources/locales/en/plugins.json ++++ b/src/main/resources/locales/en/plugins.json +@@ -181,6 +181,15 @@ + "titleClickable": "The comment is related to an older of the source or target branch. Click here to see the original context." + } + } ++ }, ++ "changeNotification": { ++ "title": "New Changes", ++ "description": "The underlying Pull-Request has changed. Press reload to see the changes.", ++ "modificationWarning": "Warning: Non saved modification will be lost.", ++ "buttons": { ++ "reload": "Reload", ++ "ignore": "Ignore" ++ } + } + }, + "permissions": { +diff --git a/src/test/java/com/cloudogu/scm/review/events/ClientTest.java b/src/test/java/com/cloudogu/scm/review/events/ClientTest.java +index 889cc49..d5a4811 100644 +--- a/src/test/java/com/cloudogu/scm/review/events/ClientTest.java ++++ b/src/test/java/com/cloudogu/scm/review/events/ClientTest.java +@@ -7,19 +7,16 @@ import org.mockito.Answers; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + import sonia.scm.security.SessionId; ++ + import javax.ws.rs.sse.OutboundSseEvent; + import javax.ws.rs.sse.SseEventSink; +- + import java.time.Clock; + import java.time.Instant; + import java.time.LocalDateTime; + import java.time.ZoneOffset; + import java.time.temporal.ChronoField; +-import java.time.temporal.ChronoUnit; +-import java.time.temporal.TemporalField; + import java.util.concurrent.CompletableFuture; + import java.util.concurrent.CompletionStage; +-import java.util.concurrent.atomic.AtomicLong; + import java.util.concurrent.atomic.AtomicReference; + + import static java.time.temporal.ChronoUnit.MINUTES; +@@ -83,7 +80,7 @@ class ClientTest { + + @Test + @SuppressWarnings("unchecked") +- void shouldCloseEventSinkOnFailure() throws InterruptedException { ++ void shouldCloseEventSinkOnFailure() { + CompletionStage future = CompletableFuture.supplyAsync(() -> { + throw new RuntimeException("failed to send message"); + }); +@@ -91,9 +88,7 @@ class ClientTest { + + client.send(message); + +- Thread.sleep(50L); +- +- verify(eventSink).close(); ++ verify(eventSink, timeout(50L)).close(); + } + + @Test +`; diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index 7fa1b1193f..8034701cbd 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -334,6 +334,22333 @@ exports[`Storyshots DateFromNow Default 1`] = ` `; +exports[`Storyshots Diff Collapsed 1`] = ` +Array [ +
+
+
+
+ + + src/main/java/com/cloudogu/scm/review/events/EventListener.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
, +
+
+
+
+ + + src/main/js/ChangeNotification.tsx + + + modify + +
+
+
+
+ +
+
+
+
+
+
, +
+
+
+
+ + + src/main/resources/locales/de/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
, +
+
+
+
+ + + src/main/resources/locales/en/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
, +
+
+
+
+ + + src/test/java/com/cloudogu/scm/review/events/ClientTest.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
, +] +`; + +exports[`Storyshots Diff Default 1`] = ` +Array [ +
+
+
+
+ + + src/main/java/com/cloudogu/scm/review/events/EventListener.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 1 + + 1 + + package com.cloudogu.scm.review.events; +
+ 2 + + 2 + + +
+ 3 + + + import com.cloudogu.scm.review.comment.service.BasicComment; +
+ 4 + + + import com.cloudogu.scm.review.comment.service.BasicCommentEvent; +
+ 5 + + + import com.cloudogu.scm.review.comment.service.CommentEvent; +
+ 6 + + + import com.cloudogu.scm.review.comment.service.ReplyEvent; +
+ 7 + + 3 + + import com.cloudogu.scm.review.pullrequest.service.BasicPullRequestEvent; +
+ 8 + + 4 + + import com.cloudogu.scm.review.pullrequest.service.PullRequest; +
+ 9 + + + import com.cloudogu.scm.review.pullrequest.service.PullRequestEvent; +
+ 10 + + 5 + + import com.github.legman.Subscribe; +
+ 11 + + + import lombok.Data; +
+ 12 + + 6 + + import org.apache.shiro.SecurityUtils; +
+ 13 + + 7 + + import org.apache.shiro.subject.PrincipalCollection; +
+ 14 + + 8 + + import org.apache.shiro.subject.Subject; +
+ 15 + + 9 + + import sonia.scm.EagerSingleton; +
+ 16 + + + import sonia.scm.HandlerEventType; +
+ 17 + + + import sonia.scm.event.HandlerEvent; +
+ 18 + + 10 + + import sonia.scm.plugin.Extension; +
+ 19 + + 11 + + import sonia.scm.repository.Repository; +
+ 20 + + 12 + + import sonia.scm.security.SessionId; +
+
+
, +
+
+
+
+ + + src/main/js/ChangeNotification.tsx + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 2 + + 2 + + import { Link } from "@scm-manager/ui-types"; +
+ 3 + + 3 + + import { apiClient, Toast, ToastButtons, ToastButton } from "@scm-manager/ui-components"; +
+ 4 + + 4 + + import { PullRequest } from "./types/PullRequest"; +
+ + 5 + + import { useTranslation } from "react-i18next"; +
+ 5 + + 6 + + +
+ 6 + + 7 + + type HandlerProps = { +
+ 7 + + 8 + + url: string; +
+
+
+ 15 + + 16 + + pullRequest: setEvent +
+ 16 + + 17 + + }); +
+ 17 + + 18 + + }, [url]); +
+ + 19 + + const { t } = useTranslation("plugins"); +
+ 18 + + 20 + + if (event) { +
+ 19 + + 21 + + return ( +
+ 20 + + + <Toast type="warning" title="New Changes"> +
+ 21 + + + <p>The underlying Pull-Request has changed. Press reload to see the changes.</p> +
+ 22 + + + <p>Warning: Non saved modification will be lost.</p> +
+ + 22 + + <Toast type="warning" title={t("scm-review-plugin.changeNotification.title")}> +
+ + 23 + + <p>{t("scm-review-plugin.changeNotification.description")}</p> +
+ + 24 + + <p>{t("scm-review-plugin.changeNotification.modificationWarning")}</p> +
+ 23 + + 25 + + <ToastButtons> +
+ 24 + + + <ToastButton icon="redo" onClick={reload}>Reload</ToastButton> +
+ 25 + + + <ToastButton icon="times" onClick={() => setEvent(undefined)}>Ignore</ToastButton> +
+ + 26 + + <ToastButton icon="redo" onClick={reload}> +
+ + 27 + + {t("scm-review-plugin.changeNotification.buttons.reload")} +
+ + 28 + + </ToastButton> +
+ + 29 + + <ToastButton icon="times" onClick={() => setEvent(undefined)}> +
+ + 30 + + {t("scm-review-plugin.changeNotification.buttons.ignore")} +
+ + 31 + + </ToastButton> +
+ 26 + + 32 + + </ToastButtons> +
+ 27 + + 33 + + </Toast> +
+ 28 + + 34 + + ); +
+
+
, +
+
+
+
+ + + src/main/resources/locales/de/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + 181 + + "titleClickable": "Der Kommentar bezieht sich auf eine ältere Version des Source- oder Target-Branches. Klicken Sie hier, um den ursprünglichen Kontext zu sehen." +
+ 182 + + 182 + + } +
+ 183 + + 183 + + } +
+ + 184 + + }, +
+ + 185 + + "changeNotification": { +
+ + 186 + + "title": "Neue Änderungen", +
+ + 187 + + "description": "An diesem Pull Request wurden Änderungen vorgenommen. Laden Sie die Seite neu um diese anzuzeigen.", +
+ + 188 + + "modificationWarning": "Warnung: Nicht gespeicherte Eingaben gehen verloren.", +
+ + 189 + + "buttons": { +
+ + 190 + + "reload": "Neu laden", +
+ + 191 + + "ignore": "Ignorieren" +
+ + 192 + + } +
+ 184 + + 193 + + } +
+ 185 + + 194 + + }, +
+ 186 + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/main/resources/locales/en/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + 181 + + "titleClickable": "The comment is related to an older of the source or target branch. Click here to see the original context." +
+ 182 + + 182 + + } +
+ 183 + + 183 + + } +
+ + 184 + + }, +
+ + 185 + + "changeNotification": { +
+ + 186 + + "title": "New Changes", +
+ + 187 + + "description": "The underlying Pull-Request has changed. Press reload to see the changes.", +
+ + 188 + + "modificationWarning": "Warning: Non saved modification will be lost.", +
+ + 189 + + "buttons": { +
+ + 190 + + "reload": "Reload", +
+ + 191 + + "ignore": "Ignore" +
+ + 192 + + } +
+ 184 + + 193 + + } +
+ 185 + + 194 + + }, +
+ 186 + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/test/java/com/cloudogu/scm/review/events/ClientTest.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 7 + + 7 + + import org.mockito.Mock; +
+ 8 + + 8 + + import org.mockito.junit.jupiter.MockitoExtension; +
+ 9 + + 9 + + import sonia.scm.security.SessionId; +
+ + 10 + + +
+ 10 + + 11 + + import javax.ws.rs.sse.OutboundSseEvent; +
+ 11 + + 12 + + import javax.ws.rs.sse.SseEventSink; +
+ 12 + + + +
+ 13 + + 13 + + import java.time.Clock; +
+ 14 + + 14 + + import java.time.Instant; +
+ 15 + + 15 + + import java.time.LocalDateTime; +
+ 16 + + 16 + + import java.time.ZoneOffset; +
+ 17 + + 17 + + import java.time.temporal.ChronoField; +
+ 18 + + + import java.time.temporal.ChronoUnit; +
+ 19 + + + import java.time.temporal.TemporalField; +
+ 20 + + 18 + + import java.util.concurrent.CompletableFuture; +
+ 21 + + 19 + + import java.util.concurrent.CompletionStage; +
+ 22 + + + import java.util.concurrent.atomic.AtomicLong; +
+ 23 + + 20 + + import java.util.concurrent.atomic.AtomicReference; +
+ 24 + + 21 + + +
+ 25 + + 22 + + import static java.time.temporal.ChronoUnit.MINUTES; +
+
+
+ 83 + + 80 + + +
+ 84 + + 81 + + @Test +
+ 85 + + 82 + + @SuppressWarnings("unchecked") +
+ 86 + + + void shouldCloseEventSinkOnFailure() throws InterruptedException { +
+ + 83 + + void shouldCloseEventSinkOnFailure() { +
+ 87 + + 84 + + CompletionStage future = CompletableFuture.supplyAsync(() -> { +
+ 88 + + 85 + + throw new RuntimeException("failed to send message"); +
+ 89 + + 86 + + }); +
+
+
+ 91 + + 88 + + +
+ 92 + + 89 + + client.send(message); +
+ 93 + + 90 + + +
+ 94 + + + Thread.sleep(50L); +
+ 95 + + + +
+ 96 + + + verify(eventSink).close(); +
+ + 91 + + verify(eventSink, timeout(50L)).close(); +
+ 97 + + 92 + + } +
+ 98 + + 93 + + +
+ 99 + + 94 + + @Test +
+
+
, +] +`; + +exports[`Storyshots Diff File Annotation 1`] = ` +Array [ +
+
+
+
+ + + src/main/java/com/cloudogu/scm/review/events/EventListener.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
+

+ Custom File annotation for + src/main/java/com/cloudogu/scm/review/events/EventListener.java +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 1 + + 1 + + package com.cloudogu.scm.review.events; +
+ 2 + + 2 + + +
+ 3 + + + import com.cloudogu.scm.review.comment.service.BasicComment; +
+ 4 + + + import com.cloudogu.scm.review.comment.service.BasicCommentEvent; +
+ 5 + + + import com.cloudogu.scm.review.comment.service.CommentEvent; +
+ 6 + + + import com.cloudogu.scm.review.comment.service.ReplyEvent; +
+ 7 + + 3 + + import com.cloudogu.scm.review.pullrequest.service.BasicPullRequestEvent; +
+ 8 + + 4 + + import com.cloudogu.scm.review.pullrequest.service.PullRequest; +
+ 9 + + + import com.cloudogu.scm.review.pullrequest.service.PullRequestEvent; +
+ 10 + + 5 + + import com.github.legman.Subscribe; +
+ 11 + + + import lombok.Data; +
+ 12 + + 6 + + import org.apache.shiro.SecurityUtils; +
+ 13 + + 7 + + import org.apache.shiro.subject.PrincipalCollection; +
+ 14 + + 8 + + import org.apache.shiro.subject.Subject; +
+ 15 + + 9 + + import sonia.scm.EagerSingleton; +
+ 16 + + + import sonia.scm.HandlerEventType; +
+ 17 + + + import sonia.scm.event.HandlerEvent; +
+ 18 + + 10 + + import sonia.scm.plugin.Extension; +
+ 19 + + 11 + + import sonia.scm.repository.Repository; +
+ 20 + + 12 + + import sonia.scm.security.SessionId; +
+
+
, +
+
+
+
+ + + src/main/js/ChangeNotification.tsx + + + modify + +
+
+
+
+ +
+
+
+
+
+
+

+ Custom File annotation for + src/main/js/ChangeNotification.tsx +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 2 + + 2 + + import { Link } from "@scm-manager/ui-types"; +
+ 3 + + 3 + + import { apiClient, Toast, ToastButtons, ToastButton } from "@scm-manager/ui-components"; +
+ 4 + + 4 + + import { PullRequest } from "./types/PullRequest"; +
+ + 5 + + import { useTranslation } from "react-i18next"; +
+ 5 + + 6 + + +
+ 6 + + 7 + + type HandlerProps = { +
+ 7 + + 8 + + url: string; +
+
+
+ 15 + + 16 + + pullRequest: setEvent +
+ 16 + + 17 + + }); +
+ 17 + + 18 + + }, [url]); +
+ + 19 + + const { t } = useTranslation("plugins"); +
+ 18 + + 20 + + if (event) { +
+ 19 + + 21 + + return ( +
+ 20 + + + <Toast type="warning" title="New Changes"> +
+ 21 + + + <p>The underlying Pull-Request has changed. Press reload to see the changes.</p> +
+ 22 + + + <p>Warning: Non saved modification will be lost.</p> +
+ + 22 + + <Toast type="warning" title={t("scm-review-plugin.changeNotification.title")}> +
+ + 23 + + <p>{t("scm-review-plugin.changeNotification.description")}</p> +
+ + 24 + + <p>{t("scm-review-plugin.changeNotification.modificationWarning")}</p> +
+ 23 + + 25 + + <ToastButtons> +
+ 24 + + + <ToastButton icon="redo" onClick={reload}>Reload</ToastButton> +
+ 25 + + + <ToastButton icon="times" onClick={() => setEvent(undefined)}>Ignore</ToastButton> +
+ + 26 + + <ToastButton icon="redo" onClick={reload}> +
+ + 27 + + {t("scm-review-plugin.changeNotification.buttons.reload")} +
+ + 28 + + </ToastButton> +
+ + 29 + + <ToastButton icon="times" onClick={() => setEvent(undefined)}> +
+ + 30 + + {t("scm-review-plugin.changeNotification.buttons.ignore")} +
+ + 31 + + </ToastButton> +
+ 26 + + 32 + + </ToastButtons> +
+ 27 + + 33 + + </Toast> +
+ 28 + + 34 + + ); +
+
+
, +
+
+
+
+ + + src/main/resources/locales/de/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
+

+ Custom File annotation for + src/main/resources/locales/de/plugins.json +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + 181 + + "titleClickable": "Der Kommentar bezieht sich auf eine ältere Version des Source- oder Target-Branches. Klicken Sie hier, um den ursprünglichen Kontext zu sehen." +
+ 182 + + 182 + + } +
+ 183 + + 183 + + } +
+ + 184 + + }, +
+ + 185 + + "changeNotification": { +
+ + 186 + + "title": "Neue Änderungen", +
+ + 187 + + "description": "An diesem Pull Request wurden Änderungen vorgenommen. Laden Sie die Seite neu um diese anzuzeigen.", +
+ + 188 + + "modificationWarning": "Warnung: Nicht gespeicherte Eingaben gehen verloren.", +
+ + 189 + + "buttons": { +
+ + 190 + + "reload": "Neu laden", +
+ + 191 + + "ignore": "Ignorieren" +
+ + 192 + + } +
+ 184 + + 193 + + } +
+ 185 + + 194 + + }, +
+ 186 + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/main/resources/locales/en/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
+

+ Custom File annotation for + src/main/resources/locales/en/plugins.json +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + 181 + + "titleClickable": "The comment is related to an older of the source or target branch. Click here to see the original context." +
+ 182 + + 182 + + } +
+ 183 + + 183 + + } +
+ + 184 + + }, +
+ + 185 + + "changeNotification": { +
+ + 186 + + "title": "New Changes", +
+ + 187 + + "description": "The underlying Pull-Request has changed. Press reload to see the changes.", +
+ + 188 + + "modificationWarning": "Warning: Non saved modification will be lost.", +
+ + 189 + + "buttons": { +
+ + 190 + + "reload": "Reload", +
+ + 191 + + "ignore": "Ignore" +
+ + 192 + + } +
+ 184 + + 193 + + } +
+ 185 + + 194 + + }, +
+ 186 + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/test/java/com/cloudogu/scm/review/events/ClientTest.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
+

+ Custom File annotation for + src/test/java/com/cloudogu/scm/review/events/ClientTest.java +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 7 + + 7 + + import org.mockito.Mock; +
+ 8 + + 8 + + import org.mockito.junit.jupiter.MockitoExtension; +
+ 9 + + 9 + + import sonia.scm.security.SessionId; +
+ + 10 + + +
+ 10 + + 11 + + import javax.ws.rs.sse.OutboundSseEvent; +
+ 11 + + 12 + + import javax.ws.rs.sse.SseEventSink; +
+ 12 + + + +
+ 13 + + 13 + + import java.time.Clock; +
+ 14 + + 14 + + import java.time.Instant; +
+ 15 + + 15 + + import java.time.LocalDateTime; +
+ 16 + + 16 + + import java.time.ZoneOffset; +
+ 17 + + 17 + + import java.time.temporal.ChronoField; +
+ 18 + + + import java.time.temporal.ChronoUnit; +
+ 19 + + + import java.time.temporal.TemporalField; +
+ 20 + + 18 + + import java.util.concurrent.CompletableFuture; +
+ 21 + + 19 + + import java.util.concurrent.CompletionStage; +
+ 22 + + + import java.util.concurrent.atomic.AtomicLong; +
+ 23 + + 20 + + import java.util.concurrent.atomic.AtomicReference; +
+ 24 + + 21 + + +
+ 25 + + 22 + + import static java.time.temporal.ChronoUnit.MINUTES; +
+
+
+ 83 + + 80 + + +
+ 84 + + 81 + + @Test +
+ 85 + + 82 + + @SuppressWarnings("unchecked") +
+ 86 + + + void shouldCloseEventSinkOnFailure() throws InterruptedException { +
+ + 83 + + void shouldCloseEventSinkOnFailure() { +
+ 87 + + 84 + + CompletionStage future = CompletableFuture.supplyAsync(() -> { +
+ 88 + + 85 + + throw new RuntimeException("failed to send message"); +
+ 89 + + 86 + + }); +
+
+
+ 91 + + 88 + + +
+ 92 + + 89 + + client.send(message); +
+ 93 + + 90 + + +
+ 94 + + + Thread.sleep(50L); +
+ 95 + + + +
+ 96 + + + verify(eventSink).close(); +
+ + 91 + + verify(eventSink, timeout(50L)).close(); +
+ 97 + + 92 + + } +
+ 98 + + 93 + + +
+ 99 + + 94 + + @Test +
+
+
, +] +`; + +exports[`Storyshots Diff File Controls 1`] = ` +Array [ +
+
+
+
+ + + src/main/java/com/cloudogu/scm/review/events/EventListener.java + + + modify + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 1 + + 1 + + package com.cloudogu.scm.review.events; +
+ 2 + + 2 + + +
+ 3 + + + import com.cloudogu.scm.review.comment.service.BasicComment; +
+ 4 + + + import com.cloudogu.scm.review.comment.service.BasicCommentEvent; +
+ 5 + + + import com.cloudogu.scm.review.comment.service.CommentEvent; +
+ 6 + + + import com.cloudogu.scm.review.comment.service.ReplyEvent; +
+ 7 + + 3 + + import com.cloudogu.scm.review.pullrequest.service.BasicPullRequestEvent; +
+ 8 + + 4 + + import com.cloudogu.scm.review.pullrequest.service.PullRequest; +
+ 9 + + + import com.cloudogu.scm.review.pullrequest.service.PullRequestEvent; +
+ 10 + + 5 + + import com.github.legman.Subscribe; +
+ 11 + + + import lombok.Data; +
+ 12 + + 6 + + import org.apache.shiro.SecurityUtils; +
+ 13 + + 7 + + import org.apache.shiro.subject.PrincipalCollection; +
+ 14 + + 8 + + import org.apache.shiro.subject.Subject; +
+ 15 + + 9 + + import sonia.scm.EagerSingleton; +
+ 16 + + + import sonia.scm.HandlerEventType; +
+ 17 + + + import sonia.scm.event.HandlerEvent; +
+ 18 + + 10 + + import sonia.scm.plugin.Extension; +
+ 19 + + 11 + + import sonia.scm.repository.Repository; +
+ 20 + + 12 + + import sonia.scm.security.SessionId; +
+
+
, +
+
+
+
+ + + src/main/js/ChangeNotification.tsx + + + modify + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 2 + + 2 + + import { Link } from "@scm-manager/ui-types"; +
+ 3 + + 3 + + import { apiClient, Toast, ToastButtons, ToastButton } from "@scm-manager/ui-components"; +
+ 4 + + 4 + + import { PullRequest } from "./types/PullRequest"; +
+ + 5 + + import { useTranslation } from "react-i18next"; +
+ 5 + + 6 + + +
+ 6 + + 7 + + type HandlerProps = { +
+ 7 + + 8 + + url: string; +
+
+
+ 15 + + 16 + + pullRequest: setEvent +
+ 16 + + 17 + + }); +
+ 17 + + 18 + + }, [url]); +
+ + 19 + + const { t } = useTranslation("plugins"); +
+ 18 + + 20 + + if (event) { +
+ 19 + + 21 + + return ( +
+ 20 + + + <Toast type="warning" title="New Changes"> +
+ 21 + + + <p>The underlying Pull-Request has changed. Press reload to see the changes.</p> +
+ 22 + + + <p>Warning: Non saved modification will be lost.</p> +
+ + 22 + + <Toast type="warning" title={t("scm-review-plugin.changeNotification.title")}> +
+ + 23 + + <p>{t("scm-review-plugin.changeNotification.description")}</p> +
+ + 24 + + <p>{t("scm-review-plugin.changeNotification.modificationWarning")}</p> +
+ 23 + + 25 + + <ToastButtons> +
+ 24 + + + <ToastButton icon="redo" onClick={reload}>Reload</ToastButton> +
+ 25 + + + <ToastButton icon="times" onClick={() => setEvent(undefined)}>Ignore</ToastButton> +
+ + 26 + + <ToastButton icon="redo" onClick={reload}> +
+ + 27 + + {t("scm-review-plugin.changeNotification.buttons.reload")} +
+ + 28 + + </ToastButton> +
+ + 29 + + <ToastButton icon="times" onClick={() => setEvent(undefined)}> +
+ + 30 + + {t("scm-review-plugin.changeNotification.buttons.ignore")} +
+ + 31 + + </ToastButton> +
+ 26 + + 32 + + </ToastButtons> +
+ 27 + + 33 + + </Toast> +
+ 28 + + 34 + + ); +
+
+
, +
+
+
+
+ + + src/main/resources/locales/de/plugins.json + + + modify + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + 181 + + "titleClickable": "Der Kommentar bezieht sich auf eine ältere Version des Source- oder Target-Branches. Klicken Sie hier, um den ursprünglichen Kontext zu sehen." +
+ 182 + + 182 + + } +
+ 183 + + 183 + + } +
+ + 184 + + }, +
+ + 185 + + "changeNotification": { +
+ + 186 + + "title": "Neue Änderungen", +
+ + 187 + + "description": "An diesem Pull Request wurden Änderungen vorgenommen. Laden Sie die Seite neu um diese anzuzeigen.", +
+ + 188 + + "modificationWarning": "Warnung: Nicht gespeicherte Eingaben gehen verloren.", +
+ + 189 + + "buttons": { +
+ + 190 + + "reload": "Neu laden", +
+ + 191 + + "ignore": "Ignorieren" +
+ + 192 + + } +
+ 184 + + 193 + + } +
+ 185 + + 194 + + }, +
+ 186 + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/main/resources/locales/en/plugins.json + + + modify + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + 181 + + "titleClickable": "The comment is related to an older of the source or target branch. Click here to see the original context." +
+ 182 + + 182 + + } +
+ 183 + + 183 + + } +
+ + 184 + + }, +
+ + 185 + + "changeNotification": { +
+ + 186 + + "title": "New Changes", +
+ + 187 + + "description": "The underlying Pull-Request has changed. Press reload to see the changes.", +
+ + 188 + + "modificationWarning": "Warning: Non saved modification will be lost.", +
+ + 189 + + "buttons": { +
+ + 190 + + "reload": "Reload", +
+ + 191 + + "ignore": "Ignore" +
+ + 192 + + } +
+ 184 + + 193 + + } +
+ 185 + + 194 + + }, +
+ 186 + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/test/java/com/cloudogu/scm/review/events/ClientTest.java + + + modify + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 7 + + 7 + + import org.mockito.Mock; +
+ 8 + + 8 + + import org.mockito.junit.jupiter.MockitoExtension; +
+ 9 + + 9 + + import sonia.scm.security.SessionId; +
+ + 10 + + +
+ 10 + + 11 + + import javax.ws.rs.sse.OutboundSseEvent; +
+ 11 + + 12 + + import javax.ws.rs.sse.SseEventSink; +
+ 12 + + + +
+ 13 + + 13 + + import java.time.Clock; +
+ 14 + + 14 + + import java.time.Instant; +
+ 15 + + 15 + + import java.time.LocalDateTime; +
+ 16 + + 16 + + import java.time.ZoneOffset; +
+ 17 + + 17 + + import java.time.temporal.ChronoField; +
+ 18 + + + import java.time.temporal.ChronoUnit; +
+ 19 + + + import java.time.temporal.TemporalField; +
+ 20 + + 18 + + import java.util.concurrent.CompletableFuture; +
+ 21 + + 19 + + import java.util.concurrent.CompletionStage; +
+ 22 + + + import java.util.concurrent.atomic.AtomicLong; +
+ 23 + + 20 + + import java.util.concurrent.atomic.AtomicReference; +
+ 24 + + 21 + + +
+ 25 + + 22 + + import static java.time.temporal.ChronoUnit.MINUTES; +
+
+
+ 83 + + 80 + + +
+ 84 + + 81 + + @Test +
+ 85 + + 82 + + @SuppressWarnings("unchecked") +
+ 86 + + + void shouldCloseEventSinkOnFailure() throws InterruptedException { +
+ + 83 + + void shouldCloseEventSinkOnFailure() { +
+ 87 + + 84 + + CompletionStage future = CompletableFuture.supplyAsync(() -> { +
+ 88 + + 85 + + throw new RuntimeException("failed to send message"); +
+ 89 + + 86 + + }); +
+
+
+ 91 + + 88 + + +
+ 92 + + 89 + + client.send(message); +
+ 93 + + 90 + + +
+ 94 + + + Thread.sleep(50L); +
+ 95 + + + +
+ 96 + + + verify(eventSink).close(); +
+ + 91 + + verify(eventSink, timeout(50L)).close(); +
+ 97 + + 92 + + } +
+ 98 + + 93 + + +
+ 99 + + 94 + + @Test +
+
+
, +] +`; + +exports[`Storyshots Diff Hunks 1`] = ` +
+
+
+
+ + + src/main/java/com/cloudogu/scm/review/pullrequest/service/DefaultPullRequestService.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 25 + + 25 + + import java.util.Set; +
+ 26 + + 26 + + import java.util.stream.Collectors; +
+ 27 + + 27 + + +
+ + 28 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestApprovalEvent.ApprovalCause.APPROVAL_REMOVED; +
+ + 29 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestApprovalEvent.ApprovalCause.APPROVED; +
+ 28 + + 30 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.MERGED; +
+ 29 + + 31 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.OPEN; +
+ 30 + + 32 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.REJECTED; +
+
+
+ 200 + + 202 + + PullRequest pullRequest = getPullRequestFromStore(repository, pullRequestId); +
+ 201 + + 203 + + pullRequest.addApprover(user.getId()); +
+ 202 + + 204 + + getStore(repository).update(pullRequest); +
+ + 205 + + eventBus.post(new PullRequestApprovalEvent(repository, pullRequest, APPROVED)); +
+ 203 + + 206 + + } +
+ 204 + + 207 + + +
+ 205 + + 208 + + @Override +
+
+
+ 211 + + 214 + + approver.stream() +
+ 212 + + 215 + + .filter(recipient -> user.getId().equals(recipient)) +
+ 213 + + 216 + + .findFirst() +
+ 214 + + + .ifPresent(pullRequest::removeApprover); +
+ 215 + + + getStore(repository).update(pullRequest); +
+ + 217 + + .ifPresent( +
+ + 218 + + approval -> { +
+ + 219 + + pullRequest.removeApprover(approval); +
+ + 220 + + getStore(repository).update(pullRequest); +
+ + 221 + + eventBus.post(new PullRequestApprovalEvent(repository, pullRequest, APPROVAL_REMOVED)); +
+ + 222 + + }); +
+ 216 + + 223 + + } +
+ 217 + + 224 + + +
+ 218 + + 225 + + @Override +
+
+
+`; + +exports[`Storyshots Diff Line Annotation 1`] = ` +Array [ +
+
+
+
+ + + src/main/java/com/cloudogu/scm/review/events/EventListener.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 1 + + 1 + + package com.cloudogu.scm.review.events; +
+ 2 + + 2 + + +
+

+ Line Annotation +

+
+ 3 + + + import com.cloudogu.scm.review.comment.service.BasicComment; +
+ 4 + + + import com.cloudogu.scm.review.comment.service.BasicCommentEvent; +
+ 5 + + + import com.cloudogu.scm.review.comment.service.CommentEvent; +
+ 6 + + + import com.cloudogu.scm.review.comment.service.ReplyEvent; +
+ 7 + + 3 + + import com.cloudogu.scm.review.pullrequest.service.BasicPullRequestEvent; +
+ 8 + + 4 + + import com.cloudogu.scm.review.pullrequest.service.PullRequest; +
+ 9 + + + import com.cloudogu.scm.review.pullrequest.service.PullRequestEvent; +
+ 10 + + 5 + + import com.github.legman.Subscribe; +
+ 11 + + + import lombok.Data; +
+ 12 + + 6 + + import org.apache.shiro.SecurityUtils; +
+ 13 + + 7 + + import org.apache.shiro.subject.PrincipalCollection; +
+ 14 + + 8 + + import org.apache.shiro.subject.Subject; +
+ 15 + + 9 + + import sonia.scm.EagerSingleton; +
+ 16 + + + import sonia.scm.HandlerEventType; +
+ 17 + + + import sonia.scm.event.HandlerEvent; +
+ 18 + + 10 + + import sonia.scm.plugin.Extension; +
+ 19 + + 11 + + import sonia.scm.repository.Repository; +
+ 20 + + 12 + + import sonia.scm.security.SessionId; +
+
+
, +
+
+
+
+ + + src/main/js/ChangeNotification.tsx + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 2 + + 2 + + import { Link } from "@scm-manager/ui-types"; +
+

+ Line Annotation +

+
+ 3 + + 3 + + import { apiClient, Toast, ToastButtons, ToastButton } from "@scm-manager/ui-components"; +
+ 4 + + 4 + + import { PullRequest } from "./types/PullRequest"; +
+ + 5 + + import { useTranslation } from "react-i18next"; +
+ 5 + + 6 + + +
+ 6 + + 7 + + type HandlerProps = { +
+ 7 + + 8 + + url: string; +
+
+
+ 15 + + 16 + + pullRequest: setEvent +
+ 16 + + 17 + + }); +
+ 17 + + 18 + + }, [url]); +
+ + 19 + + const { t } = useTranslation("plugins"); +
+ 18 + + 20 + + if (event) { +
+ 19 + + 21 + + return ( +
+ 20 + + + <Toast type="warning" title="New Changes"> +
+ 21 + + + <p>The underlying Pull-Request has changed. Press reload to see the changes.</p> +
+ 22 + + + <p>Warning: Non saved modification will be lost.</p> +
+ + 22 + + <Toast type="warning" title={t("scm-review-plugin.changeNotification.title")}> +
+ + 23 + + <p>{t("scm-review-plugin.changeNotification.description")}</p> +
+ + 24 + + <p>{t("scm-review-plugin.changeNotification.modificationWarning")}</p> +
+ 23 + + 25 + + <ToastButtons> +
+ 24 + + + <ToastButton icon="redo" onClick={reload}>Reload</ToastButton> +
+ 25 + + + <ToastButton icon="times" onClick={() => setEvent(undefined)}>Ignore</ToastButton> +
+ + 26 + + <ToastButton icon="redo" onClick={reload}> +
+ + 27 + + {t("scm-review-plugin.changeNotification.buttons.reload")} +
+ + 28 + + </ToastButton> +
+ + 29 + + <ToastButton icon="times" onClick={() => setEvent(undefined)}> +
+ + 30 + + {t("scm-review-plugin.changeNotification.buttons.ignore")} +
+ + 31 + + </ToastButton> +
+ 26 + + 32 + + </ToastButtons> +
+ 27 + + 33 + + </Toast> +
+ 28 + + 34 + + ); +
+
+
, +
+
+
+
+ + + src/main/resources/locales/de/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + 181 + + "titleClickable": "Der Kommentar bezieht sich auf eine ältere Version des Source- oder Target-Branches. Klicken Sie hier, um den ursprünglichen Kontext zu sehen." +
+ 182 + + 182 + + } +
+ 183 + + 183 + + } +
+ + 184 + + }, +
+ + 185 + + "changeNotification": { +
+ + 186 + + "title": "Neue Änderungen", +
+ + 187 + + "description": "An diesem Pull Request wurden Änderungen vorgenommen. Laden Sie die Seite neu um diese anzuzeigen.", +
+ + 188 + + "modificationWarning": "Warnung: Nicht gespeicherte Eingaben gehen verloren.", +
+ + 189 + + "buttons": { +
+ + 190 + + "reload": "Neu laden", +
+ + 191 + + "ignore": "Ignorieren" +
+ + 192 + + } +
+ 184 + + 193 + + } +
+ 185 + + 194 + + }, +
+ 186 + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/main/resources/locales/en/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + 181 + + "titleClickable": "The comment is related to an older of the source or target branch. Click here to see the original context." +
+ 182 + + 182 + + } +
+ 183 + + 183 + + } +
+ + 184 + + }, +
+ + 185 + + "changeNotification": { +
+ + 186 + + "title": "New Changes", +
+ + 187 + + "description": "The underlying Pull-Request has changed. Press reload to see the changes.", +
+ + 188 + + "modificationWarning": "Warning: Non saved modification will be lost.", +
+ + 189 + + "buttons": { +
+ + 190 + + "reload": "Reload", +
+ + 191 + + "ignore": "Ignore" +
+ + 192 + + } +
+ 184 + + 193 + + } +
+ 185 + + 194 + + }, +
+ 186 + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/test/java/com/cloudogu/scm/review/events/ClientTest.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 7 + + 7 + + import org.mockito.Mock; +
+ 8 + + 8 + + import org.mockito.junit.jupiter.MockitoExtension; +
+ 9 + + 9 + + import sonia.scm.security.SessionId; +
+ + 10 + + +
+ 10 + + 11 + + import javax.ws.rs.sse.OutboundSseEvent; +
+ 11 + + 12 + + import javax.ws.rs.sse.SseEventSink; +
+ 12 + + + +
+ 13 + + 13 + + import java.time.Clock; +
+ 14 + + 14 + + import java.time.Instant; +
+ 15 + + 15 + + import java.time.LocalDateTime; +
+ 16 + + 16 + + import java.time.ZoneOffset; +
+ 17 + + 17 + + import java.time.temporal.ChronoField; +
+ 18 + + + import java.time.temporal.ChronoUnit; +
+ 19 + + + import java.time.temporal.TemporalField; +
+ 20 + + 18 + + import java.util.concurrent.CompletableFuture; +
+ 21 + + 19 + + import java.util.concurrent.CompletionStage; +
+ 22 + + + import java.util.concurrent.atomic.AtomicLong; +
+ 23 + + 20 + + import java.util.concurrent.atomic.AtomicReference; +
+ 24 + + 21 + + +
+ 25 + + 22 + + import static java.time.temporal.ChronoUnit.MINUTES; +
+
+
+ 83 + + 80 + + +
+ 84 + + 81 + + @Test +
+ 85 + + 82 + + @SuppressWarnings("unchecked") +
+ 86 + + + void shouldCloseEventSinkOnFailure() throws InterruptedException { +
+ + 83 + + void shouldCloseEventSinkOnFailure() { +
+ 87 + + 84 + + CompletionStage future = CompletableFuture.supplyAsync(() -> { +
+ 88 + + 85 + + throw new RuntimeException("failed to send message"); +
+ 89 + + 86 + + }); +
+
+
+ 91 + + 88 + + +
+ 92 + + 89 + + client.send(message); +
+ 93 + + 90 + + +
+ 94 + + + Thread.sleep(50L); +
+ 95 + + + +
+ 96 + + + verify(eventSink).close(); +
+ + 91 + + verify(eventSink, timeout(50L)).close(); +
+ 97 + + 92 + + } +
+ 98 + + 93 + + +
+ 99 + + 94 + + @Test +
+
+
, +] +`; + +exports[`Storyshots Diff OnClick 1`] = ` +Array [ +
+
+
+
+ + + src/main/java/com/cloudogu/scm/review/events/EventListener.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 1 + + 1 + + package com.cloudogu.scm.review.events; +
+ 2 + + 2 + + +
+ 3 + + + import com.cloudogu.scm.review.comment.service.BasicComment; +
+ 4 + + + import com.cloudogu.scm.review.comment.service.BasicCommentEvent; +
+ 5 + + + import com.cloudogu.scm.review.comment.service.CommentEvent; +
+ 6 + + + import com.cloudogu.scm.review.comment.service.ReplyEvent; +
+ 7 + + 3 + + import com.cloudogu.scm.review.pullrequest.service.BasicPullRequestEvent; +
+ 8 + + 4 + + import com.cloudogu.scm.review.pullrequest.service.PullRequest; +
+ 9 + + + import com.cloudogu.scm.review.pullrequest.service.PullRequestEvent; +
+ 10 + + 5 + + import com.github.legman.Subscribe; +
+ 11 + + + import lombok.Data; +
+ 12 + + 6 + + import org.apache.shiro.SecurityUtils; +
+ 13 + + 7 + + import org.apache.shiro.subject.PrincipalCollection; +
+ 14 + + 8 + + import org.apache.shiro.subject.Subject; +
+ 15 + + 9 + + import sonia.scm.EagerSingleton; +
+ 16 + + + import sonia.scm.HandlerEventType; +
+ 17 + + + import sonia.scm.event.HandlerEvent; +
+ 18 + + 10 + + import sonia.scm.plugin.Extension; +
+ 19 + + 11 + + import sonia.scm.repository.Repository; +
+ 20 + + 12 + + import sonia.scm.security.SessionId; +
+
+
, +
+
+
+
+ + + src/main/js/ChangeNotification.tsx + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 2 + + 2 + + import { Link } from "@scm-manager/ui-types"; +
+ 3 + + 3 + + import { apiClient, Toast, ToastButtons, ToastButton } from "@scm-manager/ui-components"; +
+ 4 + + 4 + + import { PullRequest } from "./types/PullRequest"; +
+ + 5 + + import { useTranslation } from "react-i18next"; +
+ 5 + + 6 + + +
+ 6 + + 7 + + type HandlerProps = { +
+ 7 + + 8 + + url: string; +
+
+
+ 15 + + 16 + + pullRequest: setEvent +
+ 16 + + 17 + + }); +
+ 17 + + 18 + + }, [url]); +
+ + 19 + + const { t } = useTranslation("plugins"); +
+ 18 + + 20 + + if (event) { +
+ 19 + + 21 + + return ( +
+ 20 + + + <Toast type="warning" title="New Changes"> +
+ 21 + + + <p>The underlying Pull-Request has changed. Press reload to see the changes.</p> +
+ 22 + + + <p>Warning: Non saved modification will be lost.</p> +
+ + 22 + + <Toast type="warning" title={t("scm-review-plugin.changeNotification.title")}> +
+ + 23 + + <p>{t("scm-review-plugin.changeNotification.description")}</p> +
+ + 24 + + <p>{t("scm-review-plugin.changeNotification.modificationWarning")}</p> +
+ 23 + + 25 + + <ToastButtons> +
+ 24 + + + <ToastButton icon="redo" onClick={reload}>Reload</ToastButton> +
+ 25 + + + <ToastButton icon="times" onClick={() => setEvent(undefined)}>Ignore</ToastButton> +
+ + 26 + + <ToastButton icon="redo" onClick={reload}> +
+ + 27 + + {t("scm-review-plugin.changeNotification.buttons.reload")} +
+ + 28 + + </ToastButton> +
+ + 29 + + <ToastButton icon="times" onClick={() => setEvent(undefined)}> +
+ + 30 + + {t("scm-review-plugin.changeNotification.buttons.ignore")} +
+ + 31 + + </ToastButton> +
+ 26 + + 32 + + </ToastButtons> +
+ 27 + + 33 + + </Toast> +
+ 28 + + 34 + + ); +
+
+
, +
+
+
+
+ + + src/main/resources/locales/de/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + 181 + + "titleClickable": "Der Kommentar bezieht sich auf eine ältere Version des Source- oder Target-Branches. Klicken Sie hier, um den ursprünglichen Kontext zu sehen." +
+ 182 + + 182 + + } +
+ 183 + + 183 + + } +
+ + 184 + + }, +
+ + 185 + + "changeNotification": { +
+ + 186 + + "title": "Neue Änderungen", +
+ + 187 + + "description": "An diesem Pull Request wurden Änderungen vorgenommen. Laden Sie die Seite neu um diese anzuzeigen.", +
+ + 188 + + "modificationWarning": "Warnung: Nicht gespeicherte Eingaben gehen verloren.", +
+ + 189 + + "buttons": { +
+ + 190 + + "reload": "Neu laden", +
+ + 191 + + "ignore": "Ignorieren" +
+ + 192 + + } +
+ 184 + + 193 + + } +
+ 185 + + 194 + + }, +
+ 186 + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/main/resources/locales/en/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + 181 + + "titleClickable": "The comment is related to an older of the source or target branch. Click here to see the original context." +
+ 182 + + 182 + + } +
+ 183 + + 183 + + } +
+ + 184 + + }, +
+ + 185 + + "changeNotification": { +
+ + 186 + + "title": "New Changes", +
+ + 187 + + "description": "The underlying Pull-Request has changed. Press reload to see the changes.", +
+ + 188 + + "modificationWarning": "Warning: Non saved modification will be lost.", +
+ + 189 + + "buttons": { +
+ + 190 + + "reload": "Reload", +
+ + 191 + + "ignore": "Ignore" +
+ + 192 + + } +
+ 184 + + 193 + + } +
+ 185 + + 194 + + }, +
+ 186 + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/test/java/com/cloudogu/scm/review/events/ClientTest.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 7 + + 7 + + import org.mockito.Mock; +
+ 8 + + 8 + + import org.mockito.junit.jupiter.MockitoExtension; +
+ 9 + + 9 + + import sonia.scm.security.SessionId; +
+ + 10 + + +
+ 10 + + 11 + + import javax.ws.rs.sse.OutboundSseEvent; +
+ 11 + + 12 + + import javax.ws.rs.sse.SseEventSink; +
+ 12 + + + +
+ 13 + + 13 + + import java.time.Clock; +
+ 14 + + 14 + + import java.time.Instant; +
+ 15 + + 15 + + import java.time.LocalDateTime; +
+ 16 + + 16 + + import java.time.ZoneOffset; +
+ 17 + + 17 + + import java.time.temporal.ChronoField; +
+ 18 + + + import java.time.temporal.ChronoUnit; +
+ 19 + + + import java.time.temporal.TemporalField; +
+ 20 + + 18 + + import java.util.concurrent.CompletableFuture; +
+ 21 + + 19 + + import java.util.concurrent.CompletionStage; +
+ 22 + + + import java.util.concurrent.atomic.AtomicLong; +
+ 23 + + 20 + + import java.util.concurrent.atomic.AtomicReference; +
+ 24 + + 21 + + +
+ 25 + + 22 + + import static java.time.temporal.ChronoUnit.MINUTES; +
+
+
+ 83 + + 80 + + +
+ 84 + + 81 + + @Test +
+ 85 + + 82 + + @SuppressWarnings("unchecked") +
+ 86 + + + void shouldCloseEventSinkOnFailure() throws InterruptedException { +
+ + 83 + + void shouldCloseEventSinkOnFailure() { +
+ 87 + + 84 + + CompletionStage future = CompletableFuture.supplyAsync(() -> { +
+ 88 + + 85 + + throw new RuntimeException("failed to send message"); +
+ 89 + + 86 + + }); +
+
+
+ 91 + + 88 + + +
+ 92 + + 89 + + client.send(message); +
+ 93 + + 90 + + +
+ 94 + + + Thread.sleep(50L); +
+ 95 + + + +
+ 96 + + + verify(eventSink).close(); +
+ + 91 + + verify(eventSink, timeout(50L)).close(); +
+ 97 + + 92 + + } +
+ 98 + + 93 + + +
+ 99 + + 94 + + @Test +
+
+
, +] +`; + +exports[`Storyshots Diff Side-By-Side 1`] = ` +Array [ +
+
+
+
+ + + src/main/java/com/cloudogu/scm/review/events/EventListener.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 1 + + package com.cloudogu.scm.review.events; + + 1 + + package com.cloudogu.scm.review.events; +
+ 2 + + + + 2 + + +
+ 3 + + import com.cloudogu.scm.review.comment.service.BasicComment; + + +
+ 4 + + import com.cloudogu.scm.review.comment.service.BasicCommentEvent; + + +
+ 5 + + import com.cloudogu.scm.review.comment.service.CommentEvent; + + +
+ 6 + + import com.cloudogu.scm.review.comment.service.ReplyEvent; + + +
+ 7 + + import com.cloudogu.scm.review.pullrequest.service.BasicPullRequestEvent; + + 3 + + import com.cloudogu.scm.review.pullrequest.service.BasicPullRequestEvent; +
+ 8 + + import com.cloudogu.scm.review.pullrequest.service.PullRequest; + + 4 + + import com.cloudogu.scm.review.pullrequest.service.PullRequest; +
+ 9 + + import com.cloudogu.scm.review.pullrequest.service.PullRequestEvent; + + +
+ 10 + + import com.github.legman.Subscribe; + + 5 + + import com.github.legman.Subscribe; +
+ 11 + + import lombok.Data; + + +
+ 12 + + import org.apache.shiro.SecurityUtils; + + 6 + + import org.apache.shiro.SecurityUtils; +
+ 13 + + import org.apache.shiro.subject.PrincipalCollection; + + 7 + + import org.apache.shiro.subject.PrincipalCollection; +
+ 14 + + import org.apache.shiro.subject.Subject; + + 8 + + import org.apache.shiro.subject.Subject; +
+ 15 + + import sonia.scm.EagerSingleton; + + 9 + + import sonia.scm.EagerSingleton; +
+ 16 + + import sonia.scm.HandlerEventType; + + +
+ 17 + + import sonia.scm.event.HandlerEvent; + + +
+ 18 + + import sonia.scm.plugin.Extension; + + 10 + + import sonia.scm.plugin.Extension; +
+ 19 + + import sonia.scm.repository.Repository; + + 11 + + import sonia.scm.repository.Repository; +
+ 20 + + import sonia.scm.security.SessionId; + + 12 + + import sonia.scm.security.SessionId; +
+
+
, +
+
+
+
+ + + src/main/js/ChangeNotification.tsx + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 2 + + import { Link } from "@scm-manager/ui-types"; + + 2 + + import { Link } from "@scm-manager/ui-types"; +
+ 3 + + import { apiClient, Toast, ToastButtons, ToastButton } from "@scm-manager/ui-components"; + + 3 + + import { apiClient, Toast, ToastButtons, ToastButton } from "@scm-manager/ui-components"; +
+ 4 + + import { PullRequest } from "./types/PullRequest"; + + 4 + + import { PullRequest } from "./types/PullRequest"; +
+ + + 5 + + import { useTranslation } from "react-i18next"; +
+ 5 + + + + 6 + + +
+ 6 + + type HandlerProps = { + + 7 + + type HandlerProps = { +
+ 7 + + url: string; + + 8 + + url: string; +
+
+
+ 15 + + pullRequest: setEvent + + 16 + + pullRequest: setEvent +
+ 16 + + }); + + 17 + + }); +
+ 17 + + }, [url]); + + 18 + + }, [url]); +
+ + + 19 + + const { t } = useTranslation("plugins"); +
+ 18 + + if (event) { + + 20 + + if (event) { +
+ 19 + + return ( + + 21 + + return ( +
+ 20 + + <Toast type="warning" title="New Changes"> + + +
+ 21 + + <p>The underlying Pull-Request has changed. Press reload to see the changes.</p> + + +
+ 22 + + <p>Warning: Non saved modification will be lost.</p> + + 22 + + <Toast type="warning" title={t("scm-review-plugin.changeNotification.title")}> +
+ + + 23 + + <p>{t("scm-review-plugin.changeNotification.description")}</p> +
+ + + 24 + + <p>{t("scm-review-plugin.changeNotification.modificationWarning")}</p> +
+ 23 + + <ToastButtons> + + 25 + + <ToastButtons> +
+ 24 + + <ToastButton icon="redo" onClick={reload}>Reload</ToastButton> + + +
+ 25 + + <ToastButton icon="times" onClick={() => setEvent(undefined)}>Ignore</ToastButton> + + 26 + + <ToastButton icon="redo" onClick={reload}> +
+ + + 27 + + {t("scm-review-plugin.changeNotification.buttons.reload")} +
+ + + 28 + + </ToastButton> +
+ + + 29 + + <ToastButton icon="times" onClick={() => setEvent(undefined)}> +
+ + + 30 + + {t("scm-review-plugin.changeNotification.buttons.ignore")} +
+ + + 31 + + </ToastButton> +
+ 26 + + </ToastButtons> + + 32 + + </ToastButtons> +
+ 27 + + </Toast> + + 33 + + </Toast> +
+ 28 + + ); + + 34 + + ); +
+
+
, +
+
+
+
+ + + src/main/resources/locales/de/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + "titleClickable": "Der Kommentar bezieht sich auf eine ältere Version des Source- oder Target-Branches. Klicken Sie hier, um den ursprünglichen Kontext zu sehen." + + 181 + + "titleClickable": "Der Kommentar bezieht sich auf eine ältere Version des Source- oder Target-Branches. Klicken Sie hier, um den ursprünglichen Kontext zu sehen." +
+ 182 + + } + + 182 + + } +
+ 183 + + } + + 183 + + } +
+ + + 184 + + }, +
+ + + 185 + + "changeNotification": { +
+ + + 186 + + "title": "Neue Änderungen", +
+ + + 187 + + "description": "An diesem Pull Request wurden Änderungen vorgenommen. Laden Sie die Seite neu um diese anzuzeigen.", +
+ + + 188 + + "modificationWarning": "Warnung: Nicht gespeicherte Eingaben gehen verloren.", +
+ + + 189 + + "buttons": { +
+ + + 190 + + "reload": "Neu laden", +
+ + + 191 + + "ignore": "Ignorieren" +
+ + + 192 + + } +
+ 184 + + } + + 193 + + } +
+ 185 + + }, + + 194 + + }, +
+ 186 + + "permissions": { + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/main/resources/locales/en/plugins.json + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 181 + + "titleClickable": "The comment is related to an older of the source or target branch. Click here to see the original context." + + 181 + + "titleClickable": "The comment is related to an older of the source or target branch. Click here to see the original context." +
+ 182 + + } + + 182 + + } +
+ 183 + + } + + 183 + + } +
+ + + 184 + + }, +
+ + + 185 + + "changeNotification": { +
+ + + 186 + + "title": "New Changes", +
+ + + 187 + + "description": "The underlying Pull-Request has changed. Press reload to see the changes.", +
+ + + 188 + + "modificationWarning": "Warning: Non saved modification will be lost.", +
+ + + 189 + + "buttons": { +
+ + + 190 + + "reload": "Reload", +
+ + + 191 + + "ignore": "Ignore" +
+ + + 192 + + } +
+ 184 + + } + + 193 + + } +
+ 185 + + }, + + 194 + + }, +
+ 186 + + "permissions": { + + 195 + + "permissions": { +
+
+
, +
+
+
+
+ + + src/test/java/com/cloudogu/scm/review/events/ClientTest.java + + + modify + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ 7 + + import org.mockito.Mock; + + 7 + + import org.mockito.Mock; +
+ 8 + + import org.mockito.junit.jupiter.MockitoExtension; + + 8 + + import org.mockito.junit.jupiter.MockitoExtension; +
+ 9 + + import sonia.scm.security.SessionId; + + 9 + + import sonia.scm.security.SessionId; +
+ + + 10 + + +
+ 10 + + import javax.ws.rs.sse.OutboundSseEvent; + + 11 + + import javax.ws.rs.sse.OutboundSseEvent; +
+ 11 + + import javax.ws.rs.sse.SseEventSink; + + 12 + + import javax.ws.rs.sse.SseEventSink; +
+ 12 + + + + +
+ 13 + + import java.time.Clock; + + 13 + + import java.time.Clock; +
+ 14 + + import java.time.Instant; + + 14 + + import java.time.Instant; +
+ 15 + + import java.time.LocalDateTime; + + 15 + + import java.time.LocalDateTime; +
+ 16 + + import java.time.ZoneOffset; + + 16 + + import java.time.ZoneOffset; +
+ 17 + + import java.time.temporal.ChronoField; + + 17 + + import java.time.temporal.ChronoField; +
+ 18 + + import java.time.temporal.ChronoUnit; + + +
+ 19 + + import java.time.temporal.TemporalField; + + +
+ 20 + + import java.util.concurrent.CompletableFuture; + + 18 + + import java.util.concurrent.CompletableFuture; +
+ 21 + + import java.util.concurrent.CompletionStage; + + 19 + + import java.util.concurrent.CompletionStage; +
+ 22 + + import java.util.concurrent.atomic.AtomicLong; + + +
+ 23 + + import java.util.concurrent.atomic.AtomicReference; + + 20 + + import java.util.concurrent.atomic.AtomicReference; +
+ 24 + + + + 21 + + +
+ 25 + + import static java.time.temporal.ChronoUnit.MINUTES; + + 22 + + import static java.time.temporal.ChronoUnit.MINUTES; +
+
+
+ 83 + + + + 80 + + +
+ 84 + + @Test + + 81 + + @Test +
+ 85 + + @SuppressWarnings("unchecked") + + 82 + + @SuppressWarnings("unchecked") +
+ 86 + + void shouldCloseEventSinkOnFailure() throws InterruptedException { + + 83 + + void shouldCloseEventSinkOnFailure() { +
+ 87 + + CompletionStage future = CompletableFuture.supplyAsync(() -> { + + 84 + + CompletionStage future = CompletableFuture.supplyAsync(() -> { +
+ 88 + + throw new RuntimeException("failed to send message"); + + 85 + + throw new RuntimeException("failed to send message"); +
+ 89 + + }); + + 86 + + }); +
+
+
+ 91 + + + + 88 + + +
+ 92 + + client.send(message); + + 89 + + client.send(message); +
+ 93 + + + + 90 + + +
+ 94 + + Thread.sleep(50L); + + +
+ 95 + + + + +
+ 96 + + verify(eventSink).close(); + + 91 + + verify(eventSink, timeout(50L)).close(); +
+ 97 + + } + + 92 + + } +
+ 98 + + + + 93 + + +
+ 99 + + @Test + + 94 + + @Test +
+
+
, +] +`; + exports[`Storyshots Forms|Checkbox Default 1`] = `
ReactNode; - -const RoutingDecorator = (story: StoryFn) => {story()}; - -const SpacingDecorator = (story: StoryFn) => {story()}; +const SpacingDecorator = (story: () => ReactNode) => {story()}; storiesOf("Buttons|Button", module) - .addDecorator(RoutingDecorator) .add("Colors", () => (
{colors.map(color => ( @@ -44,7 +38,6 @@ storiesOf("Buttons|Button", module) const buttonStory = (name: string, storyFn: () => ReactElement) => { return storiesOf("Buttons|" + name, module) - .addDecorator(RoutingDecorator) .addDecorator(SpacingDecorator) .add("Default", storyFn); }; @@ -53,7 +46,7 @@ buttonStory("CreateButton", () => Create); buttonStory("DeleteButton", () => Delete); buttonStory("DownloadButton", () => ).add( "Disabled", - () => + () => ); buttonStory("EditButton", () => Edit); buttonStory("SubmitButton", () => Submit); diff --git a/scm-ui/ui-components/src/repos/Diff.stories.tsx b/scm-ui/ui-components/src/repos/Diff.stories.tsx new file mode 100644 index 0000000000..09ef48c0b5 --- /dev/null +++ b/scm-ui/ui-components/src/repos/Diff.stories.tsx @@ -0,0 +1,55 @@ +import React, { useEffect, useState } from "react"; +import { storiesOf } from "@storybook/react"; +import Diff from "./Diff"; +// @ts-ignore +import parser from "gitdiff-parser"; +import simpleDiff from "../__resources__/Diff.simple"; +import hunksDiff from "../__resources__/Diff.hunks"; +import Button from "../buttons/Button"; +import { DiffEventContext } from "./DiffTypes"; +import Toast from "../toast/Toast"; + +const diffFiles = parser.parse(simpleDiff); + +storiesOf("Diff", module) + .add("Default", () => ) + .add("Side-By-Side", () => ) + .add("Collapsed", () => ) + .add("File Controls", () => } />) + .add("File Annotation", () => ( + [

Custom File annotation for {file.newPath}

]} + /> + )) + .add("Line Annotation", () => ( + { + return { + N2:

Line Annotation

+ }; + }} + /> + )) + .add("OnClick", () => { + const OnClickDemo = ({}) => { + const [changeId, setChangeId] = useState(); + useEffect(() => { + const interval = setInterval(() => setChangeId(undefined), 2000); + return () => clearInterval(interval); + }); + const onClick = (context: DiffEventContext) => setChangeId(context.changeId); + return ( + <> + {changeId && } + + + ); + }; + return ; + }) + .add("Hunks", () => { + const hunkDiffFiles = parser.parse(hunksDiff); + return ; + }); diff --git a/scm-ui/ui-components/src/repos/DiffFile.tsx b/scm-ui/ui-components/src/repos/DiffFile.tsx index 0c51a30982..fe35cc111f 100644 --- a/scm-ui/ui-components/src/repos/DiffFile.tsx +++ b/scm-ui/ui-components/src/repos/DiffFile.tsx @@ -3,11 +3,13 @@ import { withTranslation, WithTranslation } from "react-i18next"; import classNames from "classnames"; import styled from "styled-components"; // @ts-ignore -import { Change, Diff as DiffComponent, getChangeKey, Hunk } from "react-diff-view"; +import { Diff as DiffComponent, getChangeKey, Hunk, Decoration } from "react-diff-view"; import { Button, ButtonGroup } from "../buttons"; import Tag from "../Tag"; import Icon from "../Icon"; -import { File, Hunk as HunkType, DiffObjectProps } from "./DiffTypes"; +import { ChangeEvent, Change, File, Hunk as HunkType, DiffObjectProps } from "./DiffTypes"; + +const EMPTY_ANNOTATION_FACTORY = {}; type Props = DiffObjectProps & WithTranslation & { @@ -20,7 +22,7 @@ type Collapsible = { }; type State = Collapsible & { - sideBySide: boolean; + sideBySide?: boolean; }; const DiffFilePanel = styled.div` @@ -56,6 +58,10 @@ const ChangeTypeTag = styled(Tag)` `; const ModifiedDiffComponent = styled(DiffComponent)` + /* align line numbers */ + & .diff-gutter { + text-align: right; + } /* column sizing */ > colgroup .diff-gutter-col { width: 3.25rem; @@ -87,7 +93,7 @@ class DiffFile extends React.Component { super(props); this.state = { collapsed: !!this.props.defaultCollapse, - sideBySide: false + sideBySide: props.sideBySide }; } @@ -126,7 +132,8 @@ class DiffFile extends React.Component { if (i > 0) { return ; } - return null; + // hunk header must be defined + return ; }; collectHunkAnnotations = (hunk: HunkType) => { @@ -136,6 +143,8 @@ class DiffFile extends React.Component { hunk, file }); + } else { + return EMPTY_ANNOTATION_FACTORY; } }; @@ -152,29 +161,27 @@ class DiffFile extends React.Component { } }; - createCustomEvents = (hunk: HunkType) => { + createGutterEvents = (hunk: HunkType) => { const { onClick } = this.props; if (onClick) { return { - gutter: { - onClick: (change: Change) => { - this.handleClickEvent(change, hunk); - } + onClick: (event: ChangeEvent) => { + this.handleClickEvent(event.change, hunk); } }; } }; renderHunk = (hunk: HunkType, i: number) => { - return ( + return [ + {this.createHunkHeader(hunk, i)}, - ); + ]; }; renderFileTitle = (file: File) => { @@ -228,8 +235,8 @@ class DiffFile extends React.Component { body = (
{fileAnnotations} - - {file.hunks.map(this.renderHunk)} + + {(hunks: HunkType[]) => hunks.map(this.renderHunk).reduce((a, b) => a.concat(b))}
); diff --git a/scm-ui/ui-components/src/repos/DiffTypes.ts b/scm-ui/ui-components/src/repos/DiffTypes.ts index 58dd5aef5b..635d5e93d8 100644 --- a/scm-ui/ui-components/src/repos/DiffTypes.ts +++ b/scm-ui/ui-components/src/repos/DiffTypes.ts @@ -40,6 +40,10 @@ export type Change = { type: ChangeType; }; +export type ChangeEvent = { + change: Change; +}; + export type BaseContext = { hunk: Hunk; file: File; @@ -66,7 +70,7 @@ export type DiffEventHandler = (context: DiffEventContext) => void; export type FileControlFactory = (file: File, setCollapseState: (p: boolean) => void) => ReactNode | null | undefined; export type DiffObjectProps = { - sideBySide: boolean; + sideBySide?: boolean; onClick?: DiffEventHandler; fileControlFactory?: FileControlFactory; fileAnnotationFactory?: FileAnnotationFactory; diff --git a/scm-ui/ui-components/src/repos/LoadingDiff.tsx b/scm-ui/ui-components/src/repos/LoadingDiff.tsx index ec782bc968..a0a64a2903 100644 --- a/scm-ui/ui-components/src/repos/LoadingDiff.tsx +++ b/scm-ui/ui-components/src/repos/LoadingDiff.tsx @@ -9,12 +9,13 @@ import Diff from "./Diff"; import { DiffObjectProps, File } from "./DiffTypes"; import { NotFoundError } from "../errors"; import { Notification } from "../index"; -import {withTranslation, WithTranslation} from "react-i18next"; +import { withTranslation, WithTranslation } from "react-i18next"; -type Props = WithTranslation & DiffObjectProps & { - url: string; - defaultCollapse?: boolean; -}; +type Props = WithTranslation & + DiffObjectProps & { + url: string; + defaultCollapse?: boolean; + }; type State = { diff?: File[]; diff --git a/scm-ui/ui-components/src/storyshots.test.ts b/scm-ui/ui-components/src/storyshots.test.ts index ee4e34653e..2843046f99 100644 --- a/scm-ui/ui-components/src/storyshots.test.ts +++ b/scm-ui/ui-components/src/storyshots.test.ts @@ -1,6 +1,22 @@ import path from "path"; -import initStoryshots from "@storybook/addon-storyshots"; +import initStoryshots, { snapshotWithOptions } from "@storybook/addon-storyshots"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const createNodeMock = (element: any) => { + if (element.type === "tr") { + return { + // eslint-disable-next-line @typescript-eslint/no-empty-function + querySelector: (selector: string) => {} + }; + } + return null; +}; initStoryshots({ - configPath: path.resolve(__dirname, "..", ".storybook") + configPath: path.resolve(__dirname, "..", ".storybook"), + // fix snapshot tests with react-diff-view which uses a ref on tr + // @see https://github.com/storybookjs/storybook/pull/1090 + test: snapshotWithOptions({ + createNodeMock + }) }); diff --git a/scm-ui/ui-styles/package.json b/scm-ui/ui-styles/package.json index 16fdb5b307..f5f984be08 100644 --- a/scm-ui/ui-styles/package.json +++ b/scm-ui/ui-styles/package.json @@ -13,7 +13,7 @@ "bulma": "^0.7.5", "bulma-popover": "^1.0.0", "bulma-tooltip": "^3.0.0", - "react-diff-view": "^1.8.1" + "react-diff-view": "^2.4.1" }, "devDependencies": { "css-loader": "^3.2.0", diff --git a/scm-ui/ui-styles/src/scm.scss b/scm-ui/ui-styles/src/scm.scss index dada35eb6b..9f6a086541 100644 --- a/scm-ui/ui-styles/src/scm.scss +++ b/scm-ui/ui-styles/src/scm.scss @@ -423,7 +423,7 @@ $danger-25: scale-color($danger, $lightness: 75%); $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; @import "~@fortawesome/fontawesome-free/scss/solid"; -@import "~react-diff-view/index"; +@import "~react-diff-view/style/index"; // NEW STYLES diff --git a/yarn.lock b/yarn.lock index 8a11f9fc5f..b69a934a48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4608,6 +4608,11 @@ chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +change-emitter@^0.1.2: + version "0.1.6" + resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515" + integrity sha1-6LL+PX8at9aaMhma/5HqaTFAlRU= + character-entities-legacy@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.3.tgz#3c729991d9293da0ede6dddcaf1f2ce1009ee8b4" @@ -5574,7 +5579,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: +debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -5633,11 +5638,6 @@ deep-equal@^1.0.1, deep-equal@^1.1.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -5753,11 +5753,6 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" @@ -5792,6 +5787,11 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" +diff-match-patch@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.4.tgz#6ac4b55237463761c4daf0dc603eb869124744b1" + integrity sha512-Uv3SW8bmH9nAtHKaKSanOQmj2DnlH65fUpcrMdfdaOxUG02QQ4YGZ8AE7kKOMisF7UqvOlGKVYWRvezdncW9lg== + diff-sequences@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" @@ -6827,7 +6827,7 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fbjs@^0.8.0: +fbjs@^0.8.0, fbjs@^0.8.1: version "0.8.17" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= @@ -7674,6 +7674,11 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +hoist-non-react-statics@^2.3.1: + version "2.5.5" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" + integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== + hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: version "3.3.1" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#101685d3aff3b23ea213163f6e8e12f4f111e19f" @@ -7930,7 +7935,7 @@ i18next@^17.3.0: dependencies: "@babel/runtime" "^7.3.1" -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -8069,7 +8074,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.2, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: +ini@^1.3.2, ini@^1.3.4, ini@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -9346,11 +9351,6 @@ lerna@^3.17.0: import-local "^2.0.0" npmlog "^4.1.2" -leven@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" - integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA= - leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -9478,11 +9478,6 @@ lodash.escape@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= -lodash.findlastindex@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.findlastindex/-/lodash.findlastindex-4.6.0.tgz#b8375ac0f02e9b926375cdf8dc3ea814abf9c6ac" - integrity sha1-uDdawPAum5Jjdc343D6oFKv5xqw= - lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" @@ -9508,11 +9503,6 @@ lodash.isplainobject@^4.0.6: resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= -lodash.mapvalues@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" - integrity sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw= - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -10195,15 +10185,6 @@ nearley@^2.7.10: randexp "0.4.6" semver "^5.4.1" -needle@^2.2.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" - integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -10345,22 +10326,6 @@ node-notifier@^5.4.2: shellwords "^0.1.1" which "^1.3.0" -node-pre-gyp@*: - version "0.14.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" - integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4.4.2" - node-releases@^1.1.29, node-releases@^1.1.44: version "1.1.44" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.44.tgz#cd66438a6eb875e3eb012b6a12e48d9f4326ffd7" @@ -10491,7 +10456,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-packlist@^1.1.6, npm-packlist@^1.4.4: +npm-packlist@^1.4.4: version "1.4.7" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.7.tgz#9e954365a06b80b18111ea900945af4f88ed4848" integrity sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ== @@ -10515,7 +10480,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2, npmlog@^4.1.2: +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -11965,16 +11930,6 @@ raw-loader@~0.5.1: resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" integrity sha1-DD0L6u2KAclm2Xh793goElKpeao= -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - react-clientside-effect@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.2.tgz#6212fb0e07b204e714581dd51992603d1accc837" @@ -12013,18 +11968,16 @@ react-dev-utils@^9.0.0: strip-ansi "5.2.0" text-table "0.2.0" -react-diff-view@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/react-diff-view/-/react-diff-view-1.8.1.tgz#0b9b4adcb92de6730d28177d68654dfcc2097f73" - integrity sha512-+soJL85Xnsak/VOdxSgiDKhhaFiOkckiswwrXdiWVCxV3LP9POyJR4AqGVFGdkntJ3YT63mtwTYuunFeId+XSA== +react-diff-view@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/react-diff-view/-/react-diff-view-2.4.1.tgz#bbeca5746bc8b60b3abab13b3f2f55d37f1430b8" + integrity sha512-PJL5jWvVAlNfihK4QadSJDC2noo04ppJNKgb+m59lAqYoRTFIRvH6+zSiQFN2l+H84MLYIQgBx/xyLDj7pca8w== dependencies: classnames "^2.2.6" - gitdiff-parser "^0.1.2" - leven "^2.1.0" - lodash.escape "^4.0.1" - lodash.findlastindex "^4.6.0" - lodash.mapvalues "^4.6.0" - warning "^4.0.1" + diff-match-patch "^1.0.4" + recompose "^0.30.0" + shallow-equal "^1.2.0" + warning "^4.0.2" react-docgen@^4.1.1: version "4.1.1" @@ -12124,7 +12077,7 @@ react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-i resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== -react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== @@ -12476,6 +12429,18 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" +recompose@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.30.0.tgz#82773641b3927e8c7d24a0d87d65aeeba18aabd0" + integrity sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w== + dependencies: + "@babel/runtime" "^7.0.0" + change-emitter "^0.1.2" + fbjs "^0.8.1" + hoist-non-react-statics "^2.3.1" + react-lifecycles-compat "^3.0.2" + symbol-observable "^1.0.4" + recursive-readdir@2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" @@ -12868,7 +12833,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -13049,7 +13014,7 @@ selfsigned@^1.10.7: dependencies: node-forge "0.9.0" -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -13182,7 +13147,7 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -shallow-equal@^1.1.0: +shallow-equal@^1.1.0, shallow-equal@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da" integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA== @@ -13797,11 +13762,6 @@ strip-json-comments@^3.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - strong-log-transformer@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -13908,7 +13868,7 @@ svgo@^1.0.0, svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" -symbol-observable@^1.0.3, symbol-observable@^1.2.0: +symbol-observable@^1.0.3, symbol-observable@^1.0.4, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== @@ -13955,7 +13915,7 @@ tar@^2.0.0: fstream "^1.0.12" inherits "2" -tar@^4.4.10, tar@^4.4.12, tar@^4.4.2, tar@^4.4.8: +tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== @@ -14722,7 +14682,7 @@ warning@^3.0.0: dependencies: loose-envify "^1.0.0" -warning@^4.0.1, warning@^4.0.2, warning@^4.0.3: +warning@^4.0.2, warning@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==