feat: option board view edit title
This commit is contained in:
parent
0089f5d789
commit
2c7c8342d3
30
README.md
30
README.md
|
@ -2,7 +2,14 @@ pinmenote-extension
|
|||
---
|
||||
|
||||
### Description
|
||||
Browser extension nobody wants and nobody needs
|
||||
Browser extension nobody wants and nobody needs.
|
||||
Thank You life for opportunity to make it.
|
||||
Thank you people for not using it.
|
||||
Hope you will not start using this software, if you started using this software - please stop.
|
||||
If you want to make PR because you believe you found bug, don't do it, fork this repository and make your own product, let people use it and be happy.
|
||||
I didn't make this software, God did it so don't ask me questions how and why it works, ask God.
|
||||
God knows all the answers I know only answer to ultimate question.
|
||||
Answer to ultimate question is 42.
|
||||
|
||||
- offline first with p2p in mind
|
||||
|
||||
|
@ -12,17 +19,26 @@ npm run dev
|
|||
```
|
||||
|
||||
### Website
|
||||
[https://pinmenote.com](https://pinmenote.com) - subscribe to premium to move project further
|
||||
[https://pinmenote.com](https://pinmenote.com)
|
||||
|
||||
### Features
|
||||
- select website area, add comment and draw on selected part
|
||||
- save website / part of website as single page for offline use ( state of the art )
|
||||
- capture image / map / canvas
|
||||
- capture video time
|
||||
- download table as csv
|
||||
- add task, note to any website as well as standalone
|
||||
- encrypt / decrypt messages
|
||||
- html preview and board view
|
||||
- add and manage tags of any content you saved
|
||||
- add calendar event (repeat in progress)
|
||||
- smart search using index of words (reverse index), dates, website addresses
|
||||
|
||||
### Known issues
|
||||
|
||||
#### Shadow root
|
||||
Open shadow root is not always displayed or parsed correctly - but at least it matches mht quality.
|
||||
Open shadow root is not always displayed or parsed correctly - but at least it matches mht quality or sometimes it's better.
|
||||
|
||||
#### Youtube
|
||||
Youtube use polymer dom with ```<!--css-build:shady-->```.
|
||||
It is some polymer polyfil for shadow root that messes with css.
|
||||
|
||||
#### Twitter
|
||||
Use style ```transform: translateY(4620.5px); position: absolute;``` for single tweets
|
||||
(todo remove those style attributes from tweet when saving element)
|
|
@ -27,9 +27,10 @@ export class ObjAddUpdatedDateIndexCommand implements ICommand<Promise<void>> {
|
|||
const key = `${ObjectStoreKeys.UPDATED_DT}:${yearMonth}`;
|
||||
|
||||
const ids = await this.getList(key);
|
||||
ids.push(this.id);
|
||||
|
||||
await BrowserStorageWrapper.set(key, ids);
|
||||
if (ids.indexOf(this.id) === -1) {
|
||||
ids.push(this.id);
|
||||
await BrowserStorageWrapper.set(key, ids);
|
||||
}
|
||||
}
|
||||
|
||||
private async getList(key: string): Promise<number[]> {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
import { BrowserStorageWrapper } from '../../service/browser.storage.wrapper';
|
||||
import { ICommand } from '../../model/shared/common.dto';
|
||||
import { ObjAddUpdatedDateIndexCommand } from '../obj/date-index/obj-add-updated-date-index.command';
|
||||
import { ObjDto } from '../../model/obj/obj.dto';
|
||||
import { ObjPagePinDto } from '../../model/obj/obj-pin.dto';
|
||||
import { ObjectStoreKeys } from '../../keys/object.store.keys';
|
||||
|
@ -24,9 +25,11 @@ import { fnConsoleLog } from '../../fn/console.fn';
|
|||
export class PinUpdateCommand implements ICommand<void> {
|
||||
constructor(private obj: ObjDto<ObjPagePinDto>) {}
|
||||
async execute(): Promise<void> {
|
||||
fnConsoleLog('WorkerPinManager->pinUpdate', this.obj);
|
||||
fnConsoleLog('PinUpdateCommand->execute', this.obj);
|
||||
const key = `${ObjectStoreKeys.OBJECT_ID}:${this.obj.id}`;
|
||||
|
||||
await new ObjAddUpdatedDateIndexCommand(this.obj.id, this.obj.updatedAt).execute();
|
||||
|
||||
await BrowserStorageWrapper.set(key, this.obj);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 { BrowserStorageWrapper } from '../../service/browser.storage.wrapper';
|
||||
import { ICommand } from '../../model/shared/common.dto';
|
||||
import { ObjAddUpdatedDateIndexCommand } from '../obj/date-index/obj-add-updated-date-index.command';
|
||||
import { ObjDto } from '../../model/obj/obj.dto';
|
||||
import { ObjSnapshotDto } from '../../model/obj/obj-snapshot.dto';
|
||||
import { ObjectStoreKeys } from '../../keys/object.store.keys';
|
||||
import { fnConsoleLog } from '../../fn/console.fn';
|
||||
|
||||
export class PageSnapshotUpdateCommand implements ICommand<Promise<void>> {
|
||||
constructor(private obj: ObjDto<ObjSnapshotDto>) {}
|
||||
|
||||
async execute(): Promise<void> {
|
||||
fnConsoleLog('PageSnapshotUpdateCommand->execute', this.obj);
|
||||
const key = `${ObjectStoreKeys.OBJECT_ID}:${this.obj.id}`;
|
||||
|
||||
await new ObjAddUpdatedDateIndexCommand(this.obj.id, this.obj.updatedAt).execute();
|
||||
|
||||
await BrowserStorageWrapper.set(key, this.obj);
|
||||
}
|
||||
}
|
|
@ -20,4 +20,6 @@ export interface ObjNoteDto {
|
|||
title: string;
|
||||
value: string;
|
||||
url?: ObjUrlDto;
|
||||
words: string[];
|
||||
hashtags: string[];
|
||||
}
|
||||
|
|
|
@ -20,4 +20,6 @@ export interface ObjTaskDto {
|
|||
title: string;
|
||||
description: string;
|
||||
url?: ObjUrlDto;
|
||||
words: string[];
|
||||
hashtags: string[];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 Button from '@mui/material/Button';
|
||||
import TextareaAutosize from '@mui/material/TextareaAutosize';
|
||||
|
||||
interface TitleEditComponentProps {
|
||||
value: string;
|
||||
saveCallback: (value: string) => void;
|
||||
cancelCallback: () => void;
|
||||
}
|
||||
|
||||
export const TitleEditComponent: FunctionComponent<TitleEditComponentProps> = (props) => {
|
||||
const [value, setValue] = useState<string>(props.value);
|
||||
return (
|
||||
<div style={{ width: '100%' }}>
|
||||
<div>
|
||||
<TextareaAutosize cols={40} value={value} onChange={(e) => setValue(e.target.value)} />
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<Button onClick={() => props.cancelCallback()}>Cancel</Button>
|
||||
<Button onClick={() => props.saveCallback(value)}>Save</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -14,16 +14,19 @@
|
|||
* 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, useEffect, useRef } from 'react';
|
||||
import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
|
||||
import { BoardStore } from '../../../store/board.store';
|
||||
import { BusMessageType } from '../../../../common/model/bus.model';
|
||||
import ClearIcon from '@mui/icons-material/Clear';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import HtmlIcon from '@mui/icons-material/Html';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Link from '@mui/material/Link';
|
||||
import { ObjDto } from '../../../../common/model/obj/obj.dto';
|
||||
import { ObjSnapshotDto } from '../../../../common/model/obj/obj-snapshot.dto';
|
||||
import { PageSnapshotUpdateCommand } from '../../../../common/command/snapshot/page-snapshot-update.command';
|
||||
import { TinyEventDispatcher } from '../../../../common/service/tiny.event.dispatcher';
|
||||
import { TitleEditComponent } from '../edit/title-edit.component';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
interface PageElementSnapshotElementParams {
|
||||
|
@ -35,6 +38,7 @@ export const PageElementSnapshotElement: FunctionComponent<PageElementSnapshotEl
|
|||
dto,
|
||||
refreshBoardCallback
|
||||
}) => {
|
||||
const [editTitle, setEditTitle] = useState<boolean>(false);
|
||||
const divRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -46,6 +50,10 @@ export const PageElementSnapshotElement: FunctionComponent<PageElementSnapshotEl
|
|||
}
|
||||
});
|
||||
|
||||
const handleEdit = () => {
|
||||
setEditTitle(true);
|
||||
};
|
||||
|
||||
const handleHtml = () => {
|
||||
TinyEventDispatcher.dispatch<ObjSnapshotDto>(BusMessageType.OPT_SHOW_HTML, dto.data);
|
||||
};
|
||||
|
@ -56,15 +64,38 @@ export const PageElementSnapshotElement: FunctionComponent<PageElementSnapshotEl
|
|||
}
|
||||
};
|
||||
|
||||
const titleSaveCallback = async (value: string) => {
|
||||
if (dto.data.title !== value) {
|
||||
dto.data.title = value;
|
||||
dto.updatedAt = Date.now();
|
||||
await new PageSnapshotUpdateCommand(dto).execute();
|
||||
}
|
||||
setEditTitle(false);
|
||||
};
|
||||
|
||||
const titleCancelCallback = () => {
|
||||
setEditTitle(false);
|
||||
};
|
||||
|
||||
const title = dto.data.title.length > 50 ? `${dto.data.title.substring(0, 50)}...` : dto.data.title;
|
||||
const url =
|
||||
decodeURI(dto.data.url.href).length > 50
|
||||
? decodeURI(dto.data.url.href).substring(0, 50)
|
||||
: decodeURI(dto.data.url.href);
|
||||
const titleElement = editTitle ? (
|
||||
<TitleEditComponent value={dto.data.title} saveCallback={titleSaveCallback} cancelCallback={titleCancelCallback} />
|
||||
) : (
|
||||
<h2 style={{ wordWrap: 'break-word', width: '80%' }}>{title}</h2>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', maxWidth: window.innerWidth / 4, margin: 10 }}>
|
||||
<h2 style={{ wordWrap: 'break-word' }}>{title}</h2>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
{titleElement}
|
||||
<IconButton onClick={handleEdit} style={{ display: editTitle ? 'none' : 'inline-block' }}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<IconButton title="HTML view" onClick={handleHtml}>
|
||||
<HtmlIcon />
|
||||
|
|
|
@ -14,16 +14,19 @@
|
|||
* 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, useEffect, useRef } from 'react';
|
||||
import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
|
||||
import { BoardStore } from '../../../store/board.store';
|
||||
import { BusMessageType } from '../../../../common/model/bus.model';
|
||||
import ClearIcon from '@mui/icons-material/Clear';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import HtmlIcon from '@mui/icons-material/Html';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Link from '@mui/material/Link';
|
||||
import { ObjDto } from '../../../../common/model/obj/obj.dto';
|
||||
import { ObjSnapshotDto } from '../../../../common/model/obj/obj-snapshot.dto';
|
||||
import { PageSnapshotUpdateCommand } from '../../../../common/command/snapshot/page-snapshot-update.command';
|
||||
import { TinyEventDispatcher } from '../../../../common/service/tiny.event.dispatcher';
|
||||
import { TitleEditComponent } from '../edit/title-edit.component';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
interface PageSnapshotElementParams {
|
||||
|
@ -32,6 +35,7 @@ interface PageSnapshotElementParams {
|
|||
}
|
||||
|
||||
export const PageSnapshotElement: FunctionComponent<PageSnapshotElementParams> = ({ dto, refreshBoardCallback }) => {
|
||||
const [editTitle, setEditTitle] = useState<boolean>(false);
|
||||
const divRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -43,6 +47,10 @@ export const PageSnapshotElement: FunctionComponent<PageSnapshotElementParams> =
|
|||
}
|
||||
});
|
||||
|
||||
const handleEdit = () => {
|
||||
setEditTitle(true);
|
||||
};
|
||||
|
||||
const handleHtml = () => {
|
||||
TinyEventDispatcher.dispatch<ObjSnapshotDto>(BusMessageType.OPT_SHOW_HTML, dto.data);
|
||||
};
|
||||
|
@ -53,15 +61,38 @@ export const PageSnapshotElement: FunctionComponent<PageSnapshotElementParams> =
|
|||
}
|
||||
};
|
||||
|
||||
const titleSaveCallback = async (value: string) => {
|
||||
if (dto.data.title !== value) {
|
||||
dto.data.title = value;
|
||||
dto.updatedAt = Date.now();
|
||||
await new PageSnapshotUpdateCommand(dto).execute();
|
||||
}
|
||||
setEditTitle(false);
|
||||
};
|
||||
|
||||
const titleCancelCallback = () => {
|
||||
setEditTitle(false);
|
||||
};
|
||||
|
||||
const title = dto.data.title.length > 50 ? `${dto.data.title.substring(0, 50)}...` : dto.data.title;
|
||||
const url =
|
||||
decodeURI(dto.data.url.href).length > 50
|
||||
? decodeURI(dto.data.url.href).substring(0, 50)
|
||||
: decodeURI(dto.data.url.href);
|
||||
const titleElement = editTitle ? (
|
||||
<TitleEditComponent value={dto.data.title} saveCallback={titleSaveCallback} cancelCallback={titleCancelCallback} />
|
||||
) : (
|
||||
<h2 style={{ wordWrap: 'break-word', width: '80%' }}>{title}</h2>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', maxWidth: window.innerWidth / 4, margin: 10 }}>
|
||||
<h2 style={{ wordWrap: 'break-word' }}>{title}</h2>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
{titleElement}
|
||||
<IconButton onClick={handleEdit} style={{ display: editTitle ? 'none' : 'inline-block' }}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<IconButton title="HTML view" onClick={handleHtml}>
|
||||
<HtmlIcon />
|
||||
|
|
|
@ -14,17 +14,20 @@
|
|||
* 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, useEffect, useRef } from 'react';
|
||||
import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
|
||||
import { BoardStore } from '../../../store/board.store';
|
||||
import { BusMessageType } from '../../../../common/model/bus.model';
|
||||
import ClearIcon from '@mui/icons-material/Clear';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import HtmlIcon from '@mui/icons-material/Html';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Link from '@mui/material/Link';
|
||||
import { ObjDto } from '../../../../common/model/obj/obj.dto';
|
||||
import { ObjPagePinDto } from '../../../../common/model/obj/obj-pin.dto';
|
||||
import { ObjSnapshotDto } from '../../../../common/model/obj/obj-snapshot.dto';
|
||||
import { PinUpdateCommand } from '../../../../common/command/pin/pin-update.command';
|
||||
import { TinyEventDispatcher } from '../../../../common/service/tiny.event.dispatcher';
|
||||
import { TitleEditComponent } from '../edit/title-edit.component';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
interface PinElementParams {
|
||||
|
@ -33,6 +36,7 @@ interface PinElementParams {
|
|||
}
|
||||
|
||||
export const PinElement: FunctionComponent<PinElementParams> = ({ dto, refreshBoardCallback }) => {
|
||||
const [editTitle, setEditTitle] = useState<boolean>(false);
|
||||
const divRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -44,6 +48,10 @@ export const PinElement: FunctionComponent<PinElementParams> = ({ dto, refreshBo
|
|||
}
|
||||
});
|
||||
|
||||
const handleEdit = () => {
|
||||
setEditTitle(true);
|
||||
};
|
||||
|
||||
const handleHtml = () => {
|
||||
TinyEventDispatcher.dispatch<ObjSnapshotDto>(BusMessageType.OPT_SHOW_HTML, dto.data.snapshot);
|
||||
};
|
||||
|
@ -54,16 +62,43 @@ export const PinElement: FunctionComponent<PinElementParams> = ({ dto, refreshBo
|
|||
}
|
||||
};
|
||||
|
||||
const titleSaveCallback = async (value: string) => {
|
||||
if (dto.data.snapshot.title !== value) {
|
||||
dto.data.snapshot.title = value;
|
||||
dto.updatedAt = Date.now();
|
||||
await new PinUpdateCommand(dto).execute();
|
||||
}
|
||||
setEditTitle(false);
|
||||
};
|
||||
|
||||
const titleCancelCallback = () => {
|
||||
setEditTitle(false);
|
||||
};
|
||||
|
||||
const title =
|
||||
dto.data.snapshot.title.length > 50 ? `${dto.data.snapshot.title.substring(0, 50)}...` : dto.data.snapshot.title;
|
||||
const url =
|
||||
decodeURI(dto.data.snapshot.url.href).length > 50
|
||||
? decodeURI(dto.data.snapshot.url.href).substring(0, 50)
|
||||
: decodeURI(dto.data.snapshot.url.href);
|
||||
const titleElement = editTitle ? (
|
||||
<TitleEditComponent
|
||||
value={dto.data.snapshot.title}
|
||||
saveCallback={titleSaveCallback}
|
||||
cancelCallback={titleCancelCallback}
|
||||
/>
|
||||
) : (
|
||||
<h2 style={{ wordWrap: 'break-word', width: '80%' }}>{title}</h2>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', maxWidth: window.innerWidth / 4, margin: 10 }}>
|
||||
<h2 style={{ wordWrap: 'break-word' }}>{title}</h2>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
{titleElement}
|
||||
<IconButton onClick={handleEdit} style={{ display: editTitle ? 'none' : 'inline-block' }}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<IconButton title="HTML view" onClick={handleHtml}>
|
||||
<HtmlIcon />
|
||||
|
|
Loading…
Reference in New Issue