Prevent local admin profile media from blurring during NSFW node updates

Hop-State: A_06FPE5MS0ZXE3ESZJJ27C98
Hop-Proposal: R_06FPE5KR2QGEVQD0MW8DSZR
Hop-Task: T_06FPE564WJTQYM53WG3W8P8
Hop-Attempt: AT_06FPE564WKTFF688KW6DM80
This commit is contained in:
2026-07-15 11:42:42 -07:00
committed by Hop
parent 30c917b3a9
commit a1841c51a7
3 changed files with 20 additions and 4 deletions
+7
View File
@@ -8,10 +8,12 @@ import { useToast } from '@/lib/contexts/ToastContext';
import { useAccentColor } from '@/lib/contexts/AccentColorContext';
import { getStorageProvider, MediaUploadError, uploadMediaFile } from '@/lib/stuffbox/browser-upload';
import { hasUnsavedChanges } from '@/lib/forms/dirty-state';
import { useRuntimeConfig } from '@/lib/contexts/ConfigContext';
export default function AdminPage() {
const { showToast } = useToast();
const { refreshAccentColor } = useAccentColor();
const { setNodeNsfw } = useRuntimeConfig();
const [isAdmin, setIsAdmin] = useState<boolean | null>(null);
const [loading, setLoading] = useState(false);
const [nodeSettings, setNodeSettings] = useState({
@@ -93,6 +95,11 @@ export default function AdminPage() {
savedNodeSettingsRef.current = payload;
const data = await res.json().catch(() => ({}));
if (data.node) {
// Keep the global local-node classification and the sidebar's
// node payload in the same React batch. Otherwise profile
// media briefly sees "remote NSFW node on a safe local node"
// and applies a blur until the next full config refresh.
setNodeNsfw(data.node.isNsfw === true);
window.dispatchEvent(new CustomEvent('synapsis:node-updated', { detail: data.node }));
}
showToast('Settings saved!', 'success');
+5 -2
View File
@@ -4,6 +4,7 @@ import { useState, useEffect } from 'react';
import Link from 'next/link';
import { AvatarImage } from './AvatarImage';
import { ProfileBanner } from './ProfileBanner';
import { useRuntimeConfig } from '@/lib/contexts/ConfigContext';
interface Admin {
handle: string;
@@ -34,6 +35,8 @@ function formatNetworkTotal(value: number | undefined): string {
}
export function RightSidebar() {
const { config } = useRuntimeConfig();
const localNodeIsNsfw = config?.isNsfw ?? false;
const fallbackDescription = process.env.NEXT_PUBLIC_NODE_DESCRIPTION || 'A swarm social network node.';
const [nodeInfo, setNodeInfo] = useState<NodeInfo>({
name: process.env.NEXT_PUBLIC_NODE_NAME || 'Synapsis Node',
@@ -138,7 +141,7 @@ export function RightSidebar() {
{nodeInfo.bannerUrl && (
<ProfileBanner
url={nodeInfo.bannerUrl}
nodeIsNsfw={nodeInfo.isNsfw}
nodeIsNsfw={localNodeIsNsfw}
height={140}
borderBottom="1px solid var(--border)"
/>
@@ -187,7 +190,7 @@ export function RightSidebar() {
style={{ display: 'flex', alignItems: 'center', gap: '10px', textDecoration: 'none', color: 'inherit' }}
>
<div className="avatar avatar-sm" style={{ flexShrink: 0 }}>
<AvatarImage avatarUrl={admin.avatarUrl} seed={admin.handle} isNsfw={admin.isNsfw} nodeIsNsfw={nodeInfo.isNsfw} alt={admin.displayName || admin.handle} />
<AvatarImage avatarUrl={admin.avatarUrl} seed={admin.handle} isNsfw={admin.isNsfw} nodeIsNsfw={localNodeIsNsfw} alt={admin.displayName || admin.handle} />
</div>
<div>
<div style={{ fontWeight: 500, fontSize: '14px' }}>
+8 -2
View File
@@ -1,6 +1,6 @@
'use client';
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { createContext, useCallback, useContext, useEffect, useState, ReactNode } from 'react';
interface RuntimeConfig {
domain: string;
@@ -10,17 +10,23 @@ interface RuntimeConfig {
interface ConfigContextType {
config: RuntimeConfig | null;
isLoading: boolean;
setNodeNsfw: (isNsfw: boolean) => void;
}
const ConfigContext = createContext<ConfigContextType>({
config: null,
isLoading: true,
setNodeNsfw: () => { },
});
export function ConfigProvider({ children }: { children: ReactNode }) {
const [config, setConfig] = useState<RuntimeConfig | null>(null);
const [isLoading, setIsLoading] = useState(true);
const setNodeNsfw = useCallback((isNsfw: boolean) => {
setConfig((current) => current ? { ...current, isNsfw } : current);
}, []);
useEffect(() => {
// Fetch runtime config on mount
fetch('/api/config')
@@ -44,7 +50,7 @@ export function ConfigProvider({ children }: { children: ReactNode }) {
}, []);
return (
<ConfigContext.Provider value={{ config, isLoading }}>
<ConfigContext.Provider value={{ config, isLoading, setNodeNsfw }}>
{children}
</ConfigContext.Provider>
);