mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-21 17:26:29 -05:00
Feature/history page (#28)
* add alert message and use only entry for card and table Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * Refresh table view when translation was changed Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * Add sort by date and pinning Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * save history to localstorage Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * improve card and table history Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * extract functions Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * Sort in history component Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * Fix i18n key Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * Move scss imports Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * fix scss import Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * modify state with setState Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * fix import Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * add sortAndFilterEntries function Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
parent
5eb8ab7517
commit
83ab0bbe7e
39 changed files with 226 additions and 163 deletions
|
@ -25,7 +25,7 @@
|
|||
"Import history": "استيراد التاريخ",
|
||||
"Clear history": "مسح التاريخ",
|
||||
"Refresh history": "حدث التاريخ",
|
||||
"No history": "ليس هناك سِجِل",
|
||||
"noHistory": "ليس هناك سِجِل",
|
||||
"Import from browser": "استيراد من المتصفح",
|
||||
"releases": "إصدارات",
|
||||
"Are you sure?": "هل أنت واثق؟",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Importar historial",
|
||||
"Clear history": "Borrar historial",
|
||||
"Refresh history": "Actualitzar historial",
|
||||
"No history": "Cap historial",
|
||||
"noHistory": "Cap historial",
|
||||
"Import from browser": "Importar del navegador",
|
||||
"releases": "Versions",
|
||||
"Are you sure?": "Estas segur?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Importovat historii",
|
||||
"Clear history": "Odstranit historii",
|
||||
"Refresh history": "Aktualizovat historii",
|
||||
"No history": "Žádná historie",
|
||||
"noHistory": "Žádná historie",
|
||||
"Import from browser": "Importovat z prohlížeče",
|
||||
"releases": "Vydání",
|
||||
"Are you sure?": "Jste si jisti?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Importér historik",
|
||||
"Clear history": "Ryd hsitorik",
|
||||
"Refresh history": "Genindlæs historik",
|
||||
"No history": "Ingen historik",
|
||||
"noHistory": "Ingen historik",
|
||||
"Import from browser": "Importér fra browser",
|
||||
"releases": "Releases",
|
||||
"Are you sure?": "Er du sikker?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Verlauf importieren",
|
||||
"Clear history": "Verlauf löschen",
|
||||
"Refresh history": "Verlauf aktualisieren",
|
||||
"No history": "Kein Verlauf",
|
||||
"noHistory": "Kein Verlauf",
|
||||
"Import from browser": "Vom Browser importieren",
|
||||
"releases": "Versionen",
|
||||
"Are you sure?": "Sind Sie sicher?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Εισαγωγή ιστορίας",
|
||||
"Clear history": "Καθαρισμός Ιστορίας",
|
||||
"Refresh history": "Ανανέωση ιστορίας",
|
||||
"No history": "Δεν υπάρχει ιστορία",
|
||||
"noHistory": "Δεν υπάρχει ιστορία",
|
||||
"Import from browser": "Εισαγωγή απο τον περιηγητή",
|
||||
"releases": "Κυκλοφορίες",
|
||||
"Are you sure?": "Είστε σίγουρος?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Import history",
|
||||
"Clear history": "Clear history",
|
||||
"Refresh history": "Refresh history",
|
||||
"No history": "No history",
|
||||
"noHistory": "No history",
|
||||
"Import from browser": "Import from browser",
|
||||
"releases": "Releases",
|
||||
"Are you sure?": "Are you sure?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Alportu historion",
|
||||
"Clear history": "Malplenigu historion",
|
||||
"Refresh history": "Refreŝigu historion",
|
||||
"No history": "Neniu historio",
|
||||
"noHistory": "Neniu historio",
|
||||
"Import from browser": "Alportu de retumilo",
|
||||
"releases": "Eldonoj",
|
||||
"Are you sure?": "Ĉu vi certas?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Importar historial",
|
||||
"Clear history": "Borrar historial",
|
||||
"Refresh history": "Actualizar historial",
|
||||
"No history": "Ningún historial",
|
||||
"noHistory": "Ningún historial",
|
||||
"Import from browser": "Importar del navegador",
|
||||
"releases": "Versiones",
|
||||
"Are you sure?": "¿Estás seguro?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Importer l'historique",
|
||||
"Clear history": "Effacer l'historique",
|
||||
"Refresh history": "Actualiser l'historique",
|
||||
"No history": "Pas d'historique",
|
||||
"noHistory": "Pas d'historique",
|
||||
"Import from browser": "Importer depuis le navigateur",
|
||||
"releases": "Versions",
|
||||
"Are you sure?": "Ëtes-vous sûr ?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "इतिहास को आयात करें",
|
||||
"Clear history": "इतिहास मिटा दें",
|
||||
"Refresh history": "इतिहास ताज़ा करे",
|
||||
"No history": "इतिहास न रखें",
|
||||
"noHistory": "इतिहास न रखें",
|
||||
"Import from browser": "ब्राउज़र से आयात",
|
||||
"releases": "विज्ञप्ति",
|
||||
"Are you sure?": "क्या आपको यकीन है?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Uvezi povijest",
|
||||
"Clear history": "Očisti povijest",
|
||||
"Refresh history": "Osvježi povijest",
|
||||
"No history": "Nema povijesti",
|
||||
"noHistory": "Nema povijesti",
|
||||
"Import from browser": "Uvezi iz preglednika",
|
||||
"releases": "Izdanja",
|
||||
"Are you sure?": "Jeste li sigurni?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Impor Riwayat",
|
||||
"Clear history": "Bersihkan Riwayat",
|
||||
"Refresh history": "Muat-ulang Riwayat",
|
||||
"No history": "Tidak ada riwayat",
|
||||
"noHistory": "Tidak ada riwayat",
|
||||
"Import from browser": "Impor dari browser",
|
||||
"releases": "Penerbitan",
|
||||
"Are you sure?": "Apakah anda yakin?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Importa cronologia",
|
||||
"Clear history": "Cancella cronologia",
|
||||
"Refresh history": "Aggiorna cronologia",
|
||||
"No history": "Nessuna cronologia",
|
||||
"noHistory": "Nessuna cronologia",
|
||||
"Import from browser": "Importa da browser",
|
||||
"releases": "Versioni",
|
||||
"Are you sure?": "Sei sicuro?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "履歴をインポート",
|
||||
"Clear history": "履歴をクリア",
|
||||
"Refresh history": "履歴を更新",
|
||||
"No history": "履歴はありません",
|
||||
"noHistory": "履歴はありません",
|
||||
"Import from browser": "ブラウザからインポート",
|
||||
"releases": "リリース",
|
||||
"Are you sure?": "本当にいいですか?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "기록 불러오기",
|
||||
"Clear history": "기록 초기화",
|
||||
"Refresh history": "기록 새로고침",
|
||||
"No history": "기록 없음",
|
||||
"noHistory": "기록 없음",
|
||||
"Import from browser": "브라우저에서 불러오기",
|
||||
"releases": "릴리즈",
|
||||
"Are you sure?": "확실합니까?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Importeer geschiedenis",
|
||||
"Clear history": "Verwijder geschiedenis",
|
||||
"Refresh history": "Ververs geschiedenis",
|
||||
"No history": "Geen geschidenis gevonden",
|
||||
"noHistory": "Geen geschidenis gevonden",
|
||||
"Import from browser": "Importeer van browser",
|
||||
"releases": "Versies",
|
||||
"Are you sure?": "Weet je het zeker?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Importuj historię",
|
||||
"Clear history": "Wyczyść historię",
|
||||
"Refresh history": "Odśwież historię",
|
||||
"No history": "Brak historii",
|
||||
"noHistory": "Brak historii",
|
||||
"Import from browser": "Importuj z przeglądarki",
|
||||
"releases": "Wydania",
|
||||
"Are you sure?": "Jesteś pewny?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Importar histórico",
|
||||
"Clear history": "Apagar histórico",
|
||||
"Refresh history": "Atualizar histórico",
|
||||
"No history": "Nenhum histórico",
|
||||
"noHistory": "Nenhum histórico",
|
||||
"Import from browser": "Importar do navegador",
|
||||
"releases": "Lançamentos",
|
||||
"Are you sure?": "Tem certeza?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Импорт истории",
|
||||
"Clear history": "Очистить историю",
|
||||
"Refresh history": "Обновить историю",
|
||||
"No history": "Нет истории",
|
||||
"noHistory": "Нет истории",
|
||||
"Import from browser": "Импорт из браузера",
|
||||
"releases": "Релизы",
|
||||
"Are you sure?": "Вы уверены?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Importovať históriu",
|
||||
"Clear history": "Odstrániť históriu",
|
||||
"Refresh history": "Aktualizovať históriu",
|
||||
"No history": "Žiadna história",
|
||||
"noHistory": "Žiadna história",
|
||||
"Import from browser": "Importovať z prehliadača",
|
||||
"releases": "Vydania",
|
||||
"Are you sure?": "Ste si istý?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Увези историјат",
|
||||
"Clear history": "Очисти историју",
|
||||
"Refresh history": "Освежи историју",
|
||||
"No history": "Нема историје",
|
||||
"noHistory": "Нема историје",
|
||||
"Import from browser": "Увези из прегледача",
|
||||
"releases": "Издања",
|
||||
"Are you sure?": "Јесте ли сигурни?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Importhistorik",
|
||||
"Clear history": "Rensa historik",
|
||||
"Refresh history": "Uppdatera historik",
|
||||
"No history": "Ingen historik",
|
||||
"noHistory": "Ingen historik",
|
||||
"Import from browser": "Importera från webbläsare",
|
||||
"releases": "Lanseringar",
|
||||
"Are you sure?": "Är du säker?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Geçmişi içe aktar",
|
||||
"Clear history": "Geçmişi temizle",
|
||||
"Refresh history": "Geçmişi yenile",
|
||||
"No history": "Geçmiş yok",
|
||||
"noHistory": "Geçmiş yok",
|
||||
"Import from browser": "Tarayıcıdan içe aktar",
|
||||
"releases": "Sürümler",
|
||||
"Are you sure?": "Emin misiniz?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Імпортувати історію",
|
||||
"Clear history": "Очистити історію",
|
||||
"Refresh history": "Оновити історію",
|
||||
"No history": "Історія відсутня",
|
||||
"noHistory": "Історія відсутня",
|
||||
"Import from browser": "Імпортувати з браузера",
|
||||
"releases": "Релізи",
|
||||
"Are you sure?": "Ви впевнені?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "Nhập lịch sử",
|
||||
"Clear history": "Xóa lịch sử",
|
||||
"Refresh history": "Làm mới lịch sử",
|
||||
"No history": "Không có lịch sử",
|
||||
"noHistory": "Không có lịch sử",
|
||||
"Import from browser": "Nhập từ trình duyệt",
|
||||
"releases": "Xuất bản",
|
||||
"Are you sure?": "Bạn có chắc chắn không ?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "导入历史",
|
||||
"Clear history": "清空历史",
|
||||
"Refresh history": "刷新历史",
|
||||
"No history": "无历史记录",
|
||||
"noHistory": "无历史记录",
|
||||
"Import from browser": "从浏览器导入",
|
||||
"releases": "版本",
|
||||
"Are you sure?": "您确定吗?",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"Import history": "匯入紀錄",
|
||||
"Clear history": "清空紀錄",
|
||||
"Refresh history": "更新紀錄",
|
||||
"No history": "沒有紀錄",
|
||||
"noHistory": "沒有紀錄",
|
||||
"Import from browser": "從瀏覽器匯入",
|
||||
"releases": "版本",
|
||||
"Are you sure?": "你確定嗎?",
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
.history-close {
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import React from "react";
|
||||
import "./close-button.scss"
|
||||
|
||||
const CloseButton: React.FC = () => {
|
||||
return (
|
||||
|
|
|
@ -11,11 +11,3 @@
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.history-close {
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -1,17 +1,18 @@
|
|||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import React from "react";
|
||||
import "./pin-button.scss"
|
||||
|
||||
export interface PinButtonProps {
|
||||
pin: boolean;
|
||||
onPinChange: () => void;
|
||||
onPinClick: () => void;
|
||||
}
|
||||
|
||||
const PinButton: React.FC<PinButtonProps> = ({pin, onPinChange}) => {
|
||||
const PinButton: React.FC<PinButtonProps> = ({pin, onPinClick}) => {
|
||||
return (
|
||||
<FontAwesomeIcon
|
||||
icon="thumbtack"
|
||||
className={`history-pin ${pin? 'active' : ''}`}
|
||||
onClick={onPinChange}
|
||||
className={`history-pin ${pin ? 'active' : ''}`}
|
||||
onClick={onPinClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import React, {Fragment} from 'react'
|
||||
import {HistoryEntriesProps} from "../history-content/history-content";
|
||||
import {HistoryCard} from "./history-card";
|
||||
|
||||
export const HistoryCardList: React.FC<HistoryEntriesProps> = ({entries, onPinClick}) => {
|
||||
return (
|
||||
<Fragment>
|
||||
{
|
||||
entries.map((entry) => (
|
||||
<HistoryCard
|
||||
key={entry.id}
|
||||
entry={entry}
|
||||
onPinClick={onPinClick}
|
||||
/>))
|
||||
}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
|
@ -1,32 +1,34 @@
|
|||
import React from 'react'
|
||||
import {HistoryInput} from '../history'
|
||||
import {Badge, Card} from 'react-bootstrap'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
|
||||
import "../common/button.scss"
|
||||
import {PinButton} from "../common/pin-button";
|
||||
import {CloseButton} from "../common/close-button";
|
||||
import moment from "moment";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {HistoryEntryProps} from "../history-content/history-content";
|
||||
import {formatHistoryDate} from "../../../../../utils/historyUtils";
|
||||
|
||||
export const HistoryCard: React.FC<HistoryInput> = ({pinned, title, lastVisited, tags, onPinChange}) => {
|
||||
export const HistoryCard: React.FC<HistoryEntryProps> = ({entry, onPinClick}) => {
|
||||
useTranslation()
|
||||
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={pinned} onPinChange={onPinChange}/>
|
||||
<Card.Title className="m-0 mt-3">{title}</Card.Title>
|
||||
<PinButton pin={entry.pinned} onPinClick={() => {
|
||||
onPinClick(entry.id)
|
||||
}}/>
|
||||
<Card.Title className="m-0 mt-3">{entry.title}</Card.Title>
|
||||
<CloseButton/>
|
||||
</div>
|
||||
<Card.Body>
|
||||
<div className="text-black-50">
|
||||
<FontAwesomeIcon icon="clock"/> {moment(lastVisited).fromNow()}<br/>
|
||||
{moment(lastVisited).format("llll")}
|
||||
<div children=
|
||||
{
|
||||
tags.map((tag) => <Badge variant={"dark"} key={tag}>{tag}</Badge>)
|
||||
}
|
||||
/>
|
||||
<FontAwesomeIcon icon="clock"/> {moment(entry.lastVisited).fromNow()}<br/>
|
||||
{formatHistoryDate(entry.lastVisited)}
|
||||
<div>
|
||||
{
|
||||
entry.tags.map((tag) => <Badge variant={"dark"} key={tag}>{tag}</Badge>)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import React from "react";
|
||||
import {HistoryEntry, pinClick, ViewStateEnum} 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";
|
||||
|
||||
export interface HistoryContentProps {
|
||||
viewState: ViewStateEnum
|
||||
entries: HistoryEntry[]
|
||||
onPinClick: pinClick
|
||||
}
|
||||
|
||||
export interface HistoryEntryProps {
|
||||
entry: HistoryEntry,
|
||||
onPinClick: pinClick
|
||||
}
|
||||
|
||||
export interface HistoryEntriesProps {
|
||||
entries: HistoryEntry[]
|
||||
onPinClick: pinClick
|
||||
}
|
||||
|
||||
export const HistoryContent: React.FC<HistoryContentProps> = ({viewState, entries, onPinClick}) => {
|
||||
|
||||
if (entries.length === 0) {
|
||||
return (
|
||||
<Alert variant={"secondary"}>
|
||||
<Trans i18nKey={"noHistory"}/>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
switch (viewState) {
|
||||
default:
|
||||
case ViewStateEnum.card:
|
||||
return <HistoryCardList entries={entries} onPinClick={onPinClick}/>
|
||||
case ViewStateEnum.table:
|
||||
return <HistoryTable entries={entries} onPinClick={onPinClick}/>;
|
||||
}
|
||||
}
|
|
@ -1,16 +1,20 @@
|
|||
import React from "react";
|
||||
import {HistoryInput} from "../history";
|
||||
import {PinButton} from "../common/pin-button";
|
||||
import {CloseButton} from "../common/close-button";
|
||||
import moment from "moment";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {HistoryEntryProps} from "../history-content/history-content";
|
||||
import {formatHistoryDate} from "../../../../../utils/historyUtils";
|
||||
|
||||
export const HistoryTableRow: React.FC<HistoryInput> = ({pinned, title, lastVisited, onPinChange}) => {
|
||||
export const HistoryTableRow: React.FC<HistoryEntryProps> = ({entry, onPinClick}) => {
|
||||
useTranslation()
|
||||
return (
|
||||
<tr>
|
||||
<td>{title}</td>
|
||||
<td>{moment(lastVisited).format("llll")}</td>
|
||||
<td>{entry.title}</td>
|
||||
<td>{formatHistoryDate(entry.lastVisited)}</td>
|
||||
<td>
|
||||
<PinButton pin={pinned} onPinChange={onPinChange}/>
|
||||
<PinButton pin={entry.pinned} onPinClick={() => {
|
||||
onPinClick(entry.id)
|
||||
}}/>
|
||||
|
||||
<CloseButton/>
|
||||
</td>
|
||||
|
|
|
@ -1,21 +1,30 @@
|
|||
import React from "react";
|
||||
import {Table} from "react-bootstrap"
|
||||
import {HistoryTableRow} from "./history-table-row";
|
||||
import {HistoryEntriesProps} from "../history-content/history-content";
|
||||
|
||||
const HistoryTable: React.FC = ({children}) => {
|
||||
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>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Last visited</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{children}
|
||||
{
|
||||
entries.map((entry) =>
|
||||
<HistoryTableRow
|
||||
key={entry.id}
|
||||
entry={entry}
|
||||
onPinClick={onPinClick}
|
||||
/>)
|
||||
}
|
||||
</tbody>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
|
||||
export { HistoryTable }
|
||||
export {HistoryTable}
|
||||
|
|
|
@ -1,26 +1,14 @@
|
|||
import React, {Fragment, useEffect, useState} from 'react'
|
||||
import {HistoryCard} from "./history-card/history-card";
|
||||
import {HistoryTable} from "./history-table/history-table";
|
||||
import {HistoryTableRow} from './history-table/history-table-row';
|
||||
import {ToggleButton, ToggleButtonGroup} from 'react-bootstrap';
|
||||
import moment from "moment";
|
||||
import {HistoryContent} from './history-content/history-content';
|
||||
import {loadHistoryFromLocalStore, sortAndFilterEntries} from "../../../../utils/historyUtils";
|
||||
|
||||
interface HistoryChange {
|
||||
onPinChange: () => void,
|
||||
}
|
||||
|
||||
interface ViewState {
|
||||
viewState: ViewStateEnum
|
||||
}
|
||||
|
||||
enum ViewStateEnum {
|
||||
export enum ViewStateEnum {
|
||||
card,
|
||||
table
|
||||
}
|
||||
|
||||
export type HistoryInput = HistoryEntry & HistoryChange
|
||||
|
||||
interface HistoryEntry {
|
||||
export interface HistoryEntry {
|
||||
id: string,
|
||||
title: string,
|
||||
lastVisited: Date,
|
||||
|
@ -28,101 +16,44 @@ interface HistoryEntry {
|
|||
pinned: boolean
|
||||
}
|
||||
|
||||
interface OldHistoryEntry {
|
||||
id: string;
|
||||
text: string;
|
||||
time: number;
|
||||
tags: string[];
|
||||
pinned: boolean;
|
||||
}
|
||||
export type pinClick = (entryId: string) => void;
|
||||
|
||||
function loadHistoryFromLocalStore() {
|
||||
const historyJsonString = window.localStorage.getItem("history");
|
||||
if (historyJsonString === null) {
|
||||
// if localStorage["history"] is empty we check the old localStorage["notehistory"]
|
||||
// and convert it to the new format
|
||||
const oldHistoryJsonString = window.localStorage.getItem("notehistory")
|
||||
const oldHistory = oldHistoryJsonString ? JSON.parse(JSON.parse(oldHistoryJsonString)) : [];
|
||||
return oldHistory.map((entry: OldHistoryEntry) => {
|
||||
return {
|
||||
id: entry.id,
|
||||
title: entry.text,
|
||||
lastVisited: moment(entry.time).toDate(),
|
||||
tags: entry.tags,
|
||||
pinned: entry.pinned,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return JSON.parse(historyJsonString)
|
||||
}
|
||||
}
|
||||
|
||||
const History: React.FC = () => {
|
||||
export const History: React.FC = () => {
|
||||
const [historyEntries, setHistoryEntries] = useState<HistoryEntry[]>([])
|
||||
const [viewState, setViewState] = useState<ViewState>({
|
||||
viewState: ViewStateEnum.card
|
||||
})
|
||||
const [viewState, setViewState] = useState<ViewStateEnum>(ViewStateEnum.card)
|
||||
|
||||
useEffect(() => {
|
||||
const history = loadHistoryFromLocalStore();
|
||||
setHistoryEntries(history);
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
window.localStorage.setItem("history", JSON.stringify(historyEntries));
|
||||
}, [historyEntries])
|
||||
|
||||
const pinClick: pinClick = (entryId: string) => {
|
||||
setHistoryEntries((entries) => {
|
||||
return entries.map((entry) => {
|
||||
if (entry.id === entryId) {
|
||||
entry.pinned = !entry.pinned;
|
||||
}
|
||||
return entry;
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h1>History</h1>
|
||||
<ToggleButtonGroup type="radio" name="options" defaultValue={ViewStateEnum.card} className="mb-2"
|
||||
onChange={(newState: ViewStateEnum) => setViewState(() => ({viewState: newState}))}>
|
||||
onChange={(newState: ViewStateEnum) => setViewState(newState)}>
|
||||
<ToggleButton value={ViewStateEnum.card}>Card</ToggleButton>
|
||||
<ToggleButton value={ViewStateEnum.table}>Table</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
{
|
||||
viewState.viewState === ViewStateEnum.card ? (
|
||||
<div className="d-flex flex-wrap">
|
||||
{
|
||||
historyEntries.length === 0 ?
|
||||
''
|
||||
:
|
||||
historyEntries.map((entry) =>
|
||||
<HistoryCard
|
||||
id={entry.id}
|
||||
tags={entry.tags}
|
||||
pinned={entry.pinned}
|
||||
title={entry.title}
|
||||
lastVisited={entry.lastVisited}
|
||||
onPinChange={() => {
|
||||
// setHistoryEntries((prev: HistoryEntry) => {
|
||||
// return {...prev, pinned: !prev.pinned};
|
||||
// });
|
||||
}}
|
||||
/>)
|
||||
}
|
||||
</div>
|
||||
) : (
|
||||
<HistoryTable>
|
||||
{
|
||||
historyEntries.length === 0 ?
|
||||
''
|
||||
:
|
||||
historyEntries.map((entry) =>
|
||||
<HistoryTableRow
|
||||
id={entry.id}
|
||||
tags={entry.tags}
|
||||
pinned={entry.pinned}
|
||||
title={entry.title}
|
||||
lastVisited={entry.lastVisited}
|
||||
onPinChange={() => {
|
||||
// setEntry((prev: HistoryEntry) => {
|
||||
// return {...prev, pinned: !prev.pinned};
|
||||
// });
|
||||
}}
|
||||
/>)
|
||||
}
|
||||
</HistoryTable>
|
||||
)
|
||||
}
|
||||
<div className="d-flex flex-wrap justify-content-center">
|
||||
<HistoryContent viewState={viewState} entries={sortAndFilterEntries(historyEntries)}
|
||||
onPinClick={pinClick}/>
|
||||
</div>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export {History}
|
||||
}
|
57
src/utils/historyUtils.ts
Normal file
57
src/utils/historyUtils.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import {HistoryEntry} from "../components/landing/pages/history/history";
|
||||
import moment from "moment";
|
||||
|
||||
export function sortAndFilterEntries(entries: HistoryEntry[]): HistoryEntry[] {
|
||||
return sortEntries(entries);
|
||||
}
|
||||
|
||||
function sortEntries(entries: HistoryEntry[]): HistoryEntry[] {
|
||||
return entries.sort((a, b) => {
|
||||
if (a.pinned && !b.pinned) {
|
||||
return -1;
|
||||
}
|
||||
if (!a.pinned && b.pinned) {
|
||||
return 1;
|
||||
}
|
||||
if (a.lastVisited < b.lastVisited) {
|
||||
return -1;
|
||||
}
|
||||
if (a.lastVisited > b.lastVisited) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
}
|
||||
|
||||
export function formatHistoryDate(date: Date) {
|
||||
return moment(date).format("llll")
|
||||
}
|
||||
|
||||
export interface OldHistoryEntry {
|
||||
id: string;
|
||||
text: string;
|
||||
time: number;
|
||||
tags: string[];
|
||||
pinned: boolean;
|
||||
}
|
||||
|
||||
export function loadHistoryFromLocalStore(): HistoryEntry[] {
|
||||
const historyJsonString = window.localStorage.getItem("history");
|
||||
if (historyJsonString === null) {
|
||||
// if localStorage["history"] is empty we check the old localStorage["notehistory"]
|
||||
// and convert it to the new format
|
||||
const oldHistoryJsonString = window.localStorage.getItem("notehistory")
|
||||
const oldHistory = oldHistoryJsonString ? JSON.parse(JSON.parse(oldHistoryJsonString)) : [];
|
||||
return oldHistory.map((entry: OldHistoryEntry) => {
|
||||
return {
|
||||
id: entry.id,
|
||||
title: entry.text,
|
||||
lastVisited: moment(entry.time).toDate(),
|
||||
tags: entry.tags,
|
||||
pinned: entry.pinned,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return JSON.parse(historyJsonString)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue