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:
mrdrogdrog 2020-05-16 19:54:08 +02:00 committed by GitHub
parent 5eb8ab7517
commit 83ab0bbe7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 226 additions and 163 deletions

View file

@ -25,7 +25,7 @@
"Import history": "استيراد التاريخ", "Import history": "استيراد التاريخ",
"Clear history": "مسح التاريخ", "Clear history": "مسح التاريخ",
"Refresh history": "حدث التاريخ", "Refresh history": "حدث التاريخ",
"No history": "ليس هناك سِجِل", "noHistory": "ليس هناك سِجِل",
"Import from browser": "استيراد من المتصفح", "Import from browser": "استيراد من المتصفح",
"releases": "إصدارات", "releases": "إصدارات",
"Are you sure?": "هل أنت واثق؟", "Are you sure?": "هل أنت واثق؟",

View file

@ -25,7 +25,7 @@
"Import history": "Importar historial", "Import history": "Importar historial",
"Clear history": "Borrar historial", "Clear history": "Borrar historial",
"Refresh history": "Actualitzar historial", "Refresh history": "Actualitzar historial",
"No history": "Cap historial", "noHistory": "Cap historial",
"Import from browser": "Importar del navegador", "Import from browser": "Importar del navegador",
"releases": "Versions", "releases": "Versions",
"Are you sure?": "Estas segur?", "Are you sure?": "Estas segur?",

View file

@ -25,7 +25,7 @@
"Import history": "Importovat historii", "Import history": "Importovat historii",
"Clear history": "Odstranit historii", "Clear history": "Odstranit historii",
"Refresh history": "Aktualizovat historii", "Refresh history": "Aktualizovat historii",
"No history": "Žádná historie", "noHistory": "Žádná historie",
"Import from browser": "Importovat z prohlížeče", "Import from browser": "Importovat z prohlížeče",
"releases": "Vydání", "releases": "Vydání",
"Are you sure?": "Jste si jisti?", "Are you sure?": "Jste si jisti?",

View file

@ -25,7 +25,7 @@
"Import history": "Importér historik", "Import history": "Importér historik",
"Clear history": "Ryd hsitorik", "Clear history": "Ryd hsitorik",
"Refresh history": "Genindlæs historik", "Refresh history": "Genindlæs historik",
"No history": "Ingen historik", "noHistory": "Ingen historik",
"Import from browser": "Importér fra browser", "Import from browser": "Importér fra browser",
"releases": "Releases", "releases": "Releases",
"Are you sure?": "Er du sikker?", "Are you sure?": "Er du sikker?",

View file

@ -25,7 +25,7 @@
"Import history": "Verlauf importieren", "Import history": "Verlauf importieren",
"Clear history": "Verlauf löschen", "Clear history": "Verlauf löschen",
"Refresh history": "Verlauf aktualisieren", "Refresh history": "Verlauf aktualisieren",
"No history": "Kein Verlauf", "noHistory": "Kein Verlauf",
"Import from browser": "Vom Browser importieren", "Import from browser": "Vom Browser importieren",
"releases": "Versionen", "releases": "Versionen",
"Are you sure?": "Sind Sie sicher?", "Are you sure?": "Sind Sie sicher?",

View file

@ -25,7 +25,7 @@
"Import history": "Εισαγωγή ιστορίας", "Import history": "Εισαγωγή ιστορίας",
"Clear history": "Καθαρισμός Ιστορίας", "Clear history": "Καθαρισμός Ιστορίας",
"Refresh history": "Ανανέωση ιστορίας", "Refresh history": "Ανανέωση ιστορίας",
"No history": "Δεν υπάρχει ιστορία", "noHistory": "Δεν υπάρχει ιστορία",
"Import from browser": "Εισαγωγή απο τον περιηγητή", "Import from browser": "Εισαγωγή απο τον περιηγητή",
"releases": "Κυκλοφορίες", "releases": "Κυκλοφορίες",
"Are you sure?": "Είστε σίγουρος?", "Are you sure?": "Είστε σίγουρος?",

View file

@ -25,7 +25,7 @@
"Import history": "Import history", "Import history": "Import history",
"Clear history": "Clear history", "Clear history": "Clear history",
"Refresh history": "Refresh history", "Refresh history": "Refresh history",
"No history": "No history", "noHistory": "No history",
"Import from browser": "Import from browser", "Import from browser": "Import from browser",
"releases": "Releases", "releases": "Releases",
"Are you sure?": "Are you sure?", "Are you sure?": "Are you sure?",

View file

@ -25,7 +25,7 @@
"Import history": "Alportu historion", "Import history": "Alportu historion",
"Clear history": "Malplenigu historion", "Clear history": "Malplenigu historion",
"Refresh history": "Refreŝigu historion", "Refresh history": "Refreŝigu historion",
"No history": "Neniu historio", "noHistory": "Neniu historio",
"Import from browser": "Alportu de retumilo", "Import from browser": "Alportu de retumilo",
"releases": "Eldonoj", "releases": "Eldonoj",
"Are you sure?": "Ĉu vi certas?", "Are you sure?": "Ĉu vi certas?",

View file

@ -25,7 +25,7 @@
"Import history": "Importar historial", "Import history": "Importar historial",
"Clear history": "Borrar historial", "Clear history": "Borrar historial",
"Refresh history": "Actualizar historial", "Refresh history": "Actualizar historial",
"No history": "Ningún historial", "noHistory": "Ningún historial",
"Import from browser": "Importar del navegador", "Import from browser": "Importar del navegador",
"releases": "Versiones", "releases": "Versiones",
"Are you sure?": "¿Estás seguro?", "Are you sure?": "¿Estás seguro?",

View file

@ -25,7 +25,7 @@
"Import history": "Importer l'historique", "Import history": "Importer l'historique",
"Clear history": "Effacer l'historique", "Clear history": "Effacer l'historique",
"Refresh history": "Actualiser l'historique", "Refresh history": "Actualiser l'historique",
"No history": "Pas d'historique", "noHistory": "Pas d'historique",
"Import from browser": "Importer depuis le navigateur", "Import from browser": "Importer depuis le navigateur",
"releases": "Versions", "releases": "Versions",
"Are you sure?": "Ëtes-vous sûr ?", "Are you sure?": "Ëtes-vous sûr ?",

View file

@ -25,7 +25,7 @@
"Import history": "इतिहास को आयात करें", "Import history": "इतिहास को आयात करें",
"Clear history": "इतिहास मिटा दें", "Clear history": "इतिहास मिटा दें",
"Refresh history": "इतिहास ताज़ा करे", "Refresh history": "इतिहास ताज़ा करे",
"No history": "इतिहास न रखें", "noHistory": "इतिहास न रखें",
"Import from browser": "ब्राउज़र से आयात", "Import from browser": "ब्राउज़र से आयात",
"releases": "विज्ञप्ति", "releases": "विज्ञप्ति",
"Are you sure?": "क्या आपको यकीन है?", "Are you sure?": "क्या आपको यकीन है?",

View file

@ -25,7 +25,7 @@
"Import history": "Uvezi povijest", "Import history": "Uvezi povijest",
"Clear history": "Očisti povijest", "Clear history": "Očisti povijest",
"Refresh history": "Osvježi povijest", "Refresh history": "Osvježi povijest",
"No history": "Nema povijesti", "noHistory": "Nema povijesti",
"Import from browser": "Uvezi iz preglednika", "Import from browser": "Uvezi iz preglednika",
"releases": "Izdanja", "releases": "Izdanja",
"Are you sure?": "Jeste li sigurni?", "Are you sure?": "Jeste li sigurni?",

View file

@ -25,7 +25,7 @@
"Import history": "Impor Riwayat", "Import history": "Impor Riwayat",
"Clear history": "Bersihkan Riwayat", "Clear history": "Bersihkan Riwayat",
"Refresh history": "Muat-ulang Riwayat", "Refresh history": "Muat-ulang Riwayat",
"No history": "Tidak ada riwayat", "noHistory": "Tidak ada riwayat",
"Import from browser": "Impor dari browser", "Import from browser": "Impor dari browser",
"releases": "Penerbitan", "releases": "Penerbitan",
"Are you sure?": "Apakah anda yakin?", "Are you sure?": "Apakah anda yakin?",

View file

@ -25,7 +25,7 @@
"Import history": "Importa cronologia", "Import history": "Importa cronologia",
"Clear history": "Cancella cronologia", "Clear history": "Cancella cronologia",
"Refresh history": "Aggiorna cronologia", "Refresh history": "Aggiorna cronologia",
"No history": "Nessuna cronologia", "noHistory": "Nessuna cronologia",
"Import from browser": "Importa da browser", "Import from browser": "Importa da browser",
"releases": "Versioni", "releases": "Versioni",
"Are you sure?": "Sei sicuro?", "Are you sure?": "Sei sicuro?",

View file

@ -25,7 +25,7 @@
"Import history": "履歴をインポート", "Import history": "履歴をインポート",
"Clear history": "履歴をクリア", "Clear history": "履歴をクリア",
"Refresh history": "履歴を更新", "Refresh history": "履歴を更新",
"No history": "履歴はありません", "noHistory": "履歴はありません",
"Import from browser": "ブラウザからインポート", "Import from browser": "ブラウザからインポート",
"releases": "リリース", "releases": "リリース",
"Are you sure?": "本当にいいですか?", "Are you sure?": "本当にいいですか?",

View file

@ -25,7 +25,7 @@
"Import history": "기록 불러오기", "Import history": "기록 불러오기",
"Clear history": "기록 초기화", "Clear history": "기록 초기화",
"Refresh history": "기록 새로고침", "Refresh history": "기록 새로고침",
"No history": "기록 없음", "noHistory": "기록 없음",
"Import from browser": "브라우저에서 불러오기", "Import from browser": "브라우저에서 불러오기",
"releases": "릴리즈", "releases": "릴리즈",
"Are you sure?": "확실합니까?", "Are you sure?": "확실합니까?",

View file

@ -25,7 +25,7 @@
"Import history": "Importeer geschiedenis", "Import history": "Importeer geschiedenis",
"Clear history": "Verwijder geschiedenis", "Clear history": "Verwijder geschiedenis",
"Refresh history": "Ververs geschiedenis", "Refresh history": "Ververs geschiedenis",
"No history": "Geen geschidenis gevonden", "noHistory": "Geen geschidenis gevonden",
"Import from browser": "Importeer van browser", "Import from browser": "Importeer van browser",
"releases": "Versies", "releases": "Versies",
"Are you sure?": "Weet je het zeker?", "Are you sure?": "Weet je het zeker?",

View file

@ -25,7 +25,7 @@
"Import history": "Importuj historię", "Import history": "Importuj historię",
"Clear history": "Wyczyść historię", "Clear history": "Wyczyść historię",
"Refresh history": "Odśwież historię", "Refresh history": "Odśwież historię",
"No history": "Brak historii", "noHistory": "Brak historii",
"Import from browser": "Importuj z przeglądarki", "Import from browser": "Importuj z przeglądarki",
"releases": "Wydania", "releases": "Wydania",
"Are you sure?": "Jesteś pewny?", "Are you sure?": "Jesteś pewny?",

View file

@ -25,7 +25,7 @@
"Import history": "Importar histórico", "Import history": "Importar histórico",
"Clear history": "Apagar histórico", "Clear history": "Apagar histórico",
"Refresh history": "Atualizar histórico", "Refresh history": "Atualizar histórico",
"No history": "Nenhum histórico", "noHistory": "Nenhum histórico",
"Import from browser": "Importar do navegador", "Import from browser": "Importar do navegador",
"releases": "Lançamentos", "releases": "Lançamentos",
"Are you sure?": "Tem certeza?", "Are you sure?": "Tem certeza?",

View file

