mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-23 03:27:05 -04:00
parent
4c785b345b
commit
d03e000bd1
48 changed files with 681 additions and 303 deletions
20
src/components/icon-button/icon-button.scss
Normal file
20
src/components/icon-button/icon-button.scss
Normal file
|
@ -0,0 +1,20 @@
|
|||
.btn-icon {
|
||||
padding: 0.375rem 0.375rem;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
|
||||
.icon-part {
|
||||
padding: 0.375rem 0.375rem;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
|
||||
.social-icon {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.text-part {
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
}
|
||||
|
24
src/components/icon-button/icon-button.tsx
Normal file
24
src/components/icon-button/icon-button.tsx
Normal file
|
@ -0,0 +1,24 @@
|
|||
import React from "react";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import "./icon-button.scss";
|
||||
import {IconProp} from "@fortawesome/fontawesome-svg-core";
|
||||
import {Button, ButtonProps} from "react-bootstrap";
|
||||
|
||||
export interface SocialButtonProps extends ButtonProps {
|
||||
icon: IconProp
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
export const IconButton: React.FC<SocialButtonProps> = ({icon, children, variant, onClick}) => {
|
||||
return (
|
||||
<Button variant={variant} className={"btn-icon p-0 d-inline-flex align-items-stretch"}
|
||||
onClick={() => onClick?.()}>
|
||||
<span className="icon-part d-flex align-items-center">
|
||||
<FontAwesomeIcon icon={icon} className={"icon"}/>
|
||||
</span>
|
||||
<span className="text-part d-flex align-items-center">
|
||||
{children}
|
||||
</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@import "../../../../../node_modules/bootstrap/scss/bootstrap";
|
||||
@import '../../../../../node_modules/react-bootstrap-typeahead/css/Typeahead';
|
||||
@import "font-pack";
|
||||
//@import "cover.scss";
|
||||
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import React from "react";
|
||||
import "./close-button.scss"
|
||||
import {Button} from "react-bootstrap";
|
||||
|
||||
const CloseButton: React.FC = () => {
|
||||
export interface CloseButtonProps {
|
||||
isDark: boolean;
|
||||
}
|
||||
|
||||
const CloseButton: React.FC<CloseButtonProps> = ({isDark}) => {
|
||||
return (
|
||||
<FontAwesomeIcon
|
||||
className="history-close"
|
||||
icon="times"
|
||||
/>
|
||||
<Button variant={isDark ? "secondary" : "light"}>
|
||||
<FontAwesomeIcon
|
||||
className="history-close"
|
||||
icon="times"
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export { CloseButton }
|
||||
export {CloseButton}
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import React from "react";
|
||||
import "./pin-button.scss"
|
||||
import {Button} from "react-bootstrap";
|
||||
|
||||
export interface PinButtonProps {
|
||||
pin: boolean;
|
||||
isPinned: boolean;
|
||||
onPinClick: () => void;
|
||||
isDark: boolean;
|
||||
}
|
||||
|
||||
const PinButton: React.FC<PinButtonProps> = ({pin, onPinClick}) => {
|
||||
export const PinButton: React.FC<PinButtonProps> = ({isPinned, onPinClick, isDark}) => {
|
||||
return (
|
||||
<FontAwesomeIcon
|
||||
icon="thumbtack"
|
||||
className={`history-pin ${pin ? 'active' : ''}`}
|
||||
onClick={onPinClick}
|
||||
/>
|
||||
<Button variant={isDark ? "secondary" : "light"}
|
||||
onClick={onPinClick}>
|
||||
<FontAwesomeIcon
|
||||
icon="thumbtack"
|
||||
className={`history-pin ${isPinned ? 'active' : ''}`}
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export { PinButton }
|
||||
|
|
|
@ -13,12 +13,12 @@ export const HistoryCard: React.FC<HistoryEntryProps> = ({entry, onPinClick}) =>
|
|||
return (
|
||||
<div className="p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4">
|
||||
<Card className="p-0" text={"dark"} bg={"light"}>
|
||||
<div className="d-flex justify-content-between p-2">
|
||||
<PinButton pin={entry.pinned} onPinClick={() => {
|
||||
<div className="d-flex justify-content-between p-2 align-items-start">
|
||||
<PinButton isDark={false} isPinned={entry.pinned} onPinClick={() => {
|
||||
onPinClick(entry.id)
|
||||
}}/>
|
||||
<Card.Title className="m-0 mt-3">{entry.title}</Card.Title>
|
||||
<CloseButton/>
|
||||
<CloseButton isDark={false}/>
|
||||
</div>
|
||||
<Card.Body>
|
||||
<div className="text-black-50">
|
||||
|
@ -26,7 +26,8 @@ export const HistoryCard: React.FC<HistoryEntryProps> = ({entry, onPinClick}) =>
|
|||
{formatHistoryDate(entry.lastVisited)}
|
||||
<div>
|
||||
{
|
||||
entry.tags.map((tag) => <Badge variant={"dark"} key={tag}>{tag}</Badge>)
|
||||
entry.tags.map((tag) => <Badge variant={"dark"} className={"mr-1 mb-1"}
|
||||
key={tag}>{tag}</Badge>)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import React from "react";
|
||||
import {HistoryEntry, pinClick, ViewStateEnum} from "../history";
|
||||
import {HistoryEntry, pinClick} from "../history";
|
||||
import {HistoryTable} from "../history-table/history-table";
|
||||
import {Alert} from "react-bootstrap";
|
||||
import {Trans} from "react-i18next";
|
||||
import {HistoryCardList} from "../history-card/history-card-list";
|
||||
import {ViewStateEnum} from "../history-toolbar/history-toolbar";
|
||||
|
||||
export interface HistoryContentProps {
|
||||
viewState: ViewStateEnum
|
||||
|
|
|
@ -12,11 +12,11 @@ export const HistoryTableRow: React.FC<HistoryEntryProps> = ({entry, onPinClick}
|
|||
<td>{entry.title}</td>
|
||||
<td>{formatHistoryDate(entry.lastVisited)}</td>
|
||||
<td>
|
||||
<PinButton pin={entry.pinned} onPinClick={() => {
|
||||
<PinButton isDark={true} isPinned={entry.pinned} onPinClick={() => {
|
||||
onPinClick(entry.id)
|
||||
}}/>
|
||||
|
||||
<CloseButton/>
|
||||
<CloseButton isDark={true}/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
|
|
@ -2,15 +2,16 @@ import React from "react";
|
|||
import {Table} from "react-bootstrap"
|
||||
import {HistoryTableRow} from "./history-table-row";
|
||||
import {HistoryEntriesProps} from "../history-content/history-content";
|
||||
import {Trans} from "react-i18next";
|
||||
|
||||
const HistoryTable: React.FC<HistoryEntriesProps> = ({entries, onPinClick}) => {
|
||||
return (
|
||||
<Table striped bordered hover size="sm" variant="dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Last visited</th>
|
||||
<th>Actions</th>
|
||||
<th><Trans i18nKey={"title"}/></th>
|
||||
<th><Trans i18nKey={"lastVisit"}/></th>
|
||||
<th><Trans i18nKey={"actions"}/></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
import {Button, Form, FormControl, InputGroup, ToggleButton, ToggleButtonGroup} from "react-bootstrap";
|
||||
import React, {ChangeEvent, useEffect, useState} from "react";
|
||||
import {Trans, useTranslation} from "react-i18next";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {SortButton, SortModeEnum} from "../../../../sort-button/sort-button";
|
||||
import {Typeahead} from 'react-bootstrap-typeahead';
|
||||
import "./typeahead-hacks.scss";
|
||||
|
||||
export type HistoryToolbarChange = (settings: HistoryToolbarState) => void;
|
||||
|
||||
export interface HistoryToolbarState {
|
||||
viewState: ViewStateEnum
|
||||
titleSortDirection: SortModeEnum
|
||||
lastVisitedSortDirection: SortModeEnum
|
||||
keywordSearch: string
|
||||
selectedTags: string[]
|
||||
}
|
||||
|
||||
export enum ViewStateEnum {
|
||||
card,
|
||||
table
|
||||
}
|
||||
|
||||
export interface HistoryToolbarProps {
|
||||
onSettingsChange: HistoryToolbarChange
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
export const initState: HistoryToolbarState = {
|
||||
viewState: ViewStateEnum.card,
|
||||
titleSortDirection: SortModeEnum.no,
|
||||
lastVisitedSortDirection: SortModeEnum.no,
|
||||
keywordSearch: "",
|
||||
selectedTags: []
|
||||
}
|
||||
|
||||
export const HistoryToolbar: React.FC<HistoryToolbarProps> = ({onSettingsChange, tags}) => {
|
||||
|
||||
const [t] = useTranslation()
|
||||
const [state, setState] = useState<HistoryToolbarState>(initState);
|
||||
|
||||
const titleSortChanged = (direction: SortModeEnum) => {
|
||||
setState(prevState => ({
|
||||
...prevState,
|
||||
titleSortDirection: direction,
|
||||
lastVisitedSortDirection: SortModeEnum.no
|
||||
}))
|
||||
}
|
||||
|
||||
const lastVisitedSortChanged = (direction: SortModeEnum) => {
|
||||
setState(prevState => ({
|
||||
...prevState,
|
||||
lastVisitedSortDirection: direction,
|
||||
titleSortDirection: SortModeEnum.no
|
||||
}))
|
||||
}
|
||||
|
||||
const keywordSearchChanged = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setState(prevState => ({...prevState, keywordSearch: event.currentTarget.value}));
|
||||
}
|
||||
|
||||
const toggleViewChanged = (newViewState: ViewStateEnum) => {
|
||||
setState((prevState) => ({...prevState, viewState: newViewState}))
|
||||
}
|
||||
|
||||
const selectedTagsChanged = (selected: string[]) => {
|
||||
setState((prevState => ({...prevState, selectedTags: selected})))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
onSettingsChange(state);
|
||||
}, [onSettingsChange, state])
|
||||
|
||||
return (
|
||||
<Form inline={true}>
|
||||
<InputGroup className={"mr-1"}>
|
||||
<Typeahead id={"tagsSelection"} options={tags} multiple={true} placeholder={t("selectTags")}
|
||||
onChange={selectedTagsChanged}/>
|
||||
</InputGroup>
|
||||
<InputGroup className={"mr-1"}>
|
||||
<FormControl
|
||||
placeholder={t("searchKeywords")}
|
||||
aria-label={t("searchKeywords")}
|
||||
onChange={keywordSearchChanged}
|
||||
/>
|
||||
</InputGroup>
|
||||
<InputGroup className={"mr-1"}>
|
||||
<SortButton onChange={titleSortChanged} direction={state.titleSortDirection} variant={"light"}><Trans
|
||||
i18nKey={"sortByTitle"}/></SortButton>
|
||||
</InputGroup>
|
||||
<InputGroup className={"mr-1"}>
|
||||
<SortButton onChange={lastVisitedSortChanged} direction={state.lastVisitedSortDirection}
|
||||
variant={"light"}><Trans i18nKey={"sortByLastVisited"}/></SortButton>
|
||||
</InputGroup>
|
||||
<InputGroup className={"mr-1"}>
|
||||
<Button variant={"light"} title={t("exportHistory")}>
|
||||
<FontAwesomeIcon icon={"download"}/>
|
||||
</Button>
|
||||
</InputGroup>
|
||||
<InputGroup className={"mr-1"}>
|
||||
<Button variant={"light"} title={t("importHistory")}>
|
||||
<FontAwesomeIcon icon={"upload"}/>
|
||||
</Button>
|
||||
</InputGroup>
|
||||
<InputGroup className={"mr-1"}>
|
||||
<Button variant={"light"} title={t("clearHistory")}>
|
||||
<FontAwesomeIcon icon={"trash"}/>
|
||||
</Button>
|
||||
</InputGroup>
|
||||
<InputGroup className={"mr-1"}>
|
||||
<Button variant={"light"} title={t("refreshHistory")}>
|
||||
<FontAwesomeIcon icon={"sync"}/>
|
||||
</Button>
|
||||
</InputGroup>
|
||||
<InputGroup className={"mr-1"}>
|
||||
<ToggleButtonGroup type="radio" name="options" value={state.viewState}
|
||||
onChange={(newViewState: ViewStateEnum) => {
|
||||
toggleViewChanged(newViewState)
|
||||
}}>
|
||||
<ToggleButton className={"btn-light"} value={ViewStateEnum.card}><Trans
|
||||
i18nKey={"cards"}/></ToggleButton>
|
||||
<ToggleButton className={"btn-light"} value={ViewStateEnum.table}><Trans
|
||||
i18nKey={"table"}/></ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</InputGroup>
|
||||
</Form>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
.rbt-input-multi {
|
||||
min-width: 200px !important;
|
||||
|
||||
.rbt-input-main {
|
||||
&[placeholder=""] {
|
||||
width: 10px !important;
|
||||
}
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
}
|
|
@ -1,12 +1,8 @@
|
|||
import React, {Fragment, useEffect, useState} from 'react'
|
||||
import {ToggleButton, ToggleButtonGroup} from 'react-bootstrap';
|
||||
import {HistoryContent} from './history-content/history-content';
|
||||
import {HistoryToolbar, HistoryToolbarState, initState as toolbarInitState} from './history-toolbar/history-toolbar';
|
||||
import {loadHistoryFromLocalStore, sortAndFilterEntries} from "../../../../utils/historyUtils";
|
||||
|
||||
export enum ViewStateEnum {
|
||||
card,
|
||||
table
|
||||
}
|
||||
import {Row} from 'react-bootstrap';
|
||||
|
||||
export interface HistoryEntry {
|
||||
id: string,
|
||||
|
@ -20,7 +16,7 @@ export type pinClick = (entryId: string) => void;
|
|||
|
||||
export const History: React.FC = () => {
|
||||
const [historyEntries, setHistoryEntries] = useState<HistoryEntry[]>([])
|
||||
const [viewState, setViewState] = useState<ViewStateEnum>(ViewStateEnum.card)
|
||||
const [viewState, setViewState] = useState<HistoryToolbarState>(toolbarInitState)
|
||||
|
||||
useEffect(() => {
|
||||
const history = loadHistoryFromLocalStore();
|
||||
|
@ -28,6 +24,9 @@ export const History: React.FC = () => {
|
|||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (historyEntries === []) {
|
||||
return;
|
||||
}
|
||||
window.localStorage.setItem("history", JSON.stringify(historyEntries));
|
||||
}, [historyEntries])
|
||||
|
||||
|
@ -42,16 +41,25 @@ export const History: React.FC = () => {
|
|||
})
|
||||
}
|
||||
|
||||
const tags = historyEntries.map(entry => entry.tags)
|
||||
.reduce((a, b) => ([...a, ...b]), [])
|
||||
.filter((value, index, array) => {
|
||||
if (index === 0) {
|
||||
return true;
|
||||
}
|
||||
return (value !== array[index - 1])
|
||||
})
|
||||
const entriesToShow = sortAndFilterEntries(historyEntries, viewState);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h1>History</h1>
|
||||
<ToggleButtonGroup type="radio" name="options" defaultValue={ViewStateEnum.card} className="mb-2"
|
||||
onChange={(newState: ViewStateEnum) => setViewState(newState)}>
|
||||
<ToggleButton value={ViewStateEnum.card}>Card</ToggleButton>
|
||||
<ToggleButton value={ViewStateEnum.table}>Table</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
<Row className={"justify-content-center mb-3"}>
|
||||
<HistoryToolbar onSettingsChange={setViewState} tags={tags}/>
|
||||
</Row>
|
||||
<div className="d-flex flex-wrap justify-content-center">
|
||||
<HistoryContent viewState={viewState} entries={sortAndFilterEntries(historyEntries)}
|
||||
<HistoryContent viewState={viewState.viewState}
|
||||
entries={entriesToShow}
|
||||
onPinClick={pinClick}/>
|
||||
</div>
|
||||
</Fragment>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.btn.btn-icon {
|
||||
.btn.social-link-button {
|
||||
color: #FFFFFF;
|
||||
|
||||
@mixin button($color) {
|
||||
|
@ -8,6 +8,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
.icon-part {
|
||||
padding: 0.375rem 0.375rem;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
|
||||
.social-icon {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.text-part {
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
&.btn-social-dropbox {
|
||||
@include button(#1087DD);
|
||||
}
|
||||
|
@ -33,16 +47,3 @@
|
|||
}
|
||||
}
|
||||
|
||||
.btn-social-button {
|
||||
padding: 0.375rem 0.375rem;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
|
||||
.social-icon {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-social-text {
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import "./icon-button.scss";
|
||||
import "./social-link-button.scss";
|
||||
import {IconProp} from "@fortawesome/fontawesome-svg-core";
|
||||
|
||||
export interface SocialButtonProps {
|
||||
|
@ -10,14 +10,14 @@ export interface SocialButtonProps {
|
|||
title?: string
|
||||
}
|
||||
|
||||
export const IconButton: React.FC<SocialButtonProps> = ({title, backgroundClass, href, icon, children}) => {
|
||||
export const SocialLinkButton: React.FC<SocialButtonProps> = ({title, backgroundClass, href, icon, children}) => {
|
||||
return (
|
||||
<a href={href} title={title}
|
||||
className={"btn btn-icon p-0 d-inline-flex align-items-stretch " + backgroundClass}>
|
||||
<span className="btn-social-button d-flex align-items-center">
|
||||
className={"btn social-link-button p-0 d-inline-flex align-items-stretch " + backgroundClass}>
|
||||
<span className="icon-part d-flex align-items-center">
|
||||
<FontAwesomeIcon icon={icon} className={"social-icon"}/>
|
||||
</span>
|
||||
<span className="btn-social-text d-flex align-items-center">
|
||||
<span className="text-part d-flex align-items-center">
|
||||
{children}
|
||||
</span>
|
||||
</a>
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import {IconProp} from "@fortawesome/fontawesome-svg-core";
|
||||
import {IconButton} from "./icon-button/icon-button";
|
||||
import {SocialLinkButton} from "./social-link-button/social-link-button";
|
||||
|
||||
export enum OneClickType {
|
||||
'DROPBOX'="dropbox",
|
||||
|
@ -101,14 +101,14 @@ const ViaOneClick: React.FC<ViaOneClickProps> = ({oneClickType, optionalName}) =
|
|||
const {name, icon, className, url} = getMetadata(oneClickType);
|
||||
const text = !!optionalName ? optionalName : name;
|
||||
return (
|
||||
<IconButton
|
||||
<SocialLinkButton
|
||||
backgroundClass={className}
|
||||
icon={icon}
|
||||
href={url}
|
||||
title={text}
|
||||
>
|
||||
{text}
|
||||
</IconButton>
|
||||
</SocialLinkButton>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
47
src/components/sort-button/sort-button.tsx
Normal file
47
src/components/sort-button/sort-button.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import React from "react";
|
||||
import {IconProp} from "@fortawesome/fontawesome-svg-core";
|
||||
import {ButtonProps} from "react-bootstrap";
|
||||
import {IconButton} from "../icon-button/icon-button";
|
||||
|
||||
export enum SortModeEnum {
|
||||
up = 1,
|
||||
down = -1,
|
||||
no = 0
|
||||
}
|
||||
|
||||
const getIcon = (direction: SortModeEnum): IconProp => {
|
||||
switch (direction) {
|
||||
default:
|
||||
case SortModeEnum.no:
|
||||
return "sort";
|
||||
case SortModeEnum.up:
|
||||
return "sort-up";
|
||||
case SortModeEnum.down:
|
||||
return "sort-down";
|
||||
}
|
||||
}
|
||||
|
||||
export interface SortButtonProps extends ButtonProps {
|
||||
onChange: (direction: SortModeEnum) => void
|
||||
direction: SortModeEnum
|
||||
}
|
||||
|
||||
const toggleDirection = (direction: SortModeEnum) => {
|
||||
switch (direction) {
|
||||
case SortModeEnum.no:
|
||||
return SortModeEnum.up;
|
||||
case SortModeEnum.up:
|
||||
return SortModeEnum.down;
|
||||
default:
|
||||
case SortModeEnum.down:
|
||||
return SortModeEnum.no;
|
||||
}
|
||||
}
|
||||
|
||||
export const SortButton: React.FC<SortButtonProps> = ({children, variant, onChange, direction}) => {
|
||||
const toggleSort = () => {
|
||||
onChange(toggleDirection(direction));
|
||||
}
|
||||
|
||||
return <IconButton onClick={toggleSort} variant={variant} icon={getIcon(direction)}>{children}</IconButton>;
|
||||
}
|
|
@ -6,14 +6,20 @@ import {
|
|||
faClock,
|
||||
faCloudDownloadAlt,
|
||||
faComment,
|
||||
faDownload,
|
||||
faFileAlt,
|
||||
faGlobe,
|
||||
faPlus,
|
||||
faSignOutAlt,
|
||||
faSort,
|
||||
faSortDown,
|
||||
faSortUp,
|
||||
faSync,
|
||||
faThumbtack,
|
||||
faTimes,
|
||||
faTrash,
|
||||
faTv,
|
||||
faUpload,
|
||||
faUsers,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import {
|
||||
|
@ -31,5 +37,5 @@ export function setUpFontAwesome() {
|
|||
library.add(faBolt, faPlus, faChartBar, faTv, faFileAlt, faCloudDownloadAlt,
|
||||
faTrash, faSignOutAlt, faComment, faDiscourse, faMastodon, faGlobe,
|
||||
faThumbtack, faClock, faTimes, faGithub, faGitlab, faGoogle, faFacebook,
|
||||
faDropbox, faTwitter, faUsers, faAddressCard)
|
||||
faDropbox, faTwitter, faUsers, faAddressCard, faSort, faDownload, faUpload, faTrash, faSync, faSortUp, faSortDown)
|
||||
}
|
||||
|
|
|
@ -1,24 +1,55 @@
|
|||
import {HistoryEntry} from "../components/landing/pages/history/history";
|
||||
import moment from "moment";
|
||||
import {HistoryToolbarState} from "../components/landing/pages/history/history-toolbar/history-toolbar";
|
||||
import {SortModeEnum} from "../components/sort-button/sort-button";
|
||||
|
||||
export function sortAndFilterEntries(entries: HistoryEntry[]): HistoryEntry[] {
|
||||
return sortEntries(entries);
|
||||
export function sortAndFilterEntries(entries: HistoryEntry[], viewState: HistoryToolbarState): HistoryEntry[] {
|
||||
return sortEntries(filterByKeywordSearch(filterBySelectedTags(entries, viewState.selectedTags), viewState.keywordSearch), viewState);
|
||||
}
|
||||
|
||||
function sortEntries(entries: HistoryEntry[]): HistoryEntry[] {
|
||||
return entries.sort((a, b) => {
|
||||
if (a.pinned && !b.pinned) {
|
||||
function filterBySelectedTags(entries: HistoryEntry[], selectedTags: string[]): HistoryEntry[] {
|
||||
return entries.filter(entry => {
|
||||
return (selectedTags.length === 0 || arrayCommonCheck(entry.tags, selectedTags))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function arrayCommonCheck<T>(array1: T[], array2: T[]): boolean {
|
||||
const foundElement = array1.find((element1) =>
|
||||
array2.find((element2) =>
|
||||
element2 === element1
|
||||
)
|
||||
)
|
||||
return !!foundElement;
|
||||
}
|
||||
|
||||
function filterByKeywordSearch(entries: HistoryEntry[], keywords: string): HistoryEntry[] {
|
||||
const searchTerm = keywords.toLowerCase();
|
||||
return entries.filter(entry => entry.title.toLowerCase().indexOf(searchTerm) !== -1);
|
||||
}
|
||||
|
||||
function sortEntries(entries: HistoryEntry[], viewState: HistoryToolbarState): HistoryEntry[] {
|
||||
return entries.sort((firstEntry, secondEntry) => {
|
||||
if (firstEntry.pinned && !secondEntry.pinned) {
|
||||
return -1;
|
||||
}
|
||||
if (!a.pinned && b.pinned) {
|
||||
if (!firstEntry.pinned && secondEntry.pinned) {
|
||||
return 1;
|
||||
}
|
||||
if (a.lastVisited < b.lastVisited) {
|
||||
return -1;
|
||||
|
||||
if (viewState.titleSortDirection !== SortModeEnum.no) {
|
||||
return firstEntry.title.localeCompare(secondEntry.title) * viewState.titleSortDirection;
|
||||
}
|
||||
if (a.lastVisited > b.lastVisited) {
|
||||
return 1;
|
||||
|
||||
if (viewState.lastVisitedSortDirection !== SortModeEnum.no) {
|
||||
if (firstEntry.lastVisited > secondEntry.lastVisited) {
|
||||
return 1 * viewState.lastVisitedSortDirection;
|
||||
}
|
||||
if (firstEntry.lastVisited < secondEntry.lastVisited) {
|
||||
return -1 * viewState.lastVisitedSortDirection;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
})
|
||||
}
|
||||
|
@ -37,7 +68,7 @@ export interface OldHistoryEntry {
|
|||
|
||||
export function loadHistoryFromLocalStore(): HistoryEntry[] {
|
||||
const historyJsonString = window.localStorage.getItem("history");
|
||||
if (historyJsonString === null) {
|
||||
if (!historyJsonString) {
|
||||
// if localStorage["history"] is empty we check the old localStorage["notehistory"]
|
||||
// and convert it to the new format
|
||||
const oldHistoryJsonString = window.localStorage.getItem("notehistory")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue