mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
Merge branch 'codex/feature/p2p-stats-settings'
# Conflicts: # package.json # public/translations/ar/default.json # public/translations/bn/default.json # public/translations/cs/default.json # public/translations/da/default.json # public/translations/de/default.json # public/translations/el/default.json # public/translations/en/default.json # public/translations/es/default.json # public/translations/fa/default.json # public/translations/fi/default.json # public/translations/fil/default.json # public/translations/fr/default.json # public/translations/he/default.json # public/translations/hi/default.json # public/translations/hu/default.json # public/translations/id/default.json # public/translations/it/default.json # public/translations/ja/default.json # public/translations/ko/default.json # public/translations/mr/default.json # public/translations/nl/default.json # public/translations/no/default.json # public/translations/pl/default.json # public/translations/pt/default.json # public/translations/ro/default.json # public/translations/ru/default.json # public/translations/sq/default.json # public/translations/sv/default.json # public/translations/te/default.json # public/translations/th/default.json # public/translations/tr/default.json # public/translations/uk/default.json # public/translations/ur/default.json # public/translations/vi/default.json # public/translations/zh/default.json # src/components/settings-modal/__tests__/settings-modal.test.tsx # src/components/settings-modal/settings-modal.tsx # yarn.lock
This commit is contained in:
@@ -1,6 +1,21 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { configureP2PBrowserPkcOptions, isP2PBrowserHostname, P2P_BROWSER_PKC_OPTIONS } from '../p2p-browser-config';
|
||||
import {
|
||||
configureP2PBrowserPkcOptions,
|
||||
getPureP2PBrowserPreference,
|
||||
isP2PBrowserHostname,
|
||||
P2P_BROWSER_PKC_OPTIONS,
|
||||
PURE_P2P_BROWSER_SETTING_KEY,
|
||||
setPureP2PBrowserPreference,
|
||||
shouldUsePureP2PBrowser,
|
||||
} from '../p2p-browser-config';
|
||||
|
||||
const createStorage = (values: Record<string, string | undefined> = {}) => ({
|
||||
getItem: (key: string) => values[key] ?? null,
|
||||
setItem: (key: string, value: string) => {
|
||||
values[key] = value;
|
||||
},
|
||||
});
|
||||
|
||||
describe('p2p-browser-config', () => {
|
||||
it('detects p2p subdomains', () => {
|
||||
@@ -10,31 +25,78 @@ describe('p2p-browser-config', () => {
|
||||
expect(isP2PBrowserHostname('www.p2p.5chan.app')).toBe(false);
|
||||
});
|
||||
|
||||
it('configures browser PKC options for p2p hostnames', () => {
|
||||
it('leaves browser PKC options untouched by default', () => {
|
||||
const targetWindow = {
|
||||
location: { hostname: 'p2p.5chan.app' },
|
||||
location: { hostname: '5chan.app' },
|
||||
localStorage: createStorage(),
|
||||
defaultPkcOptions: {
|
||||
ipfsGatewayUrls: ['https://gateway.example'],
|
||||
},
|
||||
};
|
||||
|
||||
expect(configureP2PBrowserPkcOptions(targetWindow)).toBe(false);
|
||||
expect(targetWindow.defaultPkcOptions).toEqual({
|
||||
ipfsGatewayUrls: ['https://gateway.example'],
|
||||
});
|
||||
});
|
||||
|
||||
it('configures browser PKC options when pure p2p is enabled', () => {
|
||||
const targetWindow = {
|
||||
location: { hostname: '5chan.app' },
|
||||
localStorage: createStorage({ [PURE_P2P_BROWSER_SETTING_KEY]: 'true' }),
|
||||
defaultPkcOptions: {
|
||||
ipfsGatewayUrls: ['https://gateway.example'],
|
||||
},
|
||||
};
|
||||
|
||||
expect(configureP2PBrowserPkcOptions(targetWindow)).toBe(true);
|
||||
expect(targetWindow.defaultPkcOptions).toEqual({
|
||||
ipfsGatewayUrls: ['https://gateway.example'],
|
||||
...P2P_BROWSER_PKC_OPTIONS,
|
||||
});
|
||||
expect(targetWindow.defaultPkcOptions).toEqual(P2P_BROWSER_PKC_OPTIONS);
|
||||
});
|
||||
|
||||
it('leaves normal hostnames untouched', () => {
|
||||
it('leaves browser PKC options untouched when pure p2p is disabled', () => {
|
||||
const defaultPkcOptions = {
|
||||
ipfsGatewayUrls: ['https://gateway.example'],
|
||||
};
|
||||
const targetWindow = {
|
||||
location: { hostname: '5chan.app' },
|
||||
localStorage: createStorage({ [PURE_P2P_BROWSER_SETTING_KEY]: 'false' }),
|
||||
defaultPkcOptions,
|
||||
};
|
||||
|
||||
expect(configureP2PBrowserPkcOptions(targetWindow)).toBe(false);
|
||||
expect(targetWindow.defaultPkcOptions).toBe(defaultPkcOptions);
|
||||
});
|
||||
|
||||
it('leaves electron defaults untouched', () => {
|
||||
const defaultPkcOptions = {
|
||||
pkcRpcClientsOptions: ['ws://localhost:9138'],
|
||||
};
|
||||
const targetWindow = {
|
||||
electronApi: { isElectron: true },
|
||||
location: { hostname: 'localhost' },
|
||||
localStorage: createStorage(),
|
||||
defaultPkcOptions,
|
||||
};
|
||||
|
||||
expect(configureP2PBrowserPkcOptions(targetWindow)).toBe(false);
|
||||
expect(targetWindow.defaultPkcOptions).toBe(defaultPkcOptions);
|
||||
});
|
||||
|
||||
it('persists and reads the browser pure p2p preference', () => {
|
||||
const targetWindow = {
|
||||
location: { hostname: '5chan.app' },
|
||||
localStorage: createStorage(),
|
||||
};
|
||||
|
||||
expect(getPureP2PBrowserPreference(targetWindow)).toBeUndefined();
|
||||
expect(shouldUsePureP2PBrowser(targetWindow)).toBe(false);
|
||||
|
||||
setPureP2PBrowserPreference(false, targetWindow);
|
||||
expect(getPureP2PBrowserPreference(targetWindow)).toBe(false);
|
||||
expect(shouldUsePureP2PBrowser(targetWindow)).toBe(false);
|
||||
|
||||
setPureP2PBrowserPreference(true, targetWindow);
|
||||
expect(getPureP2PBrowserPreference(targetWindow)).toBe(true);
|
||||
expect(shouldUsePureP2PBrowser(targetWindow)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
getBrowserGatewayAccountOptions,
|
||||
getBrowserPureP2PAccountOptions,
|
||||
getP2PRuntimeMode,
|
||||
isBrowserPureP2PEnabled,
|
||||
shouldShowP2PSettingsSection,
|
||||
} from '../p2p-runtime';
|
||||
|
||||
const browserWindow = {
|
||||
electronApi: undefined,
|
||||
isElectron: false,
|
||||
localStorage: {
|
||||
getItem: () => null,
|
||||
setItem: () => undefined,
|
||||
},
|
||||
} as unknown as Window;
|
||||
|
||||
const electronWindow = {
|
||||
electronApi: { isElectron: true },
|
||||
isElectron: true,
|
||||
} as unknown as Window;
|
||||
|
||||
describe('p2p-runtime', () => {
|
||||
it('detects browser libp2p accounts from options and live clients', () => {
|
||||
expect(getP2PRuntimeMode({ pkcOptions: { libp2pJsClientsOptions: [{ key: 'libp2pjs' }] } }, browserWindow)).toBe('browser-libp2p');
|
||||
expect(getP2PRuntimeMode({ pkc: { clients: { libp2pJsClients: { libp2pjs: {} } } } }, browserWindow)).toBe('browser-libp2p');
|
||||
});
|
||||
|
||||
it('detects electron Kubo RPC accounts only in electron runtime', () => {
|
||||
const account = { pkcOptions: { pkcRpcClientsOptions: ['ws://localhost:9138'] } };
|
||||
|
||||
expect(getP2PRuntimeMode(account, electronWindow)).toBe('electron-kubo-rpc');
|
||||
expect(getP2PRuntimeMode(account, browserWindow)).toBeNull();
|
||||
});
|
||||
|
||||
it('shows p2p settings in browsers only when pure p2p is enabled or active', () => {
|
||||
expect(shouldShowP2PSettingsSection(undefined, browserWindow)).toBe(false);
|
||||
expect(shouldShowP2PSettingsSection({ pkcOptions: { ipfsGatewayUrls: ['https://gateway.example'] } }, browserWindow)).toBe(false);
|
||||
expect(isBrowserPureP2PEnabled({ pkcOptions: { ipfsGatewayUrls: ['https://gateway.example'] } }, browserWindow)).toBe(false);
|
||||
});
|
||||
|
||||
it('builds browser p2p and gateway account options without a direct pkc-js import', () => {
|
||||
const account = {
|
||||
pkcOptions: {
|
||||
httpRoutersOptions: ['https://custom-router.example'],
|
||||
ipfsGatewayUrls: ['https://gateway.example'],
|
||||
pkcRpcClientsOptions: ['ws://remote.example'],
|
||||
},
|
||||
};
|
||||
|
||||
expect(getBrowserPureP2PAccountOptions(account)).toMatchObject({
|
||||
libp2pJsClientsOptions: [{ key: 'libp2pjs' }],
|
||||
ipfsGatewayUrls: undefined,
|
||||
pkcRpcClientsOptions: undefined,
|
||||
});
|
||||
expect(getBrowserGatewayAccountOptions(account)).toMatchObject({
|
||||
ipfsGatewayUrls: ['https://ipfsgateway.xyz', 'https://gateway.plebpubsub.xyz', 'https://gateway.forumindex.com'],
|
||||
libp2pJsClientsOptions: undefined,
|
||||
pkcRpcClientsOptions: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,24 +1,85 @@
|
||||
export const PURE_P2P_BROWSER_SETTING_KEY = '5chan:pure-p2p-browser-enabled';
|
||||
|
||||
export const P2P_BROWSER_PKC_OPTIONS = {
|
||||
libp2pJsClientsOptions: [{ key: 'libp2pjs' }],
|
||||
ipfsGatewayUrls: undefined,
|
||||
kuboRpcClientsOptions: undefined,
|
||||
pubsubHttpClientsOptions: undefined,
|
||||
pubsubKuboRpcClientsOptions: undefined,
|
||||
httpRoutersOptions: ['https://peers.pleb.bot', 'https://peers.forumindex.com'],
|
||||
};
|
||||
|
||||
const GATEWAY_BROWSER_PKC_OPTIONS = {
|
||||
ipfsGatewayUrls: ['https://ipfsgateway.xyz', 'https://gateway.plebpubsub.xyz', 'https://gateway.forumindex.com'],
|
||||
kuboRpcClientsOptions: undefined,
|
||||
libp2pJsClientsOptions: undefined,
|
||||
pubsubHttpClientsOptions: undefined,
|
||||
pubsubKuboRpcClientsOptions: ['https://pubsubprovider.xyz/api/v0', 'https://plebpubsub.xyz/api/v0', 'https://rannithepleb.com/api/v0'],
|
||||
httpRoutersOptions: ['https://routing.lol', 'https://peers.pleb.bot', 'https://peers.plebpubsub.xyz', 'https://peers.forumindex.com'],
|
||||
};
|
||||
|
||||
type P2PBrowserConfigWindow = {
|
||||
location: Pick<Location, 'hostname'>;
|
||||
defaultPkcOptions?: Record<string, unknown>;
|
||||
electronApi?: { isElectron?: boolean };
|
||||
isElectron?: boolean;
|
||||
localStorage?: Pick<Storage, 'getItem' | 'setItem'>;
|
||||
};
|
||||
|
||||
export const isP2PBrowserHostname = (hostname: string) => hostname.toLowerCase().startsWith('p2p.');
|
||||
|
||||
export const getBrowserPureP2PPkcOptions = () => ({
|
||||
...P2P_BROWSER_PKC_OPTIONS,
|
||||
libp2pJsClientsOptions: P2P_BROWSER_PKC_OPTIONS.libp2pJsClientsOptions.map((options) => ({ ...options })),
|
||||
httpRoutersOptions: [...P2P_BROWSER_PKC_OPTIONS.httpRoutersOptions],
|
||||
});
|
||||
|
||||
export const getBrowserGatewayPkcOptions = () => ({
|
||||
...GATEWAY_BROWSER_PKC_OPTIONS,
|
||||
ipfsGatewayUrls: [...GATEWAY_BROWSER_PKC_OPTIONS.ipfsGatewayUrls],
|
||||
pubsubKuboRpcClientsOptions: [...GATEWAY_BROWSER_PKC_OPTIONS.pubsubKuboRpcClientsOptions],
|
||||
httpRoutersOptions: [...GATEWAY_BROWSER_PKC_OPTIONS.httpRoutersOptions],
|
||||
});
|
||||
|
||||
export const getPureP2PBrowserPreference = (targetWindow: P2PBrowserConfigWindow = window) => {
|
||||
try {
|
||||
const storedValue = targetWindow.localStorage?.getItem(PURE_P2P_BROWSER_SETTING_KEY);
|
||||
if (storedValue === 'true') return true;
|
||||
if (storedValue === 'false') return false;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const setPureP2PBrowserPreference = (enabled: boolean, targetWindow: P2PBrowserConfigWindow = window) => {
|
||||
try {
|
||||
targetWindow.localStorage?.setItem(PURE_P2P_BROWSER_SETTING_KEY, String(enabled));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
export const isElectronRuntime = (targetWindow: P2PBrowserConfigWindow = window) => targetWindow.electronApi?.isElectron === true || targetWindow.isElectron === true;
|
||||
|
||||
export const shouldUsePureP2PBrowser = (targetWindow: P2PBrowserConfigWindow = window) => {
|
||||
if (isElectronRuntime(targetWindow)) return false;
|
||||
|
||||
const preference = getPureP2PBrowserPreference(targetWindow);
|
||||
if (preference !== undefined) return preference;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const configureP2PBrowserPkcOptions = (targetWindow: P2PBrowserConfigWindow = window) => {
|
||||
if (!isP2PBrowserHostname(targetWindow.location.hostname)) {
|
||||
if (!shouldUsePureP2PBrowser(targetWindow)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
targetWindow.defaultPkcOptions = {
|
||||
...targetWindow.defaultPkcOptions,
|
||||
libp2pJsClientsOptions: P2P_BROWSER_PKC_OPTIONS.libp2pJsClientsOptions.map((options) => ({ ...options })),
|
||||
httpRoutersOptions: [...P2P_BROWSER_PKC_OPTIONS.httpRoutersOptions],
|
||||
...getBrowserPureP2PPkcOptions(),
|
||||
};
|
||||
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { getBrowserGatewayPkcOptions, getBrowserPureP2PPkcOptions, isElectronRuntime, shouldUsePureP2PBrowser } from './p2p-browser-config';
|
||||
|
||||
export const P2P_STATS_SECTION_ID = 'p2p-stats-settings';
|
||||
|
||||
export type P2PRuntimeMode = 'browser-libp2p' | 'electron-kubo-rpc';
|
||||
|
||||
type AccountProtocolOptions = {
|
||||
httpRoutersOptions?: string[];
|
||||
ipfsGatewayUrls?: string[];
|
||||
kuboRpcClientsOptions?: unknown[];
|
||||
libp2pJsClientsOptions?: unknown[];
|
||||
pkcRpcClientsOptions?: string[];
|
||||
pubsubHttpClientsOptions?: unknown[];
|
||||
pubsubKuboRpcClientsOptions?: unknown[];
|
||||
};
|
||||
|
||||
type AccountShape = {
|
||||
pkc?: {
|
||||
clients?: {
|
||||
libp2pJsClients?: Record<string, unknown>;
|
||||
pkcRpcClients?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
pkcOptions?: AccountProtocolOptions;
|
||||
};
|
||||
|
||||
const toAccountShape = (account: unknown) => account as AccountShape | undefined;
|
||||
|
||||
const hasArrayItems = (value: unknown) => Array.isArray(value) && value.length > 0;
|
||||
|
||||
const hasObjectItems = (value: unknown) => !!value && typeof value === 'object' && Object.keys(value).length > 0;
|
||||
|
||||
export const getP2PRuntimeMode = (account?: unknown, targetWindow: Window = window): P2PRuntimeMode | null => {
|
||||
const accountShape = toAccountShape(account);
|
||||
const protocolOptions = accountShape?.pkcOptions;
|
||||
const clients = accountShape?.pkc?.clients;
|
||||
|
||||
if (hasArrayItems(protocolOptions?.libp2pJsClientsOptions) || hasObjectItems(clients?.libp2pJsClients)) {
|
||||
return 'browser-libp2p';
|
||||
}
|
||||
|
||||
if (isElectronRuntime(targetWindow) && (hasArrayItems(protocolOptions?.pkcRpcClientsOptions) || hasObjectItems(clients?.pkcRpcClients))) {
|
||||
return 'electron-kubo-rpc';
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const canConfigureBrowserPureP2P = (targetWindow: Window = window) => !isElectronRuntime(targetWindow);
|
||||
|
||||
export const shouldShowP2PSettingsSection = (account?: unknown, targetWindow: Window = window) =>
|
||||
getP2PRuntimeMode(account, targetWindow) !== null || (canConfigureBrowserPureP2P(targetWindow) && isBrowserPureP2PEnabled(account, targetWindow));
|
||||
|
||||
export const isBrowserPureP2PEnabled = (account?: unknown, targetWindow: Window = window) => {
|
||||
const accountShape = toAccountShape(account);
|
||||
if (!canConfigureBrowserPureP2P(targetWindow)) return false;
|
||||
if (getP2PRuntimeMode(account, targetWindow) === 'browser-libp2p') return true;
|
||||
if (hasArrayItems(accountShape?.pkcOptions?.ipfsGatewayUrls) || hasArrayItems(accountShape?.pkcOptions?.pubsubKuboRpcClientsOptions)) return false;
|
||||
return shouldUsePureP2PBrowser(targetWindow);
|
||||
};
|
||||
|
||||
export const getBrowserPureP2PAccountOptions = (account?: unknown) => ({
|
||||
...toAccountShape(account)?.pkcOptions,
|
||||
...getBrowserPureP2PPkcOptions(),
|
||||
pkcRpcClientsOptions: undefined,
|
||||
});
|
||||
|
||||
export const getBrowserGatewayAccountOptions = (account?: unknown) => ({
|
||||
...toAccountShape(account)?.pkcOptions,
|
||||
...getBrowserGatewayPkcOptions(),
|
||||
pkcRpcClientsOptions: undefined,
|
||||
});
|
||||
Reference in New Issue
Block a user