feat(ui): draft note taking popup

This commit is contained in:
sylenien 2022-09-26 15:27:22 +02:00 committed by Delirium
parent 0c79993b65
commit fdc9b1d305
12 changed files with 269 additions and 100 deletions

View file

@ -1,27 +0,0 @@
import React from 'react';
// @ts-ignore
import { Duration } from 'luxon';
import { connect } from 'react-redux';
// @ts-ignore
import stl from './timeline.module.css';
function TimeTooltip({ time, offset, isVisible, liveTimeTravel }: { time: number; offset: number; isVisible: boolean, liveTimeTravel: boolean }) {
const duration = Duration.fromMillis(time).toFormat(`${liveTimeTravel ? '-' : ''}mm:ss`);
return (
<div
className={stl.timeTooltip}
style={{
top: -30,
left: offset - 20,
display: isVisible ? 'block' : 'none' }
}
>
{!time ? 'Loading' : duration}
</div>
);
}
export default connect((state) => {
const { time = 0, offset = 0, isVisible } = state.getIn(['sessions', 'timeLineTooltip']);
return { time, offset, isVisible };
})(TimeTooltip);

View file

@ -0,0 +1,73 @@
import React from 'react';
import { Icon, Button, Checkbox } from 'UI';
import { Duration } from 'luxon';
import { connect } from 'react-redux';
import stl from './styles.module.css';
interface Props {
offset: number;
isVisible: boolean;
time: number;
}
const TAGS = ['QUERY', 'ISSUE', 'TASK', 'OTHER'];
function NoteTooltip({ offset, isVisible, time }: Props) {
const duration = Duration.fromMillis(time).toFormat('mm:ss');
const stopEvents = (e: any) => {
e.stopPropagation();
};
return (
<div
className={stl.noteTooltip}
style={{
top: -250,
width: 350,
left: offset - 20,
display: isVisible ? 'block' : 'none',
}}
onClick={stopEvents}
>
<div className="flex items-center">
<Icon name="quotes" size={20} />
<h3 className="text-xl ml-2 mr-4 font-semibold">Add Note</h3>
<div className="flex items-center cursor-pointer">
<Checkbox />
<span className="ml-1">{`at ${duration}`}</span>
</div>
<div className="ml-auto cursor-pointer">
<Icon name="close" size={20} />
</div>
</div>
<div>text field</div>
<div className="flex items-center mt-4">
{TAGS.map((tag) => (
<div className="rounded-xl px-2 py-1 mr-2 bg-gray-medium"> {tag} </div>
))}
</div>
<div className="flex mt-4">
<Button variant="primary" className="mr-4">
Add Note
</Button>
<div className="flex items-center cursor-pointer">
<Checkbox />
<Icon name="user-friends" size={16} className="mx-1" />
Visible to the team
</div>
</div>
<div className={stl.arrow} />
</div>
);
}
export default connect((state) => {
const { offset = 0, isVisible, time = 0 } = state.getIn(['sessions', 'noteTooltip']);
return { offset, isVisible, time };
})(NoteTooltip);

View file

@ -0,0 +1,38 @@
import React from 'react';
// @ts-ignore
import { Duration } from 'luxon';
import { connect } from 'react-redux';
import stl from './styles.module.css';
interface Props {
time: number;
offset: number;
isVisible: boolean;
liveTimeTravel: boolean;
}
function TimeTooltip({
time,
offset,
isVisible,
liveTimeTravel,
}: Props) {
const duration = Duration.fromMillis(time).toFormat(`${liveTimeTravel ? '-' : ''}mm:ss`);
return (
<div
className={stl.timeTooltip}
style={{
top: -30,
left: offset - 20,
display: isVisible ? 'block' : 'none',
}}
>
{!time ? 'Loading' : duration}
</div>
);
}
export default connect((state) => {
const { time = 0, offset = 0, isVisible } = state.getIn(['sessions', 'timeLineTooltip']);
return { time, offset, isVisible };
})(TimeTooltip);

View file

@ -1,5 +1,6 @@
import React from 'react'
import TimeTooltip from '../TimeTooltip';
import TimeTooltip from './TimeTooltip';
import NoteTooltip from './NoteTooltip';
import store from 'App/store';
import { Provider } from 'react-redux';
@ -7,7 +8,10 @@ function TooltipContainer({ live }: { live: boolean }) {
return (
<Provider store={store}>
<TimeTooltip liveTimeTravel={live} />
<>
<TimeTooltip liveTimeTravel={live} />
<NoteTooltip />
</>
</Provider>
)
}

View file

