feat(topbar): add customizable visibility controls for directories and subscriptions

This commit is contained in:
plebeius
2025-11-07 18:20:11 +01:00
parent bae3d76c6f
commit 669bb5b181
16 changed files with 767 additions and 46 deletions
+9 -4
View File
@@ -33,12 +33,17 @@ const useDisclaimerModalStore = create<DisclaimerModalState>((set) => ({
targetAddress: null,
targetBoardPath: null,
showDisclaimerModal: (address: string, navigate: NavigateFunction, boardPath?: string) => {
showDisclaimerModal: (address: string, navigate: NavigateFunction, boardPath?: string | null) => {
// Check if user has already accepted the disclaimer
if (hasAcceptedDisclaimer()) {
// Navigate directly without showing modal
const path = boardPath || address;
navigate(`/${path}`);
// If there's a valid board path, navigate directly
if (boardPath) {
navigate(`/${boardPath}`);
return;
}
// If no board path (placeholder), don't navigate - we'll handle this separately
// Don't show disclaimer modal either since it's already accepted
// Caller should handle showing directory modal or other UI
return;
}
+25
View File
@@ -0,0 +1,25 @@
import { create } from 'zustand';
interface TopbarEditModalState {
showModal: boolean;
openTopbarEditModal: () => void;
closeTopbarEditModal: () => void;
}
const useTopbarEditModalStore = create<TopbarEditModalState>((set) => ({
showModal: false,
openTopbarEditModal: () => {
set({
showModal: true,
});
},
closeTopbarEditModal: () => {
set({
showModal: false,
});
},
}));
export default useTopbarEditModalStore;
+117
View File
@@ -0,0 +1,117 @@
import { create } from 'zustand';
import { getAllBoardCodes } from '../constants/board-codes';
const LOCALSTORAGE_KEY_DIRECTORIES = '5chan-topbar-directories-visible';
const LOCALSTORAGE_KEY_SUBSCRIPTIONS = '5chan-topbar-subscriptions-visible';
interface TopbarVisibilityState {
// Directory codes that are visible (all visible by default)
visibleDirectories: Set<string>;
// Subscription addresses that are visible in topbar (all hidden by default)
visibleSubscriptions: Set<string>;
// Actions
toggleDirectory: (code: string) => void;
toggleSubscription: (address: string) => void;
setDirectoryVisibility: (code: string, visible: boolean) => void;
setSubscriptionVisibility: (address: string, visible: boolean) => void;
// Initialize from localStorage
initialize: () => void;
}
const loadFromLocalStorage = (key: string, defaultValue: Set<string>): Set<string> => {
try {
const stored = localStorage.getItem(key);
if (stored) {
const array = JSON.parse(stored);
return new Set(array);
}
} catch (e) {
console.warn(`Failed to load ${key} from localStorage:`, e);
}
return defaultValue;
};
const saveToLocalStorage = (key: string, set: Set<string>) => {
try {
const array = Array.from(set);
localStorage.setItem(key, JSON.stringify(array));
} catch (e) {
console.warn(`Failed to save ${key} to localStorage:`, e);
}
};
const useTopbarVisibilityStore = create<TopbarVisibilityState>((set, get) => {
// Initialize with all directories visible by default
const allBoardCodes = getAllBoardCodes();
const defaultVisibleDirectories = new Set(allBoardCodes);
const defaultVisibleSubscriptions = new Set<string>();
return {
visibleDirectories: loadFromLocalStorage(LOCALSTORAGE_KEY_DIRECTORIES, defaultVisibleDirectories),
visibleSubscriptions: loadFromLocalStorage(LOCALSTORAGE_KEY_SUBSCRIPTIONS, defaultVisibleSubscriptions),
toggleDirectory: (code: string) => {
set((state) => {
const newSet = new Set(state.visibleDirectories);
if (newSet.has(code)) {
newSet.delete(code);
} else {
newSet.add(code);
}
saveToLocalStorage(LOCALSTORAGE_KEY_DIRECTORIES, newSet);
return { visibleDirectories: newSet };
});
},
toggleSubscription: (address: string) => {
set((state) => {
const newSet = new Set(state.visibleSubscriptions);
if (newSet.has(address)) {
newSet.delete(address);
} else {
newSet.add(address);
}
saveToLocalStorage(LOCALSTORAGE_KEY_SUBSCRIPTIONS, newSet);
return { visibleSubscriptions: newSet };
});
},
setDirectoryVisibility: (code: string, visible: boolean) => {
set((state) => {
const newSet = new Set(state.visibleDirectories);
if (visible) {
newSet.add(code);
} else {
newSet.delete(code);
}
saveToLocalStorage(LOCALSTORAGE_KEY_DIRECTORIES, newSet);
return { visibleDirectories: newSet };
});
},
setSubscriptionVisibility: (address: string, visible: boolean) => {
set((state) => {
const newSet = new Set(state.visibleSubscriptions);
if (visible) {
newSet.add(address);
} else {
newSet.delete(address);
}
saveToLocalStorage(LOCALSTORAGE_KEY_SUBSCRIPTIONS, newSet);
return { visibleSubscriptions: newSet };
});
},
initialize: () => {
// Load from localStorage on initialization
const directories = loadFromLocalStorage(LOCALSTORAGE_KEY_DIRECTORIES, defaultVisibleDirectories);
const subscriptions = loadFromLocalStorage(LOCALSTORAGE_KEY_SUBSCRIPTIONS, defaultVisibleSubscriptions);
set({
visibleDirectories: directories,
visibleSubscriptions: subscriptions,
});
},
};
});
export default useTopbarVisibilityStore;