import React from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components';
import isMobile from 'is-mobile';
import { callRemote, init as initSocket } from './socket';
import { getLocale } from './utils';
import messages from './bubble-locale';
import Bubble from './Bubble';
import Modal from './Modal';
import Loading from './Loading';
import Error from './Error';
import ErrorBoundary from './ErrorBoundary';
const winPath = input => {
if (!input) {
return '';
}
const isExtendedLengthPath = /^\\\\\?\\/.test(input);
if (isExtendedLengthPath) {
return input;
}
return input.replace(/\\/g, '/');
};
const LoadingWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
text-align: center;
color: #fff;
height: 100%;
font-size: 14px;
`;
class App extends React.Component {
constructor(props) {
super(props);
const locale = getLocale();
this.state = {
open: undefined,
connected: false,
uiLoaded: false,
loading: false,
currentProject: props.currentProject,
locale,
edit: false,
editText: {},
tips: '',
};
window.addEventListener('message', this.handleMessage, false);
const { hostname, protocol } = window.location;
const { port } = this.props;
this.baseUrl = `${protocol}//${hostname}:${port}`;
}
handleLocale = locale => {
this.setState({
locale,
});
};
componentDidUpdate() {
const nextLocale = getLocale();
if (this.state.locale !== nextLocale) {
this.handleLocale(nextLocale);
}
}
componentWillUnmount() {
this.setState({
tips: '',
});
window.removeEventListener('message', this.handleMessage, false);
}
getMiniUrl = () => {
const { currentProject } = this.state;
return `${this.baseUrl}/?mini&${
currentProject && currentProject.key ? `&key=${currentProject.key}` : ''
}`;
};
handleMessage = event => {
try {
const { action, payload = {} } = JSON.parse(event.data);
switch (action) {
// 编辑态改变文字
case 'umi.ui.changeEdit': {
this.setState({
edit: true,
editText: payload,
});
break;
}
// 显示 mini
case 'umi.ui.showMini': {
this.setState({
open: true,
edit: false,
editText: {},
});
break;
}
// 隐藏 mini
case 'umi.ui.hideMini': {
this.setState({
open: false,
});
break;
}
// 切换loading
case 'umi.ui.toggleIconLoading': {
this.setState(({ loading }) => ({
loading: !loading,
}));
break;
}
default:
// no thing
}
} catch (_) {
// no thing
}
return false;
};
async componentDidMount() {
const { path } = this.props;
const message = this.getMessage();
try {
await initSocket(`${this.baseUrl}/umiui`, {
onError: e => {
// https://developer.mozilla.org/zh-CN/docs/Web/API/CloseEvent
// 非 localhost 访问
const { code } = e;
this.setState({
connected: false,
tips: message[`code_${code}`] || '',
});
},
onMessage: ({ type, payload }) => {
// 区分不同项目
if (!payload || winPath(payload.projectPath) !== winPath(path)) return;
switch (type) {
case 'org.umi.ui.bubble.showLoading':
this.setState({
loading: true,
});
break;
case 'org.umi.ui.bubble.hideLoading':
this.setState({
loading: false,
});
break;
default:
break;
}
},
});
this.setState({
connected: true,
});
} catch (e) {
console.error('Init socket failed', e);
this.setState({
connected: false,
});
}
}
initUIService = async () => {
const { currentProject, path } = this.props;
// console.log('currentProject', currentProject);
if (this.state.connected) {
// open iframe UmiUI
if (!currentProject.key) {
const res = await callRemote({
type: '@@project/getKeyOrAddWithPath',
payload: {
path,
},
});
this.setState({
currentProject: res,
});
}
}
};
closeModal = e => {
e.stopPropagation();
this.setState({
open: false,
});
};
onIframeLoad = () => {
this.setState({
uiLoaded: true,
});
};
toggleMiniOpen = async open => {
if (open) {
this.setState({
open,
});
return;
}
// or use toggle
if (typeof this.state.open === 'undefined') {
await this.initUIService();
}
this.setState(prevState => ({
open: !prevState.open,
}));
};
resetEdit = () => {
this.setState({
edit: false,
editText: {},
});
};
getMessage = () => {
const { locale } = this.state;
return messages[locale] || messages['zh-CN'];
};
render() {
const { open, connected, uiLoaded, loading, locale, edit, editText, tips } = this.state;
const { isBigfish = false } = this.props;
const miniUrl = this.getMiniUrl();
// get locale when first render
// switch in the project can't be listened, the lifecycle can't be trigger
// TODO: use Context but need to compatible with other React version
const message = this.getMessage();
return (
{open !== undefined && (
{!uiLoaded && !tips && (
{message.loading}
)}
{!connected ? (
) : (
)}
)}
);
}
}
const doc = window.document;
const node = doc.createElement('div');
doc.body.appendChild(node);
export default props => {
if (!isMobile(navigator.userAgent)) {
ReactDOM.render(
,
node,
);
}
};