@ -25,7 +25,7 @@
"Import history": "Импорт истории", "Import history": "Импорт истории",
"Clear history": "Очистить историю", "Clear history": "Очистить историю",
"Refresh history": "Обновить историю", "Refresh history": "Обновить историю",
"No history": "Нет истории", "noHistory": "Нет истории",
"Import from browser": "Импорт из браузера", "Import from browser": "Импорт из браузера",
"releases": "Релизы", "releases": "Релизы",
"Are you sure?": "Вы уверены?", "Are you sure?": "Вы уверены?",

View file

@ -25,7 +25,7 @@
"Import history": "Importovať históriu", "Import history": "Importovať históriu",
"Clear history": "Odstrániť históriu", "Clear history": "Odstrániť históriu",
"Refresh history": "Aktualizovať históriu", "Refresh history": "Aktualizovať históriu",
"No history": "Žiadna história", "noHistory": "Žiadna história",
"Import from browser": "Importovať z prehliadača", "Import from browser": "Importovať z prehliadača",
"releases": "Vydania", "releases": "Vydania",
"Are you sure?": "Ste si istý?", "Are you sure?": "Ste si istý?",

View file

@ -25,7 +25,7 @@
"Import history": "Увези историјат", "Import history": "Увези историјат",
"Clear history": "Очисти историју", "Clear history": "Очисти историју",
"Refresh history": "Освежи историју", "Refresh history": "Освежи историју",
"No history": "Нема историје", "noHistory": "Нема историје",
"Import from browser": "Увези из прегледача", "Import from browser": "Увези из прегледача",
"releases": "Издања", "releases": "Издања",
"Are you sure?": "Јесте ли сигурни?", "Are you sure?": "Јесте ли сигурни?",

View file

@ -25,7 +25,7 @@
"Import history": "Importhistorik", "Import history": "Importhistorik",
"Clear history": "Rensa historik", "Clear history": "Rensa historik",
"Refresh history": "Uppdatera historik", "Refresh history": "Uppdatera historik",
"No history": "Ingen historik", "noHistory": "Ingen historik",
"Import from browser": "Importera från webbläsare", "Import from browser": "Importera från webbläsare",
"releases": "Lanseringar", "releases": "Lanseringar",
"Are you sure?": "Är du säker?", "Are you sure?": "Är du säker?",

View file

@ -25,7 +25,7 @@
"Import history": "Geçmişi içe aktar", "Import history": "Geçmişi içe aktar",
"Clear history": "Geçmişi temizle", "Clear history": "Geçmişi temizle",
"Refresh history": "Geçmişi yenile", "Refresh history": "Geçmişi yenile",
"No history": "Geçmiş yok", "noHistory": "Geçmiş yok",
"Import from browser": "Tarayıcıdan içe aktar", "Import from browser": "Tarayıcıdan içe aktar",
"releases": "Sürümler", "releases": "Sürümler",
"Are you sure?": "Emin misiniz?", "Are you sure?": "Emin misiniz?",

View file

@ -25,7 +25,7 @@
"Import history": "Імпортувати історію", "Import history": "Імпортувати історію",
"Clear history": "Очистити історію", "Clear history": "Очистити історію",
"Refresh history": "Оновити історію", "Refresh history": "Оновити історію",
"No history": "Історія відсутня", "noHistory": "Історія відсутня",
"Import from browser": "Імпортувати з браузера", "Import from browser": "Імпортувати з браузера",
"releases": "Релізи", "releases": "Релізи",
"Are you sure?": "Ви впевнені?", "Are you sure?": "Ви впевнені?",

View file

@ -25,7 +25,7 @@
"Import history": "Nhập lịch sử", "Import history": "Nhập lịch sử",
"Clear history": "Xóa lịch sử", "Clear history": "Xóa lịch sử",
"Refresh history": "Làm mới 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", "Import from browser": "Nhập từ trình duyệt",
"releases": "Xuất bản", "releases": "Xuất bản",
"Are you sure?": "Bạn có chắc chắn không ?", "Are you sure?": "Bạn có chắc chắn không ?",

View file

