From 688cb9654ef1fb69b1c5b50094af23d66765b8df Mon Sep 17 00:00:00 2001 From: Michal Szczepanski Date: Sat, 7 Jan 2023 03:27:37 +0100 Subject: [PATCH] feat: bookmark add / get / remove --- .env | 4 +- .env.development | 4 +- @types | 2 +- .../command/bookmark/bookmark-add.command.ts | 32 +++++++-- .../command/bookmark/bookmark-get.command.ts | 30 ++++++++ .../bookmark/bookmark-remove.command.ts | 34 +++++++-- src/common/command/obj/obj-add-id.command.ts | 4 +- src/common/environment.ts | 4 +- src/common/model/obj.model.ts | 3 - .../pins/object.create.component.tsx | 42 ++++++----- .../view/menu/board-search.input.tsx | 72 +++++++++++++++++++ src/options-ui/view/pin-board/pin.board.tsx | 59 +++------------ 12 files changed, 202 insertions(+), 88 deletions(-) create mode 100644 src/common/command/bookmark/bookmark-get.command.ts create mode 100644 src/options-ui/view/menu/board-search.input.tsx diff --git a/.env b/.env index a531d57..e015247 100644 --- a/.env +++ b/.env @@ -1,4 +1,6 @@ +VERSION=1 API_URL='https://pinmenote.com' SHORT_URL='https://pmn.cl' WEBSITE_URL='https://pinmenote.com' -IS_PRODUCTION=true \ No newline at end of file +IS_PRODUCTION=true +OBJ_LIST_LIMIT=10000 \ No newline at end of file diff --git a/.env.development b/.env.development index 181e2c7..5ed11ef 100644 --- a/.env.development +++ b/.env.development @@ -1,4 +1,6 @@ +VERSION=1 API_URL='http://localhost:3000' SHORT_URL='http://localhost:8001' WEBSITE_URL='http://localhost:4200' -IS_PRODUCTION=false \ No newline at end of file +IS_PRODUCTION=false +OBJ_LIST_LIMIT=10 \ No newline at end of file diff --git a/@types b/@types index 4675716..acd09b0 160000 --- a/@types +++ b/@types @@ -1 +1 @@ -Subproject commit 4675716d73e30b7dd967d51253b6cdf64412e8f0 +Subproject commit acd09b0317ad6153003f753b3e2c56376c64a1d9 diff --git a/src/common/command/bookmark/bookmark-add.command.ts b/src/common/command/bookmark/bookmark-add.command.ts index 1671801..20b3ded 100644 --- a/src/common/command/bookmark/bookmark-add.command.ts +++ b/src/common/command/bookmark/bookmark-add.command.ts @@ -1,4 +1,21 @@ +/* + * 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 . + */ import { BrowserStorageWrapper } from '../../service/browser.storage.wrapper'; +import { ObjAddIdCommand } from '../obj/obj-add-id.command'; import { ObjNextIdCommand } from '../obj/obj-next-id.command'; import { ObjectStoreKeys } from '../../keys/object.store.keys'; import ICommand = Pinmenote.Common.ICommand; @@ -11,20 +28,23 @@ export class BookmarkAddCommand implements ICommand> { async execute(): Promise { const key = `${ObjectStoreKeys.OBJECT_BOOKMARK}:${this.url.href}`; const id = await new ObjNextIdCommand().execute(); + const data: BookmarkDto = { id, value: this.value, - url: this.url, - isDirectory: false + url: this.url }; await BrowserStorageWrapper.set(key, data); - await this.addBookmarkToList(this.url); + + await this.addBookmarkToList(id); + + await new ObjAddIdCommand(id).execute(); return data; } - private async addBookmarkToList(url: PinUrl): Promise { - const bookmarkList = (await BrowserStorageWrapper.get(ObjectStoreKeys.BOOKMARK_LIST)) || []; - bookmarkList.push(url.href); + private async addBookmarkToList(id: number): Promise { + const bookmarkList = (await BrowserStorageWrapper.get(ObjectStoreKeys.BOOKMARK_LIST)) || []; + bookmarkList.push(id); await BrowserStorageWrapper.set(ObjectStoreKeys.BOOKMARK_LIST, bookmarkList); } } diff --git a/src/common/command/bookmark/bookmark-get.command.ts b/src/common/command/bookmark/bookmark-get.command.ts new file mode 100644 index 0000000..2a8b866 --- /dev/null +++ b/src/common/command/bookmark/bookmark-get.command.ts @@ -0,0 +1,30 @@ +/* + * 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 . + */ +import { BrowserStorageWrapper } from '../../service/browser.storage.wrapper'; +import { ObjectStoreKeys } from '../../keys/object.store.keys'; +import BookmarkDto = Pinmenote.Bookmark.BookmarkDto; +import ICommand = Pinmenote.Common.ICommand; +import PinUrl = Pinmenote.Pin.PinUrl; + +export class BookmarkGetCommand implements ICommand> { + constructor(private url: PinUrl) {} + async execute(): Promise { + const key = `${ObjectStoreKeys.OBJECT_BOOKMARK}:${this.url.href}`; + const bookmark = await BrowserStorageWrapper.get(key); + return bookmark; + } +} diff --git a/src/common/command/bookmark/bookmark-remove.command.ts b/src/common/command/bookmark/bookmark-remove.command.ts index 7d1b557..421923f 100644 --- a/src/common/command/bookmark/bookmark-remove.command.ts +++ b/src/common/command/bookmark/bookmark-remove.command.ts @@ -1,22 +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 . + */ import { BrowserStorageWrapper } from '../../service/browser.storage.wrapper'; +import { ObjRemoveIdCommand } from '../obj/obj-remove-id.command'; import { ObjectStoreKeys } from '../../keys/object.store.keys'; +import BookmarkDto = Pinmenote.Bookmark.BookmarkDto; import ICommand = Pinmenote.Common.ICommand; -import PinUrl = Pinmenote.Pin.PinUrl; export class BookmarkRemoveCommand implements ICommand> { - constructor(private url: PinUrl) {} + constructor(private bookmark: BookmarkDto) {} async execute(): Promise { - const key = `${ObjectStoreKeys.OBJECT_BOOKMARK}:${this.url.href}`; + const key = `${ObjectStoreKeys.OBJECT_BOOKMARK}:${this.bookmark.url.href}`; await BrowserStorageWrapper.remove(key); - await this.removeBookmarkFromList(this.url); + + await this.removeBookmarkFromList(this.bookmark.id); + + await new ObjRemoveIdCommand(this.bookmark.id).execute(); } - private async removeBookmarkFromList(url: PinUrl): Promise { - const bookmarkList = (await BrowserStorageWrapper.get(ObjectStoreKeys.BOOKMARK_LIST)) || []; + private async removeBookmarkFromList(id: number): Promise { + const bookmarkList = (await BrowserStorageWrapper.get(ObjectStoreKeys.BOOKMARK_LIST)) || []; await BrowserStorageWrapper.set( ObjectStoreKeys.BOOKMARK_LIST, - bookmarkList.filter((u) => u !== url.href) + bookmarkList.filter((u) => u !== id) ); } } diff --git a/src/common/command/obj/obj-add-id.command.ts b/src/common/command/obj/obj-add-id.command.ts index 4a13732..65f28cc 100644 --- a/src/common/command/obj/obj-add-id.command.ts +++ b/src/common/command/obj/obj-add-id.command.ts @@ -17,10 +17,11 @@ import { BrowserStorageWrapper } from '../../service/browser.storage.wrapper'; import { ObjUpdateLastIdCommand } from './obj-update-last-id.command'; import { ObjectStoreKeys } from '../../keys/object.store.keys'; +import { environmentConfig } from '../../environment'; import ICommand = Pinmenote.Common.ICommand; export class ObjAddIdCommand implements ICommand> { - private readonly listLimit = 10; + private readonly listLimit = environmentConfig.objListLimit; constructor(private id: number) {} async execute(): Promise { @@ -28,6 +29,7 @@ export class ObjAddIdCommand implements ICommand> { let ids = await this.getList(listId); // hit limit so create new list + // this way we get faster writes and can batch if (ids.length >= this.listLimit) { listId += 1; ids = []; diff --git a/src/common/environment.ts b/src/common/environment.ts index 45813be..e0b377c 100644 --- a/src/common/environment.ts +++ b/src/common/environment.ts @@ -33,6 +33,7 @@ interface EnvironmentConfig { isProduction: boolean; settings: SettingsConfig; version: number; + objListLimit: number; } export const environmentConfig: EnvironmentConfig = { @@ -48,5 +49,6 @@ export const environmentConfig: EnvironmentConfig = { borderStyle: '2px solid #ff0000', videoDisplayTime: 5 }, - version: 1 + objListLimit: parseInt(process.env.OBJ_LIST_LIMIT || '100000'), + version: parseInt(process.env.VERSION || '1') }; diff --git a/src/common/model/obj.model.ts b/src/common/model/obj.model.ts index 8a080e2..3f99cbe 100644 --- a/src/common/model/obj.model.ts +++ b/src/common/model/obj.model.ts @@ -21,13 +21,10 @@ export interface ObjLinkDto { export interface ObjIdentityDto { user: string; - fingerprint: string; - group: string; } export interface ObjEncryptionDto { encrypted: boolean; - group: string; } export enum ObjTypeDto { diff --git a/src/default-popup/components/pins/object.create.component.tsx b/src/default-popup/components/pins/object.create.component.tsx index 0c75e01..b3fa6ee 100644 --- a/src/default-popup/components/pins/object.create.component.tsx +++ b/src/default-popup/components/pins/object.create.component.tsx @@ -1,6 +1,6 @@ /* * This file is part of the pinmenote-extension distribution (https://github.com/pinmenote/pinmenote-extension). - * Copyright (c) 2022 Michal Szczepanski. + * 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 @@ -20,6 +20,7 @@ import { ActiveTabStore } from '../../store/active-tab.store'; import AddIcon from '@mui/icons-material/Add'; import { BookmarkAddCommand } from '../../../common/command/bookmark/bookmark-add.command'; import BookmarkBorderIcon from '@mui/icons-material/BookmarkBorder'; +import { BookmarkGetCommand } from '../../../common/command/bookmark/bookmark-get.command'; import BookmarkIcon from '@mui/icons-material/Bookmark'; import { BookmarkRemoveCommand } from '../../../common/command/bookmark/bookmark-remove.command'; import { BrowserApi } from '../../../common/service/browser.api.wrapper'; @@ -27,17 +28,21 @@ import { BusMessageType } from '../../../common/model/bus.model'; import { LogManager } from '../../../common/popup/log.manager'; import { PinPopupInitData } from '../../../common/model/pin.model'; import { TinyEventDispatcher } from '../../../common/service/tiny.event.dispatcher'; +import BookmarkDto = Pinmenote.Bookmark.BookmarkDto; export const ObjectCreateComponent: FunctionComponent = () => { const [isAdding, setIsAdding] = useState(ActiveTabStore.isAddingNote); - const [isBookmarked, setIsBookmarked] = useState(ActiveTabStore.isBookmarked); + const [bookmarkData, setBookmarkData] = useState(undefined); useEffect(() => { const addingKey = TinyEventDispatcher.addListener( BusMessageType.POPUP_INIT, - (event, key, value) => { + async (event, key, value) => { setIsAdding(value.isAddingNote); - setIsBookmarked(value.isBookmarked); + if (ActiveTabStore.url) { + const bookmark = await new BookmarkGetCommand(ActiveTabStore.url).execute(); + setBookmarkData(bookmark); + } } ); return () => { @@ -69,14 +74,14 @@ export const ObjectCreateComponent: FunctionComponent = () => { const handleBookmarkAdd = async () => { if (!ActiveTabStore.url) return; - await new BookmarkAddCommand(ActiveTabStore.pageTitle, ActiveTabStore.url).execute(); - window.close(); + const bookmark = await new BookmarkAddCommand(ActiveTabStore.pageTitle, ActiveTabStore.url).execute(); + setBookmarkData(bookmark); }; const handleBookmarkRemove = async () => { - if (!ActiveTabStore.url) return; - await new BookmarkRemoveCommand(ActiveTabStore.url).execute(); - window.close(); + if (!bookmarkData) return; + await new BookmarkRemoveCommand(bookmarkData).execute(); + setBookmarkData(undefined); }; const pinBtn = isAdding ? ( @@ -89,15 +94,16 @@ export const ObjectCreateComponent: FunctionComponent = () => { ); - const bookmarkBtn = isBookmarked ? ( - - - - ) : ( - - - - ); + const bookmarkBtn = + bookmarkData !== undefined ? ( + + + + ) : ( + + + + ); return (
diff --git a/src/options-ui/view/menu/board-search.input.tsx b/src/options-ui/view/menu/board-search.input.tsx new file mode 100644 index 0000000..f0b6f65 --- /dev/null +++ b/src/options-ui/view/menu/board-search.input.tsx @@ -0,0 +1,72 @@ +/* + * 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 . + */ +import React, { ChangeEvent, FunctionComponent, useState } from 'react'; +import ClearIcon from '@mui/icons-material/Clear'; +import { IconButton } from '@mui/material'; +import Input from '@mui/material/Input'; +import { PinBoardStore } from '../store/pin-board.store'; +import SearchIcon from '@mui/icons-material/Search'; +import { fnConsoleLog } from '../../../common/fn/console.fn'; + +export const BoardSearchInput: FunctionComponent = () => { + const [searchValue, setSearchValue] = useState(PinBoardStore.getSearch() || ''); + + const handleSearchChange = (e: ChangeEvent): void => { + fnConsoleLog('handleSearchChange'); + clearTimeout(PinBoardStore.timeout); + setSearchValue(e.target.value); + PinBoardStore.clearSearch(); + // setPinData([]); + if (e.target.value.length <= 2) { + PinBoardStore.timeout = window.setTimeout(async () => { + await PinBoardStore.sendRange(); + }, 1000); + return; + } else { + PinBoardStore.setSearch(e.target.value); + } + PinBoardStore.timeout = window.setTimeout(async () => { + await PinBoardStore.sendSearch(); + }, 1000); + }; + + const handleClearSearch = async () => { + fnConsoleLog('handleClearSearch'); + setSearchValue(''); + PinBoardStore.clearSearch(); + await PinBoardStore.sendRange(); + }; + return ( +
+ } + placeholder="Find object" + style={{ width: '100%' }} + type="text" + value={searchValue} + onChange={handleSearchChange} + endAdornment={ + PinBoardStore.getSearch() ? ( + + + + ) : undefined + } + /> +
+ ); +}; diff --git a/src/options-ui/view/pin-board/pin.board.tsx b/src/options-ui/view/pin-board/pin.board.tsx index cce5400..fb65788 100644 --- a/src/options-ui/view/pin-board/pin.board.tsx +++ b/src/options-ui/view/pin-board/pin.board.tsx @@ -1,6 +1,6 @@ /* * This file is part of the pinmenote-extension distribution (https://github.com/pinmenote/pinmenote-extension). - * Copyright (c) 2022 Michal Szczepanski. + * 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 @@ -14,23 +14,21 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import React, { ChangeEvent, FunctionComponent, useEffect, useRef, useState } from 'react'; +import React, { FunctionComponent, useEffect, useRef, useState } from 'react'; +import { BoardSearchInput } from '../menu/board-search.input'; import Box from '@mui/material/Box'; import { BusMessageType } from '../../../common/model/bus.model'; -import ClearIcon from '@mui/icons-material/Clear'; import { IconButton } from '@mui/material'; -import Input from '@mui/material/Input'; import { PinBoardStore } from '../store/pin-board.store'; import { PinElement } from './pin.element'; import { PinObject } from '../../../common/model/pin.model'; -import SearchIcon from '@mui/icons-material/Search'; import Stack from '@mui/material/Stack'; import { TinyEventDispatcher } from '../../../common/service/tiny.event.dispatcher'; +import Typography from '@mui/material/Typography'; import { fnConsoleLog } from '../../../common/fn/console.fn'; export const PinBoard: FunctionComponent = () => { const [pinData, setPinData] = useState(PinBoardStore.pins); - const [searchValue, setSearchValue] = useState(PinBoardStore.getSearch() || ''); const stackRef = useRef(); @@ -46,7 +44,7 @@ export const PinBoard: FunctionComponent = () => { BusMessageType.OPTIONS_PIN_SEARCH, (event, key, value) => { PinBoardStore.pins.push(...value); - setSearchValue(PinBoardStore.getSearch() || ''); + // setSearchValue(PinBoardStore.getSearch() || ''); setPinData(PinBoardStore.pins.concat()); PinBoardStore.setLoading(false); } @@ -99,50 +97,13 @@ export const PinBoard: FunctionComponent = () => { }, 250); }; - const handleSearchChange = (e: ChangeEvent): void => { - fnConsoleLog('handleSearchChange'); - clearTimeout(PinBoardStore.timeout); - setSearchValue(e.target.value); - PinBoardStore.clearSearch(); - setPinData([]); - if (e.target.value.length <= 2) { - PinBoardStore.timeout = window.setTimeout(async () => { - await PinBoardStore.sendRange(); - }, 1000); - return; - } else { - PinBoardStore.setSearch(e.target.value); - } - PinBoardStore.timeout = window.setTimeout(async () => { - await PinBoardStore.sendSearch(); - }, 1000); - }; - - const handleClearSearch = async () => { - fnConsoleLog('handleClearSearch'); - setSearchValue(''); - PinBoardStore.clearSearch(); - await PinBoardStore.sendRange(); - }; - return (
- - } - placeholder="Find note" - style={{ width: '50%' }} - type="text" - value={searchValue} - onChange={handleSearchChange} - endAdornment={ - PinBoardStore.getSearch() ? ( - - - - ) : undefined - } - /> + + + + aaaa + {pinData.map((pin) => (