mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-17 14:04:21 +01:00
feat: import config from old domain
This commit is contained in:
@@ -16,6 +16,7 @@ import LazyLoad from '@/components/utils/LazyLoad';
|
||||
import usePopup from '@/components/utils/usePopup';
|
||||
import { colorThemes } from '@/core/colorThemes';
|
||||
import useConfig, { type Config } from '@/core/useConfig';
|
||||
import { useOldConfig } from '@/hooks/useInterWindow';
|
||||
import useModalState from '@/hooks/useModalState';
|
||||
import { useTranslation } from '@/i18n/useTranslation';
|
||||
import usePubkey from '@/nostr/usePubkey';
|
||||
@@ -634,6 +635,7 @@ const OtherConfig = () => {
|
||||
const ConfigUI = (props: ConfigProps) => {
|
||||
const i18n = useTranslation();
|
||||
const [menuIndex, setMenuIndex] = createSignal<number | null>(null);
|
||||
const { canImport, importConfig } = useOldConfig();
|
||||
|
||||
const menu = [
|
||||
{
|
||||
@@ -690,6 +692,19 @@ const ConfigUI = (props: ConfigProps) => {
|
||||
fallback={
|
||||
<>
|
||||
<h2 class="flex-1 text-center text-lg font-bold">{i18n()('config.config')}</h2>
|
||||
<Show when={canImport()}>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded bg-primary p-2 text-primary-fg"
|
||||
onClick={() => {
|
||||
if (window.confirm(i18n()('config.confirmImportOldDomainConfig'))) {
|
||||
importConfig();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{i18n()('config.importOldDomainConfig')}
|
||||
</button>
|
||||
</Show>
|
||||
<ul class="flex flex-col">
|
||||
<For each={menu}>
|
||||
{(menuItem, i) => (
|
||||
|
||||
123
src/hooks/useInterWindow.tsx
Normal file
123
src/hooks/useInterWindow.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import { createSignal, onMount, onCleanup, observable } from 'solid-js';
|
||||
|
||||
import useConfig from '@/core/useConfig';
|
||||
import {
|
||||
type InterWindowRequest,
|
||||
type InterWindowRequestWithId,
|
||||
type InterWindowResponseWithId,
|
||||
} from '@/interWindow';
|
||||
|
||||
const useInterWindow = (url: string) => {
|
||||
let iframeRef: HTMLIFrameElement | undefined;
|
||||
const [loaded, setLoaded] = createSignal(false);
|
||||
|
||||
onMount(() => {
|
||||
iframeRef = (<iframe title="interwindow" class="hidden" src={url} />) as HTMLIFrameElement;
|
||||
document.body.appendChild(iframeRef);
|
||||
iframeRef.addEventListener('load', () => setLoaded(true));
|
||||
onCleanup(() => {
|
||||
iframeRef?.remove();
|
||||
});
|
||||
});
|
||||
|
||||
const waitLoaded = (): Promise<void> => {
|
||||
if (loaded()) return Promise.resolve();
|
||||
return new Promise((resolve) => {
|
||||
observable(loaded).subscribe((v) => {
|
||||
if (v) resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const listen = async (
|
||||
expectedRequestId: number,
|
||||
timeout: number = 2000,
|
||||
): Promise<InterWindowResponseWithId> =>
|
||||
new Promise((resolve, reject) => {
|
||||
const listener = (event: MessageEvent) => {
|
||||
console.log('message received', event);
|
||||
if (event.origin !== new URL(url).origin) return;
|
||||
if (typeof event.data !== 'string') return;
|
||||
|
||||
const data = JSON.parse(event.data) as InterWindowResponseWithId;
|
||||
|
||||
if (data.requestId !== expectedRequestId) return;
|
||||
|
||||
window.removeEventListener('message', listener);
|
||||
resolve(data);
|
||||
};
|
||||
|
||||
setTimeout(() => {
|
||||
reject(new Error('timeout'));
|
||||
window.removeEventListener('message', listener);
|
||||
}, timeout);
|
||||
|
||||
window.addEventListener('message', listener, false);
|
||||
});
|
||||
|
||||
const request = async (message: InterWindowRequest) => {
|
||||
const requestId = Math.random();
|
||||
const withId = { ...message, requestId } satisfies InterWindowRequestWithId;
|
||||
const messageStr = JSON.stringify(withId);
|
||||
|
||||
await waitLoaded();
|
||||
const promise = listen(requestId);
|
||||
if (iframeRef == null) {
|
||||
throw new Error('iframeRef is null');
|
||||
}
|
||||
if (!loaded()) {
|
||||
throw new Error('iframeRef is not loaded');
|
||||
}
|
||||
iframeRef.contentWindow?.postMessage(messageStr, new URL(url).origin);
|
||||
return promise;
|
||||
};
|
||||
|
||||
const getConfig = () => request({ type: 'GET_CONFIG' });
|
||||
|
||||
return {
|
||||
loaded,
|
||||
request,
|
||||
getConfig,
|
||||
};
|
||||
};
|
||||
|
||||
export const useOldConfig = () => {
|
||||
const url = 'https://syusui-s.github.io/rabbit/transfer-config.html';
|
||||
const { getConfig } = useInterWindow(url);
|
||||
const { setConfig } = useConfig();
|
||||
|
||||
const [oldConfig, setOldConfig] = createSignal<string | undefined>();
|
||||
|
||||
const canImport = () => {
|
||||
const c = oldConfig();
|
||||
return c != null && c.length > 0;
|
||||
};
|
||||
|
||||
const importConfig = () => {
|
||||
const c = oldConfig();
|
||||
if (c != null && c.length > 0) {
|
||||
setConfig(JSON.parse(c));
|
||||
}
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
setTimeout(() => {
|
||||
getConfig()
|
||||
.then((obtainedConfig) => {
|
||||
if (typeof obtainedConfig.payload === 'string' && obtainedConfig.payload.length > 0) {
|
||||
setOldConfig(obtainedConfig.payload);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
|
||||
return {
|
||||
canImport,
|
||||
importConfig,
|
||||
};
|
||||
};
|
||||
|
||||
export default useInterWindow;
|
||||
38
src/interWindow.ts
Normal file
38
src/interWindow.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
export type InterWindowRequest = {
|
||||
type: 'GET_CONFIG';
|
||||
};
|
||||
|
||||
export type InterWindowRequestWithId = InterWindowRequest & {
|
||||
requestId: number;
|
||||
};
|
||||
|
||||
export type InterWindowResponse = {
|
||||
type: 'OK' | 'BAD_REQUEST' | 'NOT_FOUND' | 'INTERNAL_PEER_ERROR';
|
||||
payload?: string;
|
||||
};
|
||||
|
||||
export type InterWindowResponseWithId = InterWindowResponse & {
|
||||
requestId: number;
|
||||
};
|
||||
|
||||
export type RequestHandler = (request: InterWindowRequestWithId) => InterWindowResponse;
|
||||
|
||||
export const ok = (payload?: string): InterWindowResponse => ({
|
||||
type: 'OK',
|
||||
payload,
|
||||
});
|
||||
|
||||
export const badRequest = (payload?: string): InterWindowResponse => ({
|
||||
type: 'BAD_REQUEST',
|
||||
payload,
|
||||
});
|
||||
|
||||
export const notFound = (payload?: string): InterWindowResponse => ({
|
||||
type: 'NOT_FOUND',
|
||||
payload,
|
||||
});
|
||||
|
||||
export const internalPeerError = (payload?: string): InterWindowResponse => ({
|
||||
type: 'INTERNAL_PEER_ERROR',
|
||||
payload,
|
||||
});
|
||||
@@ -125,6 +125,8 @@ export default {
|
||||
},
|
||||
config: {
|
||||
config: 'Settings',
|
||||
importOldDomainConfig: 'Import config from the old domain',
|
||||
confirmImportOldDomainConfig: 'Import? (The config will be overwritten)',
|
||||
profile: {
|
||||
profile: 'Profile',
|
||||
openProfile: 'Open',
|
||||
|
||||
@@ -121,6 +121,8 @@ export default {
|
||||
},
|
||||
config: {
|
||||
config: '設定',
|
||||
importOldDomainConfig: '古いドメインから設定をインポート',
|
||||
confirmImportOldDomainConfig: 'インポートしますか?(現在の設定は上書きされます)',
|
||||
profile: {
|
||||
profile: 'プロフィール',
|
||||
openProfile: '開く',
|
||||
|
||||
@@ -2,6 +2,8 @@ import { createSignal, onMount, Switch, Match, type Component } from 'solid-js';
|
||||
|
||||
import { useNavigate } from '@solidjs/router';
|
||||
|
||||
import useConfig from '@/core/useConfig';
|
||||
import { useOldConfig } from '@/hooks/useInterWindow';
|
||||
import usePersistStatus from '@/hooks/usePersistStatus';
|
||||
import { useTranslation } from '@/i18n/useTranslation';
|
||||
import resolveAsset from '@/utils/resolveAsset';
|
||||
@@ -36,13 +38,24 @@ const Hello: Component = () => {
|
||||
const signerStatus = useSignerStatus();
|
||||
const navigate = useNavigate();
|
||||
const { persistStatus, loggedIn } = usePersistStatus();
|
||||
const { config } = useConfig();
|
||||
const { canImport, importConfig } = useOldConfig();
|
||||
|
||||
const handleLogin = () => {
|
||||
if (
|
||||
config().columns.length === 0 &&
|
||||
canImport() &&
|
||||
window.confirm('import config from old domain?')
|
||||
) {
|
||||
importConfig();
|
||||
}
|
||||
|
||||
loggedIn();
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
console.log();
|
||||
if (persistStatus().loggedIn) {
|
||||
navigate('/');
|
||||
}
|
||||
|
||||
9
transfer-config.html
Normal file
9
transfer-config.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Transfer Config</title>
|
||||
<script src="transfer-config.ts" type="module"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
53
transfer-config.ts
Normal file
53
transfer-config.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
ok,
|
||||
notFound,
|
||||
internalPeerError,
|
||||
type InterWindowRequestWithId,
|
||||
type RequestHandler,
|
||||
} from '@/interWindow';
|
||||
|
||||
(() => {
|
||||
const acceptableOrigins = [
|
||||
window.location.origin,
|
||||
'http://localhost:3000',
|
||||
'http://localhost:12345',
|
||||
'https://rabbit.syusui.net',
|
||||
];
|
||||
|
||||
const rawMessageHandler = (handler: RequestHandler) => (event: MessageEvent) => {
|
||||
console.log('transfer-config: received request', event.data, event.origin);
|
||||
if (!acceptableOrigins.includes(event.origin)) return;
|
||||
|
||||
const { origin, source } = event;
|
||||
if (typeof event.data !== 'string') return;
|
||||
const request = JSON.parse(event.data) as InterWindowRequestWithId;
|
||||
|
||||
let responseObj;
|
||||
try {
|
||||
responseObj = handler(request);
|
||||
} catch (e) {
|
||||
responseObj = internalPeerError(undefined);
|
||||
}
|
||||
|
||||
const response = JSON.stringify({ requestId: request.requestId, ...responseObj });
|
||||
// @ts-expect-error postMessage
|
||||
source?.postMessage(response, origin);
|
||||
};
|
||||
|
||||
window.addEventListener(
|
||||
'message',
|
||||
rawMessageHandler((request) => {
|
||||
switch (request.type) {
|
||||
case 'GET_CONFIG': {
|
||||
const value = window.localStorage.getItem('RabbitConfig') ?? '';
|
||||
return ok(value);
|
||||
}
|
||||
default:
|
||||
return notFound();
|
||||
}
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
||||
console.log('transfer-config: mounted');
|
||||
})();
|
||||
@@ -14,6 +14,12 @@ export default defineConfig({
|
||||
build: {
|
||||
target: 'esnext',
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: './index.html',
|
||||
transferConfig: './transfer-config.html',
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
|
||||
Reference in New Issue
Block a user