@ -25,7 +25,7 @@
"Import history": "导入历史", "Import history": "导入历史",
"Clear history": "清空历史", "Clear history": "清空历史",
"Refresh history": "刷新历史", "Refresh history": "刷新历史",
"No history": "无历史记录", "noHistory": "无历史记录",
"Import from browser": "从浏览器导入", "Import from browser": "从浏览器导入",
"releases": "版本", "releases": "版本",
"Are you sure?": "您确定吗?", "Are you sure?": "您确定吗?",

View file

@ -25,7 +25,7 @@
"Import history": "匯入紀錄", "Import history": "匯入紀錄",
"Clear history": "清空紀錄", "Clear history": "清空紀錄",
"Refresh history": "更新紀錄", "Refresh history": "更新紀錄",
"No history": "沒有紀錄", "noHistory": "沒有紀錄",
"Import from browser": "從瀏覽器匯入", "Import from browser": "從瀏覽器匯入",
"releases": "版本", "releases": "版本",
"Are you sure?": "你確定嗎?", "Are you sure?": "你確定嗎?",

View file

@ -0,0 +1,7 @@
.history-close {
opacity: 0.5;
&:hover {
opacity: 1;
}
}

View file

@ -1,5 +1,6 @@
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import React from "react"; import React from "react";
import "./close-button.scss"
const CloseButton: React.FC = () => { const CloseButton: React.FC = () => {
return ( return (

View file

@ -11,11 +11,3 @@
opacity: 1; opacity: 1;
} }
} }
.history-close {
opacity: 0.5;
&:hover {
opacity: 1;
}
}

View file

@ -1,17 +1,18 @@
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import React from "react"; import React from "react";
import "./pin-button.scss"
export interface PinButtonProps { export interface PinButtonProps {
pin: boolean; pin: boolean;
onPinChange: () => void; onPinClick: () => void;
} }
const PinButton: React.FC<PinButtonProps> = ({pin, onPinChange}) => { const PinButton: React.FC<PinButtonProps> = ({pin, onPinClick}) => {
return ( return (
<FontAwesomeIcon <FontAwesomeIcon
icon="thumbtack" icon="thumbtack"
className={`history-pin ${pin? 'active' : ''}`} className={`history-pin ${pin ? 'active' : ''}`}
onClick={onPinChange} onClick={onPinClick}
/> />
); );
} }

View file

@ -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>
)
}

View file

@ -1,32 +1,34 @@
import React from 'react' import React from 'react'
import {HistoryInput} from '../history'
import {Badge, Card} from 'react-bootstrap' import {Badge, Card} from 'react-bootstrap'
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
import "../common/button.scss"
import {PinButton} from "../common/pin-button"; import {PinButton} from "../common/pin-button";
import {CloseButton} from "../common/close-button"; import {CloseButton} from "../common/close-button";
import moment from "moment"; import moment from "moment";
import {useTranslation} from "react-i18next"; 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() useTranslation()
return ( return (
<div className="p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4"> <div className="p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4">
<Card className="p-0" text={"dark"} bg={"light"}> <Card className="p-0" text={"dark"} bg={"light"}>
<div className="d-flex justify-content-between p-2"> <div className="d-flex justify-content-between p-2">
<PinButton pin={pinned} onPinChange={onPinChange}/> <PinButton pin={entry.pinned} onPinClick={() => {
<Card.Title className="m-0 mt-3">{title}</Card.Title> onPinClick(entry.id)
}}/>
<Card.Title className="m-0 mt-3">{entry.title}</Card.Title>
<CloseButton/> <CloseButton/>
</div> </div>
<Card.Body> <Card.Body>
<div className="text-black-50"> <div className="text-black-50">
<FontAwesomeIcon icon="clock"/> {moment(lastVisited).fromNow()}<br/> <FontAwesomeIcon icon="clock"/> {moment(entry.lastVisited).fromNow()}<br/>
{moment(lastVisited).format("llll")} {formatHistoryDate(entry.lastVisited)}
<div children= <div>
{ {
tags.map((tag) => <Badge variant={"dark"} key={tag}>{tag}</Badge>) entry.tags.map((tag) => <Badge variant={"dark"} key={tag}>{tag}</Badge>)
} }
/> </div>
</div> </div>
</Card.Body> </Card.Body>
</Card> </Card>

View file

@ -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}/>;
}
}