@ -0,0 +1,52 @@
.timeTooltip {
position: absolute;
padding: 0.25rem;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
background: black;
top: -35px;
color: white;
&:after {
content:'';
position: absolute;
top: 100%;
left: 0;
right: 0;
margin: 0 auto;
width: 0;
height: 0;
border-top: solid 5px black;
border-left: solid 5px transparent;
border-right: solid 5px transparent;
}
}
.noteTooltip {
position: absolute;
padding: 1rem;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
background: #F5F5F5;
top: -35px;
color: black;
border-radius: 12px;
cursor: default;
box-shadow: 0 4px 20px 4px rgb(0 20 60 / 10%), 0 4px 80px -8px rgb(0 20 60 / 20%);
}
.arrow {
position: absolute;
top: 100%;
left: 0;
right: 0;
margin: 0 auto;
width: 0;
height: 0;
border-top: solid 10px #f5f5f5;
border-left: solid 10px transparent;
border-right: solid 10px transparent;
}

View file

@ -1,80 +1,91 @@
import React from 'react';
import { Icon } from 'UI';
import Autoplay from './Autoplay';
import Bookmark from 'Shared/Bookmark'
import Bookmark from 'Shared/Bookmark';
import SharePopup from '../shared/SharePopup/SharePopup';
import { connectPlayer } from 'Player';
import { connectPlayer, pause } from 'Player';
import copy from 'copy-to-clipboard';
import { Tooltip } from 'react-tippy';
import Issues from './Issues/Issues';
function SubHeader(props) {
const [isCopied, setCopied] = React.useState(false);
const [isCopied, setCopied] = React.useState(false);
const isAssist = window.location.pathname.includes('/assist/');
const isAssist = window.location.pathname.includes('/assist/');
const location = props.currentLocation && props.currentLocation.length > 60 ? `${props.currentLocation.slice(0, 60)}...` : props.currentLocation
return (
<div className="w-full px-4 py-2 flex items-center border-b">
{location && (
<div
className="flex items-center cursor-pointer color-gray-medium text-sm p-1 hover:bg-gray-light-shade rounded-md"
onClick={() => {
copy(props.currentLocation);
setCopied(true)
setTimeout(() => setCopied(false), 5000)
}}
>
<Icon size="20" name="event/link" className="mr-1" />
<Tooltip
delay={0}
arrow
animation="fade"
hideOnClick={false}
position="bottom center"
title={isCopied ? 'URL Copied to clipboard' : 'Click to copy'}
>
{location}
</Tooltip>
</div>
)}
{!isAssist ? (
<div className="ml-auto text-sm flex items-center color-gray-medium" style={{ width: 'max-content' }}>
<div className="cursor-pointer mr-4 hover:bg-gray-light-shade rounded-md p-1">
{props.jiraConfig && props.jiraConfig.token && <Issues sessionId={props.sessionId} />}
</div>
<div className="cursor-pointer">
<SharePopup
entity="sessions"
id={ props.sessionId }
showCopyLink={true}
trigger={
<div className="flex items-center hover:bg-gray-light-shade rounded-md p-1">
<Icon
className="mr-2"
disabled={ props.disabled }
name="share-alt"
size="16"
/>
<span>Share</span>
</div>
}
/>
</div>
<div className="mx-4 hover:bg-gray-light-shade rounded-md p-1">
<Bookmark noMargin sessionId={props.sessionId} />
</div>
<div>
<Autoplay />
</div>
<div>
</div>
</div>
) : null}
const location =
props.currentLocation && props.currentLocation.length > 60
? `${props.currentLocation.slice(0, 60)}...`
: props.currentLocation;
const toggleNotePopup = () => {
pause();
};
return (
<div className="w-full px-4 py-2 flex items-center border-b">
{location && (
<div
className="flex items-center cursor-pointer color-gray-medium text-sm p-1 hover:bg-gray-light-shade rounded-md"
onClick={() => {
copy(props.currentLocation);
setCopied(true);
setTimeout(() => setCopied(false), 5000);
}}
>
<Icon size="20" name="event/link" className="mr-1" />
<Tooltip
delay={0}
arrow
animation="fade"
hideOnClick={false}
position="bottom center"
title={isCopied ? 'URL Copied to clipboard' : 'Click to copy'}
>
{location}
</Tooltip>
</div>
)
)}
{!isAssist ? (
<div
className="ml-auto text-sm flex items-center color-gray-medium"
style={{ width: 'max-content' }}
>
<div
onClick={toggleNotePopup}
className="cursor-pointer mr-4 hover:bg-gray-light-shade rounded-md p-1 flex items-center"
>
<Icon name="quotes" size="16" className="mr-2" />
Add note
</div>
<div className="cursor-pointer mr-4 hover:bg-gray-light-shade rounded-md p-1">
{props.jiraConfig && props.jiraConfig.token && <Issues sessionId={props.sessionId} />}
</div>
<div className="cursor-pointer">
<SharePopup
entity="sessions"
id={props.sessionId}
showCopyLink={true}
trigger={
<div className="flex items-center hover:bg-gray-light-shade rounded-md p-1">
<Icon className="mr-2" disabled={props.disabled} name="share-alt" size="16" />
<span>Share</span>
</div>
}
/>
</div>
<div className="mx-4 hover:bg-gray-light-shade rounded-md p-1">
<Bookmark noMargin sessionId={props.sessionId} />
</div>
<div>
<Autoplay />
</div>
<div></div>
</div>
) : null}
</div>
);
}
const SubH = connectPlayer(state => ({ currentLocation: state.location }))(SubHeader)
const SubH = connectPlayer((state) => ({ currentLocation: state.location }))(SubHeader);
export default React.memo(SubH)
export default React.memo(SubH);

