fix(subplebbit): resolve aliased board metadata in subplebbit selectors

This commit is contained in:
plebeius
2026-03-10 12:47:38 +08:00
parent 2c3ef23dc7
commit a862852e53
2 changed files with 110 additions and 2 deletions
@@ -0,0 +1,90 @@
import * as React from 'react';
import { createElement } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { useStableSubplebbit, useSubplebbitField } from '../use-stable-subplebbit';
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
const testState = vi.hoisted(() => ({
subplebbits: {} as Record<string, unknown>,
}));
vi.mock('@bitsocialnet/bitsocial-react-hooks/dist/stores/subplebbits', () => ({
default: (selector: (state: { subplebbits: typeof testState.subplebbits }) => unknown) =>
selector({
subplebbits: testState.subplebbits,
}),
}));
let latestValue: unknown;
let container: HTMLDivElement;
let root: Root;
let renderCount = 0;
const HookHarness = ({ useValue }: { useValue: () => unknown }) => {
latestValue = useValue();
return null;
};
const renderHookValue = (useValue: () => unknown) => {
act(() => {
root.render(createElement(HookHarness, { key: renderCount++, useValue }));
});
return latestValue;
};
describe('use-stable-subplebbit', () => {
beforeEach(() => {
latestValue = undefined;
renderCount = 0;
testState.subplebbits = {};
container = document.createElement('div');
document.body.appendChild(container);
root = createRoot(container);
});
afterEach(() => {
act(() => root.unmount());
container.remove();
});
it('resolves alias board addresses when the store key uses a different suffix', () => {
testState.subplebbits = {
'international-sfw.bso': {
address: 'international-sfw.bso',
roles: {
'plebeius.eth': { role: 'owner' },
},
title: '/int/ - International',
},
};
expect(renderHookValue(() => useSubplebbitField('international-sfw.eth', (subplebbit) => subplebbit?.roles))).toEqual({
'plebeius.eth': { role: 'owner' },
});
expect(renderHookValue(() => useStableSubplebbit('international-sfw.eth'))).toMatchObject({
address: 'international-sfw.bso',
title: '/int/ - International',
});
});
it('prefers an exact key match when both exact and alias variants are present', () => {
testState.subplebbits = {
'business.eth': {
address: 'business.eth',
title: '/biz/ - Exact',
},
'business.bso': {
address: 'business.bso',
title: '/biz/ - Alias',
},
};
expect(renderHookValue(() => useSubplebbitField('business.eth', (subplebbit) => subplebbit?.title))).toBe('/biz/ - Exact');
});
});
+20 -2
View File
@@ -1,4 +1,22 @@
import useSubplebbitsStore from '@bitsocialnet/bitsocial-react-hooks/dist/stores/subplebbits';
import { normalizeBoardAddress } from './use-directories';
const getSubplebbitByAddress = (subplebbits: Record<string, any> | undefined, subplebbitAddress: string | undefined) => {
if (!subplebbits || !subplebbitAddress) {
return undefined;
}
const exactMatch = subplebbits[subplebbitAddress];
if (exactMatch) {
return exactMatch;
}
const normalizedAddress = normalizeBoardAddress(subplebbitAddress);
return Object.entries(subplebbits).find(([key, subplebbit]) => {
const candidateAddress = typeof subplebbit?.address === 'string' ? subplebbit.address : key;
return normalizeBoardAddress(candidateAddress) === normalizedAddress;
})?.[1];
};
/**
* Shallow compare two objects by keys and values.
@@ -45,7 +63,7 @@ const isSubplebbitEqual = (prev: any, next: any): boolean => {
*/
export const useStableSubplebbit = (subplebbitAddress: string | undefined) => {
// Use selector with custom equality to ignore transient state
const subplebbit = useSubplebbitsStore((state) => (subplebbitAddress ? state.subplebbits[subplebbitAddress] : undefined), isSubplebbitEqual);
const subplebbit = useSubplebbitsStore((state) => getSubplebbitByAddress(state.subplebbits, subplebbitAddress), isSubplebbitEqual);
return subplebbit;
};
@@ -61,7 +79,7 @@ export const useStableSubplebbit = (subplebbitAddress: string | undefined) => {
export const useSubplebbitField = <T>(subplebbitAddress: string | undefined, selector: (subplebbit: any) => T): T | undefined => {
const field = useSubplebbitsStore(
(state) => {
const subplebbit = subplebbitAddress ? state.subplebbits[subplebbitAddress] : undefined;
const subplebbit = getSubplebbitByAddress(state.subplebbits, subplebbitAddress);
return subplebbit ? selector(subplebbit) : undefined;
},
(prev, next) => prev === next,