feat: sync get changes from server

This commit is contained in:
Michal Szczepanski 2023-09-22 03:18:30 +02:00
parent 4e3cb1f889
commit 9c8f0804e7
16 changed files with 154 additions and 62 deletions

@ -1,10 +1,8 @@
#!/bin/bash
export NODE_ENV=development
# swap manifests
mv src/manifest.json src/manifest.dev.json
mv src/manifest.ch.json src/manifest.json
cp src/manifest.ch.json src/manifest.json
# build
npm run prod
# swap manifests back
mv src/manifest.json src/manifest.ch.json
mv src/manifest.dev.json src/manifest.json
cp src/manifest.dev.json src/manifest.json

@ -1,10 +1,8 @@
#!/bin/bash
export NODE_ENV=production
# swap manifests
mv src/manifest.json src/manifest.dev.json
mv src/manifest.ch.json src/manifest.json
cp src/manifest.ch.json src/manifest.json
# build
npm run prod
# swap manifests back
mv src/manifest.json src/manifest.ch.json
mv src/manifest.dev.json src/manifest.json
cp src/manifest.dev.json src/manifest.json

@ -1,10 +1,8 @@
#!/bin/bash
export NODE_ENV=development
# swap manifests
mv src/manifest.json src/manifest.ch.json
mv src/manifest.ff.json src/manifest.json
cp src/manifest.ff.json src/manifest.json
# build
npm run dev:ff
# swap manifests back
mv src/manifest.json src/manifest.ff.json
mv src/manifest.ch.json src/manifest.json
cp src/manifest.dev.json src/manifest.json

@ -1,10 +1,8 @@
#!/bin/bash
export NODE_ENV=production
# swap manifests
mv src/manifest.json src/manifest.dev.json
mv src/manifest.ff.json src/manifest.json
cp src/manifest.ff.json src/manifest.json
# build
npm run prod:ff
# swap manifests back
mv src/manifest.json src/manifest.ff.json
mv src/manifest.dev.json src/manifest.json
cp src/manifest.dev.json src/manifest.json

@ -34,10 +34,6 @@ export class ObjGetOriginCommand implements ICommand<Promise<ObjDto<ObjPageDataD
const out: ObjDto<ObjPageDataDto>[] = [];
for (const id of pinIds) {
const obj = await new ObjGetCommand<ObjPageDataDto | ObjPinDto>(id).execute();
if (!obj) {
fnConsoleLog('PROBLEM !!!!!!!!!');
continue;
}
if ([ObjTypeDto.PageSnapshot, ObjTypeDto.PageElementSnapshot].includes(obj.type)) {
if ((obj.data as ObjPageDto).snapshot.info.url.href === this.data.href) continue;
} else if (obj.type === ObjTypeDto.PageElementPin) {
@ -48,8 +44,6 @@ export class ObjGetOriginCommand implements ICommand<Promise<ObjDto<ObjPageDataD
if ((obj.data as ObjPdfDto).data.rawUrl === this.data.href) continue;
}
out.push(obj);
// TODO pagination - now show last 10
if (out.length === 10) break;
}
return out;
}

@ -58,7 +58,7 @@ export const LoginComponent: FunctionComponent<Props> = ({ loginSuccess }) => {
if (value.ok) {
loginSuccess(value.data);
} else {
setResponseError(value.data as ServerErrorDto);
setResponseError(value.data as any as ServerErrorDto);
}
}
);

43
src/manifest.dev.json Normal file

@ -0,0 +1,43 @@
{
"manifest_version": 3,
"name": "pinmenote-dev",
"short_name": "pinmenote-dev",
"description": "Pin note, modify, draw, comment and archive websites.",
"version": "0.1.0",
"icons": {
"16": "assets/icon/16.png",
"32": "assets/icon/32.png",
"64": "assets/icon/64.png",
"128": "assets/icon/128.png"
},
"author": "pinmenote (contact@pintmenote.com)",
"action": {
"default_icon": {
"16": "assets/icon/16.png",
"24": "assets/icon/24.png",
"32": "assets/icon/32.png"
},
"default_title": "pinmenote",
"default_popup": "default-popup/default-popup.html"
},
"permissions": ["activeTab", "storage", "unlimitedStorage", "scripting", "downloads"],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "service-worker/service-worker.ts",
"type": "module"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"all_frames": false,
"js": ["content-script/content-script.ts"],
"run_at": "document_start"
},{
"matches": ["<all_urls>"],
"all_frames": true,
"js": ["iframe/iframe-script.ts"],
"run_at": "document_idle"
}],
"options_ui": {
"page": "options-ui/options-ui.html"
}
}

