适配Chrome75浏览器消息弹出

41 阅读1分钟
import { message as antdMessage } from 'antd';
import PubSub from 'pubsub-js';
import { useEffect } from 'react';

export type NoticeType = 'info' | 'success' | 'error' | 'warning' | 'loading' | 'open' | 'destroy';
export interface ConfigOptions {
  top?: number;
  duration?: number;
  prefixCls?: string;
  getContainer?: () => HTMLElement;
  transitionName?: string;
  maxCount?: number;
  rtl?: boolean;
}
export interface ArgsProps {
  content: React.ReactNode;
  duration?: number;
  type?: NoticeType;
  onClose?: () => void;
  icon?: React.ReactNode;
  key?: string | number;
  style?: React.CSSProperties;
  className?: string;
  onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
}
export type JointContent = React.ReactNode | ArgsProps;
export interface MessageType extends PromiseLike<boolean> {
  (): void;
}
export type TypeOpen = (
  content: JointContent,
  duration?: number | VoidFunction, // Also can use onClose directly
  onClose?: VoidFunction,
) => void;

interface MessageMethods {
  info: TypeOpen;
  success: TypeOpen;
  error: TypeOpen;
  warning: TypeOpen;
  loading: TypeOpen;
}

interface BaseMethods {
  open(args: ArgsProps): MessageType;
  destroy(key?: React.Key): void;
}

interface AntdMessageEventConfig {
  messageType: NoticeType;
  content: JointContent;
  duration?: number | VoidFunction;
  onClose?: VoidFunction;

  key?: React.Key;
}

const isLegacyChrome = () => {
  const ua = navigator.userAgent;
  if (ua.indexOf('Chrome') === -1) {
    return false;
  }
  const version = parseInt(ua.split('Chrome/')[1], 10);

  return version < 89;
};

const ANTD_MESSAGE_EVENT = 'ANTD_MESSAGE_EVENT';

const messageAdapter = () => {
  const message: Partial<MessageMethods & BaseMethods> = {};

  const typeList: NoticeType[] = [
    'info',
    'success',
    'error',
    'warning',
    'loading',
    'open',
    'destroy',
  ];
  typeList.forEach((messageType) => {
    if (['info', 'success', 'error', 'warning', 'loading'].includes(messageType)) {
      message[messageType as 'info' | 'success' | 'error' | 'warning' | 'loading'] = (
        content: JointContent,
        duration?: number | VoidFunction,
        onClose?: VoidFunction,
      ) => {
        try {
          PubSub.publish(ANTD_MESSAGE_EVENT, { messageType, content, duration, onClose });
        } catch (e) {
          console.error(e);
        }
      };
    } else if (['open'].includes(messageType)) {
      message[messageType as 'open'] = (args: ArgsProps): MessageType => {
        let result = false;
        try {
          result = PubSub.publish(ANTD_MESSAGE_EVENT, { messageType, ...args });
        } catch (e) {
          console.error(e);
        }

        return Promise.resolve(result) as unknown as MessageType;
      };
    } else if (['destroy'].includes(messageType)) {
      message[messageType as 'destroy'] = (key?: React.Key) => {
        try {
          PubSub.publish(ANTD_MESSAGE_EVENT, { messageType, key });
        } catch (e) {
          console.error(e);
        }
      };
    }
  });

  return message as MessageMethods & BaseMethods;
};

const useMessageAdapter = () => {
  const [messageApi, contextHolder] = antdMessage.useMessage();
  useEffect(() => {
    if (!isLegacyChrome()) return;

    const token = PubSub.subscribe(
      ANTD_MESSAGE_EVENT,
      (eventName, config: AntdMessageEventConfig) => {
        const { messageType, content, duration, onClose } = config;

        if (['info', 'success', 'error', 'warning', 'loading'].includes(messageType)) {
          (messageApi[messageType] as TypeOpen)(content, duration, onClose);
        } else if (['open'].includes(messageType)) {
          (messageApi[messageType] as (args: ArgsProps) => MessageType)(config as ArgsProps);
        } else if (['destroy'].includes(messageType)) {
          (messageApi[messageType] as (key?: React.Key) => void)(config.key);
        }
      },
    );

    return () => {
      PubSub.unsubscribe(token);
    };
  }, []);

  if (!isLegacyChrome()) {
    return [null];
  }

  return [contextHolder];
};

/**
 * 你应该使用这个 `message` 来弹窗提示
 *
 * `import { message } from 'antd';` antd 在 chrome 75上会有兼容性问题,无法降级 `:where()` 选择器
 *
 * 或者使用
 * ```typescript
 * import { App } from 'antd';
 * const { message, modal } = App.useApp();
 * ```
 */
const message = isLegacyChrome() ? messageAdapter() : antdMessage;

export { message, useMessageAdapter };