feat: sync pdf and note
This commit is contained in:
parent
f10d617689
commit
6e1ea32255
@ -22,7 +22,6 @@ import { ObjPageDto } from '../../../model/obj/obj-page.dto';
|
||||
import { ObjPageNoteDto } from '../../../model/obj/obj-note.dto';
|
||||
import { ObjPdfDto } from '../../../model/obj/obj-pdf.dto';
|
||||
import { ObjPinDto } from '../../../model/obj/obj-pin.dto';
|
||||
import { ObjTaskDto } from '../../../model/obj/obj-task.dto';
|
||||
import { fnConsoleLog } from '../../../fn/fn-console';
|
||||
|
||||
export class ObjGetOriginCommand implements ICommand<Promise<ObjDto<ObjPageDataDto>[]>> {
|
||||
@ -45,10 +44,8 @@ export class ObjGetOriginCommand implements ICommand<Promise<ObjDto<ObjPageDataD
|
||||
if ((obj.data as ObjPinDto).data.url.href === this.data.href) continue;
|
||||
} else if (obj.type === ObjTypeDto.PageNote) {
|
||||
if ((obj.data as ObjPageNoteDto).url.href === this.data.href) continue;
|
||||
} else if (obj.type === ObjTypeDto.PageTask) {
|
||||
if ((obj.data as ObjTaskDto).url?.href === this.data.href) continue;
|
||||
} else if (obj.type === ObjTypeDto.Pdf) {
|
||||
if ((obj.data as ObjPdfDto).rawUrl === this.data.href) continue;
|
||||
if ((obj.data as ObjPdfDto).data.rawUrl === this.data.href) continue;
|
||||
}
|
||||
out.push(obj);
|
||||
// TODO pagination - now show last 10
|
||||
|
@ -45,7 +45,7 @@ export class PageNoteAddCommand implements ICommand<Promise<void>> {
|
||||
};
|
||||
|
||||
await SwTaskStore.addTask(SwTaskType.WORDS_ADD_INDEX, {
|
||||
words: this.note.words,
|
||||
words: this.note.data.words,
|
||||
objectId: id
|
||||
});
|
||||
|
||||
|
@ -47,7 +47,7 @@ export class PageNoteRemoveCommand implements ICommand<void> {
|
||||
await LinkHrefStore.noteDel(data.url, this.obj.id);
|
||||
|
||||
await SwTaskStore.addTask(SwTaskType.WORDS_REMOVE_INDEX, {
|
||||
words: data.words,
|
||||
words: data.data.words,
|
||||
objectId: this.obj.id
|
||||
});
|
||||
|
||||
|
@ -17,14 +17,14 @@
|
||||
import { BrowserStorage } from '@pinmenote/browser-api';
|
||||
import { ICommand } from '../../model/shared/common.dto';
|
||||
import { ObjDto } from '../../model/obj/obj.dto';
|
||||
import { ObjPageNoteDto } from '../../model/obj/obj-note.dto';
|
||||
import { ObjNoteDataDto, ObjPageNoteDto } from '../../model/obj/obj-note.dto';
|
||||
import { ObjUpdateIndexAddCommand } from '../obj/index/obj-update-index-add.command';
|
||||
import { ObjectStoreKeys } from '../../keys/object.store.keys';
|
||||
import { SwTaskStore } from '../../store/sw-task.store';
|
||||
import { SwTaskType } from '../../model/sw-task.model';
|
||||
import { WordFactory } from '../../text/word.factory';
|
||||
import { fnConsoleLog } from '../../fn/fn-console';
|
||||
import { fnSha256 } from '../../fn/fn-hash';
|
||||
import { fnSha256Object } from '../../fn/fn-hash';
|
||||
|
||||
export class PageNoteUpdateCommand implements ICommand<void> {
|
||||
constructor(private obj: ObjDto<ObjPageNoteDto>, private title: string, private description: string) {}
|
||||
@ -37,24 +37,28 @@ export class PageNoteUpdateCommand implements ICommand<void> {
|
||||
await BrowserStorage.set<ObjPageNoteDto>(`${ObjectStoreKeys.NOTE_HASH}:${this.obj.data.hash}`, this.obj.data);
|
||||
|
||||
this.obj.data.prev = this.obj.data.hash;
|
||||
this.obj.data.hash = fnSha256(this.title + this.description + (this.obj.data.url?.href || '') + dt.toString());
|
||||
|
||||
this.obj.data.title = this.title;
|
||||
this.obj.data.description = this.description;
|
||||
const words = new Set<string>([...WordFactory.toWordList(this.title), ...WordFactory.toWordList(this.description)]);
|
||||
|
||||
const newData: ObjNoteDataDto = {
|
||||
title: this.title,
|
||||
description: this.description,
|
||||
words: Array.from(words),
|
||||
hashtags: this.obj.data.data.hashtags
|
||||
};
|
||||
this.obj.data.hash = fnSha256Object({ ...newData, url: this.obj.data.url, dt });
|
||||
|
||||
this.obj.data.data = newData;
|
||||
this.obj.updatedAt = dt;
|
||||
|
||||
// Remove words from index
|
||||
await SwTaskStore.addTask(SwTaskType.WORDS_REMOVE_INDEX, {
|
||||
words: this.obj.data.words,
|
||||
words: this.obj.data.data.words,
|
||||
objectId: this.obj.id
|
||||
});
|
||||
|
||||
// Update words
|
||||
const words = new Set<string>([...WordFactory.toWordList(this.title), ...WordFactory.toWordList(this.description)]);
|
||||
this.obj.data.words = Array.from(words);
|
||||
|
||||
await SwTaskStore.addTask(SwTaskType.WORDS_ADD_INDEX, {
|
||||
words: this.obj.data.words,
|
||||
words: Array.from(words),
|
||||
objectId: this.obj.id
|
||||
});
|
||||
|
||||
|
@ -22,11 +22,11 @@ import { ICommand } from '../../model/shared/common.dto';
|
||||
import { LinkHrefStore } from '../../store/link-href.store';
|
||||
import { ObjAddIdCommand } from '../obj/id/obj-add-id.command';
|
||||
import { ObjNextIdCommand } from '../obj/id/obj-next-id.command';
|
||||
import { ObjPdfDto } from '../../model/obj/obj-pdf.dto';
|
||||
import { ObjPdfDataDto, ObjPdfDto } from '../../model/obj/obj-pdf.dto';
|
||||
import { ObjectStoreKeys } from '../../keys/object.store.keys';
|
||||
import { ScreenshotFactory } from '../../factory/screenshot.factory';
|
||||
import { UrlFactory } from '../../factory/url.factory';
|
||||
import { fnSha256 } from '../../fn/fn-hash';
|
||||
import { fnSha256, fnSha256Object } from '../../fn/fn-hash';
|
||||
import { ImageResizeFactory } from '../../factory/image-resize.factory';
|
||||
|
||||
export class PdfAddCommand implements ICommand<Promise<void>> {
|
||||
@ -46,13 +46,17 @@ export class PdfAddCommand implements ICommand<Promise<void>> {
|
||||
|
||||
const url = UrlFactory.newUrl();
|
||||
|
||||
const data: ObjPdfDto = {
|
||||
hash,
|
||||
const pdfData: Omit<ObjPdfDataDto, 'hash'> = {
|
||||
screenshot,
|
||||
rawUrl: this.value.url,
|
||||
url,
|
||||
hashtags: []
|
||||
};
|
||||
const pdfDataHash = fnSha256Object(pdfData);
|
||||
const data: ObjPdfDto = {
|
||||
hash,
|
||||
data: { ...pdfData, hash: pdfDataHash }
|
||||
};
|
||||
|
||||
const dto: ObjDto<ObjPdfDto> = {
|
||||
id,
|
||||
|
@ -27,7 +27,7 @@ export class PdfRemoveCommand implements ICommand<Promise<void>> {
|
||||
await BrowserStorage.remove(`${ObjectStoreKeys.PDF_DATA}:${this.dto.hash}`);
|
||||
await BrowserStorage.remove(`${ObjectStoreKeys.OBJECT_ID}:${this.id}`);
|
||||
|
||||
await LinkHrefStore.del(this.dto.url, this.id);
|
||||
await LinkHrefStore.del(this.dto.data.url, this.id);
|
||||
|
||||
await new ObjRemoveIdCommand(this.id, ObjectStoreKeys.OBJECT_LIST).execute();
|
||||
}
|
||||
|
@ -77,15 +77,13 @@ export class DownloadImageButton {
|
||||
};
|
||||
|
||||
private downloadScreenshot = async (screenshot: string): Promise<void> => {
|
||||
let url = '';
|
||||
const url = window.URL.createObjectURL(fnB64toBlob(screenshot));
|
||||
let filename = '';
|
||||
switch (this.model.doc.settings.screenshotFormat) {
|
||||
case 'jpeg':
|
||||
url = window.URL.createObjectURL(fnB64toBlob(screenshot, 'image/jpeg'));
|
||||
filename = `${fnUid()}.jpg`;
|
||||
break;
|
||||
default:
|
||||
url = window.URL.createObjectURL(fnB64toBlob(screenshot, 'image/png'));
|
||||
filename = `${fnUid()}.png`;
|
||||
}
|
||||
const data = { url, filename };
|
||||
|
@ -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 const fnB64toBlob = (b64Data: string, contentType = '', sliceSize = 512): Blob => {
|
||||
const byteCharacters = window.atob(b64Data.split(',')[1]);
|
||||
export const fnB64toBlob = (b64Data: string, sliceSize = 512): Blob => {
|
||||
const a = b64Data.split(',');
|
||||
const contentType = a[0].substring(5).split(';')[0];
|
||||
let data = a[1];
|
||||
if (data.endsWith('%3D')) data = data.replaceAll('%3D', '=');
|
||||
// console.log('fnB64toBlob', 'contentType', contentType, 'data', data);
|
||||
const byteCharacters = atob(data);
|
||||
const byteArrays = [];
|
||||
|
||||
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
||||
|
@ -22,6 +22,10 @@ export interface ObjPageNoteDto extends ObjNoteDto {
|
||||
export interface ObjNoteDto {
|
||||
hash: string;
|
||||
prev?: string;
|
||||
data: ObjNoteDataDto;
|
||||
}
|
||||
|
||||
export interface ObjNoteDataDto {
|
||||
title: string;
|
||||
description: string;
|
||||
words: string[];
|
||||
|
@ -18,8 +18,13 @@ import { ObjUrlDto } from './obj.dto';
|
||||
|
||||
export interface ObjPdfDto {
|
||||
hash: string;
|
||||
data: ObjPdfDataDto;
|
||||
}
|
||||
|
||||
export interface ObjPdfDataDto {
|
||||
screenshot: string;
|
||||
rawUrl: string;
|
||||
url: ObjUrlDto;
|
||||
hashtags: string[];
|
||||
hash: string;
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* 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 { ObjUrlDto } from './obj.dto';
|
||||
|
||||
export interface ObjTaskDto {
|
||||
title: string;
|
||||
description: string;
|
||||
url?: ObjUrlDto;
|
||||
words: string[];
|
||||
hashtags: string[];
|
||||
}
|
@ -28,7 +28,6 @@ import { ObjDto } from '../../../common/model/obj/obj.dto';
|
||||
import { ObjPageNoteDto } from '../../../common/model/obj/obj-note.dto';
|
||||
import { ObjViewComponent } from '../obj/obj-view.component';
|
||||
import { PopupFunctionsComponent } from '../popup-functions/popup-functions.component';
|
||||
import { TaskComponent } from '../task/task.component';
|
||||
|
||||
export const MainViewComponent: FunctionComponent = () => {
|
||||
const [previousView, setPreviousView] = useState<MainViewEnum>(MainViewEnum.PAGE_OBJECTS);
|
||||
@ -57,8 +56,6 @@ export const MainViewComponent: FunctionComponent = () => {
|
||||
return <DecryptComponent />;
|
||||
case MainViewEnum.CALENDAR:
|
||||
return <CalendarComponent />;
|
||||
case MainViewEnum.TASK:
|
||||
return <TaskComponent />;
|
||||
case MainViewEnum.BUG_REPORT:
|
||||
return <BugReportComponent cancelCallback={() => setCurrentView(MainViewEnum.PAGE_OBJECTS)} />;
|
||||
case MainViewEnum.NOTE:
|
||||
|
@ -18,14 +18,14 @@ import { COLOR_DEFAULT_BORDER, DEFAULT_BORDER_RADIUS } from '../../../../common/
|
||||
import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
|
||||
import Button from '@mui/material/Button';
|
||||
import { EditorView } from 'prosemirror-view';
|
||||
import { ObjPageNoteDto } from '../../../../common/model/obj/obj-note.dto';
|
||||
import { ObjNoteDataDto } from '../../../../common/model/obj/obj-note.dto';
|
||||
import { PageNoteAddCommand } from '../../../../common/command/page-note/page-note-add.command';
|
||||
import { PopupActiveTabStore } from '../../../store/popup-active-tab.store';
|
||||
import { StyledInput } from '../../../../common/components/react/styled.input';
|
||||
import { WordFactory } from '../../../../common/text/word.factory';
|
||||
import { createTextEditorState } from '../../../../common/components/text-editor/text.editor.state';
|
||||
import { defaultMarkdownSerializer } from 'prosemirror-markdown';
|
||||
import { fnSha256 } from '../../../../common/fn/fn-hash';
|
||||
import { fnSha256Object } from '../../../../common/fn/fn-hash';
|
||||
|
||||
interface Props {
|
||||
addCallback: () => void;
|
||||
@ -76,16 +76,21 @@ export const NoteAddComponent: FunctionComponent<Props> = (props) => {
|
||||
const description = LocalModel.description;
|
||||
const words = new Set<string>([...WordFactory.toWordList(title), ...WordFactory.toWordList(description)]);
|
||||
const dt = Date.now();
|
||||
const hash = fnSha256(title + description + (url?.href || '') + dt.toString());
|
||||
const note: ObjPageNoteDto = {
|
||||
hash,
|
||||
const data: ObjNoteDataDto = {
|
||||
title,
|
||||
description,
|
||||
url,
|
||||
words: Array.from(words),
|
||||
hashtags: []
|
||||
};
|
||||
await new PageNoteAddCommand(note, dt).execute();
|
||||
const hash = fnSha256Object({ ...data, url, dt });
|
||||
await new PageNoteAddCommand(
|
||||
{
|
||||
hash,
|
||||
url,
|
||||
data
|
||||
},
|
||||
dt
|
||||
).execute();
|
||||
props.addCallback();
|
||||
};
|
||||
|
||||
|
@ -43,7 +43,7 @@ class LocalModel {
|
||||
}
|
||||
|
||||
export const NoteEditComponent: FunctionComponent<Props> = (props) => {
|
||||
const [title, setTitle] = useState<string>(props.editNote.data.title);
|
||||
const [title, setTitle] = useState<string>(props.editNote.data.data.title);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@ -56,7 +56,7 @@ export const NoteEditComponent: FunctionComponent<Props> = (props) => {
|
||||
}, []);
|
||||
|
||||
const create = (el: HTMLDivElement): void => {
|
||||
let state = createTextEditorState(props.editNote.data.description);
|
||||
let state = createTextEditorState(props.editNote.data.data.description);
|
||||
LocalModel.editorView = new EditorView(el, {
|
||||
state,
|
||||
handleKeyDown: (view: EditorView, event: KeyboardEvent) => {
|
||||
|
@ -43,7 +43,7 @@ export const NoteElementComponent: FunctionComponent<Props> = ({ obj, editCallba
|
||||
);
|
||||
const expandComponent = isExpanded ? <NoteListExpandComponent note={obj} /> : '';
|
||||
|
||||
const title = obj.data.title.length > 50 ? `${obj.data.title.substring(0, 50)}...` : obj.data.title;
|
||||
const title = obj.data.data.title.length > 50 ? `${obj.data.data.title.substring(0, 50)}...` : obj.data.data.title;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
|
@ -50,7 +50,7 @@ export const NoteListElementComponent: FunctionComponent<Props> = (props) => {
|
||||
);
|
||||
const expandComponent = isExpanded ? <NoteListExpandComponent note={props.obj} /> : '';
|
||||
|
||||
const value = props.obj.data.title;
|
||||
const value = props.obj.data.data.title;
|
||||
const title = value.length > 30 ? `${value.substring(0, 30)}...` : value;
|
||||
|
||||
return (
|
||||
|
@ -27,7 +27,8 @@ export const NoteListExpandComponent: FunctionComponent<PinExpandProps> = ({ not
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
if (!ref.current) return;
|
||||
ref.current.innerHTML = marked(note.data.description);
|
||||
const { data } = note.data;
|
||||
ref.current.innerHTML = marked(data.description);
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -37,8 +37,8 @@ export const PdfListElementComponent: FunctionComponent<Props> = (props) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const handleNavigate = async (data: ObjDto<ObjPdfDto>): Promise<void> => {
|
||||
if (PopupActiveTabStore.url?.href !== data.data.url.href) {
|
||||
await BrowserApi.setActiveTabUrl(data.data.url.href);
|
||||
if (PopupActiveTabStore.url?.href !== data.data.data.url.href) {
|
||||
await BrowserApi.setActiveTabUrl(data.data.data.url.href);
|
||||
window.close();
|
||||
}
|
||||
};
|
||||
@ -58,7 +58,7 @@ export const PdfListElementComponent: FunctionComponent<Props> = (props) => {
|
||||
);
|
||||
const expandComponent = isExpanded ? <PdfListExpandComponent obj={props.obj}></PdfListExpandComponent> : '';
|
||||
|
||||
const a = props.obj.data.url.pathname.split('/');
|
||||
const a = props.obj.data.data.url.pathname.split('/');
|
||||
let title = a[a.length - 1];
|
||||
title = title.length > 30 ? `${title.substring(0, 30)}...` : title;
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
|
||||
export const TaskAddComponent: FunctionComponent = () => {
|
||||
return <div>Task add</div>;
|
||||
};
|
@ -1,15 +0,0 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { ObjDto } from '../../../common/model/obj/obj.dto';
|
||||
import { ObjTaskDto } from '../../../common/model/obj/obj-task.dto';
|
||||
|
||||
interface Props {
|
||||
editCallback: (obj: ObjDto<ObjTaskDto>) => void;
|
||||
}
|
||||
|
||||
export const TaskListComponent: FunctionComponent<Props> = () => {
|
||||
return (
|
||||
<div>
|
||||
<h2>List</h2>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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 React, { FunctionComponent, useState } from 'react';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import { TaskListComponent } from './task-list.component';
|
||||
import { fnConsoleLog } from '../../../common/fn/fn-console';
|
||||
|
||||
enum CurrentView {
|
||||
TASK_LIST = 'TASK_LIST',
|
||||
TASK_ADD = 'TASK_ADD',
|
||||
TASK_EDIT = 'TASK_EDIT'
|
||||
}
|
||||
|
||||
export const TaskComponent: FunctionComponent = () => {
|
||||
const [state, setState] = useState<CurrentView>(CurrentView.TASK_LIST);
|
||||
const getComponent = (state: CurrentView) => {
|
||||
switch (state) {
|
||||
case CurrentView.TASK_LIST:
|
||||
return (
|
||||
<TaskListComponent
|
||||
editCallback={() => {
|
||||
fnConsoleLog('editCallback');
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case CurrentView.TASK_ADD:
|
||||
return 'TODO';
|
||||
}
|
||||
};
|
||||
const component = getComponent(state);
|
||||
return (
|
||||
<div>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<h2>Task</h2>
|
||||
<IconButton onClick={() => setState(CurrentView.TASK_ADD)}>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
{component}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -17,8 +17,8 @@
|
||||
import { ObjDto, ObjTypeDto } from '../../../common/model/obj/obj.dto';
|
||||
import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
|
||||
import { BoardStore } from '../../store/board.store';
|
||||
import { ObjNoteDto } from '../../../common/model/obj/obj-note.dto';
|
||||
import { ObjPageDto } from '../../../common/model/obj/obj-page.dto';
|
||||
import { ObjPageNoteDto } from '../../../common/model/obj/obj-note.dto';
|
||||
import { ObjPdfDto } from '../../../common/model/obj/obj-pdf.dto';
|
||||
import { PageNoteElement } from './page-note/page-note.element';
|
||||
import { PageSnapshotElement } from './page-snapshot/page-snapshot.element';
|
||||
@ -84,7 +84,11 @@ export const BoardComponent: FunctionComponent = () => {
|
||||
}
|
||||
case ObjTypeDto.PageNote: {
|
||||
boardElements.push(
|
||||
<PageNoteElement key={obj.id} dto={obj as ObjDto<ObjNoteDto>} refreshBoardCallback={refreshBoardCallback} />
|
||||
<PageNoteElement
|
||||
key={obj.id}
|
||||
dto={obj as ObjDto<ObjPageNoteDto>}
|
||||
refreshBoardCallback={refreshBoardCallback}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
@ -31,13 +31,13 @@ interface Props {
|
||||
|
||||
export const PageNoteElement: FunctionComponent<Props> = (props) => {
|
||||
const [edit, setEdit] = useState<boolean>(false);
|
||||
const [hashtags, setHashtags] = useState<string[]>(props.dto.data.hashtags);
|
||||
const [hashtags, setHashtags] = useState<string[]>(props.dto.data.data.hashtags);
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return;
|
||||
ref.current.innerHTML = marked(props.dto.data.description);
|
||||
ref.current.innerHTML = marked(props.dto.data.data.description);
|
||||
}, []);
|
||||
|
||||
const handleEdit = () => {
|
||||
@ -51,14 +51,14 @@ export const PageNoteElement: FunctionComponent<Props> = (props) => {
|
||||
};
|
||||
|
||||
const handleTagSave = (newTags: string[]) => {
|
||||
props.dto.data.hashtags = newTags;
|
||||
props.dto.data.data.hashtags = newTags;
|
||||
setHashtags(newTags);
|
||||
fnConsoleLog('PageSnapshotElement->handleTagSave->newTags', newTags);
|
||||
};
|
||||
|
||||
return (
|
||||
<BoardItem>
|
||||
<BoardItemTitle title={props.dto.data.title} editCallback={handleEdit} removeCallback={handleRemove} />
|
||||
<BoardItemTitle title={props.dto.data.data.title} editCallback={handleEdit} removeCallback={handleRemove} />
|
||||
<div>
|
||||
<div ref={ref}></div>
|
||||
</div>
|
||||
@ -66,9 +66,9 @@ export const PageNoteElement: FunctionComponent<Props> = (props) => {
|
||||
<BoardItemFooter
|
||||
title="page note"
|
||||
saveTags={handleTagSave}
|
||||
tags={props.dto.data.hashtags}
|
||||
tags={props.dto.data.data.hashtags}
|
||||
createdAt={props.dto.createdAt}
|
||||
words={props.dto.data.words}
|
||||
words={props.dto.data.data.words}
|
||||
url={props.dto.data.url?.href}
|
||||
/>
|
||||
</BoardItem>
|
||||
|
@ -30,9 +30,9 @@ interface Props {
|
||||
|
||||
export const PdfElement: FunctionComponent<Props> = (props) => {
|
||||
const [edit, setEdit] = useState<boolean>(false);
|
||||
const [hashtags, setHashtags] = useState<string[]>(props.dto.data.hashtags || []);
|
||||
const [hashtags, setHashtags] = useState<string[]>(props.dto.data.data.hashtags || []);
|
||||
|
||||
const a = props.dto.data.url.pathname.split('/');
|
||||
const a = props.dto.data.data.url.pathname.split('/');
|
||||
const title = a[a.length - 1];
|
||||
|
||||
const handleEdit = () => {
|
||||
@ -50,7 +50,7 @@ export const PdfElement: FunctionComponent<Props> = (props) => {
|
||||
};
|
||||
|
||||
const handleTagSave = (newTags: string[]) => {
|
||||
props.dto.data.hashtags = newTags;
|
||||
props.dto.data.data.hashtags = newTags;
|
||||
setHashtags(newTags);
|
||||
fnConsoleLog('PageSnapshotElement->handleTagSave->newTags', newTags);
|
||||
};
|
||||
@ -60,7 +60,7 @@ export const PdfElement: FunctionComponent<Props> = (props) => {
|
||||
<BoardItemTitle title={title} htmlCallback={handleHtml} editCallback={handleEdit} removeCallback={handleRemove} />
|
||||
<img
|
||||
style={{ height: '100%', width: '100%', objectFit: 'contain', maxHeight: 220 }}
|
||||
src={props.dto.data.screenshot}
|
||||
src={props.dto.data.data.screenshot}
|
||||
/>
|
||||
<div style={{ display: 'flex', flexGrow: 1 }}></div>
|
||||
<BoardItemFooter
|
||||
@ -69,7 +69,7 @@ export const PdfElement: FunctionComponent<Props> = (props) => {
|
||||
createdAt={props.dto.createdAt}
|
||||
tags={hashtags}
|
||||
words={[]}
|
||||
url={props.dto.data.rawUrl}
|
||||
url={props.dto.data.data.rawUrl}
|
||||
/>
|
||||
</BoardItem>
|
||||
);
|
||||
|
@ -63,7 +63,7 @@ export const PdfPreviewComponent: FunctionComponent<Props> = (props) => {
|
||||
if (!pdfRef.current || !titleRef.current) return;
|
||||
const obj = await new ObjGetCommand<ObjPdfDto>(id).execute();
|
||||
const data = await BrowserStorage.get<string | undefined>(`${ObjectStoreKeys.PDF_DATA}:${obj.data.hash}`);
|
||||
const a = obj.data.url.pathname.split('/');
|
||||
const a = obj.data.data.url.pathname.split('/');
|
||||
|
||||
titleRef.current.innerText = `${a[a.length - 1]}`;
|
||||
|
||||
@ -95,7 +95,7 @@ export const PdfPreviewComponent: FunctionComponent<Props> = (props) => {
|
||||
}
|
||||
}, 250);
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
fnConsoleLog('render->ERROR', e);
|
||||
// eslint-disable-next-line
|
||||
pdfRef.current.innerHTML = `<h1>ERROR RENDERING PDF</h1><br /><p>${e.toString()}</p>`;
|
||||
|
@ -17,9 +17,11 @@
|
||||
export interface BeginTxResponse {
|
||||
tx: string;
|
||||
locked: boolean;
|
||||
lockedBy?: string;
|
||||
lockExpire: number;
|
||||
lockedBy?: string;
|
||||
lockReason?: string;
|
||||
refreshToken: boolean;
|
||||
resetStorage: boolean;
|
||||
}
|
||||
|
||||
enum HashOperation {
|
||||
|
@ -29,37 +29,21 @@ export interface FileDataDto {
|
||||
}
|
||||
|
||||
export class ApiSegmentAddCommand extends ApiCallBase implements ICommand<Promise<boolean>> {
|
||||
constructor(private tx: BeginTxResponse, private file: string, private data: FileDataDto) {
|
||||
constructor(private tx: BeginTxResponse, private file: string | Blob, private data: FileDataDto) {
|
||||
super();
|
||||
}
|
||||
async execute(): Promise<boolean> {
|
||||
await this.initTokenData();
|
||||
if (!this.storeUrl) return false;
|
||||
if (await this.hasSegment()) return this.addRef();
|
||||
if (await this.hasSegment()) return true;
|
||||
return await this.addSegment();
|
||||
}
|
||||
|
||||
async addRef(): Promise<boolean> {
|
||||
if (!this.data.parent) return true;
|
||||
const authHeaders = this.getAuthHeaders(false);
|
||||
const resp = await FetchService.fetch<BeginTxResponse>(
|
||||
`${this.storeUrl!}/api/v1/segment/ref/${this.tx.tx}/${this.data.hash}/${this.data.parent}`,
|
||||
{
|
||||
type: 'TEXT',
|
||||
headers: {
|
||||
...authHeaders
|
||||
}
|
||||
},
|
||||
this.refreshParams()
|
||||
);
|
||||
fnConsoleLog('ApiSegmentAddCommand->addRef', resp);
|
||||
return true;
|
||||
}
|
||||
|
||||
async hasSegment(): Promise<boolean> {
|
||||
const authHeaders = this.getAuthHeaders();
|
||||
const params = this.data.parent ? `?parent=${this.data.parent}` : '';
|
||||
const resp = await FetchService.fetch<BeginTxResponse>(
|
||||
`${this.storeUrl!}/api/v1/segment/${this.data.hash}/has`,
|
||||
`${this.storeUrl!}/api/v1/segment/has/${this.tx.tx}/${this.data.hash}${params}`,
|
||||
{
|
||||
type: 'TEXT',
|
||||
headers: {
|
||||
@ -73,7 +57,8 @@ export class ApiSegmentAddCommand extends ApiCallBase implements ICommand<Promis
|
||||
|
||||
async addSegment(): Promise<boolean> {
|
||||
const formData = new FormData();
|
||||
const fileData = new Blob([this.file], { type: 'application/json' });
|
||||
let fileData = this.file;
|
||||
if (!(this.file instanceof Blob)) fileData = new Blob([this.file], { type: 'application/json' });
|
||||
formData.append('file', fileData);
|
||||
if (this.data.parent) formData.append('parent', this.data.parent);
|
||||
formData.append('key', this.data.key);
|
||||
|
@ -26,7 +26,6 @@ export class SyncObjectCommand implements ICommand<Promise<void>> {
|
||||
constructor(private obj: ObjDto, private hash: string, private tx: BeginTxResponse) {}
|
||||
|
||||
async execute(): Promise<void> {
|
||||
fnConsoleLog('SyncObjectCommand', this.tx);
|
||||
if (this.obj.server?.id) return;
|
||||
const resp: ObjAddResponse | ServerErrorDto = await new ApiObjAddCommand(this.obj, this.hash, this.tx).execute();
|
||||
if ('serverId' in resp) {
|
||||
@ -34,6 +33,7 @@ export class SyncObjectCommand implements ICommand<Promise<void>> {
|
||||
} else if ('code' in resp && resp.code === ApiErrorCode.SYNC_DUPLICATED_HASH) {
|
||||
return await this.setByHash();
|
||||
}
|
||||
fnConsoleLog('SyncObjectCommand', 'tx', this.tx, 'resp', resp);
|
||||
throw new Error('PROBLEM !!!!!!!!!!!!!!!');
|
||||
}
|
||||
|
||||
|
@ -14,20 +14,43 @@
|
||||
* 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 { BrowserStorage } from '@pinmenote/browser-api';
|
||||
import { ICommand } from '../../../../common/model/shared/common.dto';
|
||||
import { ObjDto } from '../../../../common/model/obj/obj.dto';
|
||||
import { ObjPageNoteDto } from '../../../../common/model/obj/obj-note.dto';
|
||||
import { SyncObjectCommand } from './sync-object.command';
|
||||
import { SyncObjectStatus } from '../sync.model';
|
||||
import { SyncHashType, SyncObjectStatus } from '../sync.model';
|
||||
import { fnConsoleLog } from '../../../../common/fn/fn-console';
|
||||
import { BeginTxResponse } from '../../api/store/api-store.model';
|
||||
import { ObjectStoreKeys } from '../../../../common/keys/object.store.keys';
|
||||
import { ApiSegmentAddCommand } from '../../api/store/segment/api-segment-add.command';
|
||||
|
||||
const TEMP_KEY = 'foo';
|
||||
|
||||
export class SyncPageNoteCommand implements ICommand<Promise<SyncObjectStatus>> {
|
||||
constructor(private obj: ObjDto<ObjPageNoteDto>, private tx: BeginTxResponse) {}
|
||||
async execute(): Promise<SyncObjectStatus> {
|
||||
fnConsoleLog('SyncPageNoteCommand');
|
||||
const data = this.obj.data;
|
||||
|
||||
await new SyncObjectCommand(this.obj, data.hash, this.tx).execute();
|
||||
return SyncObjectStatus.SERVER_ERROR;
|
||||
|
||||
await this.syncNote(data);
|
||||
|
||||
return SyncObjectStatus.OK;
|
||||
}
|
||||
|
||||
private async syncNote(data: ObjPageNoteDto): Promise<void> {
|
||||
const content = JSON.stringify(data);
|
||||
await new ApiSegmentAddCommand(this.tx, content, {
|
||||
hash: data.hash,
|
||||
parent: data.hash,
|
||||
type: SyncHashType.ObjPdfDataDto,
|
||||
key: TEMP_KEY
|
||||
}).execute();
|
||||
if (data.prev) {
|
||||
const prevData = await BrowserStorage.get<ObjPageNoteDto>(`${ObjectStoreKeys.NOTE_HASH}:${data.prev}`);
|
||||
fnConsoleLog('SyncPageNoteCommand->prev', data.prev, 'data', prevData);
|
||||
await this.syncNote(prevData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,18 +16,50 @@
|
||||
*/
|
||||
import { ICommand } from '../../../../common/model/shared/common.dto';
|
||||
import { ObjDto } from '../../../../common/model/obj/obj.dto';
|
||||
import { ObjPdfDto } from '../../../../common/model/obj/obj-pdf.dto';
|
||||
import { ObjPdfDataDto, ObjPdfDto } from '../../../../common/model/obj/obj-pdf.dto';
|
||||
import { SyncObjectCommand } from './sync-object.command';
|
||||
import { SyncObjectStatus } from '../sync.model';
|
||||
import { SyncHashType, SyncObjectStatus } from '../sync.model';
|
||||
import { fnConsoleLog } from '../../../../common/fn/fn-console';
|
||||
import { BeginTxResponse } from '../../api/store/api-store.model';
|
||||
import { ObjectStoreKeys } from '../../../../common/keys/object.store.keys';
|
||||
import { BrowserStorage } from '@pinmenote/browser-api';
|
||||
import { fnB64toBlob } from '../../../../common/fn/fn-b64-to-blob';
|
||||
import { ApiSegmentAddCommand } from '../../api/store/segment/api-segment-add.command';
|
||||
|
||||
const TEMP_KEY = 'foo';
|
||||
|
||||
export class SyncPdfCommand implements ICommand<Promise<SyncObjectStatus>> {
|
||||
constructor(private obj: ObjDto<ObjPdfDto>, private tx: BeginTxResponse) {}
|
||||
async execute(): Promise<SyncObjectStatus> {
|
||||
fnConsoleLog('SyncPdfCommand');
|
||||
const data = this.obj.data;
|
||||
await new SyncObjectCommand(this.obj, data.hash, this.tx).execute();
|
||||
return SyncObjectStatus.SERVER_ERROR;
|
||||
|
||||
await this.syncPdf(data.hash);
|
||||
await this.syncData(data.data, data.hash);
|
||||
|
||||
return SyncObjectStatus.OK;
|
||||
}
|
||||
|
||||
private async syncData(data: ObjPdfDataDto, parent: string): Promise<void> {
|
||||
const content = JSON.stringify(data);
|
||||
await new ApiSegmentAddCommand(this.tx, content, {
|
||||
hash: data.hash,
|
||||
parent,
|
||||
type: SyncHashType.ObjPdfDataDto,
|
||||
key: TEMP_KEY
|
||||
}).execute();
|
||||
}
|
||||
|
||||
private async syncPdf(parent: string): Promise<void> {
|
||||
const pdfData = await BrowserStorage.get<string | undefined>(`${ObjectStoreKeys.PDF_DATA}:${parent}`);
|
||||
if (!pdfData) return;
|
||||
const pdfBlob = fnB64toBlob(pdfData);
|
||||
fnConsoleLog('SyncPdfCommand->syncPdf->blob', pdfBlob);
|
||||
await new ApiSegmentAddCommand(this.tx, pdfBlob, {
|
||||
hash: parent,
|
||||
parent,
|
||||
type: SyncHashType.ObjPdf,
|
||||
key: TEMP_KEY
|
||||
}).execute();
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
* 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 { SegmentPage, SegmentType } from '@pinmenote/page-compute';
|
||||
import { SegmentPage, SegmentType, SegmentImg } from '@pinmenote/page-compute';
|
||||
import { ICommand } from '../../../../common/model/shared/common.dto';
|
||||
import { ObjDto } from '../../../../common/model/obj/obj.dto';
|
||||
import { ObjPageDto } from '../../../../common/model/obj/obj-page.dto';
|
||||
@ -25,6 +25,7 @@ import { fnConsoleLog } from '../../../../common/fn/fn-console';
|
||||
import { BeginTxResponse } from '../../api/store/api-store.model';
|
||||
import { PageSnapshotDto } from '../../../../common/model/obj/page-snapshot.dto';
|
||||
import { ApiSegmentAddCommand } from '../../api/store/segment/api-segment-add.command';
|
||||
import { fnB64toBlob } from '../../../../common/fn/fn-b64-to-blob';
|
||||
|
||||
const TEMP_KEY = 'foo';
|
||||
|
||||
@ -42,7 +43,7 @@ export class SyncSnapshotCommand implements ICommand<Promise<SyncObjectStatus>>
|
||||
await this.syncSnapshot(snapshot, snapshot.hash);
|
||||
// TODO await this.syncComments(page.comments, snapshot.hash);
|
||||
if (!snapshot.segment) {
|
||||
fnConsoleLog('SyncSnapshotCommand->OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO', snapshot.segment, this.obj);
|
||||
fnConsoleLog('SyncSnapshotCommand->!!!!!!!!!!!!1SEGMENT->EMPTY', snapshot.segment, this.obj);
|
||||
return SyncObjectStatus.OK;
|
||||
}
|
||||
await this.syncSegment(snapshot.segment, snapshot.hash);
|
||||
@ -73,14 +74,25 @@ export class SyncSnapshotCommand implements ICommand<Promise<SyncObjectStatus>>
|
||||
private async syncSegment(hash: string, parent: string) {
|
||||
const segment = await new PageSegmentGetCommand(hash).execute();
|
||||
if (!segment) return;
|
||||
let content: string | Blob | undefined;
|
||||
if (segment.type === (3 as SegmentType)) return;
|
||||
if (segment.type == SegmentType.IMG) {
|
||||
const src = (segment.content as SegmentImg).src;
|
||||
if (src.startsWith('data:image/svg') || src === 'data:') {
|
||||
content = src;
|
||||
} else {
|
||||
content = fnB64toBlob(src);
|
||||
}
|
||||
} else {
|
||||
content = JSON.stringify(segment.content);
|
||||
}
|
||||
|
||||
const isSynchronized = await new ApiSegmentAddCommand(this.tx, JSON.stringify(segment), {
|
||||
await new ApiSegmentAddCommand(this.tx, content, {
|
||||
hash,
|
||||
parent,
|
||||
type: this.convertSegmentTypeSyncHashType(segment.type),
|
||||
key: TEMP_KEY
|
||||
}).execute();
|
||||
if (isSynchronized) return;
|
||||
|
||||
switch (segment.type) {
|
||||
case SegmentType.SNAPSHOT: {
|
||||
@ -93,12 +105,11 @@ export class SyncSnapshotCommand implements ICommand<Promise<SyncObjectStatus>>
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3 as SegmentType: // FIXME - remove later - SHADOW - obsolete - no longer exists
|
||||
case SegmentType.CSS:
|
||||
case SegmentType.IMG: {
|
||||
break;
|
||||
}
|
||||
case 3 as SegmentType: // FIXME - remove later - SHADOW - obsolete - no longer exists
|
||||
return;
|
||||
case SegmentType.IFRAME: {
|
||||
const content = segment.content as SegmentPage;
|
||||
for (const css of content.css) {
|
||||
@ -126,6 +137,7 @@ export class SyncSnapshotCommand implements ICommand<Promise<SyncObjectStatus>>
|
||||
case SegmentType.SNAPSHOT:
|
||||
return SyncHashType.Snapshot;
|
||||
default:
|
||||
fnConsoleLog('convertSegmentTypeSyncHashType', type);
|
||||
throw new Error('PROBLEM !!!!!!!!!!!!!!!!!!!!');
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +85,9 @@ export class SyncResetProgressCommand implements ICommand<Promise<void>> {
|
||||
});
|
||||
await this.setList(yearMonth, newList);
|
||||
}
|
||||
|
||||
// clear tx
|
||||
await BrowserStorage.remove(ObjectStoreKeys.SYNC_TX);
|
||||
fnConsoleLog('SyncResetProgressCommand->complete in ', Date.now() - a);
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ export class SyncIndexCommand implements ICommand<Promise<SyncObjectStatus>> {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
fnConsoleLog('SyncObjectCommand->PROBLEM', obj.type, 'index', this.index);
|
||||
fnConsoleLog('SyncObjectCommand->PROBLEM', obj, 'index', this.index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export class SyncMonthCommand implements ICommand<Promise<SyncIndex>> {
|
||||
|
||||
const indexListKey = `${ObjectStoreKeys.UPDATED_DT}:${this.yearMonth}`;
|
||||
const indexList = await SyncTxHelper.getList(indexListKey);
|
||||
fnConsoleLog('SyncMonthCommand->syncList', indexList);
|
||||
fnConsoleLog('SyncMonthCommand->syncList', this.yearMonth, 'size', indexList.length);
|
||||
|
||||
if (indexList.length === 0)
|
||||
return { dt: this.progress.timestamp, id: this.progress.id, status: SyncObjectStatus.EMPTY_LIST };
|
||||
@ -64,8 +64,8 @@ export class SyncMonthCommand implements ICommand<Promise<SyncIndex>> {
|
||||
}
|
||||
if ([SyncObjectStatus.INDEX_NOT_EXISTS, SyncObjectStatus.OBJECT_NOT_EXISTS].includes(status)) {
|
||||
fnConsoleLog('syncIndex->PROBLEM !!!!!!!!!!!!!!!!!!!!!!!!', status, 'index', index);
|
||||
await SyncTxHelper.commit();
|
||||
throw new Error(`Status ERROR ${status}`);
|
||||
// await SyncTxHelper.commit();
|
||||
// throw new Error(`Status ERROR ${status}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ export class SyncServerCommand implements ICommand<Promise<void>> {
|
||||
}
|
||||
|
||||
dt.setMonth(dt.getMonth() + 1);
|
||||
fnConsoleLog('sync dt', dt, 'lastDay', lastDay, 'syncResult', syncResult);
|
||||
// fnConsoleLog('sync dt', dt, 'lastDay', lastDay, 'syncResult', syncResult);
|
||||
|
||||
// reset id so we start next month from 0
|
||||
await new SyncSetProgressCommand({
|
||||
|
@ -36,5 +36,8 @@ export enum SyncHashType {
|
||||
IFrame = '3',
|
||||
Img = '4',
|
||||
Css = '5',
|
||||
Snapshot = '6'
|
||||
Snapshot = '6',
|
||||
ObjPdfDataDto = '7',
|
||||
ObjPdf = '8',
|
||||
ObjPageNoteDto = '9'
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user