@ -14,6 +14,8 @@
* 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 { ObjTypeDto } from '../../../../common/model/obj/obj.dto';
export interface BeginTxResponse {
tx: string;
locked: boolean;
@ -24,13 +26,15 @@ export interface BeginTxResponse {
resetStorage: boolean;
}
enum HashOperation {
ADD = 'ADD',
DEL = 'DEL'
}
export interface HashChangeResponse {
export interface ObjSingleChange {
localId: number;
serverId: number;
deletedId: number;
type: ObjTypeDto;
createdAt: number;
operation: HashOperation;
hash: string;
}
export interface ObjChangesResponse {
data: ObjSingleChange[];
}

@ -20,15 +20,7 @@ import { ObjTypeDto } from '../../../../../common/model/obj/obj.dto';
import { ICommand, ServerErrorDto } from '../../../../../common/model/shared/common.dto';
import { ApiErrorCode } from '../../../../../common/model/shared/api.error-code';
import { FetchService } from '@pinmenote/fetch-service';
import { BeginTxResponse } from '../api-store.model';
export interface ObjSingleChange {
serverId: number;
localId: number;
type: ObjTypeDto;
createdAt: number;
hash: string;
}
import { BeginTxResponse, ObjSingleChange } from '../api-store.model';
export class ApiObjGetByHashCommand extends ApiCallBase implements ICommand<Promise<ObjSingleChange | ServerErrorDto>> {
constructor(private hash: string, private tx: BeginTxResponse) {

@ -14,24 +14,21 @@
* 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 { ApiCallBase } from '../api-call.base';
import { ApiCallBase } from '../../api-call.base';
import { FetchService } from '@pinmenote/fetch-service';
import { HashChangeResponse } from './api-store.model';
import { ICommand } from '../../../../common/model/shared/common.dto';
import { fnConsoleLog } from '../../../../common/fn/fn-console';
import { ICommand } from '../../../../../common/model/shared/common.dto';
import { fnConsoleLog } from '../../../../../common/fn/fn-console';
import { ObjChangesResponse } from '../api-store.model';
export class ApiStoreChangesCommand
extends ApiCallBase
implements ICommand<Promise<{ data: HashChangeResponse[] } | undefined>>
{
export class ApiObjGetChangesCommand extends ApiCallBase implements ICommand<Promise<ObjChangesResponse | undefined>> {
constructor() {
super();
}
async execute(): Promise<{ data: HashChangeResponse[] } | undefined> {
async execute(): Promise<ObjChangesResponse | undefined> {
await this.initTokenData();
if (!this.storeUrl) return;
try {
const resp = await FetchService.fetch<{ data: HashChangeResponse[] }>(
const resp = await FetchService.fetch<ObjChangesResponse>(
`${this.storeUrl}/api/v1/obj/changes`,
{ headers: this.getAuthHeaders() },
this.refreshParams()

@ -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 { ApiCallBase } from '../../api-call.base';
import { FetchService } from '@pinmenote/fetch-service';
import { ObjSingleChange } from '../api-store.model';
import { ICommand } from '../../../../../common/model/shared/common.dto';
import { fnConsoleLog } from '../../../../../common/fn/fn-console';
export class ApiObjGetChangesCommand extends ApiCallBase implements ICommand<Promise<ObjSingleChange | undefined>> {
constructor(private id: number) {
super();
}
async execute(): Promise<ObjSingleChange | undefined> {
await this.initTokenData();
if (!this.storeUrl) return;
try {
const resp = await FetchService.fetch<ObjSingleChange>(
`${this.storeUrl}/api/v1/obj/${this.id}`,
{ headers: this.getAuthHeaders() },
this.refreshParams()
);
fnConsoleLog('ApiStoreChangesCommand->response', resp);
return resp.data;
} catch (e) {
fnConsoleLog('ApiStoreChangesCommand->Error', e);
}
}
}

@ -24,20 +24,20 @@ import { SyncProgress } from '../sync.model';
export class SyncGetProgressCommand implements ICommand<Promise<SyncProgress>> {
async execute(): Promise<SyncProgress> {
const sync = await BrowserStorage.get<SyncProgress | undefined>(ObjectStoreKeys.SYNC_PROGRESS);
if (!sync) {
const obj = await SyncGetProgressCommand.getFirstObject();
return { state: 'update', timestamp: obj.createdAt, id: obj.id };
}
return sync;
if (sync) return sync;
const obj = await SyncGetProgressCommand.getFirstObject();
if (!obj) return { timestamp: -1, id: -1, serverId: -1 };
return { timestamp: obj.createdAt, id: obj.id, serverId: -1 };
}
static async getFirstObject(): Promise<ObjDto> {
static async getFirstObject(): Promise<ObjDto | undefined> {
let id = undefined;
let i = 1;
// find first not empty list
while (!id) {
const key = `${ObjectStoreKeys.OBJECT_LIST}:${i}`;
const list = await BrowserStorage.get<number[]>(key);
if (!list) return undefined;
id = list.shift();
i++;
}

@ -27,10 +27,12 @@ import { fnConsoleLog } from '../../../../common/fn/fn-console';
export class SyncResetProgressCommand implements ICommand<Promise<void>> {
async execute(): Promise<void> {
const obj = await SyncGetProgressCommand.getFirstObject();
const timestamp = obj?.createdAt || -1;
const id = obj?.id || -1;
await BrowserStorage.set<SyncProgress>(ObjectStoreKeys.SYNC_PROGRESS, {
state: 'update',
timestamp: obj.createdAt,
id: obj.id
timestamp,
id,
serverId: -1
});
await this.resetObjects();
}

@ -38,8 +38,10 @@ export class SyncMonthCommand implements ICommand<Promise<SyncObjectStatus>> {
const lastIndexElement = indexList[indexList.length - 1];
// fnConsoleLog('SyncMonthCommand->last', lastIndexElement, 'progress', this.progress);
// we are last so escape early, so we don't waste request for begin / commit
if (this.progress.id === lastIndexElement.id && this.progress.timestamp === lastIndexElement.dt)
if (this.progress.id === lastIndexElement.id && this.progress.timestamp === lastIndexElement.dt) {
// TODO check if last object got deleted
return SyncObjectStatus.LAST_ELEMENT;
}
let nextObjectIndex = indexList.findIndex((value) => value.id === this.progress.id);

@ -22,6 +22,10 @@ import { SyncProgress } from './sync.model';
import { SyncTxHelper } from './sync-tx.helper';
import { fnConsoleLog } from '../../../common/fn/fn-console';
import { fnDateKeyFormat } from '../../../common/fn/fn-date-format';
import { TokenStorageGetCommand } from '../../../common/command/server/token/token-storage-get.command';
import jwtDecode from 'jwt-decode';
import { TokenDataDto } from '../../../common/model/shared/token.dto';
import { ApiObjGetChangesCommand } from '../api/store/obj/api-obj-get-changes.command';
export class SyncServerCommand implements ICommand<Promise<void>> {
private static isInSync = false;
@ -32,11 +36,15 @@ export class SyncServerCommand implements ICommand<Promise<void>> {
try {
// await new SyncResetProgressCommand().execute();
const token = await new TokenStorageGetCommand().execute();
if (token) console.log(jwtDecode<TokenDataDto>(token.access_token));
SyncServerCommand.isInSync = true;
const a = Date.now();
const progress = await new SyncGetProgressCommand().execute();
await this.sync(progress);
await this.syncOutgoing(progress);
await this.syncIncoming(progress);
fnConsoleLog('SyncServerCommand->execute', progress, 'in', Date.now() - a);
} finally {
@ -44,7 +52,23 @@ export class SyncServerCommand implements ICommand<Promise<void>> {
}
}
private async sync(progress: SyncProgress): Promise<void> {
private async syncIncoming(progress: SyncProgress): Promise<void> {
fnConsoleLog('SyncServerCommand->syncIncoming');
const changesResp = await new ApiObjGetChangesCommand().execute();
if (!changesResp) return;
for (const change of changesResp.data) {
console.log(change);
break;
}
const token = await new TokenStorageGetCommand().execute();
if (!token) return;
const tokenData = jwtDecode(token.access_token);
console.log('tokenData', tokenData);
}
private async syncOutgoing(progress: SyncProgress): Promise<void> {
// Empty list - fresh install
if (progress.id == -1) return;
const dt = fnDateToMonthFirstDay(new Date(progress.timestamp));
const lastDay = fnMonthLastDay();

@ -16,8 +16,8 @@
*/
export interface SyncProgress {
timestamp: number;
state: 'update' | 'remove' | 'remote';
id: number;
serverId: number;
}
export enum SyncObjectStatus {