diff --git a/dist/workers/graph.js b/dist/workers/graph.js index faf46c50d3..94395585e3 100644 --- a/dist/workers/graph.js +++ b/dist/workers/graph.js @@ -623,7 +623,9 @@ onClick = ({ x, y }) => { onSelect = ({ x, y, selectRelated }) => { const d = getNodeByCoords(x, y); - let related = []; + + let related = []; + if (d) { if (selectRelated) { related = edgeMap.get(d.id); @@ -637,6 +639,7 @@ onSetRootId = ({ x, y }) => { const d = getNodeByCoords(x, y); if (d) { this.setRootId({ rootId: d.id }); + send('setRootId', { node: d.id }); }; }; diff --git a/package-lock.json b/package-lock.json index aa3f0c5521..c13a6fcf90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "anytype", - "version": "0.43.26-alpha", + "version": "0.43.27-alpha", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "anytype", - "version": "0.43.26-alpha", + "version": "0.43.27-alpha", "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { diff --git a/package.json b/package.json index 549f57a6d0..c3ad36fc0a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "anytype", - "version": "0.43.26-alpha", + "version": "0.43.27-alpha", "description": "Anytype", "main": "electron.js", "scripts": { diff --git a/src/ts/component/block/chat.tsx b/src/ts/component/block/chat.tsx index a28c0c78b8..677c9a1f5d 100644 --- a/src/ts/component/block/chat.tsx +++ b/src/ts/component/block/chat.tsx @@ -115,7 +115,7 @@ const BlockChat = observer(class BlockChat extends React.Component { onReplyClear () { this.replyingId = ''; this.forceUpdate(); + this.props.scrollToBottom(); }; onDelete (id: string) { diff --git a/src/ts/component/block/featured.tsx b/src/ts/component/block/featured.tsx index 73061ab4e5..4a38c3ceb8 100644 --- a/src/ts/component/block/featured.tsx +++ b/src/ts/component/block/featured.tsx @@ -57,7 +57,7 @@ const BlockFeatured = observer(class BlockFeatured extends React.Component this.node = node} - className={[ 'wrap', 'focusable', 'c' + block.id ].join(' ')} + className={[ 'wrap', 'focusable', `c${block.id}` ].join(' ')} tabIndex={0} onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} diff --git a/src/ts/component/drag/box.tsx b/src/ts/component/drag/box.tsx index 237ecf4c46..57c6b68dbc 100644 --- a/src/ts/component/drag/box.tsx +++ b/src/ts/component/drag/box.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import React, { FC, useRef } from 'react'; import { U } from 'Lib'; interface Props { @@ -6,61 +6,29 @@ interface Props { onDragEnd(oldIndex: number, newIndex: number): void; }; -class DragBox extends React.Component { - - _isMounted = false; - node: any = null; - cache: any = {}; - ox = 0; - oy = 0; - oldIndex = -1; - newIndex = -1; - - constructor (props: Props) { - super(props); +const DragBox: FC = ({ children: initialChildren, onDragEnd }) => { - this.onDragStart = this.onDragStart.bind(this); - }; - - render () { - const children = React.Children.map(this.props.children, (child: any) => { - return React.cloneElement(child, { - onDragStart: this.onDragStart - }); - }); + const nodeRef = useRef(null); + const cache = useRef({}); + const ox = useRef(0); + const oy = useRef(0); + const oldIndex = useRef(-1); + const newIndex = useRef(-1); - return ( - this.node = node} - className="dragbox" - > - {children} - - ); - }; - - componentDidMount () { - this._isMounted = true; - }; - - componentWillUnmount () { - this._isMounted = false; - }; - - onDragStart (e: any) { + const onDragStart = (e: any) => { e.preventDefault(); e.stopPropagation(); - if (!this._isMounted) { + if (!nodeRef.current) { return; }; const win = $(window); - const node = $(this.node); + const node = $(nodeRef.current); const items = node.find('.isDraggable'); const element = $(e.currentTarget); const clone = element.clone(); - const offset = node.offset(); + const { left, top } = node.offset(); items.each((i: number, item: any) => { item = $(item); @@ -71,7 +39,7 @@ class DragBox extends React.Component { }; const p = item.position(); - this.cache[id] = { + cache.current[id] = { x: p.left, y: p.top, width: item.outerWidth(), @@ -79,58 +47,57 @@ class DragBox extends React.Component { }; }); - this.ox = offset.left; - this.oy = offset.top; - this.oldIndex = element.data('index'); + ox.current = left; + oy.current = top; + oldIndex.current = element.data('index'); node.append(clone); clone.addClass('isClone'); element.addClass('isDragging'); win.off('mousemove.dragbox mouseup.dragbox'); - win.on('mousemove.dragbox', e => this.onDragMove(e)); - win.on('mouseup.dragbox', e => this.onDragEnd(e)); + win.on('mousemove.dragbox', e => onDragMove(e)); + win.on('mouseup.dragbox', e => onDragEndHandler(e)); }; - onDragMove (e: any) { - if (!this._isMounted) { + const onDragMove = (e: any) => { + if (!nodeRef.current) { return; }; - const node = $(this.node); + const node = $(nodeRef.current); const items = node.find('.isDraggable'); const clone = node.find('.isDraggable.isClone'); const width = clone.outerWidth(); const height = clone.outerHeight(); - const x = e.pageX - this.ox - width / 2; - const y = e.pageY - this.oy - height / 2; + const x = e.pageX - ox.current - width / 2; + const y = e.pageY - oy.current - height / 2; const center = x + width / 2; - this.newIndex = -1; + newIndex.current = -1; node.find('.isDraggable.isOver').removeClass('isOver left right'); clone.css({ transform: `translate3d(${x}px,${y}px,0px)` }); for (let i = 0; i < items.length; ++i) { const el = $(items.get(i)); - const rect = this.cache[el.data('id')]; + const rect = cache.current[el.data('id')]; if (rect && U.Common.rectsCollide({ x: center, y, width: 2, height }, rect)) { const isLeft = center <= rect.x + rect.width / 2; - this.newIndex = i; + newIndex.current = i; el.addClass('isOver ' + (isLeft ? 'left' : 'right')); break; }; }; }; - onDragEnd (e: any) { - if (!this._isMounted) { + const onDragEndHandler = (e: any) => { + if (!nodeRef.current) { return; }; - const node = $(this.node); - const { onDragEnd } = this.props; + const node = $(nodeRef.current); node.find('.isDraggable.isClone').remove(); node.find('.isDraggable.isDragging').removeClass('isDragging'); @@ -138,15 +105,25 @@ class DragBox extends React.Component { $(window).off('mousemove.dragbox mouseup.dragbox'); - if (this.newIndex >= 0) { - onDragEnd(this.oldIndex, this.newIndex); + if (newIndex.current >= 0) { + onDragEnd(oldIndex.current, newIndex.current); }; - this.cache = {}; - this.oldIndex = -1; - this.newIndex = -1; + cache.current = {}; + oldIndex.current = -1; + newIndex.current = -1; }; - + + const children = React.Children.map(initialChildren, (child: any) => React.cloneElement(child, { onDragStart })); + + return ( + + {children} + + ); }; export default DragBox; \ No newline at end of file diff --git a/src/ts/component/drag/layer.tsx b/src/ts/component/drag/layer.tsx index e76b9f67fc..ce7f6bba50 100644 --- a/src/ts/component/drag/layer.tsx +++ b/src/ts/component/drag/layer.tsx @@ -1,93 +1,22 @@ -import * as React from 'react'; +import React, { forwardRef, useRef, useImperativeHandle } from 'react'; import * as ReactDOM from 'react-dom'; import $ from 'jquery'; +import { observer } from 'mobx-react'; import { I, M, S, U, J, keyboard } from 'Lib'; -interface State { - rootId: string; - type: I.DropType; - width: number; - ids: string[]; -}; +const DragLayer = observer(forwardRef((_, ref: any) => { + + const nodeRef = useRef(null); -class DragLayer extends React.Component { - - _isMounted = false; - node: any = null; - state = { - rootId: '', - type: I.DropType.None, - width: 0, - ids: [] as string[], - }; - - constructor (props: any) { - super(props); - - this.show = this.show.bind(this); - this.hide = this.hide.bind(this); - }; - - render () { - const { width } = this.state; - - return ( -
this.node = node} - id="dragLayer" - className="dragLayer" - style={{ width }} - > -
-
- ); - }; - - componentDidMount () { - this._isMounted = true; - }; - - componentDidUpdate () { - if (!this._isMounted) { - return; - }; - - const node = $(this.node); - - node.find('.block').attr({ id: '' }); - node.find('.selectionTarget').attr({ id: '' }); - - this.renderContent(); - }; - - componentWillUnmount () { - this._isMounted = false; - }; - - show (rootId: string, type: I.DropType, ids: string[], component: any, x: number, y: number) { - if (!this._isMounted) { - return; - }; - + const show = (rootId: string, type: I.DropType, ids: string[], component: any) => { const comp = $(ReactDOM.findDOMNode(component)); const rect = (comp.get(0) as Element).getBoundingClientRect(); - - this.setState({ rootId, type, width: rect.width - J.Size.blockMenu, ids }); - }; - - hide () { - if (this._isMounted) { - this.setState({ rootId: '', type: I.DropType.None, ids: [], width: 0 }); - }; - }; - - renderContent () { - const { rootId, type, ids } = this.state; - const node = $(this.node); + const width = rect.width - J.Size.blockMenu; + const node = $(nodeRef.current); const inner = node.find('#inner').html(''); const container = U.Common.getPageContainer(keyboard.isPopup()); const wrap = $('
'); - + switch (type) { case I.DropType.Block: { wrap.addClass('blocks'); @@ -157,8 +86,31 @@ class DragLayer extends React.Component { }; inner.append(wrap); + + node.css({ width }); + node.find('.block').attr({ id: '' }); + node.find('.selectionTarget').attr({ id: '' }); }; - -}; + + const hide = () => { + $(nodeRef.current).find('#inner').html('') + }; + + useImperativeHandle(ref, () => ({ + show, + hide, + })); + + return ( +
+
+
+ ); + +})); export default DragLayer; \ No newline at end of file diff --git a/src/ts/component/drag/provider.tsx b/src/ts/component/drag/provider.tsx index 1eff185b7d..a590693fa6 100644 --- a/src/ts/component/drag/provider.tsx +++ b/src/ts/component/drag/provider.tsx @@ -218,6 +218,7 @@ const DragProvider = observer(class DragProvider extends React.Component this.initData(); this.unbind(); + console.log('SET LAYER'); e.dataTransfer.setDragImage(layer.get(0), 0, 0); e.dataTransfer.setData('text/plain', JSON.stringify(dataTransfer)); e.dataTransfer.setData('data-' + JSON.stringify(dataTransfer), '1'); diff --git a/src/ts/component/graph/provider.tsx b/src/ts/component/graph/provider.tsx index e9a4744e6d..17862c9006 100644 --- a/src/ts/component/graph/provider.tsx +++ b/src/ts/component/graph/provider.tsx @@ -343,6 +343,10 @@ const Graph = observer(forwardRef(({ break; }; + case 'setRootId': { + $(window).trigger('updateGraphRoot', { id: data.node }); + }; + }; }; diff --git a/src/ts/component/header/index.tsx b/src/ts/component/header/index.tsx index af324dae7b..72d99d44b3 100644 --- a/src/ts/component/header/index.tsx +++ b/src/ts/component/header/index.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; +import React, { forwardRef, useRef, useEffect, useImperativeHandle } from 'react'; import { I, S, U, J, Renderer, keyboard, sidebar, Preview, translate } from 'Lib'; -import { Icon } from 'Component'; +import { Icon, Sync } from 'Component'; import HeaderAuthIndex from './auth'; import HeaderMainObject from './main/object'; @@ -25,80 +25,51 @@ const Components = { mainEmpty: HeaderMainEmpty, }; -class Header extends React.Component { +const Header = forwardRef<{}, Props>((props, ref) => { - refChild: any = null; + const { + component, + className = '', + withBanner = false, + rootId = '', + tab = '', + tabs = [], + layout = I.ObjectLayout.Page, + isPopup = false, + onTab, + } = props; - constructor (props: Props) { - super(props); + const childRef = useRef(null); + const Component = Components[component] || null; + const cn = [ 'header', component, className ]; - this.menuOpen = this.menuOpen.bind(this); - this.renderLeftIcons = this.renderLeftIcons.bind(this); - this.renderTabs = this.renderTabs.bind(this); - this.onSearch = this.onSearch.bind(this); - this.onTooltipShow = this.onTooltipShow.bind(this); - this.onTooltipHide = this.onTooltipHide.bind(this); - this.onDoubleClick = this.onDoubleClick.bind(this); - this.onExpand = this.onExpand.bind(this); - this.onRelation = this.onRelation.bind(this); + if (![ 'authIndex' ].includes(component)) { + cn.push('isCommon'); }; - - render () { - const { component, className, withBanner } = this.props; - const Component = Components[component] || null; - const cn = [ 'header', component, className ]; - if (![ 'authIndex' ].includes(component)) { - cn.push('isCommon'); - }; + if (withBanner) { + cn.push('withBanner'); + }; - if (withBanner) { - cn.push('withBanner'); - }; + const renderLeftIcons = (onOpen?: () => void) => { + const object = S.Detail.get(rootId, rootId, J.Relation.template); + const isTypeOrRelation = U.Object.isTypeOrRelationLayout(object.layout); + const showMenu = !isTypeOrRelation; + const canSync = showMenu && !object.templateIsBundled && !U.Object.isParticipantLayout(object.layout); return ( - - ); - }; - - componentDidMount () { - sidebar.resizePage(null, null, false); - }; - - componentDidUpdate () { - sidebar.resizePage(null, null, false); - this.refChild?.forceUpdate(); - }; - - renderLeftIcons (onOpen?: () => void) { - return ( - + <> - + {canSync ? : ''} + ); }; - renderTabs () { - const { tab, tabs, onTab } = this.props; - + const renderTabs = () => { return (
{tabs.map((item: any, i: number) => ( @@ -106,8 +77,8 @@ class Header extends React.Component { key={i} className={[ 'tab', (item.id == tab ? 'active' : '') ].join(' ')} onClick={() => onTab(item.id)} - onMouseOver={e => this.onTooltipShow(e, item.tooltip, item.tooltipCaption)} - onMouseOut={this.onTooltipHide} + onMouseOver={e => onTooltipShow(e, item.tooltip, item.tooltipCaption)} + onMouseOut={onTooltipHide} > {item.name}
@@ -116,37 +87,34 @@ class Header extends React.Component { ); }; - onExpand () { - const { rootId, layout } = this.props; - + const onExpand = () => { S.Popup.closeAll(null, () => U.Object.openRoute({ id: rootId, layout })); }; - onSearch () { + const onSearch = () => { keyboard.onSearchPopup('Header'); }; - onTooltipShow (e: any, text: string, caption?: string) { + const onTooltipShow = (e: any, text: string, caption?: string) => { const t = Preview.tooltipCaption(text, caption); if (t) { Preview.tooltipShow({ text: t, element: $(e.currentTarget), typeY: I.MenuDirection.Bottom }); }; }; - onTooltipHide () { + const onTooltipHide = () => { Preview.tooltipHide(false); }; - onDoubleClick () { + const onDoubleClick = () => { if (U.Common.isPlatformMac()) { Renderer.send('winCommand', 'maximize'); }; }; - menuOpen (id: string, elementId: string, param: Partial) { - const { isPopup } = this.props; + const menuOpen = (id: string, elementId: string, param: Partial) => { const st = $(window).scrollTop(); - const element = $(`${this.getContainer()} ${elementId}`); + const element = $(`${getContainer()} ${elementId}`); const menuParam: any = Object.assign({ element, offsetY: 4, @@ -160,18 +128,17 @@ class Header extends React.Component { S.Menu.closeAllForced(null, () => S.Menu.open(id, menuParam)); }; - onRelation (param?: Partial, data?: any) { + const onRelation = (param?: Partial, data?: any) => { param = param || {}; data = data || {}; - const { isPopup, rootId } = this.props; const cnw = [ 'fixed' ]; if (!isPopup) { cnw.push('fromHeader'); }; - this.menuOpen('blockRelationView', '#button-header-relation', { + menuOpen('blockRelationView', '#button-header-relation', { noFlipX: true, noFlipY: true, horizontal: I.MenuDirection.Right, @@ -186,10 +153,65 @@ class Header extends React.Component { }); }; - getContainer () { - return (this.props.isPopup ? '.popup' : '') + ' .header'; + const onSync = () => { + menuOpen('syncStatus', '#button-header-sync', { + subIds: [ 'syncStatusInfo' ], + data: { + rootId, + } + }); }; -}; + const getContainer = () => { + return (isPopup ? '.popup' : '') + ' .header'; + }; + + useEffect(() => { + sidebar.resizePage(null, null, false); + }); + + useImperativeHandle(ref, () => ({ + setRootId: (rootId: string) => { + if (childRef.current && childRef.current.setRootId) { + childRef.current.setRootId(rootId); + }; + }, + + setVersion: (version: string) => { + if (childRef.current && childRef.current.setVersion) { + childRef.current.setVersion(version); + }; + }, + + forceUpdate: () => { + if (childRef.current && childRef.current.forceUpdate) { + childRef.current.forceUpdate(); + }; + }, + })); + + return ( + + ); + +}); export default Header; \ No newline at end of file diff --git a/src/ts/component/header/main/chat.tsx b/src/ts/component/header/main/chat.tsx index 87a68cca4a..c9435dd667 100644 --- a/src/ts/component/header/main/chat.tsx +++ b/src/ts/component/header/main/chat.tsx @@ -1,60 +1,25 @@ -import * as React from 'react'; +import React, { forwardRef } from 'react'; import { observer } from 'mobx-react'; -import { Sync } from 'Component'; -import { I, S, U, J, keyboard } from 'Lib'; +import { I, S, U, keyboard } from 'Lib'; -interface State { - templatesCnt: number; -}; - -const HeaderMainChat = observer(class HeaderMainChat extends React.Component { - - state = { - templatesCnt: 0 - }; - - constructor (props: I.HeaderComponent) { - super(props); - - this.onSync = this.onSync.bind(this); - this.onOpen = this.onOpen.bind(this); - }; - - render () { - const { rootId, renderLeftIcons } = this.props; - - return ( - -
- {renderLeftIcons(this.onOpen)} - -
- -
-
- - ); - }; - - onOpen () { - const { rootId } = this.props; +const HeaderMainChat = observer(forwardRef<{}, I.HeaderComponent>((props, ref) => { + const { rootId, renderLeftIcons } = props; + + const onOpen = () => { const object = S.Detail.get(rootId, rootId, []); keyboard.disableClose(true); S.Popup.closeAll(null, () => U.Object.openRoute(object)); }; - onSync () { - const { rootId, menuOpen } = this.props; + return ( + <> +
{renderLeftIcons(onOpen)}
+
+
+ + ); - menuOpen('syncStatus', '#button-header-sync', { - subIds: [ 'syncStatusInfo' ], - data: { - rootId, - } - }); - }; - -}); +})); export default HeaderMainChat; \ No newline at end of file diff --git a/src/ts/component/header/main/empty.tsx b/src/ts/component/header/main/empty.tsx index 620255503c..d05081fa8e 100644 --- a/src/ts/component/header/main/empty.tsx +++ b/src/ts/component/header/main/empty.tsx @@ -1,17 +1,17 @@ -import * as React from 'react'; +import React, { FC } from 'react'; import { I } from 'Lib'; -class HeaderMainEmpty extends React.Component { +const HeaderMainEmpty: FC = (props) => { + + const { renderLeftIcons } = props; - render () { - return ( - -
{this.props.renderLeftIcons()}
-
-
- - ); - }; + return ( + <> +
{renderLeftIcons()}
+
+
+ + ); }; diff --git a/src/ts/component/header/main/graph.tsx b/src/ts/component/header/main/graph.tsx index 459a58830d..5c599c7ab2 100644 --- a/src/ts/component/header/main/graph.tsx +++ b/src/ts/component/header/main/graph.tsx @@ -1,48 +1,25 @@ -import * as React from 'react'; +import React, { forwardRef, useRef, useEffect, useImperativeHandle } from 'react'; import { Icon } from 'Component'; import { I, S, U, J, translate } from 'Lib'; -class HeaderMainGraph extends React.Component { +interface HeaderComponentRefProps { + setRootId: (id: string) => void; +}; - refFilter: any = null; - rootId = ''; +const HeaderMainGraph = forwardRef((props, ref) => { - constructor (props: I.HeaderComponent) { - super(props); - - this.onSearch = this.onSearch.bind(this); - this.onFilter = this.onFilter.bind(this); - this.onSettings = this.onSettings.bind(this); - }; + const { renderLeftIcons, renderTabs, menuOpen, rootId } = props; + const rootIdRef = useRef(''); - render () { - const { renderLeftIcons, renderTabs } = this.props; + const onSearch = () => { + const rootId = rootIdRef.current; - return ( - -
{renderLeftIcons()}
-
{renderTabs()}
- -
- - - -
-
- ); - }; - - componentDidMount(): void { - this.setRootId(this.props.rootId); - }; - - onSearch () { - this.props.menuOpen('searchObject', '#button-header-search', { + menuOpen('searchObject', '#button-header-search', { horizontal: I.MenuDirection.Right, data: { - rootId: this.rootId, - blockId: this.rootId, - blockIds: [ this.rootId ], + rootId: rootId, + blockId: rootId, + blockIds: [ rootId ], filters: U.Data.graphFilters(), filter: S.Common.getGraph(J.Constant.graphId.global).filter, canAdd: true, @@ -56,11 +33,11 @@ class HeaderMainGraph extends React.Component { }); }; - onFilter () { + const onFilter = () => { }; - onSettings () { - this.props.menuOpen('graphSettings', '#button-header-settings', { + const onSettings = () => { + menuOpen('graphSettings', '#button-header-settings', { horizontal: I.MenuDirection.Right, data: { allowLocal: true, @@ -69,10 +46,27 @@ class HeaderMainGraph extends React.Component { }); }; - setRootId (id: string) { - this.rootId = id; - }; + useImperativeHandle(ref, () => ({ + setRootId: (id: string) => rootIdRef.current = id, + })); -}; + useEffect(() => { + rootIdRef.current = rootId; + }, []); + + return ( + <> +
{renderLeftIcons()}
+
{renderTabs()}
+ +
+ + + +
+ + ); + +}); export default HeaderMainGraph; \ No newline at end of file diff --git a/src/ts/component/header/main/navigation.tsx b/src/ts/component/header/main/navigation.tsx index 79a56e3b5c..be5bd62474 100644 --- a/src/ts/component/header/main/navigation.tsx +++ b/src/ts/component/header/main/navigation.tsx @@ -1,20 +1,18 @@ -import * as React from 'react'; +import React, { forwardRef } from 'react'; import { I } from 'Lib'; -class HeaderMainNavigation extends React.Component { +const HeaderMainNavigation = forwardRef<{}, I.HeaderComponent>((props, ref) => { + + const { renderLeftIcons, renderTabs } = props; - render () { - const { renderLeftIcons, renderTabs } = this.props; + return ( + <> +
{renderLeftIcons()}
+
{renderTabs()}
+
+ + ); - return ( - -
{renderLeftIcons()}
-
{renderTabs()}
-
- - ); - }; - -}; +}); export default HeaderMainNavigation; \ No newline at end of file diff --git a/src/ts/component/header/main/object.tsx b/src/ts/component/header/main/object.tsx index 0a4ba8f3b5..34d2b4dbfd 100644 --- a/src/ts/component/header/main/object.tsx +++ b/src/ts/component/header/main/object.tsx @@ -1,149 +1,75 @@ -import * as React from 'react'; +import React, { forwardRef, useState, useEffect, useImperativeHandle } from 'react'; import { observer } from 'mobx-react'; -import { Icon, IconObject, Sync, ObjectName, Label } from 'Component'; -import { I, S, U, J, keyboard, translate, sidebar } from 'Lib'; +import { Icon, IconObject, ObjectName, Label } from 'Component'; +import { I, S, U, J, keyboard, translate } from 'Lib'; import HeaderBanner from 'Component/page/elements/head/banner'; -interface State { - templatesCnt: number; -}; +const HeaderMainObject = observer(forwardRef<{}, I.HeaderComponent>((props, ref) => { -const HeaderMainObject = observer(class HeaderMainObject extends React.Component { + const { rootId, match, isPopup, onSearch, onTooltipShow, onTooltipHide, renderLeftIcons, onRelation, menuOpen } = props; + const [ templatesCnt, setTemplateCnt ] = useState(0); + const [ dummy, setDummy ] = useState(0); + const root = S.Block.getLeaf(rootId, rootId); + const object = S.Detail.get(rootId, rootId, J.Relation.template); + const isLocked = root ? root.isLocked() : false; + const isTypeOrRelation = U.Object.isTypeOrRelationLayout(object.layout); + const isDate = U.Object.isDateLayout(object.layout); + const showRelations = !isTypeOrRelation && !isDate; + const showMenu = !isTypeOrRelation; + const cmd = keyboard.cmdSymbol(); + const allowedTemplateSelect = (object.internalFlags || []).includes(I.ObjectFlag.SelectTemplate); + const bannerProps: any = {}; - state = { - templatesCnt: 0 + let center = null; + let banner = I.BannerType.None; + let locked = ''; + + if (object.isArchived && U.Space.canMyParticipantWrite()) { + banner = I.BannerType.IsArchived; + } else + if (U.Object.isTemplate(object.type)) { + banner = I.BannerType.IsTemplate; + } else + if (allowedTemplateSelect && templatesCnt) { + banner = I.BannerType.TemplateSelect; + bannerProps.count = templatesCnt + 1; }; - constructor (props: I.HeaderComponent) { - super(props); - - this.onRelation = this.onRelation.bind(this); - this.onMore = this.onMore.bind(this); - this.onSync = this.onSync.bind(this); - this.onOpen = this.onOpen.bind(this); - this.updateTemplatesCnt = this.updateTemplatesCnt.bind(this); + if (isLocked) { + locked = translate('headerObjectLocked'); + } else + if (U.Object.isTypeOrRelationLayout(object.layout) && !S.Block.isAllowed(object.restrictions, [ I.RestrictionObject.Delete ])) { + locked = translate('commonSystem'); }; - render () { - const { rootId, onSearch, onTooltipShow, onTooltipHide, isPopup, renderLeftIcons } = this.props; - const { templatesCnt } = this.state; - const root = S.Block.getLeaf(rootId, rootId); - - if (!root) { - return null; - }; - - const object = S.Detail.get(rootId, rootId, J.Relation.template); - const isLocked = root ? root.isLocked() : false; - const isTypeOrRelation = U.Object.isTypeOrRelationLayout(object.layout); - const isDate = U.Object.isDateLayout(object.layout); - const showRelations = !isTypeOrRelation && !isDate; - const showMenu = true; //!isTypeOrRelation; - const canSync = showMenu && !object.templateIsBundled && !U.Object.isParticipantLayout(object.layout); - const cmd = keyboard.cmdSymbol(); - const allowedTemplateSelect = (object.internalFlags || []).includes(I.ObjectFlag.SelectTemplate); - const bannerProps: any = {}; - - let center = null; - let banner = I.BannerType.None; - let locked = ''; - - if (object.isArchived && U.Space.canMyParticipantWrite()) { - banner = I.BannerType.IsArchived; - } else - if (U.Object.isTemplate(object.type)) { - banner = I.BannerType.IsTemplate; - } else - if (allowedTemplateSelect && templatesCnt) { - banner = I.BannerType.TemplateSelect; - bannerProps.count = templatesCnt + 1; - }; - - if (isLocked) { - locked = translate('headerObjectLocked'); - } else - if (U.Object.isTypeOrRelationLayout(object.layout) && !S.Block.isAllowed(object.restrictions, [ I.RestrictionObject.Delete ])) { - locked = translate('commonSystem'); - }; - - if (banner == I.BannerType.None) { - center = ( -
onTooltipShow(e, translate('headerTooltipPath'))} - onMouseOut={onTooltipHide} - > -
- - - {locked ?
+ if (banner == I.BannerType.None) { + center = ( +
onTooltipShow(e, translate('headerTooltipPath'))} + onMouseOut={onTooltipHide} + > +
+ + + {locked ?
- ); - } else { - center = ; - }; - - return ( - -
- {renderLeftIcons(this.onOpen)} - {canSync ? : ''} -
- -
- {center} -
- -
- {showRelations ? ( - - ) : ''} - - {showMenu ? ( - - ) : ''} -
-
+
); + } else { + center = ; }; - componentDidMount () { - this.init(); - }; - - componentDidUpdate () { - this.init(); - }; - - init () { - this.updateTemplatesCnt(); - }; - - onOpen () { - const { rootId } = this.props; + const onOpen = () => { const object = S.Detail.get(rootId, rootId, []); keyboard.disableClose(true); S.Popup.closeAll(null, () => U.Object.openRoute(object)); }; - onMore () { - const { isPopup, match, rootId, menuOpen } = this.props; - + const onMore = () => { menuOpen('object', '#button-header-more', { horizontal: I.MenuDirection.Right, subIds: J.Menu.object, @@ -157,29 +83,13 @@ const HeaderMainObject = observer(class HeaderMainObject extends React.Component }); }; - onSync () { - const { rootId, menuOpen } = this.props; - - menuOpen('syncStatus', '#button-header-sync', { - subIds: [ 'syncStatusInfo' ], - data: { - rootId, - } - }); - }; - - onRelation () { - const { rootId } = this.props; + const onRelationHandler = () => { const object = S.Detail.get(rootId, rootId, [ 'isArchived' ]); - sidebar.rightPanelToggle(!S.Common.showSidebarRight, 'object/relation', { rootId }); - - //this.props.onRelation({}, { readonly: object.isArchived }); + onRelation({}, { readonly: object.isArchived }); }; - updateTemplatesCnt () { - const { rootId } = this.props; - const { templatesCnt } = this.state; + const updateTemplatesCnt = () => { const object = S.Detail.get(rootId, rootId, [ 'internalFlags' ]); const allowedTemplateSelect = (object.internalFlags || []).includes(I.ObjectFlag.SelectTemplate); @@ -193,11 +103,52 @@ const HeaderMainObject = observer(class HeaderMainObject extends React.Component }; if (message.records.length != templatesCnt) { - this.setState({ templatesCnt: message.records.length }); + setTemplateCnt(message.records.length); }; }); }; -}); + useEffect(() => { + updateTemplatesCnt(); + }); -export default HeaderMainObject; + useImperativeHandle(ref, () => ({ + forceUpdate: () => setDummy(dummy + 1), + })); + + return ( + <> +
+ {renderLeftIcons(onOpen)} +
+ +
+ {center} +
+ +
+ {showRelations ? ( + + ) : ''} + + {showMenu ? ( + + ) : ''} +
+ + ); + +})); + +export default HeaderMainObject; \ No newline at end of file diff --git a/src/ts/component/menu/object.tsx b/src/ts/component/menu/object.tsx index a4f72e9d26..85c45064c6 100644 --- a/src/ts/component/menu/object.tsx +++ b/src/ts/component/menu/object.tsx @@ -176,7 +176,7 @@ class MenuObject extends React.Component { const allowedSearch = !isFilePreview && !isInSetLayouts; const allowedHistory = !object.isArchived && !isInFileOrSystemLayouts && !isParticipant && !isDate && !object.templateIsBundled; const allowedFav = canWrite && !object.isArchived && !object.templateIsBundled; - const allowedLock = canWrite && !object.isArchived && S.Block.checkFlags(rootId, rootId, [ I.RestrictionObject.Details ]); + const allowedLock = canWrite && !object.isArchived && S.Block.checkFlags(rootId, rootId, [ I.RestrictionObject.Details ]) && !isInFileOrSystemLayouts; const allowedLinkTo = canWrite && !object.isArchived; const allowedAddCollection = canWrite && !object.isArchived; const allowedPageLink = !object.isArchived; diff --git a/src/ts/component/page/main/date.tsx b/src/ts/component/page/main/date.tsx index 11165ba975..d0e80cb885 100644 --- a/src/ts/component/page/main/date.tsx +++ b/src/ts/component/page/main/date.tsx @@ -283,6 +283,10 @@ const PageMainDate = observer(class PageMainDate extends React.Component { const relations = (message.relations || []).map(it => S.Record.getRelationByKey(it.relationKey)).filter(it => { + if (!it) { + return false; + }; + if ([ RELATION_KEY_MENTION ].includes(it.relationKey)) { return true; }; diff --git a/src/ts/component/page/main/graph.tsx b/src/ts/component/page/main/graph.tsx index d21365e590..5e99cc711c 100644 --- a/src/ts/component/page/main/graph.tsx +++ b/src/ts/component/page/main/graph.tsx @@ -166,7 +166,7 @@ const PageMainGraph = observer(class PageMainGraph extends React.Component { +const PopupAbout: FC = () => { - constructor (props: I.Popup) { - super(props); + const version = U.Common.getElectron().version.app; - this.onVersionCopy = this.onVersionCopy.bind(this); - }; + return ( + <> +
+ +
+ + <Label text={translate('popupAboutDescription')} /> - render () { - return ( - <React.Fragment> - <div className="iconWrapper"> - <Icon /> - </div> - <Title text={translate('popupAboutTitle')} /> - <Label text={translate('popupAboutDescription')} /> - - <div className="version"> - {U.Common.sprintf(translate('popupAboutVersion'), this.getVersion())} - <Button onClick={this.onVersionCopy} text={translate('commonCopy')} className="c28" color="blank" /> - </div> - <div className="copyright">{translate('popupAboutCopyright')}</div> - </React.Fragment> - ); - }; - - getVersion () { - return U.Common.getElectron().version.app; - }; - - onVersionCopy () { - U.Common.copyToast(translate('commonVersion'), this.getVersion()); - }; + <div className="version"> + {U.Common.sprintf(translate('popupAboutVersion'), version)} + <Button + onClick={() => U.Common.copyToast(translate('commonVersion'), version)} + text={translate('commonCopy')} + className="c28" + color="blank" + /> + </div> + <div className="copyright">{translate('popupAboutCopyright')}</div> + </> + ); }; diff --git a/src/ts/component/popup/phrase.tsx b/src/ts/component/popup/phrase.tsx index 02a182c533..8bf7d7b22e 100644 --- a/src/ts/component/popup/phrase.tsx +++ b/src/ts/component/popup/phrase.tsx @@ -1,44 +1,42 @@ -import * as React from 'react'; +import React, { FC } from 'react'; import { Title, Label, Button, IconObject } from 'Component'; import { I, translate } from 'Lib'; -class PopupPhrase extends React.Component<I.Popup> { +const PopupPhrase: FC<I.Popup> = (props) => { - render () { - return ( - <div> - <Title text={translate('popupPhraseTitle1')} /> - <div className="rows"> - <div className="row"> - <IconObject size={40} iconSize={40} object={{ iconEmoji: ':game_die:' }} /> - <Label text={translate('popupPhraseLabel1')} /> - </div> - <div className="row"> - <IconObject size={40} iconSize={40} object={{ iconEmoji: ':old_key:' }} /> - <Label text={translate('popupPhraseLabel2')} /> - </div> - <div className="row"> - <IconObject size={40} iconSize={40} object={{ iconEmoji: ':point_up:' }} /> - <Label text={translate('popupPhraseLabel3')} /> - </div> + return ( + <> + <Title text={translate('popupPhraseTitle1')} /> + <div className="rows"> + <div className="row"> + <IconObject size={40} iconSize={40} object={{ iconEmoji: ':game_die:' }} /> + <Label text={translate('popupPhraseLabel1')} /> </div> - - <Title className="c2" text={translate('popupPhraseTitle2')} /> - <div className="columns"> - <div className="column"> - <li>{translate('popupPhraseLabel4')}</li> - </div> - <div className="column"> - <li>{translate('popupPhraseLabel5')}</li> - </div> + <div className="row"> + <IconObject size={40} iconSize={40} object={{ iconEmoji: ':old_key:' }} /> + <Label text={translate('popupPhraseLabel2')} /> </div> - - <div className="buttons"> - <Button text={translate('commonOkay')} onClick={() => this.props.close()} /> + <div className="row"> + <IconObject size={40} iconSize={40} object={{ iconEmoji: ':point_up:' }} /> + <Label text={translate('popupPhraseLabel3')} /> </div> </div> - ); - }; + + <Title className="c2" text={translate('popupPhraseTitle2')} /> + <div className="columns"> + <div className="column"> + <li>{translate('popupPhraseLabel4')}</li> + </div> + <div className="column"> + <li>{translate('popupPhraseLabel5')}</li> + </div> + </div> + + <div className="buttons"> + <Button text={translate('commonOkay')} onClick={() => props.close()} /> + </div> + </> + ); }; diff --git a/src/ts/component/popup/share.tsx b/src/ts/component/popup/share.tsx index dd1dcb4d63..89b26d8f49 100644 --- a/src/ts/component/popup/share.tsx +++ b/src/ts/component/popup/share.tsx @@ -1,35 +1,26 @@ -import * as React from 'react'; +import React, { FC } from 'react'; import { Title, Label, Button } from 'Component'; import { I, U, J, translate, analytics } from 'Lib'; -class PopupShare extends React.Component<I.Popup> { +const PopupShare: FC<I.Popup> = () => { - constructor (props: I.Popup) { - super(props); - - this.onClick = this.onClick.bind(this); - }; - - render () { - return ( - <div> - <Title text={translate('popupShareTitle')} /> - <Label text={translate('popupShareLabel')} /> - - <div className="section"> - <Label text={U.Common.sprintf(translate('popupShareLinkText'), J.Url.share, J.Url.share)} /> - </div> - - <Button text={translate('commonCopyLink')} onClick={this.onClick} /> - </div> - ); - }; - - onClick () { + const onClick = () => { U.Common.copyToast(translate('commonLink'), J.Url.share); analytics.event('ClickShareAppCopyLink'); }; + return ( + <> + <Title text={translate('popupShareTitle')} /> + <Label text={translate('popupShareLabel')} /> + + <div className="section"> + <Label text={U.Common.sprintf(translate('popupShareLinkText'), J.Url.share, J.Url.share)} /> + </div> + + <Button text={translate('commonCopyLink')} onClick={onClick} /> + </> + ); }; -export default PopupShare; +export default PopupShare; \ No newline at end of file