feat: sync pdf and note

This commit is contained in:
Michal Szczepanski 2023-09-20 03:57:47 +02:00
parent f10d617689
commit 6e1ea32255
36 changed files with 186 additions and 204 deletions

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