fix(hooks): handle pkc rebrand regressions

This commit is contained in:
Tommaso Casaburi
2026-04-11 16:57:00 +07:00
parent dcb3c4427c
commit 19095aefa1
19 changed files with 317 additions and 1186 deletions
+1 -1
View File
@@ -8,7 +8,7 @@
"license": "GPL-2.0-only", "license": "GPL-2.0-only",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@bitsocialnet/bitsocial-react-hooks": "https://codeload.github.com/bitsocialnet/bitsocial-react-hooks/tar.gz/d609b67dea7e518f7931d3a69fa75e4e86591d37", "@bitsocialnet/bitsocial-react-hooks": "https://codeload.github.com/bitsocialnet/bitsocial-react-hooks/tar.gz/95b46a8350ed5a2f96721f0dc14f2aaf26d410bf",
"@capacitor/app": "7.0.1", "@capacitor/app": "7.0.1",
"@capacitor/status-bar": "7.0.1", "@capacitor/status-bar": "7.0.1",
"@capawesome/capacitor-android-edge-to-edge-support": "7.2.2", "@capawesome/capacitor-android-edge-to-edge-support": "7.2.2",
+22 -3
View File
@@ -5,6 +5,7 @@ const path = require('path');
const packageDistPath = path.join(__dirname, '..', 'node_modules', '@bitsocialnet', 'bitsocial-react-hooks', 'dist'); const packageDistPath = path.join(__dirname, '..', 'node_modules', '@bitsocialnet', 'bitsocial-react-hooks', 'dist');
const logPrefix = '[patch-bitsocial-react-hooks-esm]'; const logPrefix = '[patch-bitsocial-react-hooks-esm]';
const packageIndexPath = path.join(packageDistPath, 'index.js');
if (!fs.existsSync(packageDistPath)) { if (!fs.existsSync(packageDistPath)) {
console.log(`${logPrefix} Skip: @bitsocialnet/bitsocial-react-hooks dist not found.`); console.log(`${logPrefix} Skip: @bitsocialnet/bitsocial-react-hooks dist not found.`);
@@ -14,6 +15,10 @@ if (!fs.existsSync(packageDistPath)) {
const relativeImportPattern = /(from\s+|import\s+)(['"])(\.\.?\/[^'"]+)\2/g; const relativeImportPattern = /(from\s+|import\s+)(['"])(\.\.?\/[^'"]+)\2/g;
let touchedFiles = 0; let touchedFiles = 0;
let rewrittenImports = 0; let rewrittenImports = 0;
let removedNodeDebugPatches = 0;
const nodeDebugPatchPattern =
/\/\/ fix DEBUG_DEPTH bug https:\/\/github\.com\/debug-js\/debug\/issues\/746\s*try\s*\{\s*if \(process\.env\.DEBUG_DEPTH\) \{\s*require\("util"\)\.inspect\.defaultOptions\.depth = process\.env\.DEBUG_DEPTH;\s*\}\s*if \(process\.env\.DEBUG_ARRAY\) \{\s*require\("util"\)\.inspect\.defaultOptions\.maxArrayLength = process\.env\.DEBUG_ARRAY;\s*\}\s*\}\s*catch \(e\) \{ \}/m;
const splitSpecifier = (specifier) => { const splitSpecifier = (specifier) => {
const suffixStart = specifier.search(/[?#]/); const suffixStart = specifier.search(/[?#]/);
@@ -52,7 +57,7 @@ const patchFile = (filePath) => {
const source = fs.readFileSync(filePath, 'utf8'); const source = fs.readFileSync(filePath, 'utf8');
let fileImportCount = 0; let fileImportCount = 0;
const updated = source.replace(relativeImportPattern, (match, prefix, quote, specifier) => { let updated = source.replace(relativeImportPattern, (match, prefix, quote, specifier) => {
const resolvedSpecifier = resolveSpecifier(filePath, specifier); const resolvedSpecifier = resolveSpecifier(filePath, specifier);
if (!resolvedSpecifier || resolvedSpecifier === specifier) { if (!resolvedSpecifier || resolvedSpecifier === specifier) {
@@ -63,7 +68,19 @@ const patchFile = (filePath) => {
return `${prefix}${quote}${resolvedSpecifier}${quote}`; return `${prefix}${quote}${resolvedSpecifier}${quote}`;
}); });
if (!fileImportCount) { if (filePath === packageIndexPath) {
const nextUpdated = updated.replace(
nodeDebugPatchPattern,
'// Browser bundle: skip Node-only util DEBUG_DEPTH/DEBUG_ARRAY tuning.\n',
);
if (nextUpdated !== updated) {
updated = nextUpdated;
removedNodeDebugPatches += 1;
}
}
if (!fileImportCount && updated === source) {
return; return;
} }
@@ -94,4 +111,6 @@ if (!touchedFiles) {
process.exit(0); process.exit(0);
} }
console.log(`${logPrefix} Patched ${rewrittenImports} imports across ${touchedFiles} files.`); console.log(
`${logPrefix} Patched ${rewrittenImports} imports across ${touchedFiles} files${removedNodeDebugPatches ? ` and removed ${removedNodeDebugPatches} browser-incompatible debug util block(s)` : ''}.`,
);
@@ -9,20 +9,20 @@ const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise
const testState = vi.hoisted(() => ({ const testState = vi.hoisted(() => ({
account: { account: {
mediaIpfsGatewayUrl: 'https://media.old.example', mediaIpfsGatewayUrl: 'https://media.old.example',
plebbitOptions: { pkcOptions: {
chainProviders: { chainProviders: {
eth: { chainId: 1, urls: ['https://eth.old.example'] }, eth: { chainId: 1, urls: ['https://eth.old.example'] },
sol: { chainId: 101, urls: ['https://sol.old.example'] }, sol: { chainId: 101, urls: ['https://sol.old.example'] },
}, },
httpRoutersOptions: ['https://router.old.example'], httpRoutersOptions: ['https://router.old.example'],
ipfsGatewayUrls: ['https://ipfs.old.example'], ipfsGatewayUrls: ['https://ipfs.old.example'],
plebbitRpcClientsOptions: ['ws://old.example/key'], pkcRpcClientsOptions: ['ws://old.example/key'],
pubsubHttpClientsOptions: ['https://pubsub.old.example'], pubsubKuboRpcClientsOptions: ['https://pubsub.old.example'],
}, },
} as Record<string, any>, } as Record<string, any>,
rpcSettings: { rpcSettings: {
plebbitRpcSettings: { pkcRpcSettings: {
plebbitOptions: { pkcOptions: {
dataPath: '/tmp/plebbit-data', dataPath: '/tmp/plebbit-data',
}, },
}, },
@@ -40,7 +40,7 @@ vi.mock('react-i18next', () => ({
vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({ vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
setAccount: (account: unknown) => testState.setAccountMock(account), setAccount: (account: unknown) => testState.setAccountMock(account),
useAccount: () => testState.account, useAccount: () => testState.account,
usePlebbitRpcSettings: () => testState.rpcSettings, usePkcRpcSettings: () => testState.rpcSettings,
})); }));
let alertSpy: ReturnType<typeof vi.spyOn>; let alertSpy: ReturnType<typeof vi.spyOn>;
@@ -86,20 +86,20 @@ describe('AdvancedSettings', () => {
vi.clearAllMocks(); vi.clearAllMocks();
testState.account = { testState.account = {
mediaIpfsGatewayUrl: 'https://media.old.example', mediaIpfsGatewayUrl: 'https://media.old.example',
plebbitOptions: { pkcOptions: {
chainProviders: { chainProviders: {
eth: { chainId: 1, urls: ['https://eth.old.example'] }, eth: { chainId: 1, urls: ['https://eth.old.example'] },
sol: { chainId: 101, urls: ['https://sol.old.example'] }, sol: { chainId: 101, urls: ['https://sol.old.example'] },
}, },
httpRoutersOptions: ['https://router.old.example'], httpRoutersOptions: ['https://router.old.example'],
ipfsGatewayUrls: ['https://ipfs.old.example'], ipfsGatewayUrls: ['https://ipfs.old.example'],
plebbitRpcClientsOptions: ['ws://old.example/key'], pkcRpcClientsOptions: ['ws://old.example/key'],
pubsubHttpClientsOptions: ['https://pubsub.old.example'], pubsubKuboRpcClientsOptions: ['https://pubsub.old.example'],
}, },
}; };
testState.rpcSettings = { testState.rpcSettings = {
plebbitRpcSettings: { pkcRpcSettings: {
plebbitOptions: { pkcOptions: {
dataPath: '/tmp/plebbit-data', dataPath: '/tmp/plebbit-data',
}, },
}, },
@@ -154,7 +154,7 @@ describe('AdvancedSettings', () => {
expect(testState.setAccountMock).toHaveBeenCalledWith({ expect(testState.setAccountMock).toHaveBeenCalledWith({
mediaIpfsGatewayUrl: 'https://media.new.example', mediaIpfsGatewayUrl: 'https://media.new.example',
plebbitOptions: { pkcOptions: {
chainProviders: { chainProviders: {
eth: { chainId: 1, urls: ['https://eth.one.example'] }, eth: { chainId: 1, urls: ['https://eth.one.example'] },
sol: { chainId: 101, urls: ['https://sol.one.example'] }, sol: { chainId: 101, urls: ['https://sol.one.example'] },
@@ -162,8 +162,8 @@ describe('AdvancedSettings', () => {
dataPath: '/tmp/next-plebbit', dataPath: '/tmp/next-plebbit',
httpRoutersOptions: ['https://router.one.example'], httpRoutersOptions: ['https://router.one.example'],
ipfsGatewayUrls: ['https://ipfs.one.example', 'https://ipfs.two.example'], ipfsGatewayUrls: ['https://ipfs.one.example', 'https://ipfs.two.example'],
plebbitRpcClientsOptions: ['ws://127.0.0.1:9138/secret'], pkcRpcClientsOptions: ['ws://127.0.0.1:9138/secret'],
pubsubHttpClientsOptions: ['https://pubsub.one.example'], pubsubKuboRpcClientsOptions: ['https://pubsub.one.example'],
}, },
}); });
expect(alertSpy).toHaveBeenCalledWith('Options saved, reloading...'); expect(alertSpy).toHaveBeenCalledWith('Options saved, reloading...');
@@ -172,8 +172,8 @@ describe('AdvancedSettings', () => {
it('disables remote-managed textareas and shows the electron data path when RPC is connected', async () => { it('disables remote-managed textareas and shows the electron data path when RPC is connected', async () => {
testState.rpcSettings = { testState.rpcSettings = {
plebbitRpcSettings: { pkcRpcSettings: {
plebbitOptions: { pkcOptions: {
dataPath: '/tmp/connected-node', dataPath: '/tmp/connected-node',
}, },
}, },
@@ -1,5 +1,5 @@
import { memo, RefObject, useRef, useState } from 'react'; import { memo, RefObject, useRef, useState } from 'react';
import { setAccount, useAccount, usePlebbitRpcSettings } from '@bitsocialnet/bitsocial-react-hooks'; import { setAccount, useAccount, usePkcRpcSettings } from '@bitsocialnet/bitsocial-react-hooks';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import styles from './advanced-settings.module.css'; import styles from './advanced-settings.module.css';
@@ -14,12 +14,47 @@ interface SettingsProps {
p2pDataPathRef?: RefObject<HTMLInputElement>; p2pDataPathRef?: RefObject<HTMLInputElement>;
} }
type AccountProtocolOptions = {
chainProviders?: Record<string, { urls?: string[]; chainId: number }>;
dataPath?: string;
httpRoutersOptions?: string[];
ipfsGatewayUrls?: string[];
pkcRpcClientsOptions?: string[];
plebbitRpcClientsOptions?: string[];
pubsubHttpClientsOptions?: string[];
pubsubKuboRpcClientsOptions?: string[];
};
type AccountShape = {
chainProviders?: AccountProtocolOptions['chainProviders'];
mediaIpfsGatewayUrl?: string;
pkcOptions?: AccountProtocolOptions;
plebbitOptions?: AccountProtocolOptions;
};
type RpcSettingsShape = {
pkcOptions?: { dataPath?: string };
plebbitOptions?: { dataPath?: string };
};
const getProtocolOptions = (account?: AccountShape) => account?.pkcOptions ?? account?.plebbitOptions;
const getChainProviders = (account?: AccountShape) => account?.chainProviders ?? getProtocolOptions(account)?.chainProviders;
const getNodeRpcClientsOptions = (protocolOptions?: AccountProtocolOptions) => protocolOptions?.pkcRpcClientsOptions ?? protocolOptions?.plebbitRpcClientsOptions;
const getPubsubRpcClientsOptions = (protocolOptions?: AccountProtocolOptions) =>
protocolOptions?.pubsubKuboRpcClientsOptions ?? protocolOptions?.pubsubHttpClientsOptions;
const getRpcSettingsDataPath = (rpcSettings?: RpcSettingsShape) => rpcSettings?.pkcOptions?.dataPath ?? rpcSettings?.plebbitOptions?.dataPath ?? '';
const IPFSGatewaysSettings = ({ ipfsGatewayUrlsRef, mediaIpfsGatewayUrlRef }: SettingsProps) => { const IPFSGatewaysSettings = ({ ipfsGatewayUrlsRef, mediaIpfsGatewayUrlRef }: SettingsProps) => {
const account = useAccount(); const account = useAccount() as AccountShape | undefined;
const { plebbitOptions, mediaIpfsGatewayUrl } = account || {}; const protocolOptions = getProtocolOptions(account);
const { ipfsGatewayUrls } = plebbitOptions || {}; const { ipfsGatewayUrls } = protocolOptions || {};
const plebbitRpc = usePlebbitRpcSettings(); const { mediaIpfsGatewayUrl } = account || {};
const isConnectedToRpc = plebbitRpc?.state === 'connected'; const pkcRpc = usePkcRpcSettings();
const isConnectedToRpc = pkcRpc?.state === 'connected';
const ipfsGatewayUrlsDefaultValue = ipfsGatewayUrls?.join('\n'); const ipfsGatewayUrlsDefaultValue = ipfsGatewayUrls?.join('\n');
return ( return (
@@ -52,12 +87,12 @@ const IPFSGatewaysSettings = ({ ipfsGatewayUrlsRef, mediaIpfsGatewayUrlRef }: Se
}; };
const PubsubProvidersSettings = ({ pubsubProvidersRef }: SettingsProps) => { const PubsubProvidersSettings = ({ pubsubProvidersRef }: SettingsProps) => {
const account = useAccount(); const account = useAccount() as AccountShape | undefined;
const { plebbitOptions } = account || {}; const protocolOptions = getProtocolOptions(account);
const { pubsubHttpClientsOptions } = plebbitOptions || {}; const pubsubKuboRpcClientsOptions = getPubsubRpcClientsOptions(protocolOptions);
const plebbitRpc = usePlebbitRpcSettings(); const pkcRpc = usePkcRpcSettings();
const isConnectedToRpc = plebbitRpc?.state === 'connected'; const isConnectedToRpc = pkcRpc?.state === 'connected';
const pubsubProvidersDefaultValue = pubsubHttpClientsOptions?.join('\n'); const pubsubProvidersDefaultValue = pubsubKuboRpcClientsOptions?.join('\n');
return ( return (
<div className={styles.pubsubProvidersSettings}> <div className={styles.pubsubProvidersSettings}>
@@ -69,18 +104,18 @@ const PubsubProvidersSettings = ({ pubsubProvidersRef }: SettingsProps) => {
autoCapitalize='off' autoCapitalize='off'
autoComplete='off' autoComplete='off'
spellCheck='false' spellCheck='false'
rows={pubsubHttpClientsOptions?.length || 1} rows={pubsubKuboRpcClientsOptions?.length || 1}
/> />
</div> </div>
); );
}; };
const HttpRoutersSettings = ({ httpRoutersRef }: SettingsProps) => { const HttpRoutersSettings = ({ httpRoutersRef }: SettingsProps) => {
const account = useAccount(); const account = useAccount() as AccountShape | undefined;
const { plebbitOptions } = account || {}; const protocolOptions = getProtocolOptions(account);
const { httpRoutersOptions } = plebbitOptions || {}; const { httpRoutersOptions } = protocolOptions || {};
const plebbitRpc = usePlebbitRpcSettings(); const pkcRpc = usePkcRpcSettings();
const isConnectedToRpc = plebbitRpc?.state === 'connected'; const isConnectedToRpc = pkcRpc?.state === 'connected';
const httpRoutersDefaultValue = httpRoutersOptions?.join('\n'); const httpRoutersDefaultValue = httpRoutersOptions?.join('\n');
return ( return (
@@ -100,11 +135,10 @@ const HttpRoutersSettings = ({ httpRoutersRef }: SettingsProps) => {
}; };
const BlockchainProvidersSettings = ({ ethRpcRef, solRpcRef }: SettingsProps) => { const BlockchainProvidersSettings = ({ ethRpcRef, solRpcRef }: SettingsProps) => {
const account = useAccount(); const account = useAccount() as AccountShape | undefined;
const { plebbitOptions } = account || {}; const chainProviders = getChainProviders(account);
const { chainProviders } = plebbitOptions || {}; const ethRpcDefaultValue = chainProviders?.['eth']?.urls?.join('\n');
const ethRpcDefaultValue = chainProviders?.['eth']?.urls.join('\n'); const solRpcDefaultValue = chainProviders?.['sol']?.urls?.join('\n');
const solRpcDefaultValue = chainProviders?.['sol']?.urls.join('\n');
return ( return (
<div className={styles.blockchainProvidersSettings}> <div className={styles.blockchainProvidersSettings}>
@@ -136,14 +170,14 @@ const BlockchainProvidersSettings = ({ ethRpcRef, solRpcRef }: SettingsProps) =>
const P2pRPCSettings = ({ p2pRpcRef }: SettingsProps) => { const P2pRPCSettings = ({ p2pRpcRef }: SettingsProps) => {
const [showInfo, setShowInfo] = useState(false); const [showInfo, setShowInfo] = useState(false);
const account = useAccount(); const account = useAccount() as AccountShape | undefined;
const { plebbitOptions } = account || {}; const protocolOptions = getProtocolOptions(account);
const { plebbitRpcClientsOptions } = plebbitOptions || {}; const pkcRpcClientsOptions = getNodeRpcClientsOptions(protocolOptions);
return ( return (
<div className={styles.p2pRPCSettings}> <div className={styles.p2pRPCSettings}>
<div> <div>
<input type='text' defaultValue={plebbitRpcClientsOptions} ref={p2pRpcRef} autoCorrect='off' autoCapitalize='off' spellCheck='false' /> <input type='text' defaultValue={pkcRpcClientsOptions} ref={p2pRpcRef} autoCorrect='off' autoCapitalize='off' spellCheck='false' />
<button onClick={() => setShowInfo(!showInfo)}>{showInfo ? 'X' : '?'}</button> <button onClick={() => setShowInfo(!showInfo)}>{showInfo ? 'X' : '?'}</button>
</div> </div>
{showInfo && ( {showInfo && (
@@ -165,10 +199,10 @@ const P2pRPCSettings = ({ p2pRpcRef }: SettingsProps) => {
}; };
const P2pDataPathSettings = ({ p2pDataPathRef }: SettingsProps) => { const P2pDataPathSettings = ({ p2pDataPathRef }: SettingsProps) => {
const plebbitRpc = usePlebbitRpcSettings(); const pkcRpc = usePkcRpcSettings();
const { plebbitRpcSettings } = plebbitRpc || {}; const { pkcRpcSettings } = pkcRpc || {};
const isConnectedToRpc = plebbitRpc?.state === 'connected'; const isConnectedToRpc = pkcRpc?.state === 'connected';
const path = plebbitRpcSettings?.plebbitOptions?.dataPath || ''; const path = getRpcSettingsDataPath(pkcRpcSettings as RpcSettingsShape | undefined);
return ( return (
<div className={styles.p2pDataPathSettings}> <div className={styles.p2pDataPathSettings}>
@@ -183,8 +217,8 @@ const isElectron = window.electronApi?.isElectron === true;
const AdvancedSettings = () => { const AdvancedSettings = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const account = useAccount(); const account = useAccount() as AccountShape | undefined;
const { plebbitOptions } = account || {}; const protocolOptions = getProtocolOptions(account);
const ipfsGatewayUrlsRef = useRef<HTMLTextAreaElement>(null); const ipfsGatewayUrlsRef = useRef<HTMLTextAreaElement>(null);
const mediaIpfsGatewayUrlRef = useRef<HTMLInputElement>(null); const mediaIpfsGatewayUrlRef = useRef<HTMLInputElement>(null);
@@ -223,7 +257,7 @@ const AdvancedSettings = () => {
.map((url) => url.trim()) .map((url) => url.trim())
.filter((url) => url !== ''); .filter((url) => url !== '');
const plebbitRpcClientsOptions = p2pRpcRef.current?.value.trim() ? [p2pRpcRef.current.value.trim()] : undefined; const pkcRpcClientsOptions = p2pRpcRef.current?.value.trim() ? [p2pRpcRef.current.value.trim()] : undefined;
const dataPath = p2pDataPathRef.current?.value.trim() || undefined; const dataPath = p2pDataPathRef.current?.value.trim() || undefined;
const chainProviders: Record<string, { urls: string[] | undefined; chainId: number }> = {}; const chainProviders: Record<string, { urls: string[] | undefined; chainId: number }> = {};
@@ -238,13 +272,13 @@ const AdvancedSettings = () => {
await setAccount({ await setAccount({
...account, ...account,
mediaIpfsGatewayUrl, mediaIpfsGatewayUrl,
plebbitOptions: { pkcOptions: {
...plebbitOptions, ...protocolOptions,
ipfsGatewayUrls, ipfsGatewayUrls,
pubsubHttpClientsOptions, pubsubKuboRpcClientsOptions: pubsubHttpClientsOptions,
chainProviders, chainProviders,
httpRoutersOptions, httpRoutersOptions,
plebbitRpcClientsOptions, pkcRpcClientsOptions,
dataPath, dataPath,
}, },
}); });
@@ -76,10 +76,10 @@ describe('usePublishPost', () => {
expect(latestValue.publishPost).toBe(testState.publishCommentMock); expect(latestValue.publishPost).toBe(testState.publishCommentMock);
expect(latestValue.publishPostOptions).toMatchObject({ expect(latestValue.publishPostOptions).toMatchObject({
author: { displayName: 'Alice' }, author: { displayName: 'Alice' },
communityAddress: 'music.eth',
content: undefined, content: undefined,
link: undefined, link: undefined,
spoiler: true, spoiler: true,
subplebbitAddress: 'music.eth',
title: 'Hello world', title: 'Hello world',
}); });
expect(typeof latestValue.publishPostOptions.onChallengeVerification).toBe('function'); expect(typeof latestValue.publishPostOptions.onChallengeVerification).toBe('function');
@@ -104,13 +104,13 @@ describe('usePublishReply', () => {
expect(typeof latestValue.publishReply).toBe('function'); expect(typeof latestValue.publishReply).toBe('function');
expect(testState.lastPublishOptions).toMatchObject({ expect(testState.lastPublishOptions).toMatchObject({
author: { displayName: 'Bob' }, author: { displayName: 'Bob' },
communityAddress: 'music.eth',
content: 'Replying to >>12', content: 'Replying to >>12',
link: undefined, link: undefined,
parentCid: 'parent-cid', parentCid: 'parent-cid',
postCid: 'parent-cid', postCid: 'parent-cid',
quotedCids: ['quoted-cid'], quotedCids: ['quoted-cid'],
spoiler: true, spoiler: true,
subplebbitAddress: 'music.eth',
}); });
}); });
+11 -2
View File
@@ -32,7 +32,6 @@ const usePublishPost = ({ communityAddress: requestedCommunityAddress, subplebbi
const createBaseOptions = useCallback(() => { const createBaseOptions = useCallback(() => {
const baseOptions: Comment = { const baseOptions: Comment = {
communityAddress, communityAddress,
subplebbitAddress: communityAddress,
title, title,
content, content,
link, link,
@@ -58,7 +57,17 @@ const usePublishPost = ({ communityAddress: requestedCommunityAddress, subplebbi
{} as Partial<Comment>, {} as Partial<Comment>,
); );
const newOptions = { ...baseOptions, ...sanitizedOptions }; const {
communityAddress: nextCommunityAddress,
subplebbitAddress: legacyCommunityAddress,
...restOptions
} = sanitizedOptions as Partial<Comment> & { subplebbitAddress?: string };
const resolvedCommunityAddress = nextCommunityAddress ?? legacyCommunityAddress ?? baseOptions.communityAddress;
const newOptions = {
...baseOptions,
...restOptions,
...(resolvedCommunityAddress ? { communityAddress: resolvedCommunityAddress } : {}),
};
setPublishPostStore(newOptions); setPublishPostStore(newOptions);
}, },
[createBaseOptions, setPublishPostStore], [createBaseOptions, setPublishPostStore],
+11 -2
View File
@@ -50,7 +50,6 @@ const usePublishReply = ({ cid, communityAddress: requestedCommunityAddress, sub
const createBaseOptions = useCallback(() => { const createBaseOptions = useCallback(() => {
const baseOptions: Comment = { const baseOptions: Comment = {
communityAddress, communityAddress,
subplebbitAddress: communityAddress,
parentCid, parentCid,
postCid: postCid ?? parentCid, postCid: postCid ?? parentCid,
content, content,
@@ -77,7 +76,17 @@ const usePublishReply = ({ cid, communityAddress: requestedCommunityAddress, sub
{} as Partial<Comment>, {} as Partial<Comment>,
); );
const newOptions = { ...baseOptions, ...sanitizedOptions }; const {
communityAddress: nextCommunityAddress,
subplebbitAddress: legacyCommunityAddress,
...restOptions
} = sanitizedOptions as Partial<Comment> & { subplebbitAddress?: string };
const resolvedCommunityAddress = nextCommunityAddress ?? legacyCommunityAddress ?? baseOptions.communityAddress;
const newOptions = {
...baseOptions,
...restOptions,
...(resolvedCommunityAddress ? { communityAddress: resolvedCommunityAddress } : {}),
};
setPublishReplyStore(newOptions); setPublishReplyStore(newOptions);
}, },
[createBaseOptions, setPublishReplyStore], [createBaseOptions, setPublishReplyStore],
@@ -7,6 +7,8 @@ describe('buildEditableAccountJson', () => {
id: 'abc', id: 'abc',
name: 'Account 1', name: 'Account 1',
author: { address: '0x123', shortAddress: '0x1...3', avatar: { url: 'https://example.com' } }, author: { address: '0x123', shortAddress: '0x1...3', avatar: { url: 'https://example.com' } },
pkc: { someOption: true },
pkcReactOptions: { foo: 'baz' },
plebbit: { someOption: true }, plebbit: { someOption: true },
karma: 42, karma: 42,
plebbitReactOptions: { foo: 'bar' }, plebbitReactOptions: { foo: 'bar' },
@@ -17,6 +19,8 @@ describe('buildEditableAccountJson', () => {
expect(result.account.name).toBe('Account 1'); expect(result.account.name).toBe('Account 1');
expect(result.account.author.address).toBe('0x123'); expect(result.account.author.address).toBe('0x123');
expect(result.account.author.avatar).toBeUndefined(); expect(result.account.author.avatar).toBeUndefined();
expect(result.account.pkc).toBeUndefined();
expect(result.account.pkcReactOptions).toBeUndefined();
expect(result.account.plebbit).toBeUndefined(); expect(result.account.plebbit).toBeUndefined();
expect(result.account.karma).toBeUndefined(); expect(result.account.karma).toBeUndefined();
expect(result.account.plebbitReactOptions).toBeUndefined(); expect(result.account.plebbitReactOptions).toBeUndefined();
@@ -50,6 +50,18 @@ describe('challenge-utils', () => {
expect(alertMock).toHaveBeenCalledWith('Error from unknown-board.eth: first error second error'); expect(alertMock).toHaveBeenCalledWith('Error from unknown-board.eth: first error second error');
}); });
it('uses communityAddress when the upgraded publication no longer exposes subplebbitAddress', () => {
alertChallengeVerificationFailed(
{
challengeErrors: ['first error'],
challengeSuccess: false,
} as never,
{ communityAddress: 'business-and-finance.bso' },
);
expect(alertMock).toHaveBeenCalledWith('Error from /biz/: first error');
});
it('warns about invalid challenge error payloads and falls back to an unknown error', () => { it('warns about invalid challenge error payloads and falls back to an unknown error', () => {
alertChallengeVerificationFailed( alertChallengeVerificationFailed(
{ {
+4
View File
@@ -4,6 +4,8 @@ type AccountLike = {
id?: string; id?: string;
name?: string; name?: string;
author?: { address?: string; shortAddress?: string; avatar?: unknown }; author?: { address?: string; shortAddress?: string; avatar?: unknown };
pkc?: unknown;
pkcReactOptions?: unknown;
plebbit?: unknown; plebbit?: unknown;
karma?: unknown; karma?: unknown;
plebbitReactOptions?: unknown; plebbitReactOptions?: unknown;
@@ -19,6 +21,8 @@ export const buildEditableAccountJson = (account: AccountLike | undefined): stri
account: { account: {
...account, ...account,
author: { ...account?.author, avatar: undefined }, author: { ...account?.author, avatar: undefined },
pkc: undefined,
pkcReactOptions: undefined,
plebbit: undefined, plebbit: undefined,
karma: undefined, karma: undefined,
plebbitReactOptions: undefined, plebbitReactOptions: undefined,
+6 -5
View File
@@ -2,13 +2,13 @@ import { ChallengeVerification } from '@bitsocialnet/bitsocial-react-hooks';
import directoriesData from '../../data/5chan-directories.json'; import directoriesData from '../../data/5chan-directories.json';
import { getBoardPath } from './route-utils'; import { getBoardPath } from './route-utils';
const resolveBoardIdentifier = (subplebbitAddress: unknown): string => { const resolveBoardIdentifier = (communityAddress: unknown): string => {
if (typeof subplebbitAddress !== 'string' || !subplebbitAddress) { if (typeof communityAddress !== 'string' || !communityAddress) {
return 'unknown board'; return 'unknown board';
} }
const boardPath = getBoardPath(subplebbitAddress, directoriesData.communities); const boardPath = getBoardPath(communityAddress, directoriesData.communities);
return boardPath === subplebbitAddress ? subplebbitAddress : `/${boardPath}/`; return boardPath === communityAddress ? communityAddress : `/${boardPath}/`;
}; };
export const alertChallengeVerificationFailed = (challengeVerification: ChallengeVerification, publication: any) => { export const alertChallengeVerificationFailed = (challengeVerification: ChallengeVerification, publication: any) => {
@@ -35,8 +35,9 @@ export const alertChallengeVerificationFailed = (challengeVerification: Challeng
} }
const finalMessage = errorMessages.filter(Boolean).join(' '); const finalMessage = errorMessages.filter(Boolean).join(' ');
const publicationCommunityAddress = publication?.communityAddress || publication?.subplebbitAddress;
alert(`Error from ${resolveBoardIdentifier(publication?.subplebbitAddress)}: ${finalMessage || 'unknown error'}`); alert(`Error from ${resolveBoardIdentifier(publicationCommunityAddress)}: ${finalMessage || 'unknown error'}`);
} else { } else {
console.log('Challenge verification succeeded:', challengeVerification); console.log('Challenge verification succeeded:', challengeVerification);
} }
+2 -1
View File
@@ -51,7 +51,7 @@ describe('publish stores', () => {
expect(state.displayName).toBe('Poster Alias'); expect(state.displayName).toBe('Poster Alias');
expect(state.author).toEqual({ address: '0x123', role: 'mod', displayName: 'Poster Alias' }); expect(state.author).toEqual({ address: '0x123', role: 'mod', displayName: 'Poster Alias' });
expect(state.publishCommentOptions.author).toEqual({ address: '0x123', role: 'mod', displayName: 'Poster Alias' }); expect(state.publishCommentOptions.author).toEqual({ address: '0x123', role: 'mod', displayName: 'Poster Alias' });
expect(state.publishCommentOptions.subplebbitAddress).toBe('music-posting.eth'); expect(state.publishCommentOptions.communityAddress).toBe('music-posting.eth');
expect(state.publishCommentOptions.title).toBe('Hello'); expect(state.publishCommentOptions.title).toBe('Hello');
state.publishCommentOptions.onChallengeVerification?.({} as never, comment); state.publishCommentOptions.onChallengeVerification?.({} as never, comment);
@@ -82,6 +82,7 @@ describe('publish stores', () => {
const state = usePublishReplyStore.getState(); const state = usePublishReplyStore.getState();
expect(state.displayName['parent-1']).toBe('Reply Alias'); expect(state.displayName['parent-1']).toBe('Reply Alias');
expect(state.author['parent-1']).toEqual({ address: '0x123', role: 'mod', displayName: 'Reply Alias' }); expect(state.author['parent-1']).toEqual({ address: '0x123', role: 'mod', displayName: 'Reply Alias' });
expect(state.publishCommentOptions['parent-1']?.communityAddress).toBe('music-posting.eth');
expect(state.publishCommentOptions['parent-1']?.parentCid).toBe('parent-1'); expect(state.publishCommentOptions['parent-1']?.parentCid).toBe('parent-1');
expect(state.publishCommentOptions['parent-1']?.postCid).toBe('parent-1'); expect(state.publishCommentOptions['parent-1']?.postCid).toBe('parent-1');
-1
View File
@@ -40,7 +40,6 @@ const usePublishPostStore = create<SubmitState>((set) => ({
const publishCommentOptions: PublishCommentOptions = { const publishCommentOptions: PublishCommentOptions = {
communityAddress, communityAddress,
subplebbitAddress: communityAddress,
title, title,
content, content,
link, link,
-1
View File
@@ -35,7 +35,6 @@ const usePublishReplyStore = create<ReplyState>((set) => ({
const publishCommentOptions: PublishCommentOptions = { const publishCommentOptions: PublishCommentOptions = {
communityAddress, communityAddress,
subplebbitAddress: communityAddress,
parentCid, parentCid,
postCid: comment?.postCid || parentCid, postCid: comment?.postCid || parentCid,
content, content,
@@ -71,6 +71,8 @@ vi.mock('react-ace', async () => {
vi.mock('ace-builds/src-noconflict/mode-json', () => ({})); vi.mock('ace-builds/src-noconflict/mode-json', () => ({}));
vi.mock('ace-builds/src-noconflict/theme-monokai', () => ({})); vi.mock('ace-builds/src-noconflict/theme-monokai', () => ({}));
vi.mock('ace-builds/esm-resolver', () => ({}));
vi.mock('ace-builds/src-noconflict/worker-json?url', () => ({ default: '/worker-json.js' }));
let root: Root; let root: Root;
let container: HTMLDivElement; let container: HTMLDivElement;
@@ -7,12 +7,29 @@ import styles from './account-data-editor.module.css';
const DEFAULT_RETURN_TO = '/subs/settings#account-settings'; const DEFAULT_RETURN_TO = '/subs/settings#account-settings';
type AceModuleLoadResult = {
Editor: React.ComponentType<any>;
onBeforeLoad: (ace: { config?: { setModuleUrl?: (name: string, value: string) => void } }) => void;
};
const loadAce = async () => { const loadAce = async () => {
const aceModule = await import('react-ace'); const [aceModule, workerJsonModule] = await Promise.all([
await Promise.all([import('ace-builds/src-noconflict/mode-json'), import('ace-builds/src-noconflict/theme-monokai')]); import('react-ace'),
import('ace-builds/src-noconflict/worker-json?url'),
import('ace-builds/esm-resolver'),
import('ace-builds/src-noconflict/mode-json'),
import('ace-builds/src-noconflict/theme-monokai'),
]);
// Vite CJS interop can double-wrap the default export // Vite CJS interop can double-wrap the default export
const mod = aceModule.default; const mod = aceModule.default;
return typeof mod === 'function' ? mod : (mod as unknown as { default: typeof mod }).default; const Editor = typeof mod === 'function' ? mod : (mod as unknown as { default: typeof mod }).default;
return {
Editor,
onBeforeLoad: (ace) => {
ace.config?.setModuleUrl?.('ace/mode/json_worker', workerJsonModule.default);
},
} satisfies AceModuleLoadResult;
}; };
const AccountDataEditor = () => { const AccountDataEditor = () => {
@@ -25,17 +42,20 @@ const AccountDataEditor = () => {
const [phase, setPhase] = useState<'warning' | 'loading' | 'editor' | 'fallback'>('warning'); const [phase, setPhase] = useState<'warning' | 'loading' | 'editor' | 'fallback'>('warning');
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const [AceEditor, setAceEditor] = useState<React.ComponentType<any> | null>(null); const [AceEditor, setAceEditor] = useState<React.ComponentType<any> | null>(null);
const [aceOnBeforeLoad, setAceOnBeforeLoad] = useState<AceModuleLoadResult['onBeforeLoad'] | undefined>(undefined);
const [text, setText] = useState(''); const [text, setText] = useState('');
useEffect(() => { useEffect(() => {
if (phase !== 'loading') return; if (phase !== 'loading') return;
loadAce() loadAce()
.then((Editor) => { .then(({ Editor, onBeforeLoad }) => {
setAceEditor(() => Editor); setAceEditor(() => Editor);
setAceOnBeforeLoad(() => onBeforeLoad);
setText(buildEditableAccountJson(account)); setText(buildEditableAccountJson(account));
setPhase('editor'); setPhase('editor');
}) })
.catch(() => { .catch(() => {
setAceOnBeforeLoad(undefined);
setText(buildEditableAccountJson(account)); setText(buildEditableAccountJson(account));
setPhase('fallback'); setPhase('fallback');
}); });
@@ -94,7 +114,17 @@ const AccountDataEditor = () => {
{phase === 'fallback' && <div className={styles.fallbackWarning}>{t('editor_fallback_warning')}</div>} {phase === 'fallback' && <div className={styles.fallbackWarning}>{t('editor_fallback_warning')}</div>}
<div className={styles.editorContainer}> <div className={styles.editorContainer}>
{phase === 'editor' && AceEditor ? ( {phase === 'editor' && AceEditor ? (
<AceEditor mode='json' theme='monokai' width='100%' height='500px' fontSize={13} showPrintMargin={false} value={text} onChange={setText} /> <AceEditor
mode='json'
theme='monokai'
width='100%'
height='500px'
fontSize={13}
showPrintMargin={false}
value={text}
onChange={setText}
onBeforeLoad={aceOnBeforeLoad}
/>
) : ( ) : (
<textarea <textarea
value={text} value={text}
+3 -3
View File
@@ -199,9 +199,9 @@ export default defineConfig({
'node:events': 'events', 'node:events': 'events',
'node:process': 'process', 'node:process': 'process',
'node:stream': 'stream-browserify', 'node:stream': 'stream-browserify',
'node:util': 'util', 'node:util': 'util/',
'util/': 'util', 'util/': 'util/',
util: 'util', util: 'util/',
}, },
}, },
server: { server: {
+114 -1106
View File
File diff suppressed because it is too large Load Diff