Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions apps/editor/src/assets/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@
"export-tooltip": "Export (Strg + E)",
"exporting": "Exportieren",

"annotations": "Annotationen",
"unify-annotations": "Annotationen vereinigen",
"annotation-consensus": "Annotationskonsens",
"show-annotation-density": "Annotationsdichte anzeigen",

"annotation-time": "Annotationszeit",
"show-delta": "Delta anzeigen",
"confirm-task-annotation-tooltip": "Bestätigen",
"skip-task-annotation-tooltip": "Überspringen",

Expand Down
7 changes: 7 additions & 0 deletions apps/editor/src/assets/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@
"export-tooltip": "Export (Ctrl + E)",
"exporting": "Exporting",

"annotations": "Annotations",
"unify-annotations": "Unify annotations",
"annotation-consensus": "Annotation consensus",
"show-annotation-density": "Show annotation density",

"annotation-time": "Annotation Time",
"show-delta": "Show delta",
"confirm-task-annotation-tooltip": "Confirm",
"skip-task-annotation-tooltip": "Skip",

Expand Down
2 changes: 1 addition & 1 deletion apps/editor/src/components/editor/ai-bar/ai-bar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
AnnotationStatus,
BlueButtonParam,
color,
fontSize,
Expand All @@ -20,7 +21,6 @@ import styled from "styled-components";

import { useStore } from "../../../app/root-store";
import { whoHome } from "../../../constants";
import { AnnotationStatus } from "../../../models/who/annotation";
import { AnnotationData } from "../../../models/who/annotationData";

const AIBarSheet = styled(Sheet)`
Expand Down
8 changes: 6 additions & 2 deletions apps/editor/src/components/editor/layers/layers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ import {
stopPropagation,
styledScrollbarMixin,
SubtleText,
TaskType,
useDelay,
useDoubleTap,
useForwardEvent,
useModalRoot,
useShortTap,
useTranslation,
} from "@visian/ui-shared";
import { Pixel } from "@visian/utils";
import { isFromWHO, Pixel } from "@visian/utils";
import { Observer, observer } from "mobx-react-lite";
import React, { useCallback, useEffect, useRef, useState } from "react";
import {
Expand Down Expand Up @@ -365,6 +366,8 @@ export const Layers: React.FC = observer(() => {
const layerCount = layers?.length;
const activeLayer = store?.editor.activeDocument?.activeLayer;
const activeLayerIndex = layers?.findIndex((layer) => layer === activeLayer);
const isSupervisorMode =
isFromWHO() && store?.currentTask?.kind === TaskType.Review;
return (
<>
<FloatingUIButton
Expand All @@ -389,7 +392,8 @@ export const Layers: React.FC = observer(() => {
tooltipTx="add-annotation-layer"
isDisabled={
!layerCount ||
layerCount >= (store?.editor.activeDocument?.maxLayers || 0)
layerCount >= (store?.editor.activeDocument?.maxLayers || 0) ||
isSupervisorMode
}
onPointerDown={
store?.editor.activeDocument?.addNewAnnotationLayer
Expand Down
1 change: 1 addition & 0 deletions apps/editor/src/components/editor/reviewer-panel/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./reviewer-panel";
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {
BooleanParam,
Divider,
Modal,
NumberParam,
TaskType,
} from "@visian/ui-shared";
import { observer } from "mobx-react-lite";
import React, { useCallback, useState } from "react";
import styled, { keyframes } from "styled-components";
import { useStore } from "../../../app/root-store";

const scaleAnimationIn = keyframes`
0% {
opacity: 0;
animation-timing-function: ease-in;
}
100% {
opacity: 1;
}
`;

const UnificationOptionsContainer = styled.div`
display: flex;
flex-direction: column;
animation: ${scaleAnimationIn} 0.5s;
`;

export const ReviewerPanel = observer(() => {
const store = useStore();
if (!(store?.currentTask?.kind === TaskType.Correct)) return <></>;

const [shouldUnifyAnnotations, setShouldUnifyAnnotations] = useState(false);
const [
shouldShowAnnotationDensity,
setShouldShowAnnotationDensity,
] = useState(false);
const setAnnotationConsensus = useCallback(
(value: number) => {
store?.editor.activeDocument?.setAnnotationConsensusCount(value);
},
[store?.editor.activeDocument],
);

return (
<Modal labelTx="annotations">
<BooleanParam
labelTx="unify-annotations"
value={shouldUnifyAnnotations}
setValue={() => {
setShouldUnifyAnnotations(!shouldUnifyAnnotations);
}}
/>
{shouldUnifyAnnotations && (
<UnificationOptionsContainer>
<Divider />
<NumberParam
labelTx="annotation-consensus"
min={1}
max={Math.max(store.currentTask.annotations.length, 1)}
stepSize={1}
value={store?.editor.activeDocument?.annotationConsensusCount}
setValue={setAnnotationConsensus}
/>
<Divider />
<BooleanParam
labelTx="show-annotation-density"
value={shouldShowAnnotationDensity}
setValue={() => {
setShouldShowAnnotationDensity(!shouldShowAnnotationDensity);
}}
/>
</UnificationOptionsContainer>
)}
</Modal>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./supervisor-panel";
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import {
BooleanParam,
color,
Divider,
fontSize,
Icon,
Modal,
TaskType,
Text,
UserRole,
} from "@visian/ui-shared";
import { observer } from "mobx-react-lite";
import React, { useState } from "react";
import styled from "styled-components";
import { useStore } from "../../../app/root-store";

interface AnnotatorSectionProps {
annotatorRole: UserRole;
annotatorName: string;
annotationTime?: string;
colorAddition: string;
colorDeletion?: string;
isLast?: boolean;
}

const SectionContainer = styled.div<{ isLast?: boolean }>`
display: flex;
flex-direction: column;
margin-bottom: ${(props) => (props.isLast ? "0px" : "12px")};
width: 100%;
`;

const AnnotatorInformationContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
`;

