Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clever-paws-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@growi/core': minor
---

Add global EventTarget instance provider
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import React, {
useCallback, useState, useEffect, type JSX,
} from 'react';

import type EventEmitter from 'events';

import { PageGrant } from '@growi/core';
import { globalEventTarget } from '@growi/core/dist/utils';
import { isTopPage, isUsersProtectedPages } from '@growi/core/dist/utils/page-path-utils';
import { LoadingSpinner } from '@growi/ui/dist/components';
import { useTranslation } from 'next-i18next';
Expand All @@ -25,16 +24,11 @@ import loggerFactory from '~/utils/logger';

import { NotAvailable } from '../../NotAvailable';
import { SlackNotification } from '../../SlackNotification';
import type { SaveOptions } from '../PageEditor';

import { GrantSelector } from './GrantSelector';


declare global {
// eslint-disable-next-line vars-on-top, no-var
var globalEmitter: EventEmitter;
}


const logger = loggerFactory('growi:SavePageControls');


Expand All @@ -45,25 +39,35 @@ const SavePageButton = (props: { slackChannels: string, isSlackEnabled?: boolean
const [isSavePageModalShown, setIsSavePageModalShown] = useState<boolean>(false);
const { data: selectedGrant } = useSelectedGrant();

const { slackChannels, isSlackEnabled, isDeviceLargerThanMd } = props;
const { slackChannels, isSlackEnabled = false, isDeviceLargerThanMd } = props;

const isWaitingSaveProcessing = _isWaitingSaveProcessing === true; // ignore undefined

const save = useCallback(async(): Promise<void> => {
// save
globalEmitter.emit('saveAndReturnToView', { wip: false, slackChannels, isSlackEnabled });
globalEventTarget.dispatchEvent(new CustomEvent<SaveOptions>('saveAndReturnToView', {
detail: {
wip: false, slackChannels, isSlackEnabled,
},
}));
}, [isSlackEnabled, slackChannels]);

const saveAndOverwriteScopesOfDescendants = useCallback(() => {
// save
globalEmitter.emit('saveAndReturnToView', {
wip: false, overwriteScopesOfDescendants: true, slackChannels, isSlackEnabled,
});
globalEventTarget.dispatchEvent(new CustomEvent<SaveOptions>('saveAndReturnToView', {
detail: {
wip: false, overwriteScopesOfDescendants: true, slackChannels, isSlackEnabled,
},
}));
}, [isSlackEnabled, slackChannels]);

const saveAndMakeWip = useCallback(() => {
// save
globalEmitter.emit('saveAndReturnToView', { wip: true, slackChannels, isSlackEnabled });
globalEventTarget.dispatchEvent(new CustomEvent<SaveOptions>('saveAndReturnToView', {
detail: {
wip: true, slackChannels, isSlackEnabled,
},
}));
}, [isSlackEnabled, slackChannels]);

const labelSubmitButton = t('Update');
Expand Down
16 changes: 5 additions & 11 deletions apps/app/src/client/components/PageEditor/PageEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import React, {
useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState,
} from 'react';

import type EventEmitter from 'events';
import nodePath from 'path';

import { Origin } from '@growi/core';
import type { IPageHasId } from '@growi/core/dist/interfaces';
import { pathUtils } from '@growi/core/dist/utils';
import { pathUtils, globalEventTarget } from '@growi/core/dist/utils';
import { GlobalCodeMirrorEditorKey } from '@growi/editor';
import { CodeMirrorEditorMain } from '@growi/editor/dist/client/components/CodeMirrorEditorMain';
import { useCodeMirrorEditorIsolated } from '@growi/editor/dist/client/stores/codemirror-editor';
Expand Down Expand Up @@ -59,11 +58,6 @@ import '@growi/editor/dist/style.css';
const logger = loggerFactory('growi:PageEditor');


declare global {
// eslint-disable-next-line vars-on-top, no-var
var globalEmitter: EventEmitter;
}

export type SaveOptions = {
wip: boolean,
slackChannels: string,
Expand Down Expand Up @@ -219,10 +213,10 @@ export const PageEditorSubstance = (props: Props): JSX.Element => {
}
}, [pageId, selectedGrant, mutateWaitingSaveProcessing, updatePage, mutateIsGrantNormalized, t]);

const saveAndReturnToViewHandler = useCallback(async(opts: SaveOptions) => {
const saveAndReturnToViewHandler = useCallback(async(evt: CustomEvent<SaveOptions>) => {
const markdown = codeMirrorEditor?.getDocString();
const revisionId = isRevisionIdRequiredForPageUpdate ? currentRevisionId : undefined;
const page = await save(revisionId, markdown, opts, onConflict);
const page = await save(revisionId, markdown, evt.detail, onConflict);
if (page == null) {
return;
}
Expand Down Expand Up @@ -280,10 +274,10 @@ export const PageEditorSubstance = (props: Props): JSX.Element => {

// set handler to save and return to View
useEffect(() => {
globalEmitter.on('saveAndReturnToView', saveAndReturnToViewHandler);
globalEventTarget.addEventListener('saveAndReturnToView', saveAndReturnToViewHandler);

return function cleanup() {
globalEmitter.removeListener('saveAndReturnToView', saveAndReturnToViewHandler);
globalEventTarget.removeEventListener('saveAndReturnToView', saveAndReturnToViewHandler);
};
}, [saveAndReturnToViewHandler]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useCallback, useState, type JSX } from 'react';

import type EventEmitter from 'events';

import { globalEventTarget } from '@growi/core/dist/utils';
import {
DrawioViewer,
type DrawioEditByViewerProps,
Expand All @@ -19,12 +18,6 @@ import '@growi/remark-drawio/dist/style.css';
import styles from './DrawioViewerWithEditButton.module.scss';


declare global {
// eslint-disable-next-line vars-on-top, no-var
var globalEmitter: EventEmitter;
}


export const DrawioViewerWithEditButton = React.memo((props: DrawioViewerProps): JSX.Element => {
const { t } = useTranslation();

Expand All @@ -40,10 +33,11 @@ export const DrawioViewerWithEditButton = React.memo((props: DrawioViewerProps):
const [mxfile, setMxfile] = useState('');

const editButtonClickHandler = useCallback(() => {
const data: DrawioEditByViewerProps = {
bol, eol, drawioMxFile: mxfile,
};
globalEmitter.emit('launchDrawioModal', data);
globalEventTarget.dispatchEvent(new CustomEvent<DrawioEditByViewerProps>('launchDrawioModal', {
detail: {
bol, eol, drawioMxFile: mxfile,
},
}));
}, [bol, eol, mxfile]);

const renderingStartHandler = useCallback(() => {
Expand Down
19 changes: 9 additions & 10 deletions apps/app/src/client/components/ReactMarkdownComponents/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import {
useCallback, useEffect, useState, type JSX,
} from 'react';

import type EventEmitter from 'events';

import { globalEventTarget } from '@growi/core/dist/utils';
import type { Element } from 'hast';
import { useRouter } from 'next/router';

import { NextLink } from '~/components/ReactMarkdownComponents/NextLink';
import {
useIsGuestUser, useIsReadOnlyUser, useIsSharedUser, useShareLinkId,
} from '~/stores-universal/context';
import type { ReservedNextCaretLineEventDetail } from '~/stores/editor';
import { useCurrentPageYjsData } from '~/stores/yjs';
import loggerFactory from '~/utils/logger';

Expand All @@ -21,15 +21,14 @@ import styles from './Header.module.scss';
const logger = loggerFactory('growi:components:Header');
const moduleClass = styles['revision-head'] ?? '';

declare global {
// eslint-disable-next-line vars-on-top, no-var
var globalEmitter: EventEmitter;
}


function setCaretLine(line?: number): void {
if (line != null) {
globalEmitter.emit('reservedNextCaretLine', line);
function setCaretLine(lineNumber?: number): void {
if (lineNumber != null) {
globalEventTarget.dispatchEvent(new CustomEvent<ReservedNextCaretLineEventDetail>('reservedNextCaretLine', {
detail: {
lineNumber,
},
}));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { useCallback, type JSX } from 'react';

import type EventEmitter from 'events';

import { globalEventTarget } from '@growi/core/dist/utils';
import type { Element } from 'hast';

import type { LaunchHandsonTableModalEventDetail } from '~/client/interfaces/handsontable-modal';
import {
useIsGuestUser, useIsReadOnlyUser, useIsSharedUser, useShareLinkId,
} from '~/stores-universal/context';
Expand All @@ -12,10 +12,6 @@ import { useCurrentPageYjsData } from '~/stores/yjs';

import styles from './TableWithEditButton.module.scss';

declare global {
// eslint-disable-next-line vars-on-top, no-var
var globalEmitter: EventEmitter;
}

type TableWithEditButtonProps = {
children: React.ReactNode,
Expand All @@ -33,11 +29,16 @@ const TableWithEditButtonNoMemorized = (props: TableWithEditButtonProps): JSX.El
const { data: isRevisionOutdated } = useIsRevisionOutdated();
const { data: currentPageYjsData } = useCurrentPageYjsData();

const bol = node.position?.start.line;
const eol = node.position?.end.line;
const bol = node.position?.start.line ?? 0;
const eol = node.position?.end.line ?? 0;

const editButtonClickHandler = useCallback(() => {
globalEmitter.emit('launchHandsonTableModal', bol, eol);
globalEventTarget.dispatchEvent(new CustomEvent<LaunchHandsonTableModalEventDetail>('launchHandsonTableModal', {
detail: {
bol,
eol,
},
}));
}, [bol, eol]);

const isNoEditingUsers = currentPageYjsData?.awarenessStateSize == null || currentPageYjsData?.awarenessStateSize === 0;
Expand Down
4 changes: 4 additions & 0 deletions apps/app/src/client/interfaces/handsontable-modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type LaunchHandsonTableModalEventDetail = {
bol: number,
eol: number,
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useCallback, useEffect } from 'react';

import type EventEmitter from 'events';

import { Origin } from '@growi/core';
import { globalEventTarget } from '@growi/core/dist/utils';
import type { DrawioEditByViewerProps } from '@growi/remark-drawio';

import { replaceDrawioInMarkdown } from '~/client/components/Page/markdown-drawio-util-for-view';
Expand All @@ -17,12 +16,6 @@ import loggerFactory from '~/utils/logger';
const logger = loggerFactory('growi:cli:side-effects:useDrawioModalLauncherForView');


declare global {
// eslint-disable-next-line vars-on-top, no-var
var globalEmitter: EventEmitter;
}


export const useDrawioModalLauncherForView = (opts?: {
onSaveSuccess?: () => void,
onSaveError?: (error: any) => void,
Expand Down Expand Up @@ -68,7 +61,7 @@ export const useDrawioModalLauncherForView = (opts?: {
logger.error('failed to save', error);
opts?.onSaveError?.(error);
}
}, [closeConflictDiffModal, currentPage, opts, shareLinkId]);
}, [_updatePage, closeConflictDiffModal, currentPage, opts, shareLinkId]);

// eslint-disable-next-line max-len
const generateResolveConflictHandler = useCallback((revisionId: string, onConflict: (conflictData: RemoteRevisionData, newMarkdown: string) => void) => {
Expand Down Expand Up @@ -107,13 +100,14 @@ export const useDrawioModalLauncherForView = (opts?: {
return;
}

const handler = (data: DrawioEditByViewerProps) => {
const handler = (evt: CustomEvent<DrawioEditByViewerProps>) => {
const data = evt.detail;
openDrawioModal(data.drawioMxFile, drawioMxFile => saveByDrawioModal(drawioMxFile, data.bol, data.eol));
};
globalEmitter.on('launchDrawioModal', handler);
globalEventTarget.addEventListener('launchDrawioModal', handler);

return function cleanup() {
globalEmitter.removeListener('launchDrawioModal', handler);
globalEventTarget.removeEventListener('launchDrawioModal', handler);
};
}, [openDrawioModal, saveByDrawioModal, shareLinkId]);
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useCallback, useEffect } from 'react';

import type EventEmitter from 'events';

import { Origin } from '@growi/core';
import { globalEventTarget } from '@growi/core/dist/utils';
import type { MarkdownTable } from '@growi/editor';

import { getMarkdownTableFromLine, replaceMarkdownTableInMarkdown } from '~/client/components/Page/markdown-table-util-for-view';
import type { LaunchHandsonTableModalEventDetail } from '~/client/interfaces/handsontable-modal';
import { extractRemoteRevisionDataFromErrorObj, useUpdatePage } from '~/client/services/update-page';
import { useShareLinkId } from '~/stores-universal/context';
import { useHandsontableModal, useConflictDiffModal } from '~/stores/modal';
Expand All @@ -17,12 +17,6 @@ import loggerFactory from '~/utils/logger';
const logger = loggerFactory('growi:cli:side-effects:useHandsontableModalLauncherForView');


declare global {
// eslint-disable-next-line vars-on-top, no-var
var globalEmitter: EventEmitter;
}


export const useHandsontableModalLauncherForView = (opts?: {
onSaveSuccess?: () => void,
onSaveError?: (error: any) => void,
Expand Down Expand Up @@ -68,7 +62,7 @@ export const useHandsontableModalLauncherForView = (opts?: {
logger.error('failed to save', error);
opts?.onSaveError?.(error);
}
}, [closeConflictDiffModal, currentPage, opts, shareLinkId]);
}, [_updatePage, closeConflictDiffModal, currentPage, opts, shareLinkId]);

// eslint-disable-next-line max-len
const generateResolveConflictHandler = useCallback((revisionId: string, onConflict: (conflictData: RemoteRevisionData, newMarkdown: string) => void) => {
Expand Down Expand Up @@ -106,17 +100,18 @@ export const useHandsontableModalLauncherForView = (opts?: {
return;
}

const handler = (bol: number, eol: number) => {
const handler = (evt: CustomEvent<LaunchHandsonTableModalEventDetail>) => {
if (currentPage.revision == null) return;

const markdown = currentPage.revision.body;
const { bol, eol } = evt.detail;
const currentMarkdownTable = getMarkdownTableFromLine(markdown, bol, eol);
openHandsontableModal(currentMarkdownTable, false, table => saveByHandsontableModal(table, bol, eol));
};
globalEmitter.on('launchHandsonTableModal', handler);
globalEventTarget.addEventListener('launchHandsonTableModal', handler);

return function cleanup() {
globalEmitter.removeListener('launchHandsonTableModal', handler);
globalEventTarget.removeEventListener('launchHandsonTableModal', handler);
};
}, [currentPage, openHandsontableModal, saveByHandsontableModal, shareLinkId]);
};
11 changes: 0 additions & 11 deletions apps/app/src/pages/[[...path]].page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import type {
} from '@growi/core';
import { isIPageInfo } from '@growi/core';
import { isClient, pagePathUtils, pathUtils } from '@growi/core/dist/utils';
import EventEmitter from 'events';
import ExtensibleCustomError from 'extensible-custom-error';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import superjson from 'superjson';
Expand Down Expand Up @@ -101,11 +100,6 @@ import {
useInitSidebarConfig,
} from './utils/commons';

declare global {
// eslint-disable-next-line vars-on-top, no-var
var globalEmitter: EventEmitter;
}

const GrowiContextualSubNavigationSubstance = dynamic(
() => import('~/client/components/Navbar/GrowiContextualSubNavigation'),
{ ssr: false },
Expand Down Expand Up @@ -311,11 +305,6 @@ type Props = CommonProps & {
};

const Page: NextPageWithLayout<Props> = (props: Props) => {
// register global EventEmitter
if (isClient() && window.globalEmitter == null) {
window.globalEmitter = new EventEmitter();
}

const router = useRouter();

useCurrentUser(props.currentUser ?? null);
Expand Down
Loading
Loading