feat(rn): 增加插件逻辑注入

并增加内部选择颜色推送到外部的功能
pull/90/head
moonrailgun 2 years ago
parent 93985b02fb
commit 92d5e39cfc

@ -1,3 +0,0 @@
const PLUGIN_NAME = 'ReactNative支持';
console.log(`Plugin ${PLUGIN_NAME} is loaded`);

@ -10,10 +10,15 @@ import { SafeAreaView, StatusBar, useColorScheme } from 'react-native';
import { AppMain } from './AppMain';
import { Entry } from './Entry';
import { useServerStore } from './store/server';
import { useUIStore } from './store/ui';
import { theme } from './theme';
function App(): JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
const { colorScheme } = useUIStore();
const systemColorScheme = useColorScheme();
const finalColorScheme =
colorScheme === 'auto' ? systemColorScheme : colorScheme;
const isDarkMode = finalColorScheme === 'dark';
const selectedServerInfo = useServerStore(
(state) => state.selectedServerInfo
);

@ -1,7 +1,8 @@
import React, { useEffect, useRef } from 'react';
import { StyleSheet, View } from 'react-native';
import { WebView } from 'react-native-webview';
import { generateInjectScript } from './lib/inject';
import { generatePostMessageScript } from './lib/inject';
import { handleTailchatMessage } from './lib/inject/message-handler';
import { initNotificationEnv } from './lib/notifications';
/**
@ -18,15 +19,34 @@ export const AppMain: React.FC<Props> = React.memo((props) => {
useEffect(() => {
initNotificationEnv();
if (webviewRef.current) {
webviewRef.current.injectJavaScript(generateInjectScript());
}
}, []);
return (
<View style={styles.root}>
<WebView ref={webviewRef} source={{ uri: props.host }} />
<WebView
ref={webviewRef}
source={{ uri: props.host }}
injectedJavaScriptBeforeContentLoaded={generatePostMessageScript()}
onMessage={(e) => {
if (!webviewRef.current) {
return;
}
try {
const raw = e.nativeEvent.data as string;
const data = JSON.parse(raw);
if (typeof data === 'object' && data._isTailchat === true) {
handleTailchatMessage(
data.type,
data.payload,
webviewRef.current
);
}
} catch (err) {
console.error('webview onmessage:', err);
}
}}
/>
</View>
);
});

@ -1,8 +1,32 @@
// @ts-nocheck
/**
* Webviewjs
*/
export function generateInjectScript() {
// console.log(require('../../../dist/plugins/com.msgbyte.env.rn/index.js'));
export function generateInstallPluginScript() {
/**
* manifest copy from:
* com.msgbyte.env.rn/manifest.json
*/
const inner = `function main() {
window.tailchat
.installPlugin({
label: 'ReactNative支持',
name: 'com.msgbyte.env.rn',
url: '/plugins/com.msgbyte.env.rn/index.js',
version: '0.0.0',
author: 'moonrailgun',
description: '在Tailchat添加对ReactNative环境的支持',
requireRestart: true,
});
}`;
const raw = `(${inner})()`;
return raw;
}
return `alert(JSON.stringify(window.tailchat))`;
export function generatePostMessageScript() {
return `window.postMessage = function (data) {
window.ReactNativeWebView.postMessage(JSON.stringify(data));
};`;
}

@ -0,0 +1,22 @@
import type WebView from 'react-native-webview';
import { generateInstallPluginScript } from '.';
import { useUIStore } from '../../store/ui';
export function handleTailchatMessage(
type: string,
payload: any,
webview: WebView
) {
console.log('onMessage receive:', type, payload);
if (type === 'init') {
webview.injectJavaScript(generateInstallPluginScript());
return;
}
if (type === 'loadColorScheme') {
// 设置颜色
useUIStore.getState().setColorScheme(payload);
return;
}
}

@ -0,0 +1,37 @@
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
import { persist } from 'zustand/middleware';
import { zustandRNStorage } from '../lib/utils/storage';
interface UIStoreState {
colorScheme: 'dark' | 'light' | 'auto';
setColorScheme: (colorScheme: 'dark' | 'light' | 'auto' | string) => void;
}
export const useUIStore = create<UIStoreState>()(
persist(
immer((set) => ({
colorScheme: 'dark',
setColorScheme: (colorScheme) => {
if (colorScheme === 'dark') {
set({
colorScheme: 'dark',
});
} else if (colorScheme === 'light') {
set({
colorScheme: 'light',
});
} else {
set({
colorScheme: 'auto',
});
}
},
})),
{
name: 'ui',
storage: zustandRNStorage,
partialize: (state) => ({ colorScheme: state.colorScheme }),
}
)
);

@ -0,0 +1,16 @@
import { sharedEvent } from '@capital/common';
const PLUGIN_NAME = 'ReactNative支持';
console.log(`Plugin ${PLUGIN_NAME} is loaded`);
sharedEvent.on('loadColorScheme', (colorScheme: string) => {
window.postMessage(
{
_isTailchat: true,
type: 'loadColorScheme',
payload: colorScheme,
},
'*'
);
});

@ -7,6 +7,7 @@ import { initPlugins } from './plugin/loader';
import { installServiceWorker } from './utils/sw-helper';
import { showErrorToasts, t } from 'tailchat-shared';
import { recordMeasure } from './utils/measure-helper';
import { postMessageEvent } from './utils/event-helper';
import './styles';
installServiceWorker();
@ -16,6 +17,7 @@ recordMeasure('initPluginsStart');
initPlugins()
.then(() => {
recordMeasure('initPluginsEnd');
postMessageEvent('loaded');
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const root = createRoot(document.querySelector('#app')!);
root.render(

@ -19,8 +19,10 @@ import { getPopupContainer } from './utils/dom-helper';
import { getUserJWT } from './utils/jwt-helper';
import _get from 'lodash/get';
import { recordMeasure } from './utils/measure-helper';
import { postMessageEvent } from './utils/event-helper';
recordMeasure('init');
postMessageEvent('init');
if (isDevelopment) {
import('source-ref-runtime').then(({ start }) => start());

@ -134,4 +134,7 @@ class PluginManager {
}
export const pluginManager = new PluginManager();
injectTailchatGlobalValue('installPlugin', pluginManager.installPlugin);
injectTailchatGlobalValue(
'installPlugin',
pluginManager.installPlugin.bind(pluginManager)
);

@ -0,0 +1,21 @@
/**
*
*/
interface MessageEventMap {
init: undefined; // 初始化阶段
loaded: undefined; // 插件加载完毕
}
export function postMessageEvent<T extends keyof MessageEventMap>(
eventType: T,
eventData?: MessageEventMap[T]
) {
window.postMessage(
{
_isTailchat: true,
type: eventType,
payload: eventData,
},
'*'
);
}
Loading…
Cancel
Save