const AnnotationTimeContainer = styled.div`
display: flex;
flex-direction: column;
margin-top: 8px;
`;

const TypeText = styled(Text)`
color: ${color("lightText")};
font-size: ${fontSize("small")};
`;

const InformationText = styled(Text)`
font-size: 18px;
`;

const EditCircle = styled.div<{ circleColor: string }>`
width: 17px;
height: 17px;
background-color: ${(props) =>
color(props.circleColor as any) || props.circleColor};
border-radius: 50%;
`;
const CircleContainer = styled.div`
display: flex;
flex-direction: row;
min-width: 40px;
justify-content: space-between;
`;

const AnnotatorSection: React.FC<AnnotatorSectionProps> = ({
annotatorRole,
annotatorName,
annotationTime,
colorAddition,
colorDeletion,
isLast = false,
}) => (
<>
<Divider />
<SectionContainer isLast={isLast}>
<TypeText tx={annotatorRole} />
<AnnotatorInformationContainer>
<InformationText tx={annotatorName} />
<CircleContainer>
<EditCircle circleColor={colorAddition}>
<Icon icon="pixelBrush" />
</EditCircle>
{colorDeletion && (
<EditCircle circleColor={colorDeletion}>
<Icon icon="eraser" />
</EditCircle>
)}
</CircleContainer>
</AnnotatorInformationContainer>
{annotationTime && (
<AnnotationTimeContainer>
<TypeText tx="annotation-time" />
<InformationText tx={annotationTime} />
</AnnotationTimeContainer>
)}
</SectionContainer>
</>
);

export const SupervisorPanel = observer(() => {
const store = useStore();
if (!(store?.currentTask?.kind === TaskType.Review)) return <></>;
const annotationCount = store.currentTask.annotations.length;
const annotations = store.currentTask.annotations.sort(
(firstAnnotation, secondAnnotation) =>
new Date(firstAnnotation.submittedAt).getTime() -
new Date(secondAnnotation.submittedAt).getTime(),
);

const [shouldShowDelta, setShouldShowDelta] = useState(false);

return (
<Modal>
{/* TODO: Set delta options */}
<BooleanParam
labelTx="show-delta"
value={shouldShowDelta}
setValue={() => {
setShouldShowDelta(!shouldShowDelta);
}}
/>
{annotations.map((annotation, index) => {
const isLast = index === annotationCount - 1;
// TODO: Make coloring work properly
const correspondingLayer = store.editor.activeDocument?.getLayer(
annotation.data[0].correspondingLayerId,
);
const annotationColor = correspondingLayer?.color || "yellow";
return (
<AnnotatorSection
key={index}
annotatorRole={annotation.annotator.getRoleName()}
annotatorName={annotation.annotator.username}
colorAddition={annotationColor}
colorDeletion={
annotation.annotator.getRoleName() === "Reviewer" ? "red" : ""
}
isLast={isLast}
// TODO: Remove mocked annotation time (needs to be added to the API for this)
annotationTime={isLast ? "01:12:26" : ""}
/>
);
})}
</Modal>
);
});
11 changes: 11 additions & 0 deletions apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
FloatingUIButton,
Notification,
Spacer,
TaskType,
Text,
} from "@visian/ui-shared";
import { isFromWHO } from "@visian/utils";
Expand Down Expand Up @@ -37,6 +38,8 @@ import { ViewSettings } from "../view-settings";
import { UIOverlayProps } from "./ui-overlay.props";
import { SettingsPopUp } from "../settings-popup";
import { MeasurementPopUp } from "../measurement-popup";
import { SupervisorPanel } from "../supervisor-panel";
import { ReviewerPanel } from "../reviewer-panel";

const Container = styled(AbsoluteCover)`
align-items: stretch;
Expand Down Expand Up @@ -245,6 +248,14 @@ export const UIOverlay = observer<UIOverlayProps>(
<ThresholdAnnotationModal />
<DilateErodeModal />
<MeasurementModal />
{isFromWHO() &&
store?.currentTask?.kind === TaskType.Review && (
<SupervisorPanel />
)}
{isFromWHO() &&
store?.currentTask?.kind === TaskType.Correct && (
<ReviewerPanel />
)}
<AxesAndVoxel />
</ModalRow>
</ColumnLeft>
Expand Down
Loading