Misc bugs around dm region scope + scope display, and flood-scope leak

This commit is contained in:
Jack Kingsman
2026-07-08 20:11:57 -07:00
parent db954c0f26
commit 0633ab724c
9 changed files with 251 additions and 53 deletions
+9 -7
View File
@@ -7,7 +7,7 @@ import { ChannelFloodScopeOverrideModal } from './ChannelFloodScopeOverrideModal
import { ChannelPathHashModeOverrideModal } from './ChannelPathHashModeOverrideModal';
import { handleKeyboardActivate } from '../utils/a11y';
import { isPublicChannelKey } from '../utils/publicChannel';
import { stripRegionScopePrefix } from '../utils/regionScope';
import { stripRegionScopePrefix, floodScopeOverrideLabel } from '../utils/regionScope';
import { isPrefixOnlyContact } from '../utils/pubkey';
import { cn } from '../lib/utils';
import { ContactAvatar } from './ContactAvatar';
@@ -102,7 +102,9 @@ export function ChatHeader({
const activeFloodScopeLabel = activeFloodScopeOverride
? stripRegionScopePrefix(activeFloodScopeOverride)
: null;
const activeFloodScopeDisplay = activeFloodScopeOverride ? activeFloodScopeOverride : null;
// Badge text: maps the raw override ("*", "#Region", null) to a friendly label
// so the unscoped marker renders as "unscoped" instead of a bare "*".
const activeFloodScopeBadge = floodScopeOverrideLabel(activeFloodScopeOverride);
const activePathHashModeOverride =
conversation.type === 'channel' ? (activeChannel?.path_hash_mode_override ?? null) : null;
const showPathHashModeOverride =
@@ -256,7 +258,7 @@ export function ChatHeader({
</span>
)}
</span>
{conversation.type === 'channel' && activeFloodScopeDisplay && (
{conversation.type === 'channel' && activeFloodScopeBadge && (
<button
className="mt-0.5 flex basis-full items-center gap-1 text-left sm:hidden"
onClick={handleEditFloodScopeOverride}
@@ -268,7 +270,7 @@ export function ChatHeader({
aria-hidden="true"
/>
<span className="min-w-0 truncate text-[0.6875rem] font-medium text-[hsl(var(--region-override))]">
{activeFloodScopeDisplay}
{activeFloodScopeBadge}
</span>
</button>
)}
@@ -445,9 +447,9 @@ export function ChatHeader({
className={`h-4 w-4 ${activeFloodScopeLabel ? 'text-[hsl(var(--region-override))]' : 'text-muted-foreground'}`}
aria-hidden="true"
/>
{activeFloodScopeDisplay && (
{activeFloodScopeBadge && (
<span className="hidden text-[0.6875rem] font-medium text-[hsl(var(--region-override))] sm:inline">
{activeFloodScopeDisplay}
{activeFloodScopeBadge}
</span>
)}
</button>
@@ -513,7 +515,7 @@ export function ChatHeader({
open={channelOverrideOpen}
onClose={() => setChannelOverrideOpen(false)}
roomName={conversation.name}
currentOverride={activeFloodScopeDisplay}
currentOverride={activeFloodScopeOverride}
onSetOverride={(value) => onSetChannelFloodScopeOverride(conversation.id, value)}
/>
)}
+59
View File
@@ -0,0 +1,59 @@
import { describe, it, expect } from 'vitest';
import {
UNSCOPED_OVERRIDE_MARKER,
isUnscopedMarker,
stripRegionScopePrefix,
floodScopeOverrideLabel,
} from '../utils/regionScope';
describe('regionScope helpers', () => {
describe('isUnscopedMarker', () => {
it('recognizes the unscoped marker', () => {
expect(isUnscopedMarker(UNSCOPED_OVERRIDE_MARKER)).toBe(true);
expect(isUnscopedMarker('*')).toBe(true);
});
it('rejects regions, null, undefined, and empty', () => {
expect(isUnscopedMarker('#Esperance')).toBe(false);
expect(isUnscopedMarker('Esperance')).toBe(false);
expect(isUnscopedMarker(null)).toBe(false);
expect(isUnscopedMarker(undefined)).toBe(false);
expect(isUnscopedMarker('')).toBe(false);
});
});
describe('stripRegionScopePrefix', () => {
it('strips a leading hash', () => {
expect(stripRegionScopePrefix('#Esperance')).toBe('Esperance');
});
it('leaves unprefixed values (incl. the marker) unchanged', () => {
expect(stripRegionScopePrefix('Esperance')).toBe('Esperance');
expect(stripRegionScopePrefix('*')).toBe('*');
});
it('returns empty string for nullish input', () => {
expect(stripRegionScopePrefix(null)).toBe('');
expect(stripRegionScopePrefix(undefined)).toBe('');
expect(stripRegionScopePrefix('')).toBe('');
});
});
describe('floodScopeOverrideLabel', () => {
it('returns null when there is no override (inherit)', () => {
expect(floodScopeOverrideLabel(null)).toBeNull();
expect(floodScopeOverrideLabel(undefined)).toBeNull();
expect(floodScopeOverrideLabel('')).toBeNull();
});
it('maps the unscoped marker to a friendly label, not a bare "*"', () => {
expect(floodScopeOverrideLabel(UNSCOPED_OVERRIDE_MARKER)).toBe('unscoped');
expect(floodScopeOverrideLabel('*')).toBe('unscoped');
});
it('passes a region name through unchanged', () => {
expect(floodScopeOverrideLabel('#Esperance')).toBe('#Esperance');
expect(floodScopeOverrideLabel('Esperance')).toBe('Esperance');
});
});
});
+9
View File
@@ -11,3 +11,12 @@ export function stripRegionScopePrefix(scope: string | null | undefined): string
if (!scope) return '';
return scope.startsWith('#') ? scope.slice(1) : scope;
}
// Compact, human-readable label for a channel's persisted flood-scope override,
// suitable for a header badge. Returns null when there is no override (inherit),
// "unscoped" for the unscoped marker, and the region name as-is otherwise.
export function floodScopeOverrideLabel(scope: string | null | undefined): string | null {
if (!scope) return null;
if (isUnscopedMarker(scope)) return 'unscoped';
return scope;
}