feat: hashtags same interface across all dto objects

This commit is contained in:
Michal Szczepanski 2023-09-28 00:56:51 +02:00
parent b938e9fb78
commit 9e6b30d35f
13 changed files with 100 additions and 60 deletions

@ -35,6 +35,11 @@ export interface SettingsConfig {
skipCssImageSizeMB: number;
expertMode: boolean;
history: SettingsHistoryConfig;
interface: SettingsInterfaceConfig;
}
interface SettingsInterfaceConfig {
optionsLeftSidebarOpen: boolean;
}
interface EnvironmentConfig {
@ -64,6 +69,9 @@ export const environmentConfig: EnvironmentConfig = {
pinDraw: true,
pageComment: true,
pageNote: true
},
interface: {
optionsLeftSidebarOpen: false
}
},
objListLimit: parseInt(process.env.OBJ_LIST_LIMIT || '100000')

@ -14,8 +14,13 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export interface ObjHashtagable {
hashtags?: ObjHashtagList;
}
export interface ObjHashtagList {
hashtags: ObjHashtag[];
data: ObjHashtag[];
hash: string;
}

@ -15,11 +15,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { ObjUrlDto } from './obj.dto';
import { ObjHashtagable } from './obj-hashtag.dto';
export interface ObjPageNoteDto extends ObjNoteDto {
url: ObjUrlDto;
}
export interface ObjNoteDto {
export interface ObjNoteDto extends ObjHashtagable {
hash: string;
prev?: string;
data: ObjNoteDataDto;
@ -29,5 +30,4 @@ export interface ObjNoteDataDto {
title: string;
description: string;
words: string[];
hashtags?: string[];
}

@ -16,8 +16,9 @@
*/
import { ObjCommentListDto } from './obj-comment.dto';
import { PageSnapshotDto } from './page-snapshot.dto';
import { ObjHashtagable } from './obj-hashtag.dto';
export interface ObjPageDto {
export interface ObjPageDto extends ObjHashtagable {
snapshot: PageSnapshotDto;
comments: ObjCommentListDto;
}

@ -15,8 +15,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { ObjUrlDto } from './obj.dto';
import { ObjHashtagable } from './obj-hashtag.dto';
export interface ObjPdfDto {
export interface ObjPdfDto extends ObjHashtagable {
hash: string;
data: ObjPdfDataDto;
}
@ -25,6 +26,5 @@ export interface ObjPdfDataDto {
screenshot: string;
rawUrl: string;
url: ObjUrlDto;
hashtags?: string[];
hash: string;
}

@ -27,7 +27,6 @@ export interface PageSnapshotInfoDto {
url: ObjUrlDto;
title: string;
words: string[];
hashtags?: string[];
}
export interface PageSnapshotDataDto {

@ -55,17 +55,14 @@ export class OptionsConvertObjectsCommand implements ICommand<Promise<void>> {
}
private async convertPdf(obj: ObjDto<ObjPdfDto>) {
delete obj.data.data['hashtags'];
await BrowserStorage.set<ObjDto>(`${ObjectStoreKeys.OBJECT_ID}:${obj.id}`, obj);
}
private async convertPageNote(obj: ObjDto<ObjPageNoteDto>) {
delete obj.data.data['hashtags'];
await BrowserStorage.set<ObjDto>(`${ObjectStoreKeys.OBJECT_ID}:${obj.id}`, obj);
}
private async convertSnapshot(obj: ObjDto<ObjPageDto>) {
delete obj.data.snapshot.info['hashtags'];
await BrowserStorage.set<ObjDto>(`${ObjectStoreKeys.OBJECT_ID}:${obj.id}`, obj);
//
}

@ -0,0 +1,42 @@
/*
* This file is part of the pinmenote-extension distribution (https://github.com/pinmenote/pinmenote-extension).
* Copyright (c) 2023 Michal Szczepanski.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { ObjHashtag, ObjHashtagable } from '../../../common/model/obj/obj-hashtag.dto';
import { ObjDto } from '../../../common/model/obj/obj.dto';
import { BrowserStorage } from '@pinmenote/browser-api';
import { BoardStore } from '../../store/board.store';
import { ObjectStoreKeys } from '../../../common/keys/object.store.keys';
import { fnSha256Object } from '../../../common/fn/fn-hash';
export class BoardItemMediator {
static removeObject = async (obj: ObjDto, refreshCallback: () => void) => {
if (await BoardStore.removeObj(obj)) {
refreshCallback();
}
};
static saveTags = async (
dto: ObjDto<ObjHashtagable>,
newTags: ObjHashtag[],
setNewTags: (newTags: ObjHashtag[]) => void
) => {
if (!dto.data.hashtags) dto.data.hashtags = { data: [], hash: '' };
dto.data.hashtags.hash = fnSha256Object(newTags);
dto.data.hashtags.data = newTags;
await BrowserStorage.set<ObjDto<any>>(`${ObjectStoreKeys.OBJECT_ID}:${dto.id}`, dto);
setNewTags(newTags);
};
}

@ -26,14 +26,15 @@ import TagIcon from '@mui/icons-material/Tag';
import Typography from '@mui/material/Typography';
import dayjs from 'dayjs';
import { SettingsStore } from '../../../store/settings.store';
import { ObjHashtag, ObjHashtagList } from '../../../../common/model/obj/obj-hashtag.dto';
interface Props {
title: string;
createdAt: number;
words: string[];
tags: string[];
tags: ObjHashtag[];
url?: string;
saveTags: (newTags: string[]) => void;
saveTags: (newTags: ObjHashtag[]) => void;
}
export const BoardItemFooter: FunctionComponent<Props> = (props) => {

@ -23,6 +23,8 @@ import { ObjDto } from '../../../../common/model/obj/obj.dto';
import { ObjPageNoteDto } from '../../../../common/model/obj/obj-note.dto';
import { fnConsoleLog } from '../../../../common/fn/fn-console';
import { marked } from 'marked';
import { fnSha256Object } from '../../../../common/fn/fn-hash';
import { ObjHashtag } from '../../../../common/model/obj/obj-hashtag.dto';
interface Props {
dto: ObjDto<ObjPageNoteDto>;
@ -31,7 +33,7 @@ interface Props {
export const PageNoteElement: FunctionComponent<Props> = (props) => {
const [edit, setEdit] = useState<boolean>(false);
const [hashtags, setHashtags] = useState<string[]>(props.dto.data.data.hashtags || []);
const [hashtags, setHashtags] = useState<ObjHashtag[]>(props.dto.data.hashtags?.data || []);
const ref = useRef<HTMLDivElement>(null);
@ -50,8 +52,10 @@ export const PageNoteElement: FunctionComponent<Props> = (props) => {
}
};
const handleTagSave = (newTags: string[]) => {
props.dto.data.data.hashtags = newTags;
const handleTagSave = (newTags: ObjHashtag[]) => {
if (!props.dto.data.hashtags) props.dto.data.hashtags = { data: [], hash: '' };
props.dto.data.hashtags.hash = fnSha256Object(newTags);
props.dto.data.hashtags.data = newTags;
setHashtags(newTags);
fnConsoleLog('PageSnapshotElement->handleTagSave->newTags', newTags);
};
@ -66,7 +70,7 @@ export const PageNoteElement: FunctionComponent<Props> = (props) => {
<BoardItemFooter
title="page note"
saveTags={handleTagSave}
tags={props.dto.data.data.hashtags || []}
tags={hashtags}
createdAt={props.dto.createdAt}
words={props.dto.data.data.words}
url={props.dto.data.url?.href}

@ -18,10 +18,10 @@ import React, { FunctionComponent, useState } from 'react';
import { BoardItem } from '../board/board-item';
import { BoardItemFooter } from '../board/board-item-footer';
import { BoardItemTitle } from '../board/board-item-title';
import { BoardStore } from '../../../store/board.store';
import { ObjDto } from '../../../../common/model/obj/obj.dto';
import { ObjPageDto } from '../../../../common/model/obj/obj-page.dto';
import { fnConsoleLog } from '../../../../common/fn/fn-console';
import { ObjHashtag } from '../../../../common/model/obj/obj-hashtag.dto';
import { BoardItemMediator } from '../board-item.mediator';
interface Props {
dto: ObjDto<ObjPageDto>;
@ -30,7 +30,7 @@ interface Props {
export const PageSnapshotElement: FunctionComponent<Props> = (props) => {
const [edit, setEdit] = useState<boolean>(false);
const [hashtags, setHashtags] = useState<string[]>(props.dto.data.snapshot.info.hashtags || []);
const [hashtags, setHashtags] = useState<ObjHashtag[]>(props.dto.data.hashtags?.data || []);
const handleEdit = () => {
setEdit(true);
@ -40,25 +40,13 @@ export const PageSnapshotElement: FunctionComponent<Props> = (props) => {
window.location.hash = `obj/${props.dto.id}`;
};
const handleRemove = async () => {
if (await BoardStore.removeObj(props.dto)) {
props.refreshBoardCallback();
}
};
const handleTagSave = (newTags: string[]) => {
props.dto.data.snapshot.info.hashtags = newTags;
setHashtags(newTags);
fnConsoleLog('PageSnapshotElement->handleTagSave->newTags', newTags);
};
return (
<BoardItem>
<BoardItemTitle
title={props.dto.data.snapshot.info.title}
editCallback={handleEdit}
htmlCallback={handleHtml}
removeCallback={handleRemove}
removeCallback={() => BoardItemMediator.removeObject(props.dto, props.refreshBoardCallback)}
/>
<img
style={{ height: '100%', width: '100%', objectFit: 'contain', maxHeight: 220 }}
@ -66,7 +54,7 @@ export const PageSnapshotElement: FunctionComponent<Props> = (props) => {
/>
<div style={{ display: 'flex', flexGrow: 1 }}></div>
<BoardItemFooter
saveTags={handleTagSave}
saveTags={(newTags) => BoardItemMediator.saveTags(props.dto, newTags, setHashtags)}
title="page snapshot"
createdAt={props.dto.createdAt}
tags={hashtags}

@ -18,10 +18,10 @@ import React, { FunctionComponent, useState } from 'react';
import { BoardItem } from '../board/board-item';
import { BoardItemFooter } from '../board/board-item-footer';
import { BoardItemTitle } from '../board/board-item-title';
import { BoardStore } from '../../../store/board.store';
import { ObjDto } from '../../../../common/model/obj/obj.dto';
import { ObjPdfDto } from '../../../../common/model/obj/obj-pdf.dto';
import { fnConsoleLog } from '../../../../common/fn/fn-console';
import { ObjHashtag } from '../../../../common/model/obj/obj-hashtag.dto';
import { BoardItemMediator } from '../board-item.mediator';
interface Props {
dto: ObjDto<ObjPdfDto>;
@ -30,7 +30,7 @@ interface Props {
export const PdfElement: FunctionComponent<Props> = (props) => {
const [edit, setEdit] = useState<boolean>(false);
const [hashtags, setHashtags] = useState<string[]>(props.dto.data.data.hashtags || []);
const [hashtags, setHashtags] = useState<ObjHashtag[]>(props.dto.data.hashtags?.data || []);
const a = props.dto.data.data.url.pathname.split('/');
const title = a[a.length - 1];
@ -43,28 +43,21 @@ export const PdfElement: FunctionComponent<Props> = (props) => {
window.location.hash = `pdf/${props.dto.id}`;
};
const handleRemove = async () => {
if (await BoardStore.removeObj(props.dto)) {
props.refreshBoardCallback();
}
};
const handleTagSave = (newTags: string[]) => {
props.dto.data.data.hashtags = newTags;
setHashtags(newTags);
fnConsoleLog('PageSnapshotElement->handleTagSave->newTags', newTags);
};
return (
<BoardItem>
<BoardItemTitle title={title} htmlCallback={handleHtml} editCallback={handleEdit} removeCallback={handleRemove} />
<BoardItemTitle
title={title}
htmlCallback={handleHtml}
editCallback={handleEdit}
removeCallback={() => BoardItemMediator.removeObject(props.dto, props.refreshBoardCallback)}
/>
<img
style={{ height: '100%', width: '100%', objectFit: 'contain', maxHeight: 220 }}
src={props.dto.data.data.screenshot}
/>
<div style={{ display: 'flex', flexGrow: 1 }}></div>
<BoardItemFooter
saveTags={handleTagSave}
saveTags={(newTags) => BoardItemMediator.saveTags(props.dto, newTags, setHashtags)}
title="page snapshot"
createdAt={props.dto.createdAt}
tags={hashtags}

@ -19,17 +19,17 @@ import Autocomplete from '@mui/material/Autocomplete';
import Button from '@mui/material/Button';
import Chip from '@mui/material/Chip';
import CircularProgress from '@mui/material/CircularProgress';
import { ObjHashtag } from '../../../common/model/obj/obj-hashtag.dto';
import TextField from '@mui/material/TextField';
import { fnConsoleLog } from '../../../common/fn/fn-console';
interface Props {
tags: string[];
saveCallback: (newTags: string[]) => void;
tags: ObjHashtag[];
saveCallback: (newTags: ObjHashtag[]) => void;
}
export const TagEditor: FunctionComponent<Props> = (props) => {
const [open, setOpen] = useState<boolean>(false);
const [currentValue, setCurrentValue] = useState<string[]>([...props.tags.concat()]);
const [currentValue, setCurrentValue] = useState<string[]>([...props.tags.map((t) => t.value)]);
const [tagsChanged, setTagsChanged] = useState<boolean>(false);
const [tagOptions, setTagOptions] = useState<string[] | undefined>(undefined);
const loading = open && tagOptions === undefined;
@ -39,12 +39,16 @@ export const TagEditor: FunctionComponent<Props> = (props) => {
}, []);
const handleSave = () => {
props.saveCallback(currentValue);
props.saveCallback(
currentValue.map((t) => {
return { value: t };
})
);
setTagsChanged(false);
};
const handleCancel = () => {
setCurrentValue([...props.tags.concat()]);
setCurrentValue([...props.tags.map((t) => t.value)]);
setTagsChanged(false);
};
@ -62,10 +66,8 @@ export const TagEditor: FunctionComponent<Props> = (props) => {
open={open}
onOpen={() => setOpen(true)}
onChange={(event: any, newValue: string[]) => {
const missing = newValue.filter((item) => props.tags.indexOf(item) < 0);
fnConsoleLog('TagEditor->diff', props.tags, newValue, missing);
missing.length > 0 ? setTagsChanged(true) : setTagsChanged(false);
const propTags = props.tags.map((t) => t.value).sort();
propTags.toString() === newValue.sort().toString() ? setTagsChanged(false) : setTagsChanged(true);
setCurrentValue(newValue);
}}
onClose={() => setOpen(false)}