change(ui): notes ui changes, filtering change, link change to session, link note to session with note on click

This commit is contained in:
sylenien 2022-10-04 12:25:15 +02:00
parent a04ff25381
commit 9eae28e09e
14 changed files with 127 additions and 89 deletions

View file

@ -11,7 +11,7 @@ import { setEditNoteTooltip } from 'Duck/sessions';
// TODO: incapsulate toggler in LocationEvent
@withToggle("showLoadInfo", "toggleLoadInfo")
@connect(state => ({members: state.getIn(['members', 'list']), currentUserId: state.getIn(['account', 'id']) }), { setEditNoteTooltip })
@connect(state => ({members: state.getIn(['members', 'list']), currentUserId: state.getIn(['user', 'account', 'id']) }), { setEditNoteTooltip })
class EventGroupWrapper extends React.Component {
toggleLoadInfo = (e) => {
@ -52,6 +52,8 @@ class EventGroupWrapper extends React.Component {
const whiteBg = isLastInGroup && event.type !== TYPES.LOCATION || (!isLastEvent && event.type !== TYPES.LOCATION)
const safeRef = String(event.referrer || '');
console.log(this.props.currentUserId, event.userId)
return (
<div
className={

View file

@ -29,6 +29,7 @@ function NoteEvent(props: Props) {
const { settingsStore, notesStore } = useStore();
const { timezone } = settingsStore.sessionSettings;
console.log(props.noEdit)
const onEdit = () => {
props.onEdit({
isVisible: true,
@ -40,14 +41,14 @@ function NoteEvent(props: Props) {
isPublic: props.isPublic,
message: props.message,
sessionId: props.sessionId,
noteId: props.noteId
noteId: props.noteId,
},
});
};
const onCopy = () => {
copy(
`${window.location.origin}${session(props.sessionId)}${
`${window.location.origin}/${window.location.pathname.split('/')[1]}${session(props.sessionId)}${
props.timestamp > 0 ? '?jumpto=' + props.timestamp : ''
}`
);
@ -70,7 +71,7 @@ function NoteEvent(props: Props) {
}
};
const menuItems = [
{ icon: 'pencil', text: 'Edit', onClick: onEdit, disabled: props.onEdit },
{ icon: 'pencil', text: 'Edit', onClick: onEdit, disabled: props.noEdit },
{ icon: 'link-45deg', text: 'Copy URL', onClick: onCopy },
{ icon: 'trash', text: 'Delete', onClick: onDelete },
];
@ -101,8 +102,13 @@ function NoteEvent(props: Props) {
{props.tags.map((tag) => (
<div
key={tag}
style={{ background: tagProps[tag], userSelect: 'none' }}
className="rounded-xl text-sm px-2 py-1 text-white"
style={{
background: tagProps[tag],
userSelect: 'none',
minWidth: 60,
textAlign: 'center',
}}
className="rounded-full text-sm px-2 py-1 text-white"
>
{tag}
</div>

View file

@ -227,10 +227,12 @@ export default class Timeline extends React.PureComponent {
background: 'white',
zIndex: 3,
pointerEvents: 'none',
height: 10,
width: 16,
left: `${getTimelinePosition(note.timestamp, scale)}%`,
}}
>
<Icon name="quotes" size={16} color="main" />
<Icon name="quotes" style={{ width: 16, height: 10 }} color="main" />
</div>
) : null)}
</div>

View file

@ -33,7 +33,7 @@ function CreateNote({
const [text, setText] = React.useState('');
const [isPublic, setPublic] = React.useState(false);
const [tags, setTags] = React.useState([]);
const [useTimestamp, setUseTs] = React.useState(false);
const [useTimestamp, setUseTs] = React.useState(true);
const { notesStore } = useStore();
@ -105,11 +105,9 @@ function CreateNote({
};
const tagActive = (tag: iTag) => tags.includes(tag);
const removeTag = (tag: iTag) => {
setTags(tags.filter((t) => t !== tag));
};
const addTag = (tag: iTag) => {
setTags([...tags, tag]);
setTags([tag]);
};
return (
@ -145,6 +143,7 @@ function CreateNote({
placeholder="Note..."
rows={3}
value={text}
autoFocus
onChange={(e) => setText(e.target.value)}
style={{
border: 'solid thin #ddd',
@ -162,9 +161,11 @@ function CreateNote({
style={{
background: tagActive(tag) ? tagProps[tag] : 'rgba(0,0,0, 0.38)',
userSelect: 'none',
minWidth: 60,
textAlign: 'center'
}}
className="cursor-pointer rounded-xl px-2 py-1 mr-2 text-white"
onClick={() => (tagActive(tag) ? removeTag(tag) : addTag(tag))}
className="cursor-pointer rounded-full px-2 py-1 mr-2 text-white"
onClick={() => addTag(tag)}
>
{tag}
</div>

View file

@ -26,7 +26,7 @@ function ReadNote(props: Props) {
return (
<div style={{ position: 'absolute', top: '45%', left: 'calc(50% - 200px)' }}>
<div
className="flex items-start flex-col p-4 border gap-2"
className="flex items-start flex-col p-4 border gap-2 rounded"
style={{ background: '#FFFEF5', width: 400 }}
>
<div className="flex items-start font-semibold w-full text-xl">
@ -50,8 +50,8 @@ function ReadNote(props: Props) {
return (
<div style={{ position: 'absolute', top: '45%', left: 'calc(50% - 300px)' }}>
<div
className="flex items-start flex-col p-4 border gap-2"
style={{ background: '#FFFEF5', width: 600 }}
className="flex items-start !text-lg flex-col p-4 border gap-2 rounded"
style={{ background: '#FFFEF5', width: 500 }}
>
<div className="flex items-center w-full">
<div className="p-2 bg-gray-light rounded-full">
@ -63,7 +63,7 @@ function ReadNote(props: Props) {
{formatTimeOrDate(props.date as unknown as number, timezone)}
</div>
</div>
<div className="ml-auto cursor-pointer" onClick={props.onClose}>
<div className="ml-auto cursor-pointer self-start" onClick={props.onClose}>
<Icon name="close" size={18} />
</div>
</div>

View file

@ -27,13 +27,13 @@
.noteTooltip {
position: absolute;
padding: 1rem;
border-radius: 0.25rem;
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%);
}

View file

@ -1,32 +1,37 @@
import React, { useState, useCallback, useEffect } from 'react';
import cn from 'classnames';
import { Icon } from 'UI';
import { connect } from 'react-redux';
import cls from './PlayIconLayer.module.css';
import clsOv from './overlay.module.css';
interface Props {
togglePlay: () => void,
playing: boolean,
togglePlay: () => void;
playing: boolean;
notesEdit: boolean;
}
export default function PlayIconLayer({ playing, togglePlay }: Props) {
const [ showPlayOverlayIcon, setShowPlayOverlayIcon ] = useState(false);
function PlayIconLayer({ playing, togglePlay, notesEdit }: Props) {
const [showPlayOverlayIcon, setShowPlayOverlayIcon] = useState(false);
useEffect(() => {
// TODO Find a better way to do this
document.addEventListener('keydown', onKeyDown);
return () => {
document.removeEventListener('keydown', onKeyDown);
}
}, [])
};
}, [notesEdit]);
const onKeyDown = (e) => {
const getIsEdit = React.useCallback(() => notesEdit, [notesEdit])
const onKeyDown = (e: any) => {
console.log(getIsEdit())
if (getIsEdit()) return;
if (e.key === ' ') {
togglePlayAnimated()
togglePlayAnimated();
}
}
};
const togglePlayAnimated = useCallback(() => {
setShowPlayOverlayIcon(true);
@ -35,18 +40,19 @@ export default function PlayIconLayer({ playing, togglePlay }: Props) {
}, []);
return (
<div className={ clsOv.overlay } onClick={ togglePlayAnimated }>
<div
className={ cn(cls.iconWrapper, {
[ cls.zoomIcon ]: showPlayOverlayIcon
}) }
<div className={clsOv.overlay} onClick={togglePlayAnimated}>
<div
className={cn(cls.iconWrapper, {
[cls.zoomIcon]: showPlayOverlayIcon,
})}
>
<Icon
name={ playing ? "play" : "pause" }
color="gray-medium"
size={30}
/>
<Icon name={playing ? 'play' : 'pause'} color="gray-medium" size={30} />
</div>
</div>
)
}
);
}
export default connect((state) => ({
// @ts-ignore
notesEdit: state.getIn(['sessions', 'createNoteTooltip', 'isVisible']),
}))(PlayIconLayer);

View file

@ -10,6 +10,9 @@ function NotePopup({ setCreateNoteTooltip, time }: { setCreateNoteTooltip: (args
setCreateNoteTooltip({ time: time, isVisible: true })
};
React.useEffect(() => {
return () => setCreateNoteTooltip({ time: -1, isVisible: false })
})
return (
<div
onClick={toggleNotePopup}

View file

@ -1,5 +1,5 @@
import React from 'react';
import { Icon } from 'UI';
import { Icon, Link } from 'UI';
import PlayLink from 'Shared/SessionItem/PlayLink';
import { tagProps, iTag } from 'App/services/NotesService';
import { formatTimeOrDate } from 'App/date';
@ -8,7 +8,7 @@ import { observer } from 'mobx-react-lite';
import { ItemMenu } from 'UI';
import copy from 'copy-to-clipboard';
import { toast } from 'react-toastify';
import { session } from 'App/routes'
import { session } from 'App/routes';
interface Props {
userId: number;
@ -27,63 +27,73 @@ function NoteItem(props: Props) {
const { timezone } = settingsStore.sessionSettings;
const onCopy = () => {
copy(`${window.location.origin}${session(props.sessionId)}${props.timestamp > 0 ? '?jumpto=' + props.timestamp : ''}`);
toast.success('Note URL copied to clipboard')
}
copy(
`${window.location.origin}/${window.location.pathname.split('/')[1]}${session(
props.sessionId
)}${props.timestamp > 0 ? '?jumpto=' + props.timestamp : ''}`
);
toast.success('Note URL copied to clipboard');
};
const onDelete = () => {
notesStore.deleteNote(props.noteId).then(r => {
notesStore.fetchNotes()
toast.success('Note deleted')
})
notesStore.deleteNote(props.noteId).then((r) => {
notesStore.fetchNotes();
toast.success('Note deleted');
});
};
const menuItems = [
{ icon: 'link-45deg', text: 'Copy URL', onClick: onCopy },
{ icon: 'trash', text: 'Delete', onClick: onDelete },
]
];
return (
<div
className="flex items-center p-4 border-b"
style={{ background: 'rgba(253, 243, 155, 0.1)' }}
>
<div className="flex flex-col gap-1">
<div>{props.description}</div>
<div className="flex items-center gap-2">
{props.tags.length ? (
<div className="flex items-center gap-1">
{props.tags.map((tag) => (
<div
key={tag}
style={{ background: tagProps[tag], userSelect: 'none' }}
className="rounded-xl px-2 py-1 mr-2 text-white"
>
{tag}
</div>
))}
<Link style={{ width: '90%' }} to={session(props.sessionId)+(props.timestamp > 0 ? `?jumpto=${props.timestamp}&note=${props.noteId}` : '')}>
<div className="flex flex-col gap-1 cursor-pointer">
<div>{props.description}</div>
<div className="flex items-center gap-2">
{props.tags.length ? (
<div className="flex items-center gap-1">
{props.tags.map((tag) => (
<div
key={tag}
style={{
background: tagProps[tag],
userSelect: 'none',
minWidth: 60,
textAlign: 'center',
}}
className="rounded-full px-2 py-1 mr-2 text-white"
>
{tag}
</div>
))}
</div>
) : null}
<div className="text-disabled-text flex items-center">
<span className="text-figmaColors-text-primary mr-1">By </span>
{props.userEmail}, {formatTimeOrDate(props.date as unknown as number, timezone)}
{!props.isPublic ? null : (
<>
<Icon name="user-friends" className="ml-4 mr-1" color="gray-dark" /> Team
</>
)}
</div>
) : null}
<div className="text-disabled-text flex items-center">
<span className="text-figmaColors-text-primary mr-1">By </span>
{props.userEmail}, {formatTimeOrDate(props.date as unknown as number, timezone)}
{!props.isPublic ? null : (
<>
<Icon name="user-friends" className="ml-4 mr-1" color="gray-dark" /> Team
</>
)}
</div>
</div>
</div>
</Link>
<div className="ml-auto">
<PlayLink
isAssist={false}
viewed={false}
sessionId={props.sessionId + (props.timestamp > 0 ? `?jumpto=${props.timestamp}&note=${props.noteId}` : '')}
sessionId={
props.sessionId
}
/>
</div>
<div className="ml-2 cursor-pointer">
<ItemMenu
bold
items={menuItems}
/>
<ItemMenu bold items={menuItems} />
</div>
</div>
);

View file

@ -27,7 +27,7 @@ function NotesList({ members }: {members: Array<Record<string, any>>}) {
</div>
}
>
<div className="mt-3 border-b rounded bg-white">
<div className="border-b rounded bg-white">
{sliceListPerPage(list, notesStore.page - 1, notesStore.pageSize).map(note => (
<React.Fragment key={note.noteId}>
<NoteItem

View file

@ -17,6 +17,13 @@ function NoteTags() {
return (
<div className="flex items-center">
<div>
<TagItem
onClick={() => notesStore.toggleTag()}
label="ALL"
isActive={notesStore.activeTags.length === 0}
/>
</div>
{TAGS.map((tag: iTag) => (
<div key={tag}>
<TagItem
@ -26,7 +33,7 @@ function NoteTags() {
/>
</div>
))}
<div className="ml-2" />
<Select name="sortSessions" plain right options={sortOptions} onChange={({ value }) => notesStore.toggleSort(value.value)} defaultValue={defaultOption} />
</div>
);

View file

@ -48,7 +48,7 @@ function SessionHeader(props: Props) {
return (
<div className="flex items-center px-4 justify-between">
<div className="flex items-center justify-between">
<div className="mr-3 text-lg flex items-center gap-2">
<div className="mr-3 text-lg flex items-center gap-4">
<Tab
onClick={() => props.setActiveTab({ type: 'all' })}
addBorder={activeTab === 'all'}

View file

@ -100,12 +100,12 @@ export default class NotesStore {
this.page = page
}
toggleTag(tag: iTag) {
if (this.activeTags.includes(tag)) {
this.activeTags = this.activeTags.filter(exTag => tag !== exTag)
toggleTag(tag?: iTag) {
if (!tag) {
this.activeTags = []
this.fetchNotes()
} else {
this.activeTags = [...this.activeTags, tag]
this.activeTags = [tag]
this.fetchNotes()
}
}

View file

@ -6,9 +6,10 @@ export const tagProps = {
'ISSUE': '#CC0000',
'TASK': '#7986CB',
'OTHER': 'rgba(0, 0, 0, 0.26)',
'ALL': ''
}
export type iTag = keyof typeof tagProps
export type iTag = keyof typeof tagProps | "ALL"
export const TAGS = Object.keys(tagProps) as unknown as (keyof typeof tagProps)[]