mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(subplebbit): resolve aliased board metadata in subplebbit selectors
This commit is contained in:
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user