diff --git a/dist/.gitignore b/dist/.gitignore index b14341dac8..5c961526aa 100644 --- a/dist/.gitignore +++ b/dist/.gitignore @@ -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 \ No newline at end of file diff --git a/electron.js b/electron.js index 2aaa1dde4d..780bfc75bb 100644 --- a/electron.js +++ b/electron.js @@ -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); }; diff --git a/src/img/arrow/history0.svg b/src/img/arrow/history0.svg new file mode 100644 index 0000000000..3ee27e94b6 --- /dev/null +++ b/src/img/arrow/history0.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/img/arrow/history1.svg b/src/img/arrow/history1.svg new file mode 100644 index 0000000000..64078b426c --- /dev/null +++ b/src/img/arrow/history1.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/img/arrow/historyList.svg b/src/img/arrow/historyList.svg new file mode 100644 index 0000000000..516befd259 --- /dev/null +++ b/src/img/arrow/historyList.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/img/icon/menu/action/history0.svg b/src/img/icon/menu/action/history0.svg new file mode 100644 index 0000000000..86fa73504e --- /dev/null +++ b/src/img/icon/menu/action/history0.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/scss/block/common.scss b/src/scss/block/common.scss index 42a0eca16b..5af84552ca 100644 --- a/src/scss/block/common.scss +++ b/src/scss/block/common.scss @@ -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; } diff --git a/src/scss/block/media.scss b/src/scss/block/media.scss index 3b80e5cd0a..8bd95a7be5 100644 --- a/src/scss/block/media.scss +++ b/src/scss/block/media.scss @@ -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; } diff --git a/src/scss/block/text.scss b/src/scss/block/text.scss index ef60af1453..d65f1f0ae8 100644 --- a/src/scss/block/text.scss +++ b/src/scss/block/text.scss @@ -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, diff --git a/src/scss/common.scss b/src/scss/common.scss index cea1166f80..b204148d1a 100644 --- a/src/scss/common.scss +++ b/src/scss/common.scss @@ -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 { diff --git a/src/scss/component/header.scss b/src/scss/component/header.scss index f9534002be..535f3efc19 100644 --- a/src/scss/component/header.scss +++ b/src/scss/component/header.scss @@ -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; } } diff --git a/src/scss/component/inputWithFile.scss b/src/scss/component/inputWithFile.scss index 8bd928af48..34e8430f1c 100644 --- a/src/scss/component/inputWithFile.scss +++ b/src/scss/component/inputWithFile.scss @@ -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; } diff --git a/src/scss/menu/block/icon.scss b/src/scss/menu/block/icon.scss index 29dd34f1bb..c0662b181e 100644 --- a/src/scss/menu/block/icon.scss +++ b/src/scss/menu/block/icon.scss @@ -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'); } diff --git a/src/scss/menu/common.scss b/src/scss/menu/common.scss index fc1e1afe1f..335e0de099 100644 --- a/src/scss/menu/common.scss +++ b/src/scss/menu/common.scss @@ -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; diff --git a/src/scss/page/main/history.scss b/src/scss/page/main/history.scss new file mode 100644 index 0000000000..31bdb0a10a --- /dev/null +++ b/src/scss/page/main/history.scss @@ -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; } +} \ No newline at end of file diff --git a/src/scss/popup/common.scss b/src/scss/popup/common.scss index 85f27dd789..0060d8f76f 100644 --- a/src/scss/popup/common.scss +++ b/src/scss/popup/common.scss @@ -6,6 +6,8 @@ .innerWrap { opacity: 1; transform: scale3d(1,1,1); } .dimmer { opacity: 1; } } + + .popup * { user-select: none; } .popup { .innerWrap { diff --git a/src/ts/app.tsx b/src/ts/app.tsx index 69c70c740a..913579e3d1 100644 --- a/src/ts/app.tsx +++ b/src/ts/app.tsx @@ -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 { this.setWindowEvents(); if (pageId) { - Storage.set('redirectTo', pageId); + Storage.set('redirectTo', '/main/edit/' + pageId); }; }; diff --git a/src/ts/component/block/bookmark.tsx b/src/ts/component/block/bookmark.tsx index c40e7fcacd..97ec5d0518 100644 --- a/src/ts/component/block/bookmark.tsx +++ b/src/ts/component/block/bookmark.tsx @@ -27,7 +27,7 @@ class BlockBookmark extends React.Component { }; 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 { ); } else { element = ( - + ); }; diff --git a/src/ts/component/block/cover.tsx b/src/ts/component/block/cover.tsx index 427655ff19..8960fc6169 100644 --- a/src/ts/component/block/cover.tsx +++ b/src/ts/component/block/cover.tsx @@ -60,7 +60,7 @@ class BlockCover extends React.Component { 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 { ) : ( )} -
- {elements} -
+ {!readOnly ? ( +
+ {elements} +
+ ) : ''} ); }; diff --git a/src/ts/component/block/file.tsx b/src/ts/component/block/file.tsx index ad35d366f1..7e8ef5e63a 100644 --- a/src/ts/component/block/file.tsx +++ b/src/ts/component/block/file.tsx @@ -26,7 +26,7 @@ class BlockFile extends React.Component { }; 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 { default: case I.FileState.Empty: element = ( - + ); break; @@ -66,7 +66,7 @@ class BlockFile extends React.Component { ); break; }; - + return (
{element} diff --git a/src/ts/component/block/iconPage.tsx b/src/ts/component/block/iconPage.tsx index 9a1aa11338..aaf99e51f6 100644 --- a/src/ts/component/block/iconPage.tsx +++ b/src/ts/component/block/iconPage.tsx @@ -17,13 +17,24 @@ class BlockIconPage extends React.Component { }; render (): any { - const { rootId } = this.props; + const { rootId, readOnly } = this.props; const details = blockStore.getDetails(rootId, rootId); const { iconEmoji, iconImage } = details; return ( - + ); }; diff --git a/src/ts/component/block/image.tsx b/src/ts/component/block/image.tsx index 993ca3d130..adf2f516c1 100644 --- a/src/ts/component/block/image.tsx +++ b/src/ts/component/block/image.tsx @@ -31,7 +31,7 @@ class BlockImage extends React.Component { }; 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 { default: case I.FileState.Empty: element = ( - + ); break; diff --git a/src/ts/component/block/index.tsx b/src/ts/component/block/index.tsx index b358335ced..85551947bf 100644 --- a/src/ts/component/block/index.tsx +++ b/src/ts/component/block/index.tsx @@ -62,13 +62,13 @@ class Block extends React.Component { }; 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 { cn.push('isChecked'); }; - if (block.isTextToggle()) { - if (!childrenIds.length) { - empty = ( -
Empty toggle. Click and drop block inside
- ); - }; + if (block.isTextToggle() && !childrenIds.length && !readOnly) { + empty = ( +
Empty toggle. Click and drop block inside
+ ); }; blockComponent = ; @@ -127,7 +125,7 @@ class Block extends React.Component { 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 { 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 { 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; }; diff --git a/src/ts/component/block/link.tsx b/src/ts/component/block/link.tsx index ef1e03a999..d0a90d3ecf 100644 --- a/src/ts/component/block/link.tsx +++ b/src/ts/component/block/link.tsx @@ -23,15 +23,15 @@ class BlockLink extends React.Component { }; 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 (
- +
{name}
diff --git a/src/ts/component/block/text.tsx b/src/ts/component/block/text.tsx index eb12eca83b..362d660868 100644 --- a/src/ts/component/block/text.tsx +++ b/src/ts/component/block/text.tsx @@ -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 { + 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 { - 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 { @@ -71,14 +67,14 @@ class BlockText extends React.Component { }; 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 {
{ }; 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 { }; 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 } }, diff --git a/src/ts/component/block/title.tsx b/src/ts/component/block/title.tsx index 9dd370f3ef..48b9d525a4 100644 --- a/src/ts/component/block/title.tsx +++ b/src/ts/component/block/title.tsx @@ -38,19 +38,19 @@ class BlockTitle extends React.Component { }; 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 (
{ }; 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 { default: case I.FileState.Empty: element = ( - + ); break; diff --git a/src/ts/component/editor/page.tsx b/src/ts/component/editor/page.tsx index 4821d93f30..8851aa6e14 100644 --- a/src/ts/component/editor/page.tsx +++ b/src/ts/component/editor/page.tsx @@ -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 { @@ -451,7 +450,7 @@ class EditorPage extends React.Component { const { selection } = dataset || {}; const { focused } = focus; - if (keyboard.isFocused) { + if (keyboard.isFocused || !selection) { return; }; diff --git a/src/ts/component/header/main/edit.tsx b/src/ts/component/header/main/edit.tsx index 69c278584a..9bcd78cefc 100644 --- a/src/ts/component/header/main/edit.tsx +++ b/src/ts/component/header/main/edit.tsx @@ -41,7 +41,7 @@ class HeaderMainEdit extends React.Component { 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 {
-
+
{ this.onNavigation(e, true); }} />
{ this.onNavigation(e, false); }} onMouseOver={this.onPathOver} onMouseOut={this.onPathOut}> diff --git a/src/ts/component/header/main/history.tsx b/src/ts/component/header/main/history.tsx new file mode 100644 index 0000000000..17077af1ad --- /dev/null +++ b/src/ts/component/header/main/history.tsx @@ -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 { + rootId: string; + version: I.Version; +}; + +@observer +class HeaderMainHistory extends React.Component { + + constructor (props: any) { + super(props); + + this.onBack = this.onBack.bind(this); + this.onRestore = this.onRestore.bind(this); + }; + + render () { + const { version } = this.props; + + return ( +
+
+
+ Current version +
+
+ +
+
{Util.date('d F Y H:i:s', version.time)}
+
+ +
+
Restore version
+
+
+ ); + }; + + 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; \ No newline at end of file diff --git a/src/ts/component/index.tsx b/src/ts/component/index.tsx index 668ab6405a..ee859cda22 100644 --- a/src/ts/component/index.tsx +++ b/src/ts/component/index.tsx @@ -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, diff --git a/src/ts/component/menu/block/more.tsx b/src/ts/component/menu/block/more.tsx index e32e911e6a..f01b050b6f 100644 --- a/src/ts/component/menu/block/more.tsx +++ b/src/ts/component/menu/block/more.tsx @@ -127,6 +127,7 @@ class MenuBlockMore extends React.Component { { 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 { 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 { 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 { }; }); break; + + case 'history': + history.push('/main/history/' + blockId); + break; case 'move': commonStore.popupOpen('navigation', { diff --git a/src/ts/component/page/index.tsx b/src/ts/component/page/index.tsx index 4e58f21aae..0c4700fa74 100644 --- a/src/ts/component/page/index.tsx +++ b/src/ts/component/page/index.tsx @@ -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 {}; diff --git a/src/ts/component/page/main/edit.tsx b/src/ts/component/page/main/edit.tsx index b447d7fbb1..49de9019a6 100644 --- a/src/ts/component/page/main/edit.tsx +++ b/src/ts/component/page/main/edit.tsx @@ -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'; diff --git a/src/ts/component/page/main/history.tsx b/src/ts/component/page/main/history.tsx new file mode 100644 index 0000000000..cb2dec5916 --- /dev/null +++ b/src/ts/component/page/main/history.tsx @@ -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 { }; + +interface State { + versions: I.Version[]; +}; + +const $ = require('jquery'); +const LIMIT = 100; +const GROUP_OFFSET = 300; + +@observer +class PageMainHistory extends React.Component { + + 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 ; + }; + + 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) => ( + +
+
{item.groupId}
+
+ +
+ {item.list.map((item: any, i: number) => { + return + })} +
+
+ ); + + const Version = (item: any) => { + const withChildren = item.list && item.list.length; + return ( + +
{ this.loadVersion(item.id); }}> + {withChildren ? { this.toggleChildren(e, item.id); }} /> : ''} +
{Util.date('d F, H:i', item.time)}
+ {item.authorName ?
{item.authorName}
: ''} +
+ + {withChildren ? ( +
+ {item.list.map((child: any, i: number) => { + return + })} +
+ ) : ''} +
+ ); + }; + + return ( +
+
{ this.refHeader = ref; }} {...this.props} rootId={rootId} version={this.version} /> +
+
+
+ {withCover ? : ''} +
+
+ {withIcon ? ( + + ) : ''} + + {children.map((block: I.Block, i: number) => { + return ( + {}} + onKeyUp={() => {}} + onMenuAdd={() => {}} + onPaste={() => {}} + readOnly={true} + /> + ) + })} +
+ +
+
+
+
+ +
+
+ {groups.map((item: any, i: number) => { + return
+ })} +
+
+
+
+ ); + }; + + 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; \ No newline at end of file diff --git a/src/ts/component/page/main/index.tsx b/src/ts/component/page/main/index.tsx index 07c3932996..bf7a729320 100644 --- a/src/ts/component/page/main/index.tsx +++ b/src/ts/component/page/main/index.tsx @@ -78,12 +78,13 @@ class PageMainIndex extends React.Component { }; 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'); }; diff --git a/src/ts/component/util/inputWithFile.tsx b/src/ts/component/util/inputWithFile.tsx index 2a366f9eef..5b744572c8 100644 --- a/src/ts/component/util/inputWithFile.tsx +++ b/src/ts/component/util/inputWithFile.tsx @@ -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 { 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 { if (isSmall) { cn.push('isSmall'); }; + + if (readOnly) { + cn.push('isReadOnly'); + }; if (isIcon) { cn.push('isIcon'); @@ -188,6 +193,11 @@ class InputWithFile extends React.Component { 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 { }; 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 { }; 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' ], diff --git a/src/ts/interface/block.tsx b/src/ts/interface/block.tsx index 0799c63647..06eaef99cd 100644 --- a/src/ts/interface/block.tsx +++ b/src/ts/interface/block.tsx @@ -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; }; diff --git a/src/ts/interface/common.tsx b/src/ts/interface/common.tsx index f7be730eaa..0856882ff3 100644 --- a/src/ts/interface/common.tsx +++ b/src/ts/interface/common.tsx @@ -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; }; \ No newline at end of file diff --git a/src/ts/interface/index.tsx b/src/ts/interface/index.tsx index f60669176b..b03ac5f3c4 100644 --- a/src/ts/interface/index.tsx +++ b/src/ts/interface/index.tsx @@ -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, diff --git a/src/ts/lib/command.tsx b/src/ts/lib/command.tsx index 6824c2c0f5..b4948de0eb 100644 --- a/src/ts/lib/command.tsx +++ b/src/ts/lib/command.tsx @@ -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, }; diff --git a/src/ts/lib/dispatcher.tsx b/src/ts/lib/dispatcher.tsx index 5c875d821b..fcffc2662b 100644 --- a/src/ts/lib/dispatcher.tsx +++ b/src/ts/lib/dispatcher.tsx @@ -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) { diff --git a/src/ts/lib/mapper.tsx b/src/ts/lib/mapper.tsx index 64ee2e1ee8..8fa66b52b1 100644 --- a/src/ts/lib/mapper.tsx +++ b/src/ts/lib/mapper.tsx @@ -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; }, diff --git a/src/ts/lib/response.tsx b/src/ts/lib/response.tsx index fde84d52c0..8a229e90ff 100644 --- a/src/ts/lib/response.tsx +++ b/src/ts/lib/response.tsx @@ -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, }; diff --git a/src/ts/lib/util.tsx b/src/ts/lib/util.tsx index 49972c16fb..2e2a08294d 100644 --- a/src/ts/lib/util.tsx +++ b/src/ts/lib/util.tsx @@ -265,8 +265,8 @@ class Util { } else { return s; }; - return false; }; + const f: any = { // Day d: () => { diff --git a/src/ts/store/block.tsx b/src/ts/store/block.tsx index b002bbceb9..55d132b707 100644 --- a/src/ts/store/block.tsx +++ b/src/ts/store/block.tsx @@ -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) => {