fix(ui): change notes sort and insertion

This commit is contained in:
sylenien 2022-10-04 14:44:41 +02:00
parent 6cb2197ce8
commit cc32eb0d68
11 changed files with 114 additions and 149 deletions

View file

@ -111,13 +111,7 @@ function WebPlayer(props) {
{showNoteModal ? (
<ReadNote
userEmail={props.members.find(m => m.id === noteItem?.userId)?.email || ''}
timestamp={noteItem?.timestamp}
tags={noteItem?.tags}
isPublic={noteItem?.isPublic}
message={noteItem?.message}
sessionId={noteItem?.sessionId}
date={noteItem?.createdAt}
noteId={noteItem?.noteId}
note={noteItem}
onClose={onNoteClose}
notFound={!noteItem}
/>

View file

@ -8,6 +8,7 @@ import Event from './Event'
import stl from './eventGroupWrapper.module.css';
import NoteEvent from './NoteEvent';
import { setEditNoteTooltip } from 'Duck/sessions';
import { Note } from 'App/services/NotesService';
// TODO: incapsulate toggler in LocationEvent
@withToggle("showLoadInfo", "toggleLoadInfo")
@ -74,13 +75,7 @@ class EventGroupWrapper extends React.Component {
{isNote ? (
<NoteEvent
userEmail={this.props.members.find(m => m.id === event.userId)?.email || event.userId}
timestamp={event.timestamp}
tags={event.tags}
isPublic={event.isPublic}
message={event.message}
sessionId={event.sessionId}
date={event.createdAt}
noteId={event.noteId}
note={event}
filterOutNote={filterOutNote}
onEdit={this.props.setEditNoteTooltip}
noEdit={this.props.currentUserId !== event.userId}

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Icon } from 'UI';
import { tagProps, iTag } from 'App/services/NotesService';
import { tagProps, iTag, Note } from 'App/services/NotesService';
import { formatTimeOrDate } from 'App/date';
import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite';
@ -12,15 +12,9 @@ import { confirm } from 'UI';
import { filterOutNote as filterOutTimelineNote } from 'Player';
interface Props {
userEmail: number;
timestamp: number;
tags: iTag[];
isPublic: boolean;
message: string;
sessionId: string;
date: string;
noteId: number;
note: Note;
noEdit: boolean;
userEmail: string;
filterOutNote: (id: number) => void;
onEdit: (noteTooltipObj: Record<string, any>) => void;
}
@ -29,28 +23,28 @@ function NoteEvent(props: Props) {
const { settingsStore, notesStore } = useStore();
const { timezone } = settingsStore.sessionSettings;
console.log(props.noEdit)
console.log(props.noEdit);
const onEdit = () => {
props.onEdit({
isVisible: true,
isEdit: true,
time: props.timestamp,
time: props.note.timestamp,
note: {
timestamp: props.timestamp,
tags: props.tags,
isPublic: props.isPublic,
message: props.message,
sessionId: props.sessionId,
noteId: props.noteId,
timestamp: props.note.timestamp,
tag: props.note.tag,
isPublic: props.note.isPublic,
message: props.note.message,
sessionId: props.note.sessionId,
noteId: props.note.noteId,
},
});
};
const onCopy = () => {
copy(
`${window.location.origin}/${window.location.pathname.split('/')[1]}${session(props.sessionId)}${
props.timestamp > 0 ? '?jumpto=' + props.timestamp : ''
}`
`${window.location.origin}/${window.location.pathname.split('/')[1]}${session(
props.note.sessionId
)}${props.note.timestamp > 0 ? '?jumpto=' + props.note.timestamp : ''}`
);
toast.success('Note URL copied to clipboard');
};
@ -63,9 +57,9 @@ function NoteEvent(props: Props) {
confirmation: `Are you sure you want to delete this note?`,
})
) {
notesStore.deleteNote(props.noteId).then((r) => {
props.filterOutNote(props.noteId);
filterOutTimelineNote(props.noteId);
notesStore.deleteNote(props.note.noteId).then((r) => {
props.filterOutNote(props.note.noteId);
filterOutTimelineNote(props.note.noteId);
toast.success('Note deleted');
});
}
@ -87,35 +81,31 @@ function NoteEvent(props: Props) {
<div className="ml-2">
<div>{props.userEmail}</div>
<div className="text-disabled-text">
{formatTimeOrDate(props.date as unknown as number, timezone)}
{formatTimeOrDate(props.note.createdAt as unknown as number, timezone)}
</div>
</div>
<div className="cursor-pointer ml-auto">
<ItemMenu bold items={menuItems} />
</div>
</div>
<div>{props.message}</div>
<div>{props.note.message}</div>
<div>
<div className="flex items-center gap-2 flex-wrap w-full">
{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 text-sm px-2 py-1 text-white"
>
{tag}
</div>
))}
{props.note.tag ? (
<div
key={props.note.tag}
style={{
background: tagProps[props.note.tag],
userSelect: 'none',
minWidth: 60,
textAlign: 'center',
}}
className="rounded-full text-sm px-2 py-1 text-white"
>
{props.note.tag}
</div>
) : null}
{!props.isPublic ? null : (
{!props.note.isPublic ? null : (
<>
<Icon name="user-friends" className="ml-2 mr-1" color="gray-dark" /> Team
</>

View file

@ -32,14 +32,14 @@ function CreateNote({
}: Props) {
const [text, setText] = React.useState('');
const [isPublic, setPublic] = React.useState(false);
const [tags, setTags] = React.useState([]);
const [tag, setTag] = React.useState<iTag>();
const [useTimestamp, setUseTs] = React.useState(true);
const { notesStore } = useStore();
React.useEffect(() => {
if (isEdit) {
setTags(editNote.tags);
setTag(editNote.tag);
setText(editNote.message);
setPublic(editNote.isPublic);
if (editNote.timestamp > 0) {
@ -56,7 +56,7 @@ function CreateNote({
const onSubmit = () => {
const note: WriteNote = {
message: text,
tags,
tag,
timestamp: useTimestamp ? (isEdit ? editNote.timestamp : time) : -1,
isPublic,
};
@ -76,7 +76,7 @@ function CreateNote({
.finally(() => {
setCreateNoteTooltip({ isVisible: false, time: 0 });
setText('');
setTags([]);
setTag(undefined);
});
}
@ -96,7 +96,7 @@ function CreateNote({
.finally(() => {
setCreateNoteTooltip({ isVisible: false, time: 0 });
setText('');
setTags([]);
setTag(undefined);
});
};
@ -104,10 +104,10 @@ function CreateNote({
setCreateNoteTooltip({ isVisible: false, time: 0 });
};
const tagActive = (tag: iTag) => tags.includes(tag);
const tagActive = (noteTag: iTag) => tag === noteTag;
const addTag = (tag: iTag) => {
setTags([tag]);
setTag(tag);
};
return (

View file

@ -1,21 +1,15 @@
import React from 'react';
import { Icon } from 'UI';
import { tagProps, iTag } from 'App/services/NotesService';
import { tagProps, Note } from 'App/services/NotesService';
import { formatTimeOrDate } from 'App/date';
import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite';
interface Props {
userEmail: number;
timestamp: number;
tags: iTag[];
isPublic: boolean;
message: string;
sessionId: string;
date: string;
noteId: number;
onClose: () => void;
userEmail: string;
note: Note;
notFound?: boolean;
onClose: () => void;
}
function ReadNote(props: Props) {
@ -60,29 +54,25 @@ function ReadNote(props: Props) {
<div className="ml-2">
<div>{props.userEmail}</div>
<div className="text-disabled-text">
{formatTimeOrDate(props.date as unknown as number, timezone)}
{formatTimeOrDate(props.note.createdAt as unknown as number, timezone)}
</div>
</div>
<div className="ml-auto cursor-pointer self-start" onClick={props.onClose}>
<Icon name="close" size={18} />
</div>
</div>
<div>{props.message}</div>
<div>{props.note.message}</div>
<div className="w-full">
<div className="flex items-center gap-2 flex-wrap w-full">
{props.tags.length ? (
<div className="flex items-center gap-1">
{props.tags.map((tag) => (
{props.note.tag ? (
<div
style={{ background: tagProps[tag], userSelect: 'none' }}
style={{ background: tagProps[props.note.tag], userSelect: 'none' }}
className="rounded-xl text-sm px-2 py-1 text-white"
>
{tag}
{props.note.tag}
</div>
))}
</div>
) : null}
{!props.isPublic ? null : (
{!props.note.isPublic ? null : (
<>
<Icon name="user-friends" className="ml-2 mr-1" color="gray-dark" /> Team
</>

View file

@ -12,7 +12,7 @@ type ValueObject = {
interface Props<Value extends ValueObject> {
options: Value[];
isSearchable?: boolean;
defaultValue?: string;
defaultValue?: string | number;
plain?: boolean;
components?: any;
styles?: any;
@ -103,7 +103,7 @@ export default function<Value extends ValueObject>({ placeholder='Select', name
singleValue: (provided: any, state: { isDisabled: any; }) => {
const opacity = state.isDisabled ? 0.5 : 1;
const transition = 'opacity 300ms';
return { ...provided, opacity, transition, fontWeight: '500' };
},
input: (provided: any) => ({
@ -160,13 +160,13 @@ const DropdownIndicator = (
const CustomValueContainer = ({ children, ...rest }: any) => {
const selectedCount = rest.getValue().length
const conditional = (selectedCount < 3)
let firstChild: any = []
if (!conditional) {
firstChild = [children[0].shift(), children[1]]
}
return (
<ValueContainer {...rest}>
{conditional ? children : firstChild}

View file

@ -1,7 +1,7 @@
import React from 'react';
import { Icon, Link } from 'UI';
import PlayLink from 'Shared/SessionItem/PlayLink';
import { tagProps, iTag } from 'App/services/NotesService';
import { tagProps, Note } from 'App/services/NotesService';
import { formatTimeOrDate } from 'App/date';
import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite';
@ -11,14 +11,7 @@ import { toast } from 'react-toastify';
import { session } from 'App/routes';
interface Props {
userId: number;
timestamp: number;
tags: iTag[];
isPublic: boolean;
description: string;
sessionId: string;
date: string;
noteId: number;
note: Note;
userEmail: string;
}
@ -29,13 +22,13 @@ function NoteItem(props: Props) {
const onCopy = () => {
copy(
`${window.location.origin}/${window.location.pathname.split('/')[1]}${session(
props.sessionId
)}${props.timestamp > 0 ? '?jumpto=' + props.timestamp : ''}`
props.note.sessionId
)}${props.note.timestamp > 0 ? '?jumpto=' + props.note.timestamp : ''}`
);
toast.success('Note URL copied to clipboard');
};
const onDelete = () => {
notesStore.deleteNote(props.noteId).then((r) => {
notesStore.deleteNote(props.note.noteId).then((r) => {
notesStore.fetchNotes();
toast.success('Note deleted');
});
@ -49,32 +42,36 @@ function NoteItem(props: Props) {
className="flex items-center p-4 border-b"
style={{ background: 'rgba(253, 243, 155, 0.1)' }}
>
<Link style={{ width: '90%' }} to={session(props.sessionId)+(props.timestamp > 0 ? `?jumpto=${props.timestamp}&note=${props.noteId}` : '')}>
<Link
style={{ width: '90%' }}
to={
session(props.note.sessionId) +
(props.note.timestamp > 0
? `?jumpto=${props.note.timestamp}&note=${props.note.noteId}`
: '')
}
>
<div className="flex flex-col gap-1 cursor-pointer">
<div>{props.description}</div>
<div>{props.note.message}</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>
))}
{props.note.tag ? (
<div
style={{
background: tagProps[props.note.tag],
userSelect: 'none',
minWidth: 60,
textAlign: 'center',
}}
className="rounded-full px-2 py-1 mr-2 text-white"
>
{props.note.tag}
</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 : (
{props.userEmail},{' '}
{formatTimeOrDate(props.note.createdAt as unknown as number, timezone)}
{!props.note.isPublic ? null : (
<>
<Icon name="user-friends" className="ml-4 mr-1" color="gray-dark" /> Team
</>
@ -84,13 +81,7 @@ function NoteItem(props: Props) {
</div>
</Link>
<div className="ml-auto">
<PlayLink
isAssist={false}
viewed={false}
sessionId={
props.sessionId
}
/>
<PlayLink isAssist={false} viewed={false} sessionId={props.note.sessionId} />
</div>
<div className="ml-2 cursor-pointer">
<ItemMenu bold items={menuItems} />

View file

@ -5,16 +5,16 @@ import NoteItem from './NoteItem';
import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore';
function NotesList({ members }: {members: Array<Record<string, any>>}) {
const { notesStore } = useStore()
function NotesList({ members }: { members: Array<Record<string, any>> }) {
const { notesStore } = useStore();
React.useEffect(() => {
if (!notesStore.notes.length) {
notesStore.fetchNotes()
notesStore.fetchNotes();
}
}, [])
}, []);
const list = notesStore.notes
const list = notesStore.notes;
return (
<Loader loading={notesStore.loading}>
@ -28,18 +28,11 @@ function NotesList({ members }: {members: Array<Record<string, any>>}) {
}
>
<div className="border-b rounded bg-white">
{sliceListPerPage(list, notesStore.page - 1, notesStore.pageSize).map(note => (
{sliceListPerPage(list, notesStore.page - 1, notesStore.pageSize).map((note) => (
<React.Fragment key={note.noteId}>
<NoteItem
userId={note.userId}
tags={note.tags}
timestamp={note.timestamp}
isPublic={note.isPublic}
description={note.message}
date={note.createdAt}
noteId={note.noteId}
sessionId={note.sessionId}
userEmail={members.find(m => m.id === note.userId)?.email || note.userId}
note={note}
userEmail={members.find((m) => m.id === note.userId)?.email || note.userId}
/>
</React.Fragment>
))}
@ -47,7 +40,8 @@ function NotesList({ members }: {members: Array<Record<string, any>>}) {
<div className="w-full flex items-center justify-between py-4 px-6">
<div className="text-disabled-text">
Showing <span className="font-semibold">{Math.min(list.length, notesStore.pageSize)}</span> out
Showing{' '}
<span className="font-semibold">{Math.min(list.length, notesStore.pageSize)}</span> out
of <span className="font-semibold">{list.length}</span> notes
</div>
<Pagination

View file

@ -10,10 +10,10 @@ const sortOptionsMap = {
'createdAt-ASC': 'Oldest',
};
const sortOptions = Object.entries(sortOptionsMap).map(([value, label]) => ({ value, label }));
const notesOwner = [{ value: '0', label: 'All Notes'},{ value: '1', label: 'My Notes'}]
function NoteTags() {
const { notesStore } = useStore()
const defaultOption = sortOptions[0].value;
return (
<div className="flex items-center">
@ -34,7 +34,9 @@ function NoteTags() {
</div>
))}
<div className="ml-2" />
<Select name="sortSessions" plain right options={sortOptions} onChange={({ value }) => notesStore.toggleSort(value.value)} defaultValue={defaultOption} />
<Select name="sortNotes" plain right options={sortOptions} onChange={({ value }) => notesStore.toggleSort(value.value)} defaultValue={sortOptions[0].value} />
<div className="ml-2" />
<Select name="notesOwner" plain right options={notesOwner} onChange={({ value }) => notesStore.toggleShared(value.value === '1')} defaultValue={notesOwner[0].value} />
</div>
);
}

View file

@ -15,6 +15,7 @@ export default class NotesStore {
activeTags: iTag[] = []
sort = 'createdAt'
order: 'DESC' | 'ASC' = 'DESC'
ownOnly = false
constructor() {
makeAutoObservable(this)
@ -27,6 +28,8 @@ export default class NotesStore {
sort: this.sort,
order: this.order,
tags: this.activeTags,
mineOnly: this.ownOnly,
sharedOnly: false
}
this.loading = true
@ -110,6 +113,11 @@ export default class NotesStore {
}
}
toggleShared(ownOnly: boolean) {
this.ownOnly = ownOnly
this.fetchNotes()
}
toggleSort(sort: string) {
const sortOrder = sort.split('-')[1]
// @ts-ignore

View file

@ -6,7 +6,6 @@ export const tagProps = {
'ISSUE': '#CC0000',
'TASK': '#7986CB',
'OTHER': 'rgba(0, 0, 0, 0.26)',
'ALL': ''
}
export type iTag = keyof typeof tagProps | "ALL"
@ -15,7 +14,7 @@ export const TAGS = Object.keys(tagProps) as unknown as (keyof typeof tagProps)[
export interface WriteNote {
message: string
tags: string[]
tag: iTag
isPublic: boolean
timestamp: number
noteId?: string
@ -30,7 +29,7 @@ export interface Note {
noteId: number
projectId: number
sessionId: string
tags: iTag[]
tag: iTag
timestamp: number
userId: number
}
@ -41,6 +40,8 @@ export interface NotesFilter {
sort: string
order: 'DESC' | 'ASC'
tags: iTag[]
sharedOnly: boolean
mineOnly: boolean
}
export default class NotesService {