mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-11-22 14:47:56 +01:00
Fix map loading ..
This commit is contained in:
parent
fc0c03b2bc
commit
e3d6f5dad5
@ -37,15 +37,18 @@ export const useEditor = ({
|
|||||||
const [model, setModel] = useState<Model | undefined>();
|
const [model, setModel] = useState<Model | undefined>();
|
||||||
// useEditor hook creates mindplotRef
|
// useEditor hook creates mindplotRef
|
||||||
const mindplotRef = useRef(null);
|
const mindplotRef = useRef(null);
|
||||||
// This is required to redraw in case of chansges in the canvas...
|
// This is required to redraw in case of changes in the canvas...
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const [, setCanvasUpdate] = useState<number>();
|
const [, setCanvasUpdate] = useState<number>();
|
||||||
|
|
||||||
const { widgetManager } = useWidgetManager();
|
const { widgetManager } = useWidgetManager();
|
||||||
const capability = new Capability(options.mode, mapInfo.isLocked());
|
let capability;
|
||||||
|
if (options && mapInfo) {
|
||||||
|
capability = new Capability(options.mode, mapInfo.isLocked());
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!model) {
|
if (!model && options) {
|
||||||
const model = new Model(mindplotRef.current);
|
const model = new Model(mindplotRef.current);
|
||||||
model
|
model
|
||||||
.loadMindmap(mapInfo.getId(), persistenceManager, widgetManager)
|
.loadMindmap(mapInfo.getId(), persistenceManager, widgetManager)
|
||||||
@ -59,15 +62,17 @@ export const useEditor = ({
|
|||||||
});
|
});
|
||||||
setModel(model);
|
setModel(model);
|
||||||
}
|
}
|
||||||
}, [mindplotRef]);
|
}, [mindplotRef, options]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (options.enableKeyboardEvents) {
|
if (options) {
|
||||||
DesignerKeyboard.resume();
|
if (options.enableKeyboardEvents) {
|
||||||
} else {
|
DesignerKeyboard.resume();
|
||||||
DesignerKeyboard.pause();
|
} else {
|
||||||
|
DesignerKeyboard.pause();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [options.enableKeyboardEvents]);
|
}, [options, options?.enableKeyboardEvents]);
|
||||||
|
|
||||||
return { model, mindplotRef, mapInfo, capability, options };
|
return { model, mindplotRef, mapInfo, capability, options };
|
||||||
};
|
};
|
||||||
|
@ -26,11 +26,13 @@ class RESTPersistenceManager extends PersistenceManager {
|
|||||||
|
|
||||||
private lockUrl: string;
|
private lockUrl: string;
|
||||||
|
|
||||||
private onSave: boolean;
|
private jwt: string | undefined;
|
||||||
|
|
||||||
private clearTimeout;
|
private clearTimeout;
|
||||||
|
|
||||||
constructor(options: { documentUrl: string; revertUrl: string; lockUrl: string }) {
|
private onSave: boolean;
|
||||||
|
|
||||||
|
constructor(options: { documentUrl: string; revertUrl: string; lockUrl: string; jwt?: string }) {
|
||||||
$assert(options.documentUrl, 'documentUrl can not be null');
|
$assert(options.documentUrl, 'documentUrl can not be null');
|
||||||
$assert(options.revertUrl, 'revertUrl can not be null');
|
$assert(options.revertUrl, 'revertUrl can not be null');
|
||||||
$assert(options.lockUrl, 'lockUrl can not be null');
|
$assert(options.lockUrl, 'lockUrl can not be null');
|
||||||
@ -40,6 +42,7 @@ class RESTPersistenceManager extends PersistenceManager {
|
|||||||
this.revertUrl = options.revertUrl;
|
this.revertUrl = options.revertUrl;
|
||||||
this.lockUrl = options.lockUrl;
|
this.lockUrl = options.lockUrl;
|
||||||
this.onSave = false;
|
this.onSave = false;
|
||||||
|
this.jwt = options.jwt;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveMapXml(mapId: string, mapXml: Document, pref: string, saveHistory: boolean, events): void {
|
saveMapXml(mapId: string, mapXml: Document, pref: string, saveHistory: boolean, events): void {
|
||||||
@ -61,14 +64,7 @@ class RESTPersistenceManager extends PersistenceManager {
|
|||||||
|
|
||||||
const persistence = this;
|
const persistence = this;
|
||||||
|
|
||||||
const crfs = this.getCSRFToken();
|
const headers = this._buildHttpHeader('application/json; charset=utf-8', 'application/json');
|
||||||
const headers = {
|
|
||||||
'Content-Type': 'application/json; charset=utf-8',
|
|
||||||
Accept: 'application/json',
|
|
||||||
};
|
|
||||||
if (crfs) {
|
|
||||||
headers['X-CSRF-Token'] = crfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(`${this.documentUrl.replace('{id}', mapId)}?${query}`, {
|
fetch(`${this.documentUrl.replace('{id}', mapId)}?${query}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
@ -133,15 +129,7 @@ class RESTPersistenceManager extends PersistenceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
discardChanges(mapId: string): void {
|
discardChanges(mapId: string): void {
|
||||||
const crfs = this.getCSRFToken();
|
const headers = this._buildHttpHeader('application/json; charset=utf-8');
|
||||||
const headers = {
|
|
||||||
'Content-Type': 'application/json; charset=utf-8',
|
|
||||||
Accept: 'application/json',
|
|
||||||
};
|
|
||||||
if (crfs) {
|
|
||||||
headers['X-CSRF-Token'] = crfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(this.revertUrl.replace('{id}', mapId), {
|
fetch(this.revertUrl.replace('{id}', mapId), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers,
|
headers,
|
||||||
@ -149,14 +137,7 @@ class RESTPersistenceManager extends PersistenceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unlockMap(mapId: string): void {
|
unlockMap(mapId: string): void {
|
||||||
const crfs = this.getCSRFToken();
|
const headers = this._buildHttpHeader('text/plain; charset=utf-8');
|
||||||
const headers = {
|
|
||||||
'Content-Type': 'text/plain; charset=utf-8',
|
|
||||||
};
|
|
||||||
if (crfs) {
|
|
||||||
headers['X-CSRF-Token'] = crfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(this.lockUrl.replace('{id}', mapId), {
|
fetch(this.lockUrl.replace('{id}', mapId), {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers,
|
headers,
|
||||||
@ -180,14 +161,7 @@ class RESTPersistenceManager extends PersistenceManager {
|
|||||||
|
|
||||||
loadMapDom(mapId: string): Promise<Document> {
|
loadMapDom(mapId: string): Promise<Document> {
|
||||||
const url = `${this.documentUrl.replace('{id}', mapId)}/xml`;
|
const url = `${this.documentUrl.replace('{id}', mapId)}/xml`;
|
||||||
const crfs = this.getCSRFToken();
|
const headers = this._buildHttpHeader('text/plain; charset=utf-8', 'application/xml');
|
||||||
const headers = {
|
|
||||||
'Content-Type': 'text/plain; charset=utf-8',
|
|
||||||
Accept: 'application/xml',
|
|
||||||
};
|
|
||||||
if (crfs) {
|
|
||||||
headers['X-CSRF-Token'] = crfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -202,6 +176,28 @@ class RESTPersistenceManager extends PersistenceManager {
|
|||||||
})
|
})
|
||||||
.then((xmlStr) => new DOMParser().parseFromString(xmlStr, 'text/xml'));
|
.then((xmlStr) => new DOMParser().parseFromString(xmlStr, 'text/xml'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _buildHttpHeader(contentType: string, accept?: string) {
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': contentType,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (accept) {
|
||||||
|
// eslint-disable-next-line dot-notation
|
||||||
|
headers['Accept'] = accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.jwt) {
|
||||||
|
// eslint-disable-next-line dot-notation
|
||||||
|
headers['Authorization'] = `Bearer ${this.jwt} `;
|
||||||
|
}
|
||||||
|
|
||||||
|
const crfs = this.getCSRFToken();
|
||||||
|
if (crfs) {
|
||||||
|
headers['X-CSRF-Token'] = crfs;
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RESTPersistenceManager;
|
export default RESTPersistenceManager;
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
import React, { ReactElement, Suspense, useEffect } from 'react';
|
import React, { ReactElement, Suspense, useEffect } from 'react';
|
||||||
import { FormattedMessage, IntlProvider } from 'react-intl';
|
import { FormattedMessage, IntlProvider } from 'react-intl';
|
||||||
import { Route, Routes, BrowserRouter as Router, useNavigate } from 'react-router-dom';
|
import { Route, Routes, BrowserRouter as Router, useNavigate, useParams } from 'react-router-dom';
|
||||||
import ForgotPasswordSuccessPage from './components/forgot-password-success-page';
|
import ForgotPasswordSuccessPage from './components/forgot-password-success-page';
|
||||||
import RegistationPage from './components/registration-page';
|
import RegistationPage from './components/registration-page';
|
||||||
import LoginPage from './components/login-page';
|
import LoginPage from './components/login-page';
|
||||||
@ -66,6 +66,11 @@ function Redirect({ to }) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PageEditorWhapper = ({ isTryMode }: { isTryMode: boolean }) => {
|
||||||
|
const mapId: string = useParams().id!;
|
||||||
|
return <EditorPage isTryMode={isTryMode} mapId={Number.parseInt(mapId)} />;
|
||||||
|
};
|
||||||
|
|
||||||
const App = (): ReactElement => {
|
const App = (): ReactElement => {
|
||||||
const locale = AppI18n.getDefaultLocale();
|
const locale = AppI18n.getDefaultLocale();
|
||||||
const overwriteView = window.errorMvcView;
|
const overwriteView = window.errorMvcView;
|
||||||
@ -126,7 +131,7 @@ const App = (): ReactElement => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<EditorPage isTryMode={false} />
|
<PageEditorWhapper isTryMode={false} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -143,7 +148,7 @@ const App = (): ReactElement => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<EditorPage isTryMode={true} />
|
<PageEditorWhapper isTryMode={true} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -38,7 +38,7 @@ class _AppConfig {
|
|||||||
googleOauth2Url: '/c/registration-google?code=aFakeCode',
|
googleOauth2Url: '/c/registration-google?code=aFakeCode',
|
||||||
};
|
};
|
||||||
|
|
||||||
isDevelopEnv(): boolean {
|
isMockEnv(): boolean {
|
||||||
const config = this.getInstance();
|
const config = this.getInstance();
|
||||||
return config.clientType === 'mock';
|
return config.clientType === 'mock';
|
||||||
}
|
}
|
||||||
|
@ -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 { Locale, LocaleCode } from '../app-i18n';
|
import { Locale, LocaleCode } from '../app-i18n';
|
||||||
|
|
||||||
export type JwtAuth = {
|
export type JwtAuth = {
|
||||||
@ -42,6 +59,14 @@ export type MapInfo = {
|
|||||||
role: Role;
|
role: Role;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MapMetadata = {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
isLocked: boolean;
|
||||||
|
isLockedBy?: string;
|
||||||
|
zoom: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type ChangeHistory = {
|
export type ChangeHistory = {
|
||||||
id: number;
|
id: number;
|
||||||
lastModificationBy: string;
|
lastModificationBy: string;
|
||||||
@ -99,6 +124,7 @@ interface Client {
|
|||||||
deleteMap(id: number): Promise<void>;
|
deleteMap(id: number): Promise<void>;
|
||||||
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
|
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
|
||||||
fetchAllMaps(): Promise<MapInfo[]>;
|
fetchAllMaps(): Promise<MapInfo[]>;
|
||||||
|
fetchMapMetadata(id: number): Promise<MapMetadata>;
|
||||||
|
|
||||||
fetchStarred(id: number): Promise<boolean>;
|
fetchStarred(id: number): Promise<boolean>;
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import Client, {
|
|||||||
Oauth2CallbackResult,
|
Oauth2CallbackResult,
|
||||||
ForgotPasswordResult,
|
ForgotPasswordResult,
|
||||||
JwtAuth,
|
JwtAuth,
|
||||||
|
MapMetadata,
|
||||||
} from '..';
|
} from '..';
|
||||||
import { LocaleCode, localeFromStr } from '../../app-i18n';
|
import { LocaleCode, localeFromStr } from '../../app-i18n';
|
||||||
import Cookies from 'universal-cookie';
|
import Cookies from 'universal-cookie';
|
||||||
@ -128,6 +129,15 @@ class MockClient implements Client {
|
|||||||
|
|
||||||
this.labels = [label1, label2, label3];
|
this.labels = [label1, label2, label3];
|
||||||
}
|
}
|
||||||
|
fetchMapMetadata(id: number): Promise<MapMetadata> {
|
||||||
|
return Promise.resolve({
|
||||||
|
title: 'my map',
|
||||||
|
id: id,
|
||||||
|
isLocked: false,
|
||||||
|
zoom: 0.8,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
logout(): Promise<void> {
|
logout(): Promise<void> {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -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 axios, { AxiosInstance, AxiosResponse } from 'axios';
|
import axios, { AxiosInstance, AxiosResponse } from 'axios';
|
||||||
import Client, {
|
import Client, {
|
||||||
ErrorInfo,
|
ErrorInfo,
|
||||||
@ -12,6 +29,7 @@ import Client, {
|
|||||||
Oauth2CallbackResult,
|
Oauth2CallbackResult,
|
||||||
ForgotPasswordResult,
|
ForgotPasswordResult,
|
||||||
JwtAuth,
|
JwtAuth,
|
||||||
|
MapMetadata,
|
||||||
} from '..';
|
} from '..';
|
||||||
import { getCsrfToken } from '../../../utils';
|
import { getCsrfToken } from '../../../utils';
|
||||||
import { LocaleCode, localeFromStr } from '../../app-i18n';
|
import { LocaleCode, localeFromStr } from '../../app-i18n';
|
||||||
@ -20,11 +38,11 @@ import Cookies from 'universal-cookie';
|
|||||||
export default class RestClient implements Client {
|
export default class RestClient implements Client {
|
||||||
private baseUrl: string;
|
private baseUrl: string;
|
||||||
private axios: AxiosInstance;
|
private axios: AxiosInstance;
|
||||||
|
private _onSessionExpired: () => void;
|
||||||
|
|
||||||
private checkResponseForSessionExpired = <T>(error: {
|
private checkResponseForSessionExpired = <T>(error: {
|
||||||
response?: AxiosResponse<T>;
|
response?: AxiosResponse<T>;
|
||||||
}): Promise<{ response?: AxiosResponse<T> }> => {
|
}): Promise<{ response?: AxiosResponse<T> }> => {
|
||||||
// TODO: Improve session timeout response and response handling
|
|
||||||
if (error.response && (error.response.status === 405 || error.response.status === 403)) {
|
if (error.response && (error.response.status === 405 || error.response.status === 403)) {
|
||||||
this.sessionExpired();
|
this.sessionExpired();
|
||||||
}
|
}
|
||||||
@ -63,6 +81,28 @@ export default class RestClient implements Client {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchMapMetadata(id: number): Promise<MapMetadata> {
|
||||||
|
const handler = (
|
||||||
|
success: (mapMetadata: MapMetadata) => void,
|
||||||
|
reject: (error: ErrorInfo) => void,
|
||||||
|
) => {
|
||||||
|
this.axios
|
||||||
|
.get(`${this.baseUrl}/api/restful/maps/${id}/metadata`, {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const data = response.data;
|
||||||
|
success(data);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const errorInfo = this.parseResponseOnError(error.response);
|
||||||
|
reject(errorInfo);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise(handler);
|
||||||
|
}
|
||||||
|
|
||||||
logout(): Promise<void> {
|
logout(): Promise<void> {
|
||||||
// Set jwt token on cookie ...
|
// Set jwt token on cookie ...
|
||||||
const cookies = new Cookies();
|
const cookies = new Cookies();
|
||||||
@ -100,7 +140,6 @@ export default class RestClient implements Client {
|
|||||||
return token ? `Bearer ${token}` : null;
|
return token ? `Bearer ${token}` : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onSessionExpired: () => void;
|
|
||||||
onSessionExpired(callback?: () => void): () => void {
|
onSessionExpired(callback?: () => void): () => void {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
this._onSessionExpired = callback;
|
this._onSessionExpired = callback;
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
import { EditorOptions } from '@wisemapping/editor';
|
|
||||||
import { EditorRenderMode } from '@wisemapping/editor';
|
|
||||||
import AppConfig from '../../classes/app-config';
|
|
||||||
|
|
||||||
class EditorOptionsBuilder {
|
|
||||||
static build(locale: string, mode: EditorRenderMode, hotkeys: boolean): EditorOptions {
|
|
||||||
let options: EditorOptions = {
|
|
||||||
enableKeyboardEvents: hotkeys,
|
|
||||||
locale: locale,
|
|
||||||
mode: mode,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!AppConfig.isDevelopEnv()) {
|
|
||||||
options = {
|
|
||||||
zoom: globalThis.userOptions?.zoom ? globalThis?.userOptions?.zoom : 0.8,
|
|
||||||
locked: globalThis.mindmapLocked,
|
|
||||||
lockedMsg: globalThis.mindmapLockedMsg,
|
|
||||||
mapTitle: globalThis.mapTitle,
|
|
||||||
...options,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Running in a development mode.
|
|
||||||
console.log('Running editor in development mode');
|
|
||||||
options = {
|
|
||||||
zoom: 0.8,
|
|
||||||
locked: false,
|
|
||||||
mapTitle: 'Develop Mindnap',
|
|
||||||
...options,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
static loadMapId(): number {
|
|
||||||
const result = !AppConfig.isDevelopEnv() ? globalThis.mapId : 11;
|
|
||||||
if (result === undefined) {
|
|
||||||
throw Error(
|
|
||||||
`Could not resolve mapId. Map Id: globalThis.mapId: ${result} , globalThis.mapTitle: ${globalThis.mapTitle}, globalThis.lockSession: ${globalThis.lockSession}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default EditorOptionsBuilder;
|
|
@ -4,7 +4,7 @@ import { LocalStorageManager, Mindmap, XMLSerializerFactory } from '@wisemapping
|
|||||||
export const fetchMindmap = async (mapId: number): Promise<Mindmap> => {
|
export const fetchMindmap = async (mapId: number): Promise<Mindmap> => {
|
||||||
let mindmap: Mindmap;
|
let mindmap: Mindmap;
|
||||||
if (AppConfig.isRestClient()) {
|
if (AppConfig.isRestClient()) {
|
||||||
const persistence = new LocalStorageManager(`/c/restful/maps/{id}/document/xml`, true);
|
const persistence = new LocalStorageManager(`/api/restful/maps/{id}/document/xml`, true);
|
||||||
mindmap = await persistence.load(String(mapId));
|
mindmap = await persistence.load(String(mapId));
|
||||||
} else {
|
} else {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
|
@ -34,8 +34,8 @@ import {
|
|||||||
useFetchMapById,
|
useFetchMapById,
|
||||||
activeInstance,
|
activeInstance,
|
||||||
sessionExpired,
|
sessionExpired,
|
||||||
|
useFetchMapMetadata,
|
||||||
} from '../../redux/clientSlice';
|
} from '../../redux/clientSlice';
|
||||||
import EditorOptionsBuilder from './EditorOptionsBuilder';
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import MapInfoImpl from '../../classes/editor-map-info';
|
import MapInfoImpl from '../../classes/editor-map-info';
|
||||||
import { MapInfo } from '@wisemapping/editor';
|
import { MapInfo } from '@wisemapping/editor';
|
||||||
@ -43,24 +43,31 @@ import Client from '../../classes/client';
|
|||||||
import AppConfig from '../../classes/app-config';
|
import AppConfig from '../../classes/app-config';
|
||||||
import exampleMap from '../../classes/client/mock-client/example-map.wxml';
|
import exampleMap from '../../classes/client/mock-client/example-map.wxml';
|
||||||
import ClientHealthSentinel from '../common/client-health-sentinel';
|
import ClientHealthSentinel from '../common/client-health-sentinel';
|
||||||
|
import Cookies from 'universal-cookie';
|
||||||
|
|
||||||
const buildPersistenceManagerForEditor = (mode: string): PersistenceManager => {
|
const buildPersistenceManagerForEditor = (mode: string): PersistenceManager => {
|
||||||
let persistenceManager: PersistenceManager;
|
let persistenceManager: PersistenceManager;
|
||||||
if (AppConfig.isRestClient()) {
|
if (AppConfig.isRestClient()) {
|
||||||
if (mode === 'edition-owner' || mode === 'edition-editor') {
|
if (mode === 'edition-owner' || mode === 'edition-editor') {
|
||||||
|
// Fetch JWT token ...
|
||||||
|
const cookies = new Cookies();
|
||||||
|
const token = cookies.get('jwt-auth-token');
|
||||||
|
|
||||||
persistenceManager = new RESTPersistenceManager({
|
persistenceManager = new RESTPersistenceManager({
|
||||||
documentUrl: '/c/restful/maps/{id}/document',
|
documentUrl: '/api/restful/maps/{id}/document',
|
||||||
revertUrl: '/c/restful/maps/{id}/history/latest',
|
revertUrl: '/api/restful/maps/{id}/history/latest',
|
||||||
lockUrl: '/c/restful/maps/{id}/lock',
|
lockUrl: '/api/restful/maps/{id}/lock',
|
||||||
|
jwt: token,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
persistenceManager = new LocalStorageManager(
|
persistenceManager = new LocalStorageManager(
|
||||||
`/c/restful/maps/{id}/${
|
`/api/restful/maps/{id}/${
|
||||||
globalThis.historyId ? `${globalThis.historyId}/` : ''
|
globalThis.historyId ? `${globalThis.historyId}/` : ''
|
||||||
}document/xml${mode === 'showcase' ? '-pub' : ''}`,
|
}document/xml${mode === 'showcase' ? '-pub' : ''}`,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
persistenceManager.addErrorHandler((error) => {
|
persistenceManager.addErrorHandler((error) => {
|
||||||
if (error.errorType === 'session-expired') {
|
if (error.errorType === 'session-expired') {
|
||||||
// TODO: this line was in RestPersistenceClient, do something similar here
|
// TODO: this line was in RestPersistenceClient, do something similar here
|
||||||
@ -75,6 +82,7 @@ const buildPersistenceManagerForEditor = (mode: string): PersistenceManager => {
|
|||||||
|
|
||||||
export type EditorPropsType = {
|
export type EditorPropsType = {
|
||||||
isTryMode: boolean;
|
isTryMode: boolean;
|
||||||
|
mapId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ActionType =
|
type ActionType =
|
||||||
@ -97,7 +105,14 @@ type ActionType =
|
|||||||
const ActionDispatcher = React.lazy(() => import('../maps-page/action-dispatcher'));
|
const ActionDispatcher = React.lazy(() => import('../maps-page/action-dispatcher'));
|
||||||
const AccountMenu = React.lazy(() => import('../maps-page/account-menu'));
|
const AccountMenu = React.lazy(() => import('../maps-page/account-menu'));
|
||||||
|
|
||||||
const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
|
type EditorMetadata = {
|
||||||
|
mode: EditorRenderMode;
|
||||||
|
title: string;
|
||||||
|
isLocked: boolean;
|
||||||
|
zoom: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EditorPage = ({ mapId, isTryMode }: EditorPropsType): React.ReactElement => {
|
||||||
const [activeDialog, setActiveDialog] = React.useState<ActionType | null>(null);
|
const [activeDialog, setActiveDialog] = React.useState<ActionType | null>(null);
|
||||||
const hotkey = useSelector(hotkeysEnabled);
|
const hotkey = useSelector(hotkeysEnabled);
|
||||||
const userLocale = AppI18n.getUserLocale();
|
const userLocale = AppI18n.getUserLocale();
|
||||||
@ -119,51 +134,79 @@ const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
|
|||||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: `Map Editor` });
|
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: `Map Editor` });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const useFindEditorMode = (isTryMode: boolean, mapId: number): EditorRenderMode | null => {
|
const useFindEditorMode = (isTryMode: boolean, mapId: number): EditorMetadata | undefined => {
|
||||||
let result: EditorRenderMode = null;
|
let mode: EditorRenderMode = null;
|
||||||
if (isTryMode) {
|
let title = '';
|
||||||
result = 'showcase';
|
let isLocked = false;
|
||||||
} else if (globalThis.mindmapLocked) {
|
|
||||||
result = 'viewonly';
|
|
||||||
} else {
|
|
||||||
const fetchResult = useFetchMapById(mapId);
|
|
||||||
if (!fetchResult.isLoading) {
|
|
||||||
if (fetchResult.error) {
|
|
||||||
throw new Error(`Map info could not be loaded: ${JSON.stringify(fetchResult.error)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fetchResult.map) {
|
if (isTryMode) {
|
||||||
|
mode = 'showcase';
|
||||||
|
title = 'Try map';
|
||||||
|
isLocked = false;
|
||||||
|
} else {
|
||||||
|
const fetchMapInfoResult = useFetchMapById(mapId);
|
||||||
|
const fetchMetadataResult = useFetchMapMetadata(mapId);
|
||||||
|
|
||||||
|
if (!fetchMapInfoResult.isLoading && !fetchMetadataResult.isLoading) {
|
||||||
|
if (fetchMapInfoResult.error || fetchMetadataResult.error) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Map info could not be loaded. Info not present: ${JSON.stringify(fetchResult)}`,
|
`Map info could not be loaded: ${JSON.stringify(fetchMapInfoResult.error)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
result = `edition-${fetchResult.map.role}`;
|
|
||||||
|
if (!fetchMapInfoResult.data) {
|
||||||
|
throw new Error(
|
||||||
|
`Map info could not be loaded. Info not present: ${JSON.stringify(fetchMapInfoResult)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fetchMetadataResult.data) {
|
||||||
|
throw new Error(
|
||||||
|
`Map info could not be loaded. Info not present: ${JSON.stringify(
|
||||||
|
fetchMetadataResult,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fetchMetadataResult.data?.isLocked) {
|
||||||
|
mode = 'viewonly';
|
||||||
|
} else {
|
||||||
|
mode = `edition-${fetchMapInfoResult.data.role}`;
|
||||||
|
}
|
||||||
|
isLocked = fetchMetadataResult.data.isLocked;
|
||||||
|
title = fetchMetadataResult.data.title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return mode ? { mode: mode, isLocked: isLocked, title: title, zoom: 0.8 } : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
// What is the role ?
|
// What is the role ?
|
||||||
const mapId = EditorOptionsBuilder.loadMapId();
|
const mapMetadata = useFindEditorMode(isTryMode, mapId);
|
||||||
const mode = useFindEditorMode(isTryMode, mapId);
|
|
||||||
|
|
||||||
// Account settings can be null and editor cannot be initilized multiple times. This creates problems
|
// Account settings can be null and editor cannot be initilized multiple times. This creates problems
|
||||||
// at the i18n resource loading.
|
// at the i18n resource loading.
|
||||||
const isAccountLoaded = mode === 'showcase' || useFetchAccount;
|
const isAccountLoaded = mapMetadata?.mode === 'showcase' || useFetchAccount;
|
||||||
const loadCompleted = mode && isAccountLoaded;
|
const loadCompleted = mapMetadata && isAccountLoaded;
|
||||||
|
|
||||||
let options: EditorOptions, persistence: PersistenceManager;
|
let persistence: PersistenceManager;
|
||||||
let mapInfo: MapInfo;
|
let mapInfo: MapInfo;
|
||||||
|
let options: EditorOptions;
|
||||||
if (loadCompleted) {
|
if (loadCompleted) {
|
||||||
options = EditorOptionsBuilder.build(userLocale.code, mode, hotkey);
|
// Configure de
|
||||||
persistence = buildPersistenceManagerForEditor(mode);
|
options = {
|
||||||
|
enableKeyboardEvents: hotkey,
|
||||||
|
locale: userLocale.code,
|
||||||
|
mode: mapMetadata.mode,
|
||||||
|
};
|
||||||
|
|
||||||
|
persistence = buildPersistenceManagerForEditor(mapMetadata.mode);
|
||||||
mapInfo = new MapInfoImpl(
|
mapInfo = new MapInfoImpl(
|
||||||
mapId,
|
mapId,
|
||||||
client,
|
client,
|
||||||
options.mapTitle,
|
mapMetadata.title,
|
||||||
options.locked,
|
mapMetadata.isLocked,
|
||||||
options.lockedMsg,
|
'',
|
||||||
options.zoom,
|
mapMetadata.zoom,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ const ActionChooser = (props: ActionProps): React.ReactElement => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const role = mapId !== undefined ? useFetchMapById(mapId)?.map?.role : undefined;
|
const role = mapId !== undefined ? useFetchMapById(mapId)?.data?.role : undefined;
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
anchorEl={anchor}
|
anchorEl={anchor}
|
||||||
|
@ -30,7 +30,7 @@ const DeleteDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
|
|||||||
mutation.mutate(mapId);
|
mutation.mutate(mapId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { map } = useFetchMapById(mapId);
|
const { data: map } = useFetchMapById(mapId);
|
||||||
const alertTitle = `${intl.formatMessage({
|
const alertTitle = `${intl.formatMessage({
|
||||||
id: 'action.delete-title',
|
id: 'action.delete-title',
|
||||||
defaultMessage: 'Delete',
|
defaultMessage: 'Delete',
|
||||||
|
@ -56,7 +56,7 @@ const DuplicateDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElem
|
|||||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
const { map } = useFetchMapById(mapId);
|
const { data: map } = useFetchMapById(mapId);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (map) {
|
if (map) {
|
||||||
setModel(map);
|
setModel(map);
|
||||||
|
@ -39,7 +39,7 @@ const ExportDialog = ({
|
|||||||
}: ExportDialogProps): React.ReactElement => {
|
}: ExportDialogProps): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [submit, setSubmit] = React.useState<boolean>(false);
|
const [submit, setSubmit] = React.useState<boolean>(false);
|
||||||
const { map } = useFetchMapById(mapId);
|
const { data: map } = useFetchMapById(mapId);
|
||||||
|
|
||||||
const [exportGroup, setExportGroup] = React.useState<ExportGroup>(
|
const [exportGroup, setExportGroup] = React.useState<ExportGroup>(
|
||||||
enableImgExport ? 'image' : 'document',
|
enableImgExport ? 'image' : 'document',
|
||||||
|
@ -18,7 +18,7 @@ import LocalizedFormat from 'dayjs/plugin/localizedFormat';
|
|||||||
dayjs.extend(LocalizedFormat);
|
dayjs.extend(LocalizedFormat);
|
||||||
|
|
||||||
const InfoDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const InfoDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const { map } = useFetchMapById(mapId);
|
const { data: map } = useFetchMapById(mapId);
|
||||||
const [error, setError] = React.useState<ErrorInfo>();
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -21,7 +21,7 @@ import AppConfig from '../../../../classes/app-config';
|
|||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
|
|
||||||
const PublishDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const PublishDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const { map } = useFetchMapById(mapId);
|
const { data: map } = useFetchMapById(mapId);
|
||||||
|
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const [model, setModel] = React.useState<boolean>(map ? map.isPublic : false);
|
const [model, setModel] = React.useState<boolean>(map ? map.isPublic : false);
|
||||||
|
@ -58,7 +58,7 @@ const RenameDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
|
|||||||
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
setModel({ ...model, [name as keyof BasicMapInfo]: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
const { map } = useFetchMapById(mapId);
|
const { data: map } = useFetchMapById(mapId);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (map) {
|
if (map) {
|
||||||
setModel(map);
|
setModel(map);
|
||||||
|
@ -62,6 +62,7 @@ import LabelDeleteConfirm from './maps-list/label-delete-confirm';
|
|||||||
import ReactGA from 'react-ga4';
|
import ReactGA from 'react-ga4';
|
||||||
import { CSSObject, Interpolation, Theme } from '@emotion/react';
|
import { CSSObject, Interpolation, Theme } from '@emotion/react';
|
||||||
import withEmotionStyles from '../HOCs/withEmotionStyles';
|
import withEmotionStyles from '../HOCs/withEmotionStyles';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
export type Filter = GenericFilter | LabelFilter;
|
export type Filter = GenericFilter | LabelFilter;
|
||||||
|
|
||||||
@ -91,6 +92,7 @@ const MapsPage = (): ReactElement => {
|
|||||||
localStorage.getItem('desktopDrawerOpen') === 'true',
|
localStorage.getItem('desktopDrawerOpen') === 'true',
|
||||||
);
|
);
|
||||||
const classes = useStyles(desktopDrawerOpen);
|
const classes = useStyles(desktopDrawerOpen);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleMobileDrawerToggle = () => {
|
const handleMobileDrawerToggle = () => {
|
||||||
setMobileDrawerOpen(!mobileDrawerOpen);
|
setMobileDrawerOpen(!mobileDrawerOpen);
|
||||||
@ -123,6 +125,16 @@ const MapsPage = (): ReactElement => {
|
|||||||
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Maps List' });
|
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Maps List' });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (client) {
|
||||||
|
client.onSessionExpired(() => {
|
||||||
|
navigate('/c/login');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn('Session expiration wont be handled because could not find client');
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const mutation = useMutation((id: number) => client.deleteLabel(id), {
|
const mutation = useMutation((id: number) => client.deleteLabel(id), {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries('labels');
|
queryClient.invalidateQueries('labels');
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import Client, { AccountInfo, ErrorInfo, MapInfo } from '../classes/client';
|
import Client, { AccountInfo, ErrorInfo, MapInfo, MapMetadata } from '../classes/client';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import AppConfig from '../classes/app-config';
|
import AppConfig from '../classes/app-config';
|
||||||
import { RootState } from './rootReducer';
|
import { RootState } from './rootReducer';
|
||||||
@ -55,7 +55,7 @@ export const clientSlice = createSlice({
|
|||||||
type MapLoadResult = {
|
type MapLoadResult = {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
error: ErrorInfo | null;
|
error: ErrorInfo | null;
|
||||||
map: MapInfo | undefined;
|
data: MapInfo | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useFetchMapById = (id: number): MapLoadResult => {
|
export const useFetchMapById = (id: number): MapLoadResult => {
|
||||||
@ -82,7 +82,24 @@ export const useFetchMapById = (id: number): MapLoadResult => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { isLoading: isLoading, error: errorMsg, map: map };
|
return { isLoading: isLoading, error: errorMsg, data: map };
|
||||||
|
};
|
||||||
|
|
||||||
|
type MapMetadataLoadResult = {
|
||||||
|
isLoading: boolean;
|
||||||
|
error: ErrorInfo | null;
|
||||||
|
data: MapMetadata | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchMapMetadata = (id: number): MapMetadataLoadResult => {
|
||||||
|
const client: Client = useSelector(activeInstance);
|
||||||
|
const { isLoading, error, data } = useQuery<unknown, ErrorInfo, MapMetadata>(
|
||||||
|
`maps-metadata-${id}`,
|
||||||
|
() => {
|
||||||
|
return client.fetchMapMetadata(id);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return { isLoading: isLoading, error: error, data: data };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useFetchAccount = (): AccountInfo | undefined => {
|
export const useFetchAccount = (): AccountInfo | undefined => {
|
||||||
|
@ -14,6 +14,7 @@ module.exports = merge(common, {
|
|||||||
template: path.join(__dirname, 'public/index.html'),
|
template: path.join(__dirname, 'public/index.html'),
|
||||||
templateParameters: {
|
templateParameters: {
|
||||||
PUBLIC_URL: process.env.PUBLIC_URL ? process.env.PUBLIC_URL : 'https://www.wisemapping.com',
|
PUBLIC_URL: process.env.PUBLIC_URL ? process.env.PUBLIC_URL : 'https://www.wisemapping.com',
|
||||||
|
CLIENT_TYPE: 'rest'
|
||||||
},
|
},
|
||||||
base: process.env.PUBLIC_URL ? process.env.PUBLIC_URL : 'https://www.wisemapping.com',
|
base: process.env.PUBLIC_URL ? process.env.PUBLIC_URL : 'https://www.wisemapping.com',
|
||||||
}),
|
}),
|
||||||
|
Loading…
Reference in New Issue
Block a user