1
0
Fork 0
mirror of https://github.com/anyproto/anytype-ts.git synced 2025-06-08 05:57:02 +09:00

Merge pull request #664 from anytypeio/feature/599

Feature/599: History
This commit is contained in:
Razor 2020-09-30 23:33:34 -07:00 committed by GitHub
commit 5ccfc3cf71
46 changed files with 870 additions and 159 deletions

5
dist/.gitignore vendored
View file

@ -17,6 +17,11 @@ main.js
main.js.map
commands.js
anytypeHelper*
alpha-linux.yml
*.snap
beta-linux.yml
latest-linux.yml
linux-unpacked/
*.snap
*.deb
*.rpm

View file

@ -470,25 +470,19 @@ function menuInit () {
{
label: 'Interface', type: 'checkbox', checked: config.debugUI,
click: () => {
setConfig({ debugUI: !config.debugUI }, () => {
send('toggleDebug', 'ui', config.debugUI);
});
setConfig({ debugUI: !config.debugUI });
}
},
{
label: 'Middleware', type: 'checkbox', checked: config.debugMW,
click: () => {
setConfig({ debugMW: !config.debugMW }, () => {
send('toggleDebug', 'mw', config.debugMW);
});
setConfig({ debugMW: !config.debugMW });
}
},
{
label: 'Analytics', type: 'checkbox', checked: config.debugAN,
click: () => {
setConfig({ debugAN: !config.debugAN }, () => {
send('toggleDebug', 'an', config.debugAN);
});
setConfig({ debugAN: !config.debugAN });
}
},
]
@ -499,9 +493,7 @@ function menuInit () {
},
{
label: 'Dev Tools', accelerator: 'Alt+CmdOrCtrl+I',
click: () => {
win.webContents.openDevTools();
}
click: () => { win.webContents.openDevTools(); }
}
]
});
@ -524,6 +516,7 @@ function setChannel (channel) {
function setConfig (obj, callBack) {
config = Object.assign(config, obj);
storage.set('config', config, (error) => {
send('config', config);
if (callBack) {
callBack(error);
};

View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5303 2.46967C12.2374 2.17678 11.7626 2.17678 11.4697 2.46967L3.93934 10L11.4697 17.5303C11.7626 17.8232 12.2374 17.8232 12.5303 17.5303C12.8232 17.2374 12.8232 16.7626 12.5303 16.4697L6.06066 10L12.5303 3.53033C12.8232 3.23744 12.8232 2.76256 12.5303 2.46967Z" fill="#ACA996"/>
</svg>

After

Width:  |  Height:  |  Size: 433 B

View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5303 2.46967C12.2374 2.17678 11.7626 2.17678 11.4697 2.46967L3.93934 10L11.4697 17.5303C11.7626 17.8232 12.2374 17.8232 12.5303 17.5303C12.8232 17.2374 12.8232 16.7626 12.5303 16.4697L6.06066 10L12.5303 3.53033C12.8232 3.23744 12.8232 2.76256 12.5303 2.46967Z" fill="#2c2b27"/>
</svg>

After

Width:  |  Height:  |  Size: 433 B

View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.19283 5.1772C8.42714 4.94289 8.80704 4.94289 9.04135 5.1772L13.8656 10.0015L9.04135 14.8257C8.80704 15.06 8.42714 15.06 8.19283 14.8257C7.95851 14.5914 7.95851 14.2115 8.19283 13.9772L12.1686 10.0015L8.19283 6.02573C7.95851 5.79141 7.95851 5.41152 8.19283 5.1772Z" fill="#2C2B27"/>
</svg>

After

Width:  |  Height:  |  Size: 437 B

View file

@ -0,0 +1,10 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.75 10C16.75 13.4518 13.9518 16.25 10.5 16.25C8.77411 16.25 7.21161 15.5504 6.08058 14.4194L5.01992 15.4801C6.4224 16.8826 8.3599 17.75 10.5 17.75C14.7802 17.75 18.25 14.2802 18.25 10C18.25 5.71979 14.7802 2.25 10.5 2.25C6.21979 2.25 2.75 5.71979 2.75 10H2.7207V10.16L1.03039 8.46973L-0.0302734 9.53039L3.50006 13.0607L7.03039 9.53039L5.96973 8.46973L4.2207 10.2187V10H4.25C4.25 6.54822 7.04822 3.75 10.5 3.75C13.9518 3.75 16.75 6.54822 16.75 10ZM9.75 10.386V5H11.25V9.61404L14.4359 11.8897L13.5641 13.1103L9.75 10.386Z" fill="#ACA996"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 818 B

View file

@ -47,6 +47,11 @@
}
.block > .wrapContent > .children > .block.last { margin: 0px; }
.block.isReadOnly {
.colResize { display: none; }
}
.block.isReadOnly > .wrapMenu > .icon.dnd { display: none; }
.block.align0 > .wrapContent { text-align: left; }
.block.align1 > .wrapContent { text-align: center; }

View file

@ -45,6 +45,9 @@
.icon.play:hover { background-color: rgba(255,255,255,0.9); }
}
.block.blockMedia > .wrapContent > .selectable > .dropTarget { line-height: 0px; }
.block.blockMedia.isReadOnly > .wrapContent > .selectable > .dropTarget .icon.resize { display: none; }
.block.blockMedia.isReadOnly > .wrapContent > .selectable > .dropTarget .icon.play { display: none; }
.block.blockMedia:hover {
.icon.resize { opacity: 1; }

View file

@ -5,6 +5,7 @@
.block.blockText {
.wrap { flex: auto; position: relative; padding: 0px; width: 100%; }
.value { -webkit-user-modify: read-write-plaintext-only; width: 100%; overflow: hidden; }
.placeHolder {
color: $colorDarkGrey; position: absolute; z-index: 0; left: 0px; top: 0px; display: none; overflow: hidden;
width: 100%; height: 100%; cursor: text; pointer-events: none;
@ -71,9 +72,17 @@
.marker.check, .marker.toggle {
img { width: 100%; height: 100%; }
}
}
}
.block.blockText.isReadOnly > .wrapContent > .selectable > .dropTarget .marker.check { cursor: default; }
.block.blockText.isReadOnly > .wrapContent > .selectable > .dropTarget .value { min-height: 24px; }
.block.blockText.isReadOnly > .wrapContent > .selectable > .dropTarget .placeHolder { display: none; }
.block.blockText.isReadOnly.code {
.select { display: none; }
}
.block.blockText.isReadOnly.code > .wrapContent > .selectable > .dropTarget { padding: 16px; }
.block.blockText.isChecked > .wrapContent > .selectable > .dropTarget * { color: $colorDarkGrey !important; }
@ -115,7 +124,7 @@
}
.select:hover, .select.active { background: none; }
}
.block.blockText.paragraph > .wrapContent.bgColor > .selectable > .dropTarget,
.block.blockText.header1 > .wrapContent.bgColor > .selectable > .dropTarget,
.block.blockText.header2 > .wrapContent.bgColor > .selectable > .dropTarget,

View file

@ -81,6 +81,7 @@ body.platformWindows {
.through { pointer-events: none; }
.rel { position: relative; }
.disabled { opacity: 0.5 !important; cursor: default !important; }
.readOnly { -webkit-user-modify: read-only !important; }
.fileWrap { position: relative; overflow: hidden; cursor: pointer; }
.fileWrap {

View file

@ -6,8 +6,8 @@
.icon.back { margin-right: 8px; }
.icon.logo {
width: 60px; height: 13px; position: absolute; left: 50%; margin-left: -30px; top: 13px; background: url('~img/logo.svg') no-repeat;
background-position: center center; cursor: default;
width: 60px !important; height: 13px !important; position: absolute; left: 50%; margin-left: -30px; top: 13px; background: url('~img/logo.svg') no-repeat;
background-position: center center; background-size: 100% 100% !important;
}
.path { white-space: nowrap; }
@ -45,34 +45,29 @@
.icon.add { background-image: url('~img/icon/header/add.svg'); }
.icon.settings { background-image: url('~img/icon/header/settings.svg'); }
.side.right { position: absolute; top: 5px; right: 6px; }
.side { position: absolute; top: 5px; height: 28px; line-height: 28px; }
.side.left { left: 6px; z-index: 1; }
.side.center { width: 492px; height: 28px; left: 50%; margin-left: -246px; white-space: nowrap; }
.side.right { right: 6px; z-index: 1; }
.side.right {
.icon { margin: 0px; }
}
}
.headerMainIndex { height: 38px; background: rgba(44,43,39,0.1); }
.headerMainIndex {
.header.headerMainIndex { height: 38px; background: rgba(44,43,39,0.1); }
.header.headerMainIndex {
.icon { width: 40px; height: 28px; background-size: 20px 20px; border-radius: 6px; }
.icon:hover { background-color: rgba(0,0,0,0.15); }
}
.headerMainEditSearch { background: #fff; padding: 5px 6px; height: 38px; }
.headerMainEditSearch {
.header.headerMainEdit { background: #fff; height: 38px; line-height: 20px; }
.header.headerMainEdit {
.icon { width: 40px; height: 28px; background-size: 20px 20px; border-radius: 6px; }
.icon:hover { background-color: #f3f2ec; }
.mid { width: 492px; height: 28px; position: absolute; left: 50%; margin-left: -246px; top: 5px; white-space: nowrap; }
.mid {
.icon { margin: 0px; opacity: 0; }
.icon.plus {
.arrow {
width: 8px; height: 8px; background-image: url('~img/icon/header/arrow.svg'); position: absolute; right: -12px;
top: 10px; margin: 0px; background-color: none !important; background-size: 100% 100%;
}
}
}
.path {
border: 1px solid #fff; border-radius: 6px; display: inline-block; vertical-align: top; height: 28px; margin: 0px 6px;
line-height: 24px; width: 400px; text-align: center; transition: $transitionFast; -webkit-app-region: no-drag;
@ -82,18 +77,53 @@
}
.path * { pointer-events: all; }
.side { position: absolute; top: 5px; }
.side.left { left: 6px; opacity: 0; transition: $transitionFast; }
.side.left { opacity: 0; transition: $transitionFast; }
.side.left {
.icon { margin: 0px; }
.icon.back, .icon.forward { width: 32px; }
}
.side.center {
.icon { margin: 0px; opacity: 0; }
.icon.plus {
.arrow {
width: 8px; height: 8px; background-image: url('~img/icon/header/arrow.svg'); position: absolute; right: -12px;
top: 10px; margin: 0px; background-color: none !important; background-size: 100% 100%;
}
}
}
}
.header.headerMainHistory { height: 38px; line-height: 22px; padding: 8px 16px; width: calc(100% - 254px); background: #fff; }
.header.headerMainHistory {
.side { height: 20px; top: 8px; }
.side.center { text-align: center; }
.side.right { right: 16px; }
.side {
.item { color: #6c6a5f; transition: $transitionFast; cursor: pointer; height: 20px; line-height: 20px; }
.item {
.icon.arrow { background-image: url('~img/arrow/history0.svg'); margin: 0px; }
}
.item.orange { color: #e89d00; font-weight: 500; }
.item.grey:hover { color: $colorBlack; }
.item.orange:hover { color: $colorBlack; }
.item:hover {
.icon.arrow { background-image: url('~img/arrow/history1.svg'); }
}
}
}
body.platformMac {
.headerMainEditSearch {
.header {
.side.left { left: 90px; }
}
.header.headerMainHistory {
.side.left { left: 80px; }
}
}
html.fullScreen {
@ -102,9 +132,9 @@ html.fullScreen {
}
}
.headerMainEditSearch:hover, .headerMainEditSearch.active {
.header.headerMainEdit:hover, .header.headerMainEdit.active {
.side.left { opacity: 1; }
.mid {
.side.center {
.icon { opacity: 1; }
.icon.dis { opacity: 0.5; }
}

View file

@ -7,8 +7,8 @@
.inputWithFile {
.txt { line-height: 20px; height: 20px; overflow: hidden; width: calc(100% - 26px); vertical-align: top; }
.fileWrap { display: inline-block; vertical-align: top; }
.fileWrap .border { border-bottom: 1px solid $colorDarkGrey; display: inline-block; line-height: 1; transition: $transitionFast; }
.fileWrap:hover .border { cursor: pointer; color: $colorBlack; }
.fileWrap .border { border-bottom: 1px solid $colorDarkGrey; display: inline-block; line-height: 1; transition: $transitionFast; cursor: pointer; }
.fileWrap:hover .border { color: $colorBlack; }
.input::placeholder { color: $colorDarkGrey; }
.urlToggle { cursor: text; display: inline-block; }
@ -24,6 +24,12 @@
.icon.bookmark { background-image: url('~img/icon/menu/action/block/bookmark0.svg'); }
}
.inputWithFile.isReadOnly {
.urlToggle { cursor: default; }
.border { border: 0px; cursor: default; }
.fileWrap:hover .border { color: $colorDarkGrey; }
}
.inputWithFile.isSmall {
.fileWrap { display: block; }
.txt { height: 38px; }

View file

@ -48,6 +48,7 @@
.icon.undo { background-image: url('~img/icon/menu/action/undo0.svg'); }
.icon.redo { background-image: url('~img/icon/menu/action/redo0.svg'); }
.icon.print { background-image: url('~img/icon/menu/action/print0.svg'); }
.icon.history { background-image: url('~img/icon/menu/action/history0.svg'); }
.icon.search { background-image: url('~img/icon/menu/action/search0.svg'); }
.icon.align.left { background-image: url('~img/icon/menu/align/left.svg'); }

View file

@ -3,6 +3,8 @@
.menus {
.dimmer { position: fixed; left: 0px; top: 0px; width: 100%; height: 100%; z-index: 102; background: rgba(0,0,0,0); }
#menu-polygon { z-index: 1000; position: absolute; }
.menu * { user-select: none; }
.menu {
border-radius: 8px; color: $colorBlack; position: absolute; z-index: 103; text-align: left;

View file

@ -0,0 +1,41 @@
@import "~scss/_vars";
.pageMainHistory * { user-select: none; }
.pageMainHistory {
#body { overflow: hidden; }
.wrapper { padding: 38px 0px 0px 0px; width: calc(100% - 254px); overflow: auto; position: relative; }
.loaderWrapper { position: fixed; left: 0px; top: 0px; width: 100%; height: 100%; }
.list { width: 254px; border-left: 1px solid #dfddd0; z-index: 10; position: relative; background: #fff; overflow: auto; }
.list {
.section { padding: 11px 16px; }
.section {
.date { text-transform: uppercase; @include text-very-small; }
}
.section.fix { position: fixed; right: 0px; top: 0px; background: #fff; width: 253px; }
.wrap { position: relative; }
.item { padding: 5px 16px 5px 30px; cursor: pointer; transition: $transitionFast; position: relative; }
.item {
.name { @include text-small; color: #6c6a5f; @include text-overflow-nw; }
.date { @include text-overflow-nw; }
.icon.arrow {
width: 20px; height: 20px; background-image: url('~img/arrow/historyList.svg'); position: absolute; left: 9px; top: 6px;
transition: none;
}
}
.item.expanded {
.icon.arrow { transform: rotateZ(90deg); }
}
.children { display: none; transition: $transitionFast; }
.children {
.item { padding-left: 48px; }
}
.item:hover, .item.active { background: #f3f2ec; }
}
.blockLast { height: 100px; }
}

View file

@ -6,6 +6,8 @@
.innerWrap { opacity: 1; transform: scale3d(1,1,1); }
.dimmer { opacity: 1; }
}
.popup * { user-select: none; }
.popup {
.innerWrap {

View file

@ -42,6 +42,7 @@ import 'scss/component/pin.scss';
import 'scss/page/auth.scss';
import 'scss/page/main/index.scss';
import 'scss/page/main/edit.scss';
import 'scss/page/main/history.scss';
import 'scss/block/common.scss';
import 'scss/block/dataview.scss';
@ -256,7 +257,7 @@ class App extends React.Component<Props, State> {
this.setWindowEvents();
if (pageId) {
Storage.set('redirectTo', pageId);
Storage.set('redirectTo', '/main/edit/' + pageId);
};
};

View file

@ -27,7 +27,7 @@ class BlockBookmark extends React.Component<Props, {}> {
};
render () {
const { rootId, block } = this.props;
const { rootId, block, readOnly } = this.props;
const { id, content } = block;
const { url, title, description, imageHash, faviconHash } = content;
@ -58,7 +58,7 @@ class BlockBookmark extends React.Component<Props, {}> {
);
} else {
element = (
<InputWithFile block={block} icon="bookmark" textFile="Paste a link" withFile={false} onChangeUrl={this.onChangeUrl} />
<InputWithFile block={block} icon="bookmark" textFile="Paste a link" withFile={false} onChangeUrl={this.onChangeUrl} readOnly={readOnly} />
);
};

View file

@ -60,7 +60,7 @@ class BlockCover extends React.Component<Props, State> {
render() {
const { editing, loading } = this.state;
const { rootId } = this.props;
const { rootId, readOnly } = this.props;
const details = blockStore.getDetails(rootId, rootId);
const { coverType, coverId, } = details;
const canEdit = coverType && [ I.CoverType.Image, I.CoverType.BgImage ].indexOf(coverType) >= 0;
@ -114,9 +114,11 @@ class BlockCover extends React.Component<Props, State> {
) : (
<Cover id="cover" type={coverType} className={coverId} />
)}
<div id="elements" className="elements">
{elements}
</div>
{!readOnly ? (
<div id="elements" className="elements">
{elements}
</div>
) : ''}
</div>
);
};

View file

@ -26,7 +26,7 @@ class BlockFile extends React.Component<Props, {}> {
};
render () {
const { rootId, block } = this.props;
const { rootId, block, readOnly } = this.props;
const { id, content } = block;
const { state, hash, size, name, mime } = content;
@ -37,7 +37,7 @@ class BlockFile extends React.Component<Props, {}> {
default:
case I.FileState.Empty:
element = (
<InputWithFile block={block} icon="file" textFile="Upload a file" onChangeUrl={this.onChangeUrl} onChangeFile={this.onChangeFile} />
<InputWithFile block={block} icon="file" textFile="Upload a file" onChangeUrl={this.onChangeUrl} onChangeFile={this.onChangeFile} readOnly={readOnly} />
);
break;
@ -66,7 +66,7 @@ class BlockFile extends React.Component<Props, {}> {
);
break;
};
return (
<div className={cn.join(' ')} tabIndex={0} onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} onFocus={this.onFocus}>
{element}

View file

@ -17,13 +17,24 @@ class BlockIconPage extends React.Component<Props, {}> {
};
render (): any {
const { rootId } = this.props;
const { rootId, readOnly } = this.props;
const details = blockStore.getDetails(rootId, rootId);
const { iconEmoji, iconImage } = details;
return (
<React.Fragment>
<Smile id={'block-icon-' + rootId} canEdit={true} size={32} icon={iconEmoji} hash={iconImage} offsetX={0} offsetY={16} onSelect={this.onSelect} onUpload={this.onUpload} className={'c64 ' + (commonStore.menuIsOpen('smile') ? 'active' : '')} />
<Smile
id={'block-icon-' + rootId}
canEdit={!readOnly}
size={32}
icon={iconEmoji}
hash={iconImage}
offsetX={0}
offsetY={16}
onSelect={this.onSelect}
onUpload={this.onUpload}
className={'c64 ' + (commonStore.menuIsOpen('smile') ? 'active' : '')}
/>
</React.Fragment>
);
};

View file

@ -31,7 +31,7 @@ class BlockImage extends React.Component<Props, {}> {
};
render () {
const { block } = this.props;
const { block, readOnly } = this.props;
const { id, fields, content } = block;
const { width } = fields;
const { state } = content;
@ -47,7 +47,7 @@ class BlockImage extends React.Component<Props, {}> {
default:
case I.FileState.Empty:
element = (
<InputWithFile block={block} icon="image" textFile="Upload a picture" accept={Constant.extension.image} onChangeUrl={this.onChangeUrl} onChangeFile={this.onChangeFile} />
<InputWithFile block={block} icon="image" textFile="Upload a picture" accept={Constant.extension.image} onChangeUrl={this.onChangeUrl} onChangeFile={this.onChangeFile} readOnly={readOnly} />
);
break;

View file

@ -62,13 +62,13 @@ class Block extends React.Component<Props, {}> {
};
render () {
const { rootId, cnt, css, index, className, block } = this.props;
const { rootId, cnt, css, index, className, block, readOnly } = this.props;
const { id, type, fields, content, align, bgColor } = block;
const { style, checked } = content || {};
const childrenIds = blockStore.getChildrenIds(rootId, id);
let canSelect = true;
let cn: string[] = [ 'block', (index ? 'index-' + index : ''), 'align' + align ];
let cn: string[] = [ 'block', (index ? 'index-' + index : ''), 'align' + align, (readOnly ? 'isReadOnly' : '')];
let cd: string[] = [ 'wrapContent' ];
let blockComponent = null;
let empty = null;
@ -89,12 +89,10 @@ class Block extends React.Component<Props, {}> {
cn.push('isChecked');
};
if (block.isTextToggle()) {
if (!childrenIds.length) {
empty = (
<div className="emptyToggle" onClick={this.onToggleClick}>Empty toggle. Click and drop block inside</div>
);
};
if (block.isTextToggle() && !childrenIds.length && !readOnly) {
empty = (
<div className="emptyToggle" onClick={this.onToggleClick}>Empty toggle. Click and drop block inside</div>
);
};
blockComponent = <BlockText {...this.props} onToggle={this.onToggle} onFocus={this.onFocus} onBlur={this.onBlur} />;
@ -127,7 +125,7 @@ class Block extends React.Component<Props, {}> {
if (content.state == I.FileState.Done) {
cn.push('withFile');
};
switch (content.type) {
default:
case I.FileType.File:
@ -384,11 +382,12 @@ class Block extends React.Component<Props, {}> {
onResizeStart (e: any, index: number) {
e.stopPropagation();
if (!this._isMounted) {
const { dataset, rootId, block, readOnly } = this.props;
if (!this._isMounted || readOnly) {
return;
};
const { dataset, rootId, block } = this.props;
const { id } = block;
const childrenIds = blockStore.getChildrenIds(rootId, id);
const { selection } = dataset || {};
@ -512,11 +511,11 @@ class Block extends React.Component<Props, {}> {
return;
};
const { rootId, block } = this.props;
const { rootId, block, readOnly } = this.props;
const { id } = block;
const node = $(ReactDOM.findDOMNode(this));
if (!block.isLayoutRow() || keyboard.isDragging) {
if (!block.isLayoutRow() || keyboard.isDragging || readOnly) {
return;
};

View file

@ -23,15 +23,15 @@ class BlockLink extends React.Component<Props, {}> {
};
render() {
const { rootId, block } = this.props;
const { rootId, block, readOnly } = this.props;
const { id, content } = block;
const details = blockStore.getDetails(rootId, content.targetBlockId);
const { iconEmoji, iconImage, name, isArchived } = details;
const cn = [ 'focusable', 'c' + id, (isArchived ? 'isArchived' : '') ];
return (
<div className={cn.join(' ')} tabIndex={0} onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} onFocus={this.onFocus}>
<Smile id={'block-page-' + id} offsetX={28} offsetY={-24} size={20} icon={iconEmoji} hash={iconImage} className="c24" canEdit={true} onSelect={this.onSelect} onUpload={this.onUpload} />
<Smile id={'block-page-' + id} offsetX={28} offsetY={-24} size={20} icon={iconEmoji} hash={iconImage} className="c24" canEdit={!readOnly} onSelect={this.onSelect} onUpload={this.onUpload} />
<div className="name" onClick={this.onClick}>
<div className="txt">{name}</div>
</div>

View file

@ -9,6 +9,18 @@ import { commonStore, blockStore } from 'ts/store';
import * as Prism from 'prismjs';
import 'prismjs/themes/prism.css';
interface Props extends I.BlockComponent, RouteComponentProps<any> {
onToggle?(e: any): void;
onFocus?(e: any): void;
onBlur?(e: any): void;
onMenuAdd? (id: string, text: string, range: I.TextRange): void;
onPaste? (e: any): void;
};
const { ipcRenderer } = window.require('electron');
const Constant = require('json/constant.json');
const $ = require('jquery');
// Prism languages
const langs = [
'clike', 'c', 'cpp', 'csharp', 'abap', 'arduino', 'bash', 'basic', 'clojure', 'coffeescript', 'dart', 'diff', 'docker', 'elixir',
@ -21,22 +33,6 @@ for (let lang of langs) {
require(`prismjs/components/prism-${lang}.js`);
};
interface Props extends RouteComponentProps<any> {
rootId: string;
dataset?: any;
block: I.Block;
onToggle? (e: any): void;
onFocus? (e: any): void;
onBlur? (e: any): void;
onKeyDown? (e: any, text: string, marks: I.Mark[], range: I.TextRange): void;
onMenuAdd? (id: string, text: string, range: I.TextRange): void;
onPaste? (e: any): void;
};
const { ipcRenderer } = window.require('electron');
const Constant = require('json/constant.json');
const $ = require('jquery');
@observer
class BlockText extends React.Component<Props, {}> {
@ -71,14 +67,14 @@ class BlockText extends React.Component<Props, {}> {
};
render () {
const { rootId, block } = this.props;
const { rootId, block, readOnly } = this.props;
const { id, fields, content } = block;
const { text, marks, style, checked, color } = content;
let marker: any = null;
let placeHolder = Constant.placeHolder.default;
let ct = color ? 'textColor textColor-' + color : '';
let cv: string[] = [ 'value', 'focusable', 'c' + id, ct ];
let cv: string[] = [ 'value', 'focusable', 'c' + id, ct, (readOnly ? 'readOnly' : '') ];
let additional = null;
for (let mark of marks) {
@ -126,7 +122,7 @@ class BlockText extends React.Component<Props, {}> {
<div
id="value"
className={cv.join(' ')}
contentEditable={true}
contentEditable={!readOnly}
suppressContentEditableWarning={true}
onKeyDown={this.onKeyDown}
onKeyUp={this.onKeyUp}
@ -813,9 +809,13 @@ class BlockText extends React.Component<Props, {}> {
};
onCheck (e: any) {
const { rootId, block } = this.props;
const { rootId, block, readOnly } = this.props;
const { id, content } = block;
const { checked } = content;
if (readOnly) {
return;
};
focus.clear(true);
DataUtil.blockSetText(rootId, block, this.getValue(), this.marks, true, () => {
@ -824,9 +824,13 @@ class BlockText extends React.Component<Props, {}> {
};
onLang (v: string) {
const { rootId, block } = this.props;
const { rootId, block, readOnly } = this.props;
const { id, content } = block;
const l = String(content.text || '').length;
if (readOnly) {
return;
};
C.BlockListSetFields(rootId, [
{ blockId: id, fields: { lang: v } },

View file

@ -38,19 +38,19 @@ class BlockTitle extends React.Component<Props, {}> {
};
render (): any {
const { rootId, block } = this.props;
const { rootId, block, readOnly } = this.props;
const details = blockStore.getDetails(rootId, rootId);
const { id } = block;
const name = this.checkName();
const cv = [ 'value', 'focusable', 'c' + id ];
const cv = [ 'value', 'focusable', 'c' + id, (readOnly ? 'readOnly' : '') ];
return (
<div className="rel">
<div
id="value"
className={cv.join(' ')}
contentEditable={true}
contentEditable={!readOnly}
suppressContentEditableWarning={true}
onChange={this.onChange}
onKeyDown={this.onKeyDown}

View file

@ -32,7 +32,7 @@ class BlockVideo extends React.Component<Props, {}> {
};
render () {
const { block } = this.props;
const { block, readOnly } = this.props;
const { id, fields, content } = block;
const { state, hash, type, mime } = content;
@ -49,7 +49,7 @@ class BlockVideo extends React.Component<Props, {}> {
default:
case I.FileState.Empty:
element = (
<InputWithFile block={block} icon="video" textFile="Upload a video" accept={Constant.extension.video} onChangeUrl={this.onChangeUrl} onChangeFile={this.onChangeFile} />
<InputWithFile block={block} icon="video" textFile="Upload a video" accept={Constant.extension.video} onChangeUrl={this.onChangeUrl} onChangeFile={this.onChangeFile} readOnly={readOnly} />
);
break;

View file

@ -6,7 +6,6 @@ import { commonStore, blockStore, authStore } from 'ts/store';
import { I, C, M, Key, Util, DataUtil, SmileUtil, Mark, focus, keyboard, crumbs, Storage, Mapper, Action } from 'ts/lib';
import { observer } from 'mobx-react';
import { throttle } from 'lodash';
import Controls from './controls';
interface Props extends RouteComponentProps<any> {
@ -451,7 +450,7 @@ class EditorPage extends React.Component<Props, State> {
const { selection } = dataset || {};
const { focused } = focus;
if (keyboard.isFocused) {
if (keyboard.isFocused || !selection) {
return;
};

View file

@ -41,7 +41,7 @@ class HeaderMainEdit extends React.Component<Props, {}> {
const details = blockStore.getDetails(breadcrumbs, rootId);
const { iconEmoji, iconImage, name } = details;
const cn = [ 'header', 'headerMainEditSearch' ];
const cn = [ 'header', 'headerMainEdit' ];
if (commonStore.popupIsOpen('navigation')) {
cn.push('active');
@ -55,7 +55,7 @@ class HeaderMainEdit extends React.Component<Props, {}> {
<Icon className="forward" tooltip="Forward" onClick={this.onForward} />
</div>
<div className="mid">
<div className="side center">
<Icon className="nav" tooltip="Navigation" onClick={(e: any) => { this.onNavigation(e, true); }} />
<div className="path" onMouseDown={(e: any) => { this.onNavigation(e, false); }} onMouseOver={this.onPathOver} onMouseOut={this.onPathOut}>

View file

@ -0,0 +1,60 @@
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { observer } from 'mobx-react';
import { Icon } from 'ts/component';
import { DataUtil, I } from 'ts/lib';
import { C, Util } from '../../../lib';
interface Props extends RouteComponentProps<any> {
rootId: string;
version: I.Version;
};
@observer
class HeaderMainHistory extends React.Component<Props, {}> {
constructor (props: any) {
super(props);
this.onBack = this.onBack.bind(this);
this.onRestore = this.onRestore.bind(this);
};
render () {
const { version } = this.props;
return (
<div className="header headerMainHistory">
<div className="side left">
<div className="item grey" onClick={this.onBack}>
<Icon className="arrow" />Current version
</div>
</div>
<div className="side center">
<div className="item">{Util.date('d F Y H:i:s', version.time)}</div>
</div>
<div className="side right" onClick={this.onRestore}>
<div className="item orange">Restore version</div>
</div>
</div>
);
};
onBack(e: any) {
const { rootId } = this.props;
DataUtil.pageOpen(e, rootId);
};
onRestore (e: any) {
const { rootId, version } = this.props;
C.HistorySetVersion(rootId, version.id, (message: any) => {
DataUtil.pageOpen(e, rootId);
});
};
};
export default HeaderMainHistory;

View file

@ -15,6 +15,7 @@ import ListChildren from './list/children';
import HeaderAuth from './header/auth';
import HeaderMainIndex from './header/main/index';
import HeaderMainEdit from './header/main/edit';
import HeaderMainHistory from './header/main/history';
import HeaderHelpIndex from './header/help/index';
import FooterAuth from './footer/auth';
import FooterMainIndex from './footer/main/index';
@ -68,6 +69,7 @@ export {
HeaderAuth,
HeaderMainIndex,
HeaderMainEdit,
HeaderMainHistory,
HeaderHelpIndex,
FooterAuth,
FooterMainIndex,

View file

@ -127,6 +127,7 @@ class MenuBlockMore extends React.Component<Props, {}> {
{ id: 'undo', icon: 'undo', name: 'Undo' },
{ id: 'redo', icon: 'redo', name: 'Redo' },
{ id: 'print', icon: 'print', name: 'Print' },
{ id: 'history', icon: 'history', name: 'Version history' },
{ id: 'search', icon: 'search', name: 'Search on page' },
//{ id: 'move', icon: 'move', name: 'Move to' },
//{ id: 'export', icon: 'export', name: 'Export to web' },
@ -163,7 +164,7 @@ class MenuBlockMore extends React.Component<Props, {}> {
onClick (e: any, item: any) {
const { param, history } = this.props;
const { data } = param;
const { blockId, rootId, onSelect, onSearch } = data;
const { blockId, rootId, onSelect } = data;
const { breadcrumbs } = blockStore;
const block = blockStore.getLeaf(rootId, blockId);
@ -194,9 +195,7 @@ class MenuBlockMore extends React.Component<Props, {}> {
break;
case 'print':
window.setTimeout(() => {
window.print();
}, 300);
window.setTimeout(() => { window.print(); }, 300);
break;
case 'export':
@ -206,6 +205,10 @@ class MenuBlockMore extends React.Component<Props, {}> {
};
});
break;
case 'history':
history.push('/main/history/' + blockId);
break;
case 'move':
commonStore.popupOpen('navigation', {

View file

@ -17,6 +17,7 @@ import PageAuthSuccess from './auth/success';
import PageMainIndex from './main/index';
import PageMainEdit from './main/edit';
import PageMainHistory from './main/history';
const { ipcRenderer } = window.require('electron');
const Constant = require('json/constant.json');
@ -37,6 +38,7 @@ const Components: any = {
'main/index': PageMainIndex,
'main/edit': PageMainEdit,
'main/history': PageMainHistory,
};
interface Props extends RouteComponentProps<any> {};

View file

@ -1,5 +1,4 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { RouteComponentProps } from 'react-router';
import { Storage } from 'ts/lib';
import { HeaderMainEdit as Header, FooterMainEdit as Footer, DragProvider, SelectionProvider, EditorPage } from 'ts/component';

View file

@ -0,0 +1,407 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { RouteComponentProps } from 'react-router';
import { HeaderMainHistory as Header, Block, Loader, Icon } from 'ts/component';
import { blockStore } from 'ts/store';
import { I, M, C, Util, dispatcher, Storage } from 'ts/lib';
import { observer } from 'mobx-react';
interface Props extends RouteComponentProps<any> { };
interface State {
versions: I.Version[];
};
const $ = require('jquery');
const LIMIT = 100;
const GROUP_OFFSET = 300;
@observer
class PageMainHistory extends React.Component<Props, State> {
state = {
versions: [] as I.Version[],
};
version: I.Version = null;
refHeader: any = null;
scrollLeft: number = 0;
scrollRight: number = 0;
loading: boolean = false;
lastId: string = '';
constructor (props: any) {
super(props);
};
render () {
const { match } = this.props;
const { versions } = this.state;
const rootId = match.params.id;
const groups = this.groupData(versions);
const root = blockStore.getLeaf(rootId, rootId);
if (!this.version || !root) {
return <Loader />;
};
const childrenIds = blockStore.getChildrenIds(rootId, rootId);
const children = blockStore.getChildren(rootId, rootId);
const details = blockStore.getDetails(rootId, rootId);
const length = childrenIds.length;
const withIcon = details.iconEmoji || details.iconImage;
const withCover = (details.coverType != I.CoverType.None) && details.coverId;
const cover = new M.Block({ id: rootId + '-cover', type: I.BlockType.Cover, childrenIds: [], fields: {}, content: {} });
let cn = [ 'editorWrapper' ];
let icon: any = { id: rootId + '-icon', childrenIds: [], fields: {}, content: {} };
if (root && root.isPageProfile()) {
cn.push('isProfile');
icon.type = I.BlockType.IconUser;
} else {
icon.type = I.BlockType.IconPage;
};
if (root && root.isPageSet()) {
cn.push('isDataview');
};
icon = new M.Block(icon);
if (withIcon && withCover) {
cn.push('withIconAndCover');
} else
if (withIcon) {
cn.push('withIcon');
} else
if (withCover) {
cn.push('withCover');
};
const Section = (item: any) => (
<React.Fragment>
<div className="section">
<div className="date">{item.groupId}</div>
</div>
<div className="items">
{item.list.map((item: any, i: number) => {
return <Version key={i} {...item} />
})}
</div>
</React.Fragment>
);
const Version = (item: any) => {
const withChildren = item.list && item.list.length;
return (
<React.Fragment>
<div id={'item-' + item.id} className={[ 'item', (withChildren ? 'withChildren' : '') ].join(' ')} onClick={(e: any) => { this.loadVersion(item.id); }}>
{withChildren ? <Icon className="arrow" onClick={(e: any) => { this.toggleChildren(e, item.id); }} /> : ''}
<div className="date">{Util.date('d F, H:i', item.time)}</div>
{item.authorName ? <div className="name">{item.authorName}</div> : ''}
</div>
{withChildren ? (
<div id={'children-' + item.id} className="children">
{item.list.map((child: any, i: number) => {
return <Version key={i} {...child} />
})}
</div>
) : ''}
</React.Fragment>
);
};
return (
<div>
<Header ref={(ref: any) => { this.refHeader = ref; }} {...this.props} rootId={rootId} version={this.version} />
<div id="body" className="flex">
<div id="sideLeft" className="wrapper">
<div className={cn.join(' ')}>
{withCover ? <Block {...this.props} rootId={rootId} key={cover.id} block={cover} readOnly={true} /> : ''}
<div className="editor">
<div className="blocks">
{withIcon ? (
<Block
key={icon.id}
{...this.props}
rootId={rootId}
block={icon}
className="root"
readOnly={true}
/>
) : ''}
{children.map((block: I.Block, i: number) => {
return (
<Block
key={block.id}
{...this.props}
rootId={rootId}
index={i}
block={block}
onKeyDown={() => {}}
onKeyUp={() => {}}
onMenuAdd={() => {}}
onPaste={() => {}}
readOnly={true}
/>
)
})}
</div>
<div className="blockLast" />
</div>
</div>
</div>
<div id="sideRight" className="list">
<div className="wrap">
{groups.map((item: any, i: number) => {
return <Section key={i} {...item} />
})}
</div>
</div>
</div>
</div>
);
};
componentDidMount () {
this.loadList('');
this.resize();
this.setId();
};
componentDidUpdate () {
const node = $(ReactDOM.findDOMNode(this));
const sideLeft = node.find('#sideLeft');
const sideRight = node.find('#sideRight');
this.resize();
this.setId();
if (this.version) {
this.show(this.version.id);
};
sideLeft.scrollTop(this.scrollLeft);
sideRight.scrollTop(this.scrollRight);
sideLeft.unbind('scroll').scroll(() => { this.onScrollLeft(); });
sideRight.unbind('scroll').scroll(() => { this.onScrollRight(); });
};
onScrollLeft () {
const node = $(ReactDOM.findDOMNode(this));
const sideLeft = node.find('#sideLeft');
this.scrollLeft = sideLeft.scrollTop();
};
onScrollRight () {
const { versions } = this.state;
const win = $(window);
const node = $(ReactDOM.findDOMNode(this));
const sideRight = node.find('#sideRight');
const wrap = sideRight.find('.wrap');
const sections = wrap.find('.section');
this.scrollRight = sideRight.scrollTop();
if (this.scrollRight >= wrap.height() - win.height()) {
this.loadList(versions[versions.length - 1].id);
};
sections.each((i: number, item: any) => {
item = $(item);
const top = item.offset().top;
let clone = sideRight.find('.section.fix.c' + i);
if (top < 0) {
if (!clone.length) {
clone = item.clone();
sideRight.prepend(clone);
clone.addClass('fix c' + i).css({ zIndex: i + 1 });
};
} else {
clone.remove();
};
});
};
setId () {
const { match } = this.props;
Storage.set('pageId', match.params.id);
};
show (id: string) {
if (!id) {
return;
};
const { versions } = this.state;
const version = versions.find((it: any) => { return it.id == id; });
if (!version) {
return;
};
const groups = this.groupData(versions);
const month = groups.find((it: any) => { return it.groupId == this.monthId(version.time); });
if (!month) {
return;
};
let group = month.list.find((it: any) => { return it.groupId == version.groupId; });
if (!group) {
return;
};
const node = $(ReactDOM.findDOMNode(this));
const sideRight = node.find('#sideRight');
const item = sideRight.find('#item-' + version.id);
sideRight.find('.active').removeClass('active');
item.addClass('active');
if (group) {
const groupItem = sideRight.find('#item-' + group.id);
const children = sideRight.find('#children-' + group.id);
groupItem.addClass('expanded');
children.show();
};
};
toggleChildren (e: any, id: string) {
e.stopPropagation();
const node = $(ReactDOM.findDOMNode(this));
const sideRight = node.find('#sideRight');
const item = sideRight.find('#item-' + id);
const children = sideRight.find('#children-' + id);
const isActive = item.hasClass('expanded');
let height = 0;
if (isActive) {
item.removeClass('expanded');
children.css({ overflow: 'visible', height: 'auto' });
height = children.height();
children.css({ overflow: 'hidden', height: height });
setTimeout(() => { children.css({ height: 0 }); }, 15);
setTimeout(() => { children.hide(); }, 215);
} else {
item.addClass('expanded');
children.show();
children.css({ overflow: 'visible', height: 'auto' });
height = children.height();
children.css({ overflow: 'hidden', height: 0 });
setTimeout(() => { children.css({ height: height }); }, 15);
setTimeout(() => { children.css({ overflow: 'visible', height: 'auto' }); }, 215);
};
};
loadList (lastId: string) {
const { match } = this.props;
const { versions } = this.state;
const rootId = match.params.id;
if (this.loading || (this.lastId && (lastId == this.lastId))) {
return;
};
this.loading = true;
this.lastId = lastId;
C.HistoryVersions(rootId, lastId, LIMIT, (message: any) => {
this.loading = false;
if (message.error.code || !message.versions.length) {
return;
};
this.setState({ versions: versions.concat(message.versions) });
if (!this.version) {
this.loadVersion(message.versions[0].id);
};
});
};
loadVersion (id: string) {
const { match } = this.props;
const rootId = match.params.id;
C.HistoryShow(rootId, id, (message: any) => {
if (message.error.code) {
return;
};
this.version = message.version;
let bs = message.blockShow;
dispatcher.onBlockShow(rootId, bs.type, bs.blocks, bs.details);
this.forceUpdate();
});
};
groupData (versions: I.Version[]) {
let months: any[] = [];
let groups: any[] = [];
let groupId = 0;
for (let i = 0; i < versions.length; ++i) {
let version = versions[i];
let prev = versions[i - 1];
if (prev && ((prev.time - version.time > GROUP_OFFSET) || (prev.time - version.time < 0))) {
groupId++;
};
let group = groups.find((it: any) => { return it.groupId == groupId; });
if (!group) {
group = { ...version, groupId: groupId, list: [] };
groups.push(group);
} else {
group.list.push(version);
};
};
for (let group of groups) {
let groupId = this.monthId(group.time);
let month = months.find((it: any) => { return it.groupId == groupId; });
if (!month) {
month = { groupId: groupId, list: [] };
months.push(month);
};
month.list.push(group);
};
return months;
};
monthId (time: number) {
return Util.date('F Y', time);
};
resize () {
const win = $(window);
const node = $(ReactDOM.findDOMNode(this));
const sideLeft = node.find('#sideLeft');
const sideRight = node.find('#sideRight');
const height = win.height();
sideLeft.css({ height: height });
sideRight.css({ height: height });
};
};
export default PageMainHistory;

View file

@ -78,12 +78,13 @@ class PageMainIndex extends React.Component<Props, {}> {
};
componentDidMount () {
const { history } = this.props;
const node = $(ReactDOM.findDOMNode(this));
const hello = node.find('#hello');
const redirectTo = Storage.get('redirectTo');
if (redirectTo) {
DataUtil.pageOpen(null, redirectTo);
history.push(redirectTo);
Storage.delete('redirectTo');
};

View file

@ -10,6 +10,7 @@ interface Props {
withFile?: boolean;
accept?: string[];
block?: I.Block;
readOnly?: boolean;
onChangeUrl? (e: any, url: string): void;
onChangeFile? (e: any, path: string): void;
};
@ -54,7 +55,7 @@ class InputWithFile extends React.Component<Props, State> {
render () {
const { focused, size } = this.state;
const { icon, textUrl, textFile, withFile } = this.props;
const { icon, textUrl, textFile, withFile, readOnly } = this.props;
let cn = [ 'inputWithFile', 'resizable' ];
let placeHolder = textUrl;
@ -72,6 +73,10 @@ class InputWithFile extends React.Component<Props, State> {
if (isSmall) {
cn.push('isSmall');
};
if (readOnly) {
cn.push('isReadOnly');
};
if (isIcon) {
cn.push('isIcon');
@ -188,6 +193,11 @@ class InputWithFile extends React.Component<Props, State> {
onFocus (e: any) {
e.stopPropagation();
const { readOnly } = this.props;
if (readOnly) {
return;
};
this.setState({ focused: true });
};
@ -201,7 +211,11 @@ class InputWithFile extends React.Component<Props, State> {
};
onChangeUrl (e: any, force: boolean) {
const { onChangeUrl } = this.props;
const { onChangeUrl, readOnly } = this.props;
if (readOnly) {
return;
};
window.clearTimeout(this.t);
this.t = window.setTimeout(() => {
@ -221,10 +235,14 @@ class InputWithFile extends React.Component<Props, State> {
};
onClickFile (e: any) {
const { onChangeFile, accept } = this.props;
const { onChangeFile, accept, readOnly } = this.props;
e.preventDefault();
e.stopPropagation();
if (readOnly) {
return;
};
let options: any = {
properties: [ 'openFile' ],

View file

@ -51,6 +51,7 @@ export interface BlockComponent {
dataset?: any;
rootId: string;
block: I.Block;
readOnly?: boolean;
onKeyDown?(e: any, text: string, marks: I.Mark[], range: I.TextRange): void;
onKeyUp?(e: any, text: string, marks: I.Mark[], range: I.TextRange): void;
};

View file

@ -36,4 +36,13 @@ export interface Option {
id: string;
name: string;
icon?: string;
};
export interface Version {
id: string;
previousIds: string[];
authorId: string;
authorName: string;
groupId: number;
time: number;
};

View file

@ -1,4 +1,4 @@
import { Account, Platform, DragItem, CoverType, CrumbsType, NavigationType, Option } from './common';
import { Account, Platform, DragItem, CoverType, CrumbsType, NavigationType, Option, Version } from './common';
import { Progress, ProgressType, ProgressState } from './progress';
import { PopupParam, Popup } from './popup';
import { MenuType, MenuDirection, MenuParam, Menu, MenuItem } from './menu';
@ -38,6 +38,7 @@ export {
CrumbsType,
NavigationType,
Option,
Version,
Progress,
ProgressType,

View file

@ -596,6 +596,34 @@ const BlockDeleteDataviewRecord = (contextId: string, blockId: string, recordId:
dispatcher.request('blockDeleteDataviewRecord', request, callBack);
};
const HistoryVersions = (pageId: string, lastVersionId: string, limit: number, callBack?: (message: any) => void) => {
const request = new Rpc.History.Versions.Request();
request.setPageid(pageId);
request.setLastversionid(lastVersionId);
request.setLimit(limit);
dispatcher.request('historyVersions', request, callBack);
};
const HistoryShow = (pageId: string, versionId: string, callBack?: (message: any) => void) => {
const request = new Rpc.History.Show.Request();
request.setPageid(pageId);
request.setVersionid(versionId);
dispatcher.request('historyShow', request, callBack);
};
const HistorySetVersion= (pageId: string, versionId: string, callBack?: (message: any) => void) => {
const request = new Rpc.History.Show.Request();
request.setPageid(pageId);
request.setVersionid(versionId);
dispatcher.request('historySetVersion', request, callBack);
};
export {
VersionGet,
@ -670,4 +698,8 @@ export {
BlockListSetAlign,
BlockListSetPageIsArchived,
BlockListDeletePage,
HistoryVersions,
HistoryShow,
HistorySetVersion,
};

View file

@ -165,36 +165,8 @@ class Dispatcher {
break;
case 'blockShow':
blocks = data.getBlocksList() || [];
let details = data.getDetailsList() || [];
blocks = blocks.map((it: any) => {
it = Mapper.From.Block(it);
if (it.id == rootId) {
it.type = I.BlockType.Page;
it.pageType = data.getType();
};
return new M.Block(it);
});
block = blocks.find((it: I.Block) => { return it.id == rootId; });
if (!block) {
break;
};
if (block.canHaveTitle()) {
block.childrenIds.unshift(rootId + '-title');
blocks.unshift(new M.Block({
id: rootId + '-title',
type: I.BlockType.Title,
childrenIds: [],
fields: {},
content: {},
}));
};
blockStore.blocksSet(rootId, blocks);
blockStore.detailsSet(rootId, details);
let res = Response.BlockShow(data);
this.onBlockShow(rootId, res.type, res.blocks, res.details);
break;
case 'blockAdd':
@ -517,6 +489,35 @@ class Dispatcher {
return 0;
};
onBlockShow (rootId: string, type: number, blocks: any[], details: any[]) {
blocks = blocks.map((it: any) => {
if (it.id == rootId) {
it.type = I.BlockType.Page;
it.pageType = type;
};
return new M.Block(it);
});
let root = blocks.find((it: I.Block) => { return it.id == rootId; });
if (!root) {
return;
};
if (root.canHaveTitle()) {
root.childrenIds.unshift(rootId + '-title');
blocks.unshift(new M.Block({
id: rootId + '-title',
type: I.BlockType.Title,
childrenIds: [],
fields: {},
content: {},
}));
};
blockStore.blocksSet(rootId, blocks);
blockStore.detailsSet(rootId, details);
};
public request (type: string, data: any, callBack?: (message: any) => void) {
const { config } = commonStore;
const upper = Util.toUpperCamelCase(type);
@ -595,13 +596,6 @@ class Dispatcher {
'Render time:', renderTime + 'ms',
'Total time:', totalTime + 'ms'
);
if (middleTime > 3000) {
Sentry.captureMessage(`${type}: middleware time too long`);
};
if (renderTime > 1000) {
Sentry.captureMessage(`${type}: render time too long`);
};
};
});
} catch (err) {

View file

@ -72,6 +72,13 @@ const Mapper = {
if (v == ContentCase.DATAVIEW) t = I.BlockType.Dataview;
return t;
},
Details: (obj: any) => {
return {
id: obj.getId(),
details: Decode.decodeStruct(obj.getDetails()),
};
},
Block: (obj: any): I.Block => {
let type = Mapper.From.BlockType(obj.getContentCase());
@ -240,6 +247,17 @@ const Mapper = {
return observable(new M.View(view));
},
HistoryVersion: (obj: any) => {
return {
id: obj.getId(),
previousIds: obj.getPreviousidsList() || [],
authorId: obj.getAuthorid(),
authorName: obj.getAuthorname(),
groupId: obj.getGroupid(),
time: obj.getTime(),
};
},
},
//------------------------------------------------------------
@ -384,26 +402,26 @@ const Mapper = {
return item;
},
View: (view: I.View) => {
view = Util.objectCopy(new M.View(view));
View: (obj: I.View) => {
obj = Util.objectCopy(new M.View(obj));
const item = new Model.Block.Content.Dataview.View();
item.setId(view.id);
item.setName(view.name);
item.setType(view.type);
item.setRelationsList(view.relations.map(Mapper.To.ViewRelation));
item.setFiltersList(view.filters.map(Mapper.To.Filter));
item.setSortsList(view.sorts.map(Mapper.To.Sort));
item.setId(obj.id);
item.setName(obj.name);
item.setType(obj.type);
item.setRelationsList(obj.relations.map(Mapper.To.ViewRelation));
item.setFiltersList(obj.filters.map(Mapper.To.Filter));
item.setSortsList(obj.sorts.map(Mapper.To.Sort));
return item;
},
PasteFile: (file: any) => {
PasteFile: (obj: any) => {
const item = new Rpc.Block.Paste.Request.File();
item.setName(file.name);
item.setLocalpath(file.path);
item.setName(obj.name);
item.setLocalpath(obj.path);
return item;
},

View file

@ -110,6 +110,15 @@ const BlockOpen = (response: any) => {
return {};
};
const BlockShow = (response: any) => {
return {
rootId: response.getRootid(),
type: response.getType(),
blocks: (response.getBlocksList() || []).map(Mapper.From.Block),
details: (response.getDetailsList() || []).map(Mapper.From.Details),
};
};
const BlockOpenBreadcrumbs = (response: any) => {
return {
blockId: response.getBlockid(),
@ -311,6 +320,24 @@ const BlockDeleteDataviewRecord = (response: any) => {
return {};
};
const HistoryVersions = (response: any) => {
return {
versions: (response.getVersionsList() || []).map(Mapper.From.HistoryVersion),
};
};
const HistoryShow = (response: any) => {
return {
version: Mapper.From.HistoryVersion(response.getVersion()),
blockShow: this.BlockShow(response.getBlockshow()),
};
};
const HistorySetVersion = (response: any) => {
return {
};
};
export {
VersionGet,
@ -341,6 +368,7 @@ export {
BlockOpen,
BlockOpenBreadcrumbs,
BlockSetBreadcrumbs,
BlockShow,
BlockUnlink,
BlockClose,
@ -391,4 +419,7 @@ export {
BlockListSetAlign,
BlockListDeletePage,
HistoryVersions,
HistoryShow,
HistorySetVersion,
};

View file

@ -265,8 +265,8 @@ class Util {
} else {
return s;
};
return false;
};
const f: any = {
// Day
d: () => {

View file

@ -60,7 +60,7 @@ class BlockStore {
let map = observable(new Map());
for (let item of details) {
map.set(item.getId(), Decode.decodeStruct(item.getDetails()));
map.set(item.id, item.details);
};
intercept(map as any, (change: any) => {