Skip to content

Commit

Permalink
feat(rn): 增加插件逻辑注入
Browse files Browse the repository at this point in the history
并增加内部选择颜色推送到外部的功能
  • Loading branch information
moonrailgun committed Feb 14, 2023
1 parent 93985b0 commit 92d5e39
Show file tree
Hide file tree
Showing 15 changed files with 163 additions and 14 deletions.
3 changes: 0 additions & 3 deletions client/mobile/plugins/com.msgbyte.env.rn/src/index.tsx

This file was deleted.

7 changes: 6 additions & 1 deletion client/mobile/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
Expand Down
32 changes: 26 additions & 6 deletions client/mobile/src/AppMain.tsx
Original file line number Diff line number Diff line change
@@ -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';

/**
Expand All @@ -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>
);
});
Expand Down
30 changes: 27 additions & 3 deletions client/mobile/src/lib/inject/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
// @ts-nocheck

/**
* 生成注入到Webview中的js代码
*/
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));
};`;
}
22 changes: 22 additions & 0 deletions client/mobile/src/lib/inject/message-handler.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
37 changes: 37 additions & 0 deletions client/mobile/src/store/ui.ts
Original file line number Diff line number Diff line change
@@ -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 }),
}
)
);
16 changes: 16 additions & 0 deletions client/web/plugins/com.msgbyte.env.rn/src/index.tsx
Original file line number Diff line number Diff line change
@@ -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,
},
'*'
);
});
2 changes: 2 additions & 0 deletions client/web/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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(
Expand Down
2 changes: 2 additions & 0 deletions client/web/src/init.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
5 changes: 4 additions & 1 deletion client/web/src/plugin/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,7 @@ class PluginManager {
}

export const pluginManager = new PluginManager();
injectTailchatGlobalValue('installPlugin', pluginManager.installPlugin);
injectTailchatGlobalValue(
'installPlugin',
pluginManager.installPlugin.bind(pluginManager)
);
21 changes: 21 additions & 0 deletions client/web/src/utils/event-helper.ts
Original file line number Diff line number Diff line change
@@ -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,
},
'*'
);
}

0 comments on commit 92d5e39

Please sign in to comment.