View file

@ -2,7 +2,7 @@ import React from 'react';
import cn from 'classnames';
interface Props {
classNam?: string;
className?: string;
label?: string;
[x: string]: any;
}

File diff suppressed because one or more lines are too long

View file

@ -63,7 +63,8 @@ const initialState = Map({
timelinePointer: null,
sessionPath: {},
lastPlayedSessionId: null,
timeLineTooltip: { time: 0, offset: 0, isVisible: false }
timeLineTooltip: { time: 0, offset: 0, isVisible: false },
noteTooltip: { time: 100, offset: 100, isVisible: true },
});
const reducer = (state = initialState, action = {}) => {

View file

@ -18,10 +18,23 @@ function error(...args) {
}
}
let groupTm = null;
function group(...args) {
if (!window.env.PRODUCTION || options.verbose) {
if (!groupTm) {
groupTm = setTimeout(() => console.groupEnd(), 500)
console.groupCollapsed('Openreplay: Skipping session messages')
}
console.log(...args);
}
}
export default {
info: log,
log,
warn,
error,
group,
}

View file

@ -4,7 +4,7 @@ import logger from 'App/logger';
import RawMessageReader from './RawMessageReader';
// TODO: composition instead of inheritance
// needSkipMessage() and next() methods here use buf and p protected properties,
// needSkipMessage() and next() methods here use buf and p protected properties,
// which should be probably somehow incapsulated
export default class MFileReader extends RawMessageReader {
private pLastMessageID: number = 0
@ -49,7 +49,7 @@ export default class MFileReader extends RawMessageReader {
if (!skippedMessage) {
return null
}
logger.log("Skipping message: ", skippedMessage)
logger.group("Skipping message: ", skippedMessage)
}
this.pLastMessageID = this.p
@ -65,7 +65,7 @@ export default class MFileReader extends RawMessageReader {
}
this.currentTime = rMsg.timestamp - this.startTime
return this.next()
}
}
const msg = Object.assign(rMsg, {
time: this.currentTime,

View file

@ -0,0 +1,3 @@
<svg viewBox="0 0 29 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.7" d="M11.6355 2.9045C10.8963 1.75328 9.80334 0.873101 8.521 0.396241C7.23867 -0.0806184 5.83621 -0.128393 4.5244 0.260101C3.21259 0.648595 2.06231 1.45237 1.24645 2.55061C0.430585 3.64885 -0.00678382 4.98223 7.95597e-05 6.35034C0.00076002 7.48328 0.305669 8.59524 0.882952 9.57006C1.46024 10.5449 2.28871 11.3468 3.28182 11.892C4.27493 12.4372 5.39624 12.7058 6.52859 12.6695C7.66095 12.6333 8.76279 12.2937 9.71903 11.6861C9.22188 13.1623 8.29591 14.7372 6.77033 16.316C6.47844 16.6179 6.31846 17.0234 6.32558 17.4433C6.3327 17.8632 6.50633 18.2631 6.80828 18.555C7.11022 18.8469 7.51575 19.0069 7.93566 18.9997C8.35556 18.9926 8.75543 18.819 9.04731 18.517C14.6867 12.6728 13.9542 6.31998 11.6355 2.91209V2.9045ZM26.8154 2.9045C26.0762 1.75328 24.9833 0.873101 23.7009 0.396241C22.4186 -0.0806184 21.0161 -0.128393 19.7043 0.260101C18.3925 0.648595 17.2422 1.45237 16.4264 2.55061C15.6105 3.64885 15.1731 4.98223 15.18 6.35034C15.1807 7.48328 15.4856 8.59524 16.0629 9.57006C16.6402 10.5449 17.4686 11.3468 18.4617 11.892C19.4549 12.4372 20.5762 12.7058 21.7085 12.6695C22.8409 12.6333 23.9427 12.2937 24.899 11.6861C24.4018 13.1623 23.4758 14.7372 21.9503 16.316C21.6584 16.6179 21.4984 17.0234 21.5055 17.4433C21.5126 17.8632 21.6863 18.2631 21.9882 18.555C22.2901 18.8469 22.6957 19.0069 23.1156 18.9997C23.5355 18.9926 23.9354 18.819 24.2272 18.517C29.8666 12.6728 29.1342 6.31998 26.8154 2.91209V2.9045Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB