mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-21 17:26:29 -05:00
Context menu to each history entry (#171)
- added entry-menu - added subsection in entry-menu with the location of the history entry and the action to remove an entry from history - added uploadAll functionality - show uploadAll Button in history only if the user is logged in - added deleteNote api call Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
parent
107a8eeaaf
commit
72a161ea16
16 changed files with 238 additions and 110 deletions
|
@ -26,6 +26,22 @@
|
|||
"setHistory": {
|
||||
"title": "Upload History Error",
|
||||
"text": "While trying to upload the history to the server an error occurred"
|
||||
},
|
||||
"deleteNote": {
|
||||
"title": "Delete Note Error",
|
||||
"text": "While trying to delete a note on the server an error occurred"
|
||||
},
|
||||
"updateEntry": {
|
||||
"title": "Update History Entry Error",
|
||||
"text": "While trying to update a history entry on the server an error occurred"
|
||||
},
|
||||
"deleteEntry": {
|
||||
"title": "Delete History Entry Error",
|
||||
"text": "While trying to delete a history entry on the server an error occurred"
|
||||
},
|
||||
"notFoundEntry": {
|
||||
"title": "History Entry not found",
|
||||
"text": "We can't find the history entry you requested."
|
||||
}
|
||||
},
|
||||
"noHistory": "No history",
|
||||
|
@ -40,7 +56,8 @@
|
|||
"export": "Export history",
|
||||
"import": "Import history",
|
||||
"clear": "Clear history",
|
||||
"refresh": "Refresh history"
|
||||
"refresh": "Refresh history",
|
||||
"uploadAll": "Sync the complete history to the server"
|
||||
},
|
||||
"modal": {
|
||||
"clearHistory": {
|
||||
|
@ -60,6 +77,13 @@
|
|||
"actions": "Actions",
|
||||
"tags": "Tags",
|
||||
"lastVisit": "Last Visit"
|
||||
},
|
||||
"menu": {
|
||||
"recentNotes": "Recent notes",
|
||||
"entryLocal": "Saved in your browser history",
|
||||
"entryRemote": "Saved in your user history",
|
||||
"removeEntry": "Remove from history",
|
||||
"deleteNote": "Delete note"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
|
|
10
src/api/note.ts
Normal file
10
src/api/note.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { expectResponseCode, getBackendUrl } from '../utils/apiUtils'
|
||||
import { defaultFetchConfig } from './default'
|
||||
|
||||
export const deleteNote = async (noteId: string): Promise<void> => {
|
||||
const response = await fetch(getBackendUrl() + `/notes/${noteId}`, {
|
||||
...defaultFetchConfig,
|
||||
method: 'DELETE'
|
||||
})
|
||||
expectResponseCode(response)
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
.history-close {
|
||||
.fa {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:hover .fa {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
import React from 'react'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon'
|
||||
import './close-button.scss'
|
||||
|
||||
export interface CloseButtonProps {
|
||||
isDark: boolean;
|
||||
className?: string
|
||||
}
|
||||
|
||||
const CloseButton: React.FC<CloseButtonProps> = ({ isDark, className }) => {
|
||||
return (
|
||||
<Button variant={isDark ? 'secondary' : 'light'} className={`history-close ${className || ''}`}>
|
||||
<ForkAwesomeIcon icon="times"/>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export { CloseButton }
|
10
src/components/landing/pages/history/common/entry-menu.scss
Normal file
10
src/components/landing/pages/history/common/entry-menu.scss
Normal file
|
@ -0,0 +1,10 @@
|
|||
.history-menu {
|
||||
|
||||
.fa, &::after {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:hover .fa, &:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
59
src/components/landing/pages/history/common/entry-menu.tsx
Normal file
59
src/components/landing/pages/history/common/entry-menu.tsx
Normal file
|
@ -0,0 +1,59 @@
|
|||
import React from 'react'
|
||||
import { Dropdown } from 'react-bootstrap'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon'
|
||||
import { ShowIf } from '../../../../common/show-if/show-if'
|
||||
import { HistoryEntryOrigin } from '../history'
|
||||
import './entry-menu.scss'
|
||||
|
||||
export interface EntryMenuProps {
|
||||
id: string;
|
||||
location: HistoryEntryOrigin
|
||||
isDark: boolean;
|
||||
onRemove: () => void
|
||||
onDelete: () => void
|
||||
className?: string
|
||||
}
|
||||
|
||||
const EntryMenu: React.FC<EntryMenuProps> = ({ id, location, isDark, onRemove, onDelete, className }) => {
|
||||
return (
|
||||
<Dropdown className={className || ''}>
|
||||
<Dropdown.Toggle size="sm" variant={isDark ? 'secondary' : 'light'} id={`dropdown-card-${id}`} className='history-menu d-flex align-items-center'>
|
||||
<ForkAwesomeIcon icon="ellipsis-h" className='history-menu'/>
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu>
|
||||
|
||||
<Dropdown.Header>
|
||||
<Trans i18nKey="landing.history.menu.recentNotes"/>
|
||||
</Dropdown.Header>
|
||||
|
||||
<ShowIf condition={location === HistoryEntryOrigin.LOCAL}>
|
||||
<Dropdown.Item disabled>
|
||||
<ForkAwesomeIcon icon="laptop" fixedWidth={true} className="mx-2"/>
|
||||
<Trans i18nKey="landing.history.menu.entryLocal"/>
|
||||
</Dropdown.Item>
|
||||
</ShowIf>
|
||||
<ShowIf condition={location === HistoryEntryOrigin.REMOTE}>
|
||||
<Dropdown.Item disabled>
|
||||
<ForkAwesomeIcon icon="cloud" fixedWidth={true} className="mx-2"/>
|
||||
<Trans i18nKey="landing.history.menu.entryRemote"/>
|
||||
</Dropdown.Item>
|
||||
</ShowIf>
|
||||
<Dropdown.Item onClick={onRemove}>
|
||||
<ForkAwesomeIcon icon="archive" fixedWidth={true} className="mx-2"/>
|
||||
<Trans i18nKey="landing.history.menu.removeEntry"/>
|
||||
</Dropdown.Item>
|
||||
|
||||
<Dropdown.Divider/>
|
||||
|
||||
<Dropdown.Item onClick={onDelete}>
|
||||
<ForkAwesomeIcon icon="trash" fixedWidth={true} className="mx-2"/>
|
||||
<Trans i18nKey="landing.history.menu.deleteNote"/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
|
||||
export { EntryMenu }
|
|
@ -1,10 +0,0 @@
|
|||
.sync-icon {
|
||||
.fa {
|
||||
opacity: 0.2;
|
||||
transition: opacity 0.2s ease-in-out, color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
&:hover .fa {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import React from 'react'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon'
|
||||
import { HistoryEntryOrigin } from '../history'
|
||||
import './sync-status.scss'
|
||||
|
||||
export interface SyncStatusProps {
|
||||
isDark: boolean
|
||||
location: HistoryEntryOrigin
|
||||
onSync: () => void
|
||||
className?: string
|
||||
}
|
||||
|
||||
export const SyncStatus: React.FC<SyncStatusProps> = ({ isDark, location, onSync, className }) => {
|
||||
const icon = location === HistoryEntryOrigin.REMOTE ? 'cloud' : 'laptop'
|
||||
return (
|
||||
<Button variant={isDark ? 'secondary' : 'light'} onClick={onSync} className={`sync-icon ${className || ''}`}>
|
||||
<ForkAwesomeIcon icon={icon}/>
|
||||
</Button>
|
||||
)
|
||||
}
|
|
@ -4,7 +4,7 @@ import { Pager } from '../../../../common/pagination/pager'
|
|||
import { HistoryEntriesProps } from '../history-content/history-content'
|
||||
import { HistoryCard } from './history-card'
|
||||
|
||||
export const HistoryCardList: React.FC<HistoryEntriesProps> = ({ entries, onPinClick, onSyncClick, pageIndex, onLastPageIndexChange }) => {
|
||||
export const HistoryCardList: React.FC<HistoryEntriesProps> = ({ entries, onPinClick, onRemoveClick, onDeleteClick, pageIndex, onLastPageIndexChange }) => {
|
||||
return (
|
||||
<Row className="justify-content-start">
|
||||
<Pager numberOfElementsPerPage={9} pageIndex={pageIndex} onLastPageIndexChange={onLastPageIndexChange}>
|
||||
|
@ -14,7 +14,8 @@ export const HistoryCardList: React.FC<HistoryEntriesProps> = ({ entries, onPinC
|
|||
key={entry.id}
|
||||
entry={entry}
|
||||
onPinClick={onPinClick}
|
||||
onSyncClick={onSyncClick}
|
||||
onRemoveClick={onRemoveClick}
|
||||
onDeleteClick={onDeleteClick}
|
||||
/>))
|
||||
}
|
||||
</Pager>
|
||||
|
|
|
@ -3,21 +3,18 @@ import React from 'react'
|
|||
import { Badge, Card } from 'react-bootstrap'
|
||||
import { formatHistoryDate } from '../../../../../utils/historyUtils'
|
||||
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon'
|
||||
import { CloseButton } from '../common/close-button'
|
||||
import { EntryMenu } from '../common/entry-menu'
|
||||
import { PinButton } from '../common/pin-button'
|
||||
import { SyncStatus } from '../common/sync-status'
|
||||
import { HistoryEntryProps } from '../history-content/history-content'
|
||||
import './history-card.scss'
|
||||
|
||||
export const HistoryCard: React.FC<HistoryEntryProps> = ({ entry, onPinClick, onSyncClick }) => {
|
||||
export const HistoryCard: React.FC<HistoryEntryProps> = ({ entry, onPinClick, onRemoveClick }) => {
|
||||
return (
|
||||
<div className="p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4">
|
||||
<Card className="card-min-height" text={'dark'} bg={'light'}>
|
||||
<Card.Body className="p-2 d-flex flex-row justify-content-between">
|
||||
<div className={'d-flex flex-column'}>
|
||||
<PinButton isDark={false} isPinned={entry.pinned} onPinClick={() => onPinClick(entry.id)}/>
|
||||
<SyncStatus isDark={false} location={entry.location} onSync={() => onSyncClick(entry.id)}
|
||||
className={'mt-1'}/>
|
||||
<PinButton isDark={false} isPinned={entry.pinned} onPinClick={() => onPinClick(entry.id, entry.location)}/>
|
||||
</div>
|
||||
<div className={'d-flex flex-column justify-content-between'}>
|
||||
<Card.Title className="m-0 mt-1dot5">{entry.title}</Card.Title>
|
||||
|
@ -34,7 +31,13 @@ export const HistoryCard: React.FC<HistoryEntryProps> = ({ entry, onPinClick, on
|
|||
</div>
|
||||
</div>
|
||||
<div className={'d-flex flex-column'}>
|
||||
<CloseButton isDark={false}/>
|
||||
<EntryMenu
|
||||
id={entry.id}
|
||||
location={entry.location}
|
||||
isDark={false}
|
||||
onRemove={() => onRemoveClick(entry.id, entry.location)}
|
||||
onDelete={() => onRemoveClick(entry.id, entry.location)}
|
||||
/>
|
||||
</div>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
|
|
|
@ -1,34 +1,39 @@
|
|||
import React, { Fragment, useState } from 'react'
|
||||
import { Alert, Row } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { LocatedHistoryEntry } from '../history'
|
||||
import { PagerPagination } from '../../../../common/pagination/pager-pagination'
|
||||
import { HistoryEntryOrigin, LocatedHistoryEntry } from '../history'
|
||||
import { HistoryCardList } from '../history-card/history-card-list'
|
||||
import { HistoryTable } from '../history-table/history-table'
|
||||
import { ViewStateEnum } from '../history-toolbar/history-toolbar'
|
||||
|
||||
type OnEntryClick = (entryId: string, location: HistoryEntryOrigin) => void
|
||||
|
||||
export interface HistoryContentProps {
|
||||
viewState: ViewStateEnum
|
||||
entries: LocatedHistoryEntry[]
|
||||
onPinClick: (entryId: string) => void
|
||||
onSyncClick: (entryId: string) => void
|
||||
onPinClick: OnEntryClick
|
||||
onRemoveClick: OnEntryClick
|
||||
onDeleteClick: OnEntryClick
|
||||
}
|
||||
|
||||
export interface HistoryEntryProps {
|
||||
entry: LocatedHistoryEntry,
|
||||
onPinClick: (entryId: string) => void
|
||||
onSyncClick: (entryId: string) => void
|
||||
onPinClick: OnEntryClick
|
||||
onRemoveClick: OnEntryClick
|
||||
onDeleteClick: OnEntryClick
|
||||
}
|
||||
|
||||
export interface HistoryEntriesProps {
|
||||
entries: LocatedHistoryEntry[]
|
||||
onPinClick: (entryId: string) => void
|
||||
onSyncClick: (entryId: string) => void
|
||||
onPinClick: OnEntryClick
|
||||
onRemoveClick: OnEntryClick
|
||||
onDeleteClick: OnEntryClick
|
||||
pageIndex: number
|
||||
onLastPageIndexChange: (lastPageIndex: number) => void
|
||||
}
|
||||
|
||||
export const HistoryContent: React.FC<HistoryContentProps> = ({ viewState, entries, onPinClick, onSyncClick }) => {
|
||||
export const HistoryContent: React.FC<HistoryContentProps> = ({ viewState, entries, onPinClick, onRemoveClick, onDeleteClick }) => {
|
||||
useTranslation()
|
||||
const [pageIndex, setPageIndex] = useState(0)
|
||||
const [lastPageIndex, setLastPageIndex] = useState(0)
|
||||
|
@ -49,11 +54,16 @@ export const HistoryContent: React.FC<HistoryContentProps> = ({ viewState, entri
|
|||
case ViewStateEnum.CARD:
|
||||
return <HistoryCardList entries={entries}
|
||||
onPinClick={onPinClick}
|
||||
onSyncClick={onSyncClick}
|
||||
onRemoveClick={onRemoveClick}
|
||||
onDeleteClick={onDeleteClick}
|
||||
pageIndex={pageIndex}
|
||||
onLastPageIndexChange={setLastPageIndex}/>
|
||||
case ViewStateEnum.TABLE:
|
||||
return <HistoryTable entries={entries} onPinClick={onPinClick} onSyncClick={onSyncClick} pageIndex={pageIndex}
|
||||
return <HistoryTable entries={entries}
|
||||
onPinClick={onPinClick}
|
||||
onRemoveClick={onRemoveClick}
|
||||
onDeleteClick={onDeleteClick}
|
||||
pageIndex={pageIndex}
|
||||
onLastPageIndexChange={setLastPageIndex}/>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import React from 'react'
|
||||
import { Badge } from 'react-bootstrap'
|
||||
import { formatHistoryDate } from '../../../../../utils/historyUtils'
|
||||
import { CloseButton } from '../common/close-button'
|
||||
import { EntryMenu } from '../common/entry-menu'
|
||||
import { PinButton } from '../common/pin-button'
|
||||
import { SyncStatus } from '../common/sync-status'
|
||||
import { HistoryEntryProps } from '../history-content/history-content'
|
||||
|
||||
export const HistoryTableRow: React.FC<HistoryEntryProps> = ({ entry, onPinClick, onSyncClick }) => {
|
||||
export const HistoryTableRow: React.FC<HistoryEntryProps> = ({ entry, onPinClick, onRemoveClick }) => {
|
||||
return (
|
||||
<tr>
|
||||
<td>{entry.title}</td>
|
||||
|
@ -18,9 +17,14 @@ export const HistoryTableRow: React.FC<HistoryEntryProps> = ({ entry, onPinClick
|
|||
}
|
||||
</td>
|
||||
<td>
|
||||
<SyncStatus isDark={true} location={entry.location} onSync={() => onSyncClick(entry.id)} className={'mb-1 mr-1'}/>
|
||||
<PinButton isDark={true} isPinned={entry.pinned} onPinClick={() => onPinClick(entry.id)} className={'mb-1 mr-1'}/>
|
||||
<CloseButton isDark={true} className={'mb-1 mr-1'}/>
|
||||
<PinButton isDark={true} isPinned={entry.pinned} onPinClick={() => onPinClick(entry.id, entry.location)} className={'mb-1 mr-1'}/>
|
||||
<EntryMenu
|
||||
id={entry.id}
|
||||
location={entry.location}
|
||||
isDark={true}
|
||||
onRemove={() => onRemoveClick(entry.id, entry.location)}
|
||||
onDelete={() => onRemoveClick(entry.id, entry.location)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ import { HistoryEntriesProps } from '../history-content/history-content'
|
|||
import { HistoryTableRow } from './history-table-row'
|
||||
import './history-table.scss'
|
||||
|
||||
export const HistoryTable: React.FC<HistoryEntriesProps> = ({ entries, onPinClick, onSyncClick, pageIndex, onLastPageIndexChange }) => {
|
||||
export const HistoryTable: React.FC<HistoryEntriesProps> = ({ entries, onPinClick, onRemoveClick, onDeleteClick, pageIndex, onLastPageIndexChange }) => {
|
||||
useTranslation()
|
||||
return (
|
||||
<Table striped bordered hover size="sm" variant="dark" className={'history-table'}>
|
||||
|
@ -26,7 +26,8 @@ export const HistoryTable: React.FC<HistoryEntriesProps> = ({ entries, onPinClic
|
|||
key={entry.id}
|
||||
entry={entry}
|
||||
onPinClick={onPinClick}
|
||||
onSyncClick={onSyncClick}
|
||||
onRemoveClick={onRemoveClick}
|
||||
onDeleteClick={onDeleteClick}
|
||||
/>)
|
||||
}
|
||||
</Pager>
|
||||
|
|
|
@ -2,7 +2,10 @@ import React, { ChangeEvent, useEffect, useState } from 'react'
|
|||
import { Button, Form, FormControl, InputGroup, ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
|
||||
import { Typeahead } from 'react-bootstrap-typeahead'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { ApplicationState } from '../../../../../redux'
|
||||
import { ForkAwesomeIcon } from '../../../../common/fork-awesome/fork-awesome-icon'
|
||||
import { ShowIf } from '../../../../common/show-if/show-if'
|
||||
import { SortButton, SortModeEnum } from '../../../../common/sort-button/sort-button'
|
||||
import { HistoryEntry } from '../history'
|
||||
import { ClearHistoryButton } from './clear-history-button'
|
||||
|
@ -32,6 +35,7 @@ export interface HistoryToolbarProps {
|
|||
onRefreshHistory: () => void
|
||||
onExportHistory: () => void
|
||||
onImportHistory: (entries: HistoryEntry[]) => void
|
||||
onUploadAll: () => void
|
||||
}
|
||||
|
||||
export const initState: HistoryToolbarState = {
|
||||
|
@ -42,9 +46,10 @@ export const initState: HistoryToolbarState = {
|
|||
selectedTags: []
|
||||
}
|
||||
|
||||
export const HistoryToolbar: React.FC<HistoryToolbarProps> = ({ onSettingsChange, tags, onClearHistory, onRefreshHistory, onExportHistory, onImportHistory }) => {
|
||||
export const HistoryToolbar: React.FC<HistoryToolbarProps> = ({ onSettingsChange, tags, onClearHistory, onRefreshHistory, onExportHistory, onImportHistory, onUploadAll }) => {
|
||||
const [t] = useTranslation()
|
||||
const [state, setState] = useState<HistoryToolbarState>(initState)
|
||||
const user = useSelector((state: ApplicationState) => state.user)
|
||||
|
||||
const titleSortChanged = (direction: SortModeEnum) => {
|
||||
setState(prevState => ({
|
||||
|
@ -113,6 +118,13 @@ export const HistoryToolbar: React.FC<HistoryToolbarProps> = ({ onSettingsChange
|
|||
<ForkAwesomeIcon icon='refresh'/>
|
||||
</Button>
|
||||
</InputGroup>
|
||||
<ShowIf condition={!!user}>
|
||||
<InputGroup className={'mr-1 mb-1'}>
|
||||
<Button variant={'light'} title={t('landing.history.toolbar.uploadAll')} onClick={onUploadAll}>
|
||||
<ForkAwesomeIcon icon='cloud-upload'/>
|
||||
</Button>
|
||||
</InputGroup>
|
||||
</ShowIf>
|
||||
<InputGroup className={'mr-1 mb-1'}>
|
||||
<ToggleButtonGroup type="radio" name="options" dir='ltr' value={state.viewState} className={'button-height'}
|
||||
onChange={(newViewState: ViewStateEnum) => {
|
||||
|
|
|
@ -2,7 +2,8 @@ import React, { Fragment, useCallback, useEffect, useMemo, useState } from 'reac
|
|||
import { Row } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { deleteHistory, getHistory, setHistory } from '../../../../api/history'
|
||||
import { deleteHistory, deleteHistoryEntry, getHistory, setHistory, updateHistoryEntry } from '../../../../api/history'
|
||||
import { deleteNote } from '../../../../api/note'
|
||||
import { ApplicationState } from '../../../../redux'
|
||||
import {
|
||||
collectEntries,
|
||||
|
@ -65,10 +66,9 @@ export const History: React.FC = () => {
|
|||
.then(() => setRemoteHistoryEntries(entries))
|
||||
.catch(() => setError('setHistory'))
|
||||
} else {
|
||||
historyWrite(entries)
|
||||
setLocalHistoryEntries(entries)
|
||||
}
|
||||
}, [historyWrite, user])
|
||||
}, [user])
|
||||
|
||||
const refreshHistory = useCallback(() => {
|
||||
const localHistory = loadHistoryFromLocalStore()
|
||||
|
@ -102,22 +102,72 @@ export const History: React.FC = () => {
|
|||
historyWrite([])
|
||||
}, [historyWrite, user])
|
||||
|
||||
const syncClick = useCallback((entryId: string): void => {
|
||||
console.log(entryId)
|
||||
// ToDo: add syncClick
|
||||
const uploadAll = useCallback((): void => {
|
||||
const newHistory = mergeEntryArrays(localHistoryEntries, remoteHistoryEntries)
|
||||
if (user) {
|
||||
setHistory(newHistory)
|
||||
.then(() => {
|
||||
setRemoteHistoryEntries(newHistory)
|
||||
setLocalHistoryEntries([])
|
||||
historyWrite([])
|
||||
})
|
||||
.catch(() => setError('setHistory'))
|
||||
}
|
||||
}, [historyWrite, localHistoryEntries, remoteHistoryEntries, user])
|
||||
|
||||
const deleteClick = useCallback((entryId: string, location: HistoryEntryOrigin): void => {
|
||||
if (user) {
|
||||
deleteNote(entryId)
|
||||
.then(() => {
|
||||
if (location === HistoryEntryOrigin.LOCAL) {
|
||||
setLocalHistoryEntries(entries => entries.filter(entry => entry.id !== entryId))
|
||||
} else if (location === HistoryEntryOrigin.REMOTE) {
|
||||
setRemoteHistoryEntries(entries => entries.filter(entry => entry.id !== entryId))
|
||||
}
|
||||
})
|
||||
.catch(() => setError('deleteNote'))
|
||||
}
|
||||
}, [user])
|
||||
|
||||
const removeClick = useCallback((entryId: string, location: HistoryEntryOrigin): void => {
|
||||
if (location === HistoryEntryOrigin.LOCAL) {
|
||||
setLocalHistoryEntries((entries) => entries.filter(entry => entry.id !== entryId))
|
||||
} else if (location === HistoryEntryOrigin.REMOTE) {
|
||||
deleteHistoryEntry(entryId)
|
||||
.then(() => setRemoteHistoryEntries((entries) => entries.filter(entry => entry.id !== entryId)))
|
||||
.catch(() => setError('deleteEntry'))
|
||||
}
|
||||
}, [])
|
||||
|
||||
const pinClick = useCallback((entryId: string): void => {
|
||||
// ToDo: determine if entry is local or remote
|
||||
setLocalHistoryEntries((entries) => {
|
||||
return entries.map((entry) => {
|
||||
if (entry.id === entryId) {
|
||||
entry.pinned = !entry.pinned
|
||||
}
|
||||
return entry
|
||||
const pinClick = useCallback((entryId: string, location: HistoryEntryOrigin): void => {
|
||||
if (location === HistoryEntryOrigin.LOCAL) {
|
||||
setLocalHistoryEntries((entries) => {
|
||||
return entries.map((entry) => {
|
||||
if (entry.id === entryId) {
|
||||
entry.pinned = !entry.pinned
|
||||
}
|
||||
return entry
|
||||
})
|
||||
})
|
||||
})
|
||||
}, [])
|
||||
} else if (location === HistoryEntryOrigin.REMOTE) {
|
||||
const entry = remoteHistoryEntries.find(entry => entry.id === entryId)
|
||||
if (!entry) {
|
||||
setError('notFoundEntry')
|
||||
return
|
||||
}
|
||||
entry.pinned = !entry.pinned
|
||||
updateHistoryEntry(entryId, entry)
|
||||
.then(() => setRemoteHistoryEntries((entries) => {
|
||||
return entries.map((entry) => {
|
||||
if (entry.id === entryId) {
|
||||
entry.pinned = !entry.pinned
|
||||
}
|
||||
return entry
|
||||
})
|
||||
}))
|
||||
.catch(() => setError('updateEntry'))
|
||||
}
|
||||
}, [remoteHistoryEntries])
|
||||
|
||||
const resetError = () => {
|
||||
setError('')
|
||||
|
@ -159,12 +209,15 @@ export const History: React.FC = () => {
|
|||
onRefreshHistory={refreshHistory}
|
||||
onExportHistory={exportHistory}
|
||||
onImportHistory={importHistory}
|
||||
onUploadAll={uploadAll}
|
||||
/>
|
||||
</Row>
|
||||
<HistoryContent viewState={toolbarState.viewState}
|
||||
<HistoryContent
|
||||
viewState={toolbarState.viewState}
|
||||
entries={entriesToShow}
|
||||
onPinClick={pinClick}
|
||||
onSyncClick={syncClick}
|
||||
onRemoveClick={removeClick}
|
||||
onDeleteClick={deleteClick}
|
||||
/>
|
||||
</Fragment>
|
||||
)
|
||||
|
|
|
@ -29,13 +29,13 @@ function locateEntries (entries: HistoryEntry[], location: HistoryEntryOrigin):
|
|||
})
|
||||
}
|
||||
|
||||
export function mergeEntryArrays<T extends HistoryEntry> (locatedLocalEntries: T[], locatedRemoteEntries: T[]): T[] {
|
||||
const filteredLocalEntries = locatedLocalEntries.filter(localEntry => {
|
||||
const entry = locatedRemoteEntries.find(remoteEntry => remoteEntry.id === localEntry.id)
|
||||
export function mergeEntryArrays<T extends HistoryEntry> (localEntries: T[], remoteEntries: T[]): T[] {
|
||||
const filteredLocalEntries = localEntries.filter(localEntry => {
|
||||
const entry = remoteEntries.find(remoteEntry => remoteEntry.id === localEntry.id)
|
||||
return !entry
|
||||
})
|
||||
|
||||
return filteredLocalEntries.concat(locatedRemoteEntries)
|
||||
return filteredLocalEntries.concat(remoteEntries)
|
||||
}
|
||||
|
||||
function filterBySelectedTags (entries: LocatedHistoryEntry[], selectedTags: string[]): LocatedHistoryEntry[] {
|
||||
|
|
Loading…
Reference in a new issue