Add status change support in mindmap editor

This commit is contained in:
Paulo Gustavo Veiga 2022-10-21 17:23:32 -07:00
parent 7a9c89b8ae
commit 9b10a987f3
22 changed files with 492 additions and 197 deletions

View File

@ -181,6 +181,10 @@ const ActionConfigByRenderMode: Record<ActionType, CapabilitySupport> = {
hidden: ['viewonly', 'edition-viewer', 'edition-editor', 'edition-owner'],
},
},
starred: undefined,
starred: {
desktop: {
hidden: ['showcase', 'viewonly'],
},
},
};
export default Capability;

View File

@ -48,16 +48,16 @@ class Editor {
this.component.loadMap(mapId);
}
registerEvents(setToolbarsRerenderSwitch: (timestamp: number) => void, capability: Capability) {
registerEvents(canvasUpdate: (timestamp: number) => void, capability: Capability) {
const desiger = this.component.getDesigner();
const onNodeBlurHandler = () => {
if (!desiger.getModel().selectedTopic()) {
setToolbarsRerenderSwitch(Date.now());
canvasUpdate(Date.now());
}
};
const onNodeFocusHandler = () => {
setToolbarsRerenderSwitch(Date.now());
canvasUpdate(Date.now());
};
// Register events ...

View File

@ -0,0 +1,32 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
interface MapInfo {
isStarred(): Promise<boolean>;
updateStarred(value: boolean): Promise<void>;
getTitle(): string;
setTitle(value: string): void;
isLocked(): boolean;
getLockedMessage(): string;
getZoom(): number;
getId(): string;
}
export default MapInfo;

View File

@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import React, { useEffect, useState } from 'react';
import MaterialToolbar from '@mui/material/Toolbar';
import MaterialAppBar from '@mui/material/AppBar';
import { ToolbarMenuItem } from '../toolbar';
@ -40,159 +40,168 @@ import Button from '@mui/material/Button';
import LogoTextBlackSvg from '../../../images/logo-text-black.svg';
import IconButton from '@mui/material/IconButton';
import { ToolbarActionType } from '../toolbar/ToolbarActionType';
import MapInfo from '../../classes/model/map-info';
interface AppBarProps {
model: Editor;
mapTitle: string;
mapInfo: MapInfo;
capability: Capability;
onAction?: (type: ToolbarActionType) => void;
accountConfig?;
}
const appBarDivisor = {
render: () => <Typography component="div" sx={{ flexGrow: 1 }} />,
};
const AppBar = ({ model, mapTitle, capability, onAction, accountConfig }: AppBarProps) => {
const appBarDivisor = {
render: () => <Typography component="div" sx={{ flexGrow: 1 }} />,
const AppBar = ({ model, mapInfo, capability, onAction, accountConfig }: AppBarProps) => {
const [isStarred, setStarred] = useState<undefined | boolean>(undefined);
const handleStarredOnClick = () => {
const newStatus = !isStarred;
mapInfo.updateStarred(newStatus).then(() => setStarred(newStatus));
};
const buildConfig = (
model: Editor,
mapTitle: string,
capability: Capability,
onAction: (type: ToolbarActionType) => void,
accountConfig,
): ActionConfig[] => {
return [
{
icon: <ArrowBackIosNewOutlinedIcon />,
tooltip: $msg('BACK_TO_MAP_LIST'),
onClick: () => history.back(),
},
{
render: () => <img src={LogoTextBlackSvg} />,
},
{
render: () => (
<Tooltip title={mapTitle}>
<Typography
className="truncated"
variant="body1"
component="div"
sx={{ marginX: '1.5rem' }}
>
{mapTitle}
</Typography>
</Tooltip>
),
},
null,
{
render: () => (
<UndoAndRedo
configuration={{
icon: <UndoOutlinedIcon />,
tooltip: $msg('UNDO') + ' (' + $msg('CTRL') + ' + Z)',
onClick: () => designer.undo(),
}}
disabledCondition={(event) => event.undoSteps > 0}
></UndoAndRedo>
),
visible: !capability.isHidden('undo-changes'),
disabled: () => !model?.isMapLoadded(),
},
{
render: () => (
<UndoAndRedo
configuration={{
icon: <RedoOutlinedIcon />,
tooltip: $msg('REDO') + ' (' + $msg('CTRL') + ' + Shift + Z)',
onClick: () => designer.redo(),
}}
disabledCondition={(event) => event.redoSteps > 0}
></UndoAndRedo>
),
visible: !capability.isHidden('redo-changes'),
disabled: () => !model?.isMapLoadded(),
},
null,
{
icon: <SaveOutlinedIcon />,
tooltip: $msg('SAVE') + ' (' + $msg('CTRL') + ' + S)',
onClick: () => {
model.save(true);
},
visible: !capability.isHidden('save'),
disabled: () => !model?.isMapLoadded(),
},
{
icon: <RestoreOutlinedIcon />,
tooltip: $msg('HISTORY'),
onClick: () => onAction('history'),
visible: !capability.isHidden('history'),
},
appBarDivisor,
{
tooltip: $msg('SAVE') + ' (' + $msg('CTRL') + ' + S)',
render: () => (
<IconButton size="small" onClick={() => {}}>
<StarRateRoundedIcon
color="action"
style={{
color: 'yellow',
}}
/>
</IconButton>
),
visible: !capability.isHidden('starred'),
disabled: () => !model?.isMapLoadded(),
},
{
icon: <FileDownloadOutlinedIcon />,
tooltip: $msg('EXPORT'),
onClick: () => onAction('export'),
visible: !capability.isHidden('export'),
},
{
icon: <PrintOutlinedIcon />,
tooltip: $msg('PRINT'),
onClick: () => onAction('print'),
visible: !capability.isHidden('print'),
},
{
icon: <HelpOutlineOutlinedIcon />,
onClick: () => onAction('info'),
tooltip: $msg('MAP_INFO'),
visible: !capability.isHidden('info'),
},
{
icon: <CloudUploadOutlinedIcon />,
onClick: () => onAction('publish'),
tooltip: $msg('PUBLISH'),
visible: !capability.isHidden('publish'),
},
{
render: () => (
<Button variant="contained" onClick={() => onAction('share')}>
{$msg('COLLABORATE')}
</Button>
),
visible: !capability.isHidden('share'),
},
{
render: () => accountConfig,
visible: !capability.isHidden('account'),
},
{
render: () => (
<Button variant="contained" onClick={() => (window.location.href = '/c/registration')}>
{$msg('SIGN_UP')}
</Button>
),
visible: !capability.isHidden('sign-up'),
},
];
};
useEffect(() => {
mapInfo
.isStarred()
.then((value) => setStarred(value))
.catch((e) => {
console.error(`Unexpected error loading starred status-> ${e}`);
});
}, []);
console.log(``);
const config: ActionConfig[] = [
{
icon: <ArrowBackIosNewOutlinedIcon />,
tooltip: $msg('BACK_TO_MAP_LIST'),
onClick: () => history.back(),
},
{
render: () => <img src={LogoTextBlackSvg} />,
},
{
render: () => (
<Tooltip title={mapInfo.getTitle()}>
<Typography
className="truncated"
variant="body1"
component="div"
sx={{ marginX: '1.5rem' }}
>
{mapInfo.getTitle()}
</Typography>
</Tooltip>
),
},
null,
{
render: () => (
<UndoAndRedo
configuration={{
icon: <UndoOutlinedIcon />,
tooltip: $msg('UNDO') + ' (' + $msg('CTRL') + ' + Z)',
onClick: () => designer.undo(),
}}
disabledCondition={(event) => event.undoSteps > 0}
/>
),
visible: !capability.isHidden('undo-changes'),
disabled: () => !model?.isMapLoadded(),
},
{
render: () => (
<UndoAndRedo
configuration={{
icon: <RedoOutlinedIcon />,
tooltip: $msg('REDO') + ' (' + $msg('CTRL') + ' + Shift + Z)',
onClick: () => designer.redo(),
}}
disabledCondition={(event) => event.redoSteps > 0}
/>
),
visible: !capability.isHidden('redo-changes'),
disabled: () => !model?.isMapLoadded(),
},
null,
{
icon: <SaveOutlinedIcon />,
tooltip: $msg('SAVE') + ' (' + $msg('CTRL') + ' + S)',
onClick: () => {
model.save(true);
},
visible: !capability.isHidden('save'),
disabled: () => !model?.isMapLoadded(),
},
{
icon: <RestoreOutlinedIcon />,
tooltip: $msg('HISTORY'),
onClick: () => onAction('history'),
visible: !capability.isHidden('history'),
},
appBarDivisor,
{
tooltip: $msg('STARRED'),
render: () => (
<IconButton size="small" onClick={handleStarredOnClick}>
<StarRateRoundedIcon
color="action"
style={{
color: isStarred ? 'yellow' : 'gray',
}}
/>
</IconButton>
),
visible: !capability.isHidden('starred'),
disabled: () => isStarred !== undefined,
},
{
icon: <FileDownloadOutlinedIcon />,
tooltip: $msg('EXPORT'),
onClick: () => onAction('export'),
visible: !capability.isHidden('export'),
},
{
icon: <PrintOutlinedIcon />,
tooltip: $msg('PRINT'),
onClick: () => onAction('print'),
visible: !capability.isHidden('print'),
},
{
icon: <HelpOutlineOutlinedIcon />,
onClick: () => onAction('info'),
tooltip: $msg('MAP_INFO'),
visible: !capability.isHidden('info'),
},
{
icon: <CloudUploadOutlinedIcon />,
onClick: () => onAction('publish'),
tooltip: $msg('PUBLISH'),
visible: !capability.isHidden('publish'),
},
{
render: () => (
<Button variant="contained" onClick={() => onAction('share')}>
{$msg('COLLABORATE')}
</Button>
),
visible: !capability.isHidden('share'),
},
{
render: () => accountConfig,
visible: !capability.isHidden('account'),
},
{
render: () => (
<Button variant="contained" onClick={() => (window.location.href = '/c/registration')}>
{$msg('SIGN_UP')}
</Button>
),
visible: !capability.isHidden('sign-up'),
},
];
const config = buildConfig(model, mapTitle, capability, onAction, accountConfig);
return (
<MaterialAppBar
role="menubar"

View File

@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useCallback, useEffect, useState } from 'react';
import React, { ErrorInfo, useCallback, useEffect, useState } from 'react';
import Popover from '@mui/material/Popover';
import Model from '../classes/model/editor';
import { buildEditorPanelConfig, buildZoomToolbarConfig } from './toolbar/toolbarConfigBuilder';
@ -40,19 +40,16 @@ import DefaultWidgetManager from '../classes/default-widget-manager';
import AppBar from './app-bar';
import Capability from '../classes/action/capability';
import { ToolbarActionType } from './toolbar/ToolbarActionType';
import MapInfo from '../classes/model/map-info';
type EditorOptions = {
export type EditorOptions = {
mode: EditorRenderMode;
locale: string;
zoom?: number;
locked?: boolean;
lockedMsg?: string;
mapTitle: string;
enableKeyboardEvents: boolean;
};
type EditorProps = {
mapId: string;
mapInfo: MapInfo;
options: EditorOptions;
persistenceManager: PersistenceManager;
onAction: (action: ToolbarActionType) => void;
@ -62,25 +59,24 @@ type EditorProps = {
};
const Editor = ({
mapId,
mapInfo,
options,
persistenceManager,
onAction,
theme,
accountConfiguration,
}: EditorProps) => {
const [model, setModel]: [Model | undefined, Function] = useState();
const [model, setModel] = useState<Model | undefined>();
const [canvasUpdate, setCanvasUpdate] = useState<number>();
const editorTheme: Theme = theme ? theme : defaultEditorTheme;
const [toolbarsRerenderSwitch, setToolbarsRerenderSwitch] = useState(0);
const [popoverOpen, popoverTarget, widgetManager] = DefaultWidgetManager.create();
const capability = new Capability(options.mode, options.locked);
const capability = new Capability(options.mode, mapInfo.isLocked());
const mindplotRef = useCallback((component: MindplotWebComponent) => {
// Initialized model ...
const model = new Model(component);
model.loadMindmap(mapId, persistenceManager, widgetManager);
model.registerEvents(setToolbarsRerenderSwitch, capability);
model.loadMindmap(mapInfo.getId(), persistenceManager, widgetManager);
model.registerEvents(setCanvasUpdate, capability);
setModel(model);
}, []);
@ -101,7 +97,7 @@ const Editor = ({
<IntlProvider locale={locale} messages={msg}>
<AppBar
model={model}
mapTitle={options.mapTitle}
mapInfo={mapInfo}
capability={capability}
onAction={onAction}
accountConfig={accountConfiguration}
@ -120,11 +116,9 @@ const Editor = ({
{widgetManager.getEditorContent()}
</Popover>
{!capability.isHidden('edition-toolbar') && model?.isMapLoadded() && (
<Toolbar
configurations={buildEditorPanelConfig(model)}
rerender={toolbarsRerenderSwitch}
></Toolbar>
<Toolbar configurations={buildEditorPanelConfig(model)} />
)}
<Toolbar
configurations={buildZoomToolbarConfig(model, capability)}
position={{
@ -134,22 +128,21 @@ const Editor = ({
},
vertical: false,
}}
rerender={toolbarsRerenderSwitch}
></Toolbar>
/>
<mindplot-component
ref={mindplotRef}
id="mindmap-comp"
mode={options.mode}
locale={options.locale}
></mindplot-component>
/>
<Notifier id="headerNotifier"></Notifier>
<Notifier id="headerNotifier" />
<WarningDialog
capability={capability}
message={options.locked ? options.lockedMsg : ''}
></WarningDialog>
message={mapInfo.isLocked() ? mapInfo.getLockedMessage() : ''}
/>
</IntlProvider>
</ThemeProvider>
);

View File

@ -34,7 +34,10 @@ import {
Importer,
TextImporterFactory,
} from '@wisemapping/mindplot';
import Editor from './components';
import { EditorOptions } from './components';
import MapInfo from './classes/model/map-info';
declare global {
// used in mindplot
@ -65,6 +68,8 @@ export {
Exporter,
Importer,
TextImporterFactory,
EditorOptions,
MapInfo,
};
export default Editor;

View File

@ -0,0 +1,66 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { MapInfo } from '../../../../src';
class MapInfoImpl implements MapInfo {
private id: string;
private title: string;
private locked: boolean;
private starred: boolean;
constructor(id: string, title: string, locked: boolean) {
this.id = id;
this.title = title;
this.locked = locked;
this.starred = true;
}
isStarred(): Promise<boolean> {
return Promise.resolve(this.starred);
}
updateStarred(value: boolean): Promise<void> {
this.starred = value;
return Promise.resolve();
}
getTitle(): string {
return this.title;
}
setTitle(value: string): void {
throw this.title;
}
isLocked(): boolean {
return this.locked;
}
getLockedMessage(): string {
return 'Map Is Locked !';
}
getZoom(): number {
return 0.8;
}
getId(): string {
return this.id;
}
}
export default MapInfoImpl;

View File

@ -19,6 +19,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Editor, { EditorOptions } from '../../../../src/index';
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
import MapInfoImpl from './MapInfoImpl';
const initialization = (designer: Designer) => {
designer.addEvent('loadSuccess', () => {
@ -32,9 +33,6 @@ const initialization = (designer: Designer) => {
const persistence = new LocalStorageManager('samples/{id}.wxml', false, false);
const mapId = 'welcome';
const options: EditorOptions = {
zoom: 0.8,
locked: false,
mapTitle: 'Develop WiseMapping',
mode: 'edition-owner',
locale: 'en',
enableKeyboardEvents: true,
@ -42,7 +40,7 @@ const options: EditorOptions = {
ReactDOM.render(
<Editor
mapId={mapId}
mapInfo={new MapInfoImpl('welcome', 'Develop Map Title', false)}
options={options}
persistenceManager={persistence}
onAction={(action) => console.log('action called:', action)}

View File

@ -19,6 +19,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Editor, { EditorOptions } from '../../../../src/index';
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
import MapInfoImpl from './MapInfoImpl';
const initialization = (designer: Designer) => {
designer.addEvent('loadSuccess', () => {
@ -30,20 +31,16 @@ const initialization = (designer: Designer) => {
};
const persistence = new LocalStorageManager('samples/{id}.wxml', false, false);
const mapId = 'welcome';
const options: EditorOptions = {
zoom: 0.8,
locked: true,
lockedMsg: 'Blockeado',
mapTitle: 'Develop WiseMapping',
mode: 'edition-editor',
locale: 'en',
enableKeyboardEvents: true,
};
const mapInfo = new MapInfoImpl('welcome', 'Develop WiseMapping', true);
ReactDOM.render(
<Editor
mapId={mapId}
mapInfo={mapInfo}
options={options}
persistenceManager={persistence}
onAction={(action) => console.log('action called:', action)}

View File

@ -19,6 +19,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Editor, { EditorOptions } from '../../../../src/index';
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
import MapInfoImpl from './MapInfoImpl';
const initialization = (designer: Designer) => {
designer.addEvent('loadSuccess', () => {
@ -32,9 +33,6 @@ const initialization = (designer: Designer) => {
const persistence = new LocalStorageManager('samples/{id}.wxml', false, false);
const mapId = 'welcome';
const options: EditorOptions = {
zoom: 0.8,
locked: false,
mapTitle: 'Develop WiseMapping',
mode: 'showcase',
locale: 'en',
enableKeyboardEvents: true,
@ -42,7 +40,7 @@ const options: EditorOptions = {
ReactDOM.render(
<Editor
mapId={mapId}
mapInfo={new MapInfoImpl('welcome', 'Develop Map Title', false)}
options={options}
persistenceManager={persistence}
onAction={(action) => console.log('action called:', action)}

View File

@ -3,6 +3,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Editor, { EditorOptions } from '../../../../src/index';
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
import MapInfoImpl from './MapInfoImpl';
const initialization = (designer: Designer) => {
designer.addEvent('loadSuccess', () => {
@ -32,9 +33,6 @@ const params = new URLSearchParams(window.location.search.substring(1));
const mapId = params.get('id') || 'welcome';
const persistence = new LocalStorageManager('samples/{id}.wxml', false);
const options: EditorOptions = {
zoom: 0.8,
locked: false,
mapTitle: 'Develop WiseMapping',
mode: 'viewonly',
locale: 'en',
enableKeyboardEvents: true,
@ -42,7 +40,7 @@ const options: EditorOptions = {
ReactDOM.render(
<Editor
mapId={mapId}
mapInfo={new MapInfoImpl('welcome', 'Develop Map Title', false)}
options={options}
persistenceManager={persistence}
onAction={(action) => console.log('action called:', action)}

View File

@ -13,6 +13,7 @@ import AppBar from '../../../src/components/app-bar';
import ActionConfig from '../../../src/classes/action/action-config';
import Capability from '../../../src/classes/action/capability';
import Editor from '../../../src/classes/model/editor';
import MapInfoImpl from '../../playground/map-render/js/MapInfoImpl';
require('babel-polyfill');
jest.mock('../../../src/components/app-bar/styles.css', () => '');
@ -275,7 +276,14 @@ describe('AppBar', () => {
it('When render it displays a menu', () => {
const capacity = new Capability('edition-owner', false);
const model = new Editor(null);
render(<AppBar mapTitle="Some title" capability={capacity} model={model} />);
render(
<AppBar
mapInfo={new MapInfoImpl('welcome', 'Develop Map Title', false)}
capability={capacity}
model={model}
/>,
);
screen.getByRole('menubar');
});
});

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="9.449 8.932 132.742 34.405" width="133px" height="34.5px" xmlns="http://www.w3.org/2000/svg">
<svg viewBox="9.449 8.932 132.742 34.405" width="100px" height="34.5px" xmlns="http://www.w3.org/2000/svg">
<mask id="i08j30ef6a" fill="#fff">
<path d="M 32.284 17.414 C 32.284 20.379 31.176 23.221 29.199 25.317 C 27.224 27.413 24.546 28.59 21.753 28.59 C 18.96 28.59 16.282 27.413 14.307 25.317 C 12.331 23.221 11.222 20.379 11.222 17.414 L 32.284 17.414 Z"/>
</mask>

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,3 +1,21 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Client from '../client';
import CacheDecoratorClient from '../client/cache-decorator-client';
import MockClient from '../client/mock-client';

View File

@ -17,6 +17,10 @@ class CacheDecoratorClient implements Client {
this.client = client;
}
fetchStarred(id: number): Promise<boolean> {
return this.client.fetchStarred(id);
}
onSessionExpired(callback?: () => void): () => void {
return this.client.onSessionExpired(callback);
}

View File

@ -80,6 +80,8 @@ interface Client {
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
fetchAllMaps(): Promise<MapInfo[]>;
fetchStarred(id: number): Promise<boolean>;
fetchMapPermissions(id: number): Promise<Permission[]>;
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void>;
deleteMapPermission(id: number, email: string): Promise<void>;

View File

@ -1,3 +1,20 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Client, {
AccountInfo,
BasicMapInfo,
@ -108,6 +125,10 @@ class MockClient implements Client {
this.labels = [label1, label2, label3];
}
fetchStarred(id: number): Promise<boolean> {
return Promise.resolve(this.maps.find((m) => m.id == id).starred);
}
onSessionExpired(callback?: () => void): () => void {
return callback;
}
@ -123,8 +144,6 @@ class MockClient implements Client {
let perm = this.permissionsByMap.get(id) || [];
perm = perm.concat(permissions);
this.permissionsByMap.set(id, perm);
console.debug(`Message ${message}`);
return Promise.resolve();
}

View File

@ -72,6 +72,25 @@ export default class RestClient implements Client {
};
return new Promise(handler);
}
fetchStarred(id: number): Promise<boolean> {
const handler = (success: (starred: boolean) => void, reject: (error: ErrorInfo) => void) => {
this.axios
.get(`${this.baseUrl}/c/restful/maps/${id}/starred`, {
headers: { 'Content-Type': 'text/plain' },
})
.then((response) => {
const data = response.data;
success(data);
})
.catch((error) => {
const errorInfo = this.parseResponseOnError(error.response);
reject(errorInfo);
});
};
return new Promise(handler);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void> {
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {

View File

@ -0,0 +1,77 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { MapInfo } from '@wisemapping/editor';
import Client from '../client';
class MapInfoImpl implements MapInfo {
private client: Client;
private id: number;
private title: string;
private zoom: number;
private locked: boolean;
private lockedMsg: string;
constructor(
id: number,
client: Client,
title: string,
locked: boolean,
lockedMsg: string,
zoom: number,
) {
this.id = id;
this.client = client;
this.title = title;
this.zoom = zoom;
this.locked = locked;
this.lockedMsg = lockedMsg;
}
isStarred(): Promise<boolean> {
return this.client.fetchStarred(this.id);
}
updateStarred(value: boolean): Promise<void> {
return this.client.updateStarred(this.id, value);
}
getTitle(): string {
return this.title;
}
setTitle(value: string): void {
this.client.renameMap(Number.parseInt(this.getId(), 10), { title: value });
}
isLocked(): boolean {
return this.locked;
}
getLockedMessage(): string {
return this.lockedMsg;
}
getZoom(): number {
return this.zoom;
}
getId(): string {
return String(this.id);
}
}
export default MapInfoImpl;

View File

@ -1,3 +1,20 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useEffect } from 'react';
import ActionDispatcher from '../maps-page/action-dispatcher';
import { ActionType } from '../maps-page/action-chooser';
@ -13,6 +30,10 @@ import EditorOptionsBuilder from './EditorOptionsBuilder';
import { buildPersistenceManagerForEditor } from './PersistenceManagerUtils';
import { useTheme } from '@mui/material/styles';
import AccountMenu from '../maps-page/account-menu';
import MapInfoImpl from '../../classes/editor-map-info';
import { MapInfo } from '@wisemapping/editor';
import { activeInstance } from '../../redux/clientSlice';
import Client from '../../classes/client';
export type EditorPropsType = {
isTryMode: boolean;
@ -23,6 +44,7 @@ const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
const hotkey = useSelector(hotkeysEnabled);
const userLocale = AppI18n.getUserLocale();
const theme = useTheme();
const client: Client = useSelector(activeInstance);
useEffect(() => {
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: `Map Editor` });
@ -62,16 +84,25 @@ const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
const loadCompleted = mode && isAccountLoaded;
let options, persistence: PersistenceManager;
let mapInfo: MapInfo;
if (loadCompleted) {
options = EditorOptionsBuilder.build(userLocale.code, mode, hotkey);
persistence = buildPersistenceManagerForEditor(mode);
mapInfo = new MapInfoImpl(
mapId,
client,
options.title,
options.isLocked,
options.lockedMsg,
options.zoom,
);
}
useEffect(() => {
if (options?.mapTitle) {
document.title = `${options.mapTitle} | WiseMapping `;
}
}, options?.mapTitle);
}, [loadCompleted]);
return loadCompleted ? (
<IntlProvider
@ -83,7 +114,7 @@ const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
onAction={setActiveDialog}
options={options}
persistenceManager={persistence}
mapId={mapId}
mapInfo={mapInfo}
theme={theme}
accountConfiguration={
// Prevent load on non-authenticated.

View File

@ -1,3 +1,21 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { createSlice } from '@reduxjs/toolkit';
import { useQuery } from 'react-query';

View File

@ -7,7 +7,6 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = merge(common, {
mode: 'development',
devtool: 'source-map',
watch: true,
devServer: {
contentBase: path.join(__dirname, 'dist'),
port: 3000,