View file

@ -1,16 +1,20 @@
import React from "react"; import React from "react";
import {HistoryInput} from "../history";
import {PinButton} from "../common/pin-button"; import {PinButton} from "../common/pin-button";
import {CloseButton} from "../common/close-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 ( return (
<tr> <tr>
<td>{title}</td> <td>{entry.title}</td>
<td>{moment(lastVisited).format("llll")}</td> <td>{formatHistoryDate(entry.lastVisited)}</td>
<td> <td>
<PinButton pin={pinned} onPinChange={onPinChange}/> <PinButton pin={entry.pinned} onPinClick={() => {
onPinClick(entry.id)
}}/>
&nbsp; &nbsp;
<CloseButton/> <CloseButton/>
</td> </td>

View file

@ -1,21 +1,30 @@
import React from "react"; import React from "react";
import {Table} from "react-bootstrap" 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 ( return (
<Table striped bordered hover size="sm" variant="dark"> <Table striped bordered hover size="sm" variant="dark">
<thead> <thead>
<tr> <tr>
<th>Title</th> <th>Title</th>
<th>Last visited</th> <th>Last visited</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{children} {
entries.map((entry) =>
<HistoryTableRow
key={entry.id}
entry={entry}
onPinClick={onPinClick}
/>)
}
</tbody> </tbody>
</Table> </Table>
) )
} }
export { HistoryTable } export {HistoryTable}

View file

@ -1,26 +1,14 @@
import React, {Fragment, useEffect, useState} from 'react' 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 {ToggleButton, ToggleButtonGroup} from 'react-bootstrap';
import moment from "moment"; import {HistoryContent} from './history-content/history-content';
import {loadHistoryFromLocalStore, sortAndFilterEntries} from "../../../../utils/historyUtils";
interface HistoryChange { export enum ViewStateEnum {
onPinChange: () => void,
}
interface ViewState {
viewState: ViewStateEnum
}
enum ViewStateEnum {
card, card,
table table
} }
export type HistoryInput = HistoryEntry & HistoryChange export interface HistoryEntry {
interface HistoryEntry {
id: string, id: string,
title: string, title: string,
lastVisited: Date, lastVisited: Date,
@ -28,101 +16,44 @@ interface HistoryEntry {
pinned: boolean pinned: boolean
} }
interface OldHistoryEntry { export type pinClick = (entryId: string) => void;
id: string;
text: string;
time: number;
tags: string[];
pinned: boolean;
}
function loadHistoryFromLocalStore() { export const History: React.FC = () => {
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 = () => {
const [historyEntries, setHistoryEntries] = useState<HistoryEntry[]>([]) const [historyEntries, setHistoryEntries] = useState<HistoryEntry[]>([])
const [viewState, setViewState] = useState<ViewState>({ const [viewState, setViewState] = useState<ViewStateEnum>(ViewStateEnum.card)
viewState: ViewStateEnum.card
})
useEffect(() => { useEffect(() => {
const history = loadHistoryFromLocalStore(); const history = loadHistoryFromLocalStore();
setHistoryEntries(history); 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 ( return (
<Fragment> <Fragment>
<h1>History</h1> <h1>History</h1>
<ToggleButtonGroup type="radio" name="options" defaultValue={ViewStateEnum.card} className="mb-2" <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.card}>Card</ToggleButton>
<ToggleButton value={ViewStateEnum.table}>Table</ToggleButton> <ToggleButton value={ViewStateEnum.table}>Table</ToggleButton>
</ToggleButtonGroup> </ToggleButtonGroup>
{ <div className="d-flex flex-wrap justify-content-center">
viewState.viewState === ViewStateEnum.card ? ( <HistoryContent viewState={viewState} entries={sortAndFilterEntries(historyEntries)}
<div className="d-flex flex-wrap"> onPinClick={pinClick}/>
{ </div>
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>
)
}
</Fragment> </Fragment>
) )
} }
export {History}

57
src/utils/historyUtils.ts Normal file
View 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)
}
}