refactor(ui): rename topbar to boardsbar and add desktop footer for board, thread, and catalog

This commit is contained in:
plebeius
2026-02-23 16:47:51 +08:00
parent a7721fc252
commit 4433834c1c
39 changed files with 672 additions and 294 deletions
@@ -0,0 +1,162 @@
.backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.25);
z-index: 999;
}
.boardsbarEditDialog {
position: absolute;
width: 400px;
max-height: 80vh;
top: 50%;
left: 50%;
margin-left: -200px;
transform: translateY(-50%);
padding: 2px;
background-color: var(--settings-modal-background-color);
border-top: var(--settings-modal-border-top);
border-right: var(--settings-modal-border-right);
border-bottom: var(--settings-modal-border-bottom);
border-left: var(--settings-modal-border-left);
box-shadow: rgba(0, 0, 0, 0.25) 0px 0px 5px 0px;
font-size: var(--body-font-size);
line-height: normal;
z-index: 1000;
display: flex;
flex-direction: column;
}
@media (max-width: 600px) {
.boardsbarEditDialog {
width: calc(100% - 20px);
margin-left: -50%;
left: 50%;
max-width: 600px;
}
}
.hd {
position: relative;
padding: 0px 0px 5px;
margin: 5px 0px;
text-align: center;
border-bottom: var(--settings-modal-header-border-bottom);
}
.hd h2 {
margin: 0;
padding: 0;
font-size: 16px;
font-weight: 700;
line-height: 14px;
height: 14px;
}
.closeButton {
all: unset;
cursor: pointer;
width: 18px;
height: 18px;
position: absolute;
top: -2px;
right: 3px;
background-image: var(--settings-modal-close-button-background-image);
background-size: 100%;
background-position: center;
background-repeat: no-repeat;
image-rendering: pixelated;
}
.bd {
overflow-y: auto;
flex: 1;
padding: 0;
margin: 0;
}
.section {
margin-bottom: 0;
}
.section:last-child {
margin-bottom: 0;
}
.section h3 {
margin: 0 0 8px 0;
padding: 0;
font-size: calc(var(--body-font-size) * 1.077);
font-weight: bold;
letter-spacing: 0.3px;
}
.directoryInput {
width: 100%;
padding: 2px 4px 3px;
font-size: 13.3333px;
margin: 0 0 5px;
box-sizing: border-box;
outline: none;
font-family: Arial, sans-serif;
border: var(--post-form-field-input-border, revert);
background-color: var(--post-form-field-input-background-color, revert);
color: var(--post-form-field-input-color);
}
.directoryInput:focus {
outline: none;
border: var(--post-form-field-input-focus-border, revert);
}
.checkboxGroup {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
}
.checkboxItem {
display: flex;
align-items: center;
gap: 4px;
margin-bottom: 5px;
}
.checkboxItem input[type='checkbox'] {
cursor: pointer;
}
.checkboxItem label {
cursor: pointer;
font-size: var(--body-font-size);
user-select: none;
}
.editSubscriptionsLink {
font-size: var(--body-font-size);
color: var(--post-link-text-color);
text-decoration: var(--post-content-link-text-decoration);
cursor: pointer;
}
.editSubscriptionsWrapper {
display: inline-flex;
align-items: center;
gap: 0;
}
.editSubscriptionsLink:hover {
color: var(--post-link-text-color-hover);
text-decoration: var(--post-content-link-text-decoration-hover);
}
.boardsbarEditFooter {
padding: 0;
margin: 0;
text-align: center;
padding-bottom: 5px;
}
@@ -0,0 +1,139 @@
import { useState, useMemo } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useAccount } from '@plebbit/plebbit-react-hooks';
import useBoardsBarEditModalStore from '../../stores/use-boardsbar-edit-modal-store';
import useBoardsBarVisibilityStore from '../../stores/use-boardsbar-visibility-store';
import { getAllBoardCodes } from '../../constants/board-codes';
import styles from './boardsbar-edit-modal.module.css';
const directoriesToString = (dirs: Set<string>): string => Array.from(dirs).sort().join(' ');
const stringToDirectories = (str: string): Set<string> => {
const codes = str
.trim()
.split(/\s+/)
.filter((code) => code.length > 0)
.map((code) => code.toLowerCase());
return new Set(codes);
};
// Form component keyed by store values so it remounts with fresh state when modal reopens
const BoardsBarEditModalForm = ({
visibleDirectories,
showSubscriptionsInBoardsBar,
setDirectoryVisibility,
setShowSubscriptionsInBoardsBar,
closeBoardsBarEditModal,
subscriptions,
location,
}: {
visibleDirectories: Set<string>;
showSubscriptionsInBoardsBar: boolean;
setDirectoryVisibility: (code: string, visible: boolean) => void;
setShowSubscriptionsInBoardsBar: (show: boolean) => void;
closeBoardsBarEditModal: () => void;
subscriptions: string[];
location: { pathname: string };
}) => {
const allBoardCodes = useMemo(() => getAllBoardCodes(), []);
const allVisible = allBoardCodes.every((code) => visibleDirectories.has(code));
const [localDirectoryInput, setLocalDirectoryInput] = useState(() => (allVisible ? '' : directoriesToString(visibleDirectories)));
const [showSubscriptions, setShowSubscriptions] = useState(() => showSubscriptionsInBoardsBar);
const handleSave = () => {
if (localDirectoryInput.trim() === '') {
allBoardCodes.forEach((code) => setDirectoryVisibility(code, true));
} else {
const inputDirectories = stringToDirectories(localDirectoryInput);
allBoardCodes.forEach((code) => setDirectoryVisibility(code, inputDirectories.has(code)));
}
setShowSubscriptionsInBoardsBar(showSubscriptions);
closeBoardsBarEditModal();
};
return (
<>
<div className={styles.section}>
<input
type='text'
className={styles.directoryInput}
placeholder='Example: jp tg mu'
aria-label='Directory codes'
value={localDirectoryInput}
onChange={(e) => setLocalDirectoryInput(e.target.value)}
/>
</div>
{subscriptions.length > 0 && (
<div className={styles.section}>
<div className={styles.checkboxItem}>
<input type='checkbox' id='show-subscriptions' checked={showSubscriptions} onChange={(e) => setShowSubscriptions(e.target.checked)} />
<label htmlFor='show-subscriptions'>show subscriptions</label>
<span className={styles.editSubscriptionsWrapper}>
(
<Link
to={location.pathname.replace(/\/$/, '') + '/settings#subscriptions-settings'}
className={styles.editSubscriptionsLink}
onClick={(e) => {
e.stopPropagation();
closeBoardsBarEditModal();
}}
>
edit subscriptions
</Link>
)
</span>
</div>
</div>
)}
<div className={styles.boardsbarEditFooter}>
<button onClick={handleSave}>Save</button>
</div>
</>
);
};
const BoardsBarEditModal = () => {
const { showModal, closeBoardsBarEditModal } = useBoardsBarEditModalStore();
const { visibleDirectories, showSubscriptionsInBoardsBar, setDirectoryVisibility, setShowSubscriptionsInBoardsBar } = useBoardsBarVisibilityStore();
const account = useAccount();
const subscriptions = useMemo(() => account?.subscriptions || [], [account?.subscriptions]);
const location = useLocation();
if (!showModal) {
return null;
}
const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {
if (e.target === e.currentTarget) {
closeBoardsBarEditModal();
}
};
const formKey = `${directoriesToString(visibleDirectories)}-${showSubscriptionsInBoardsBar}`;
return (
<div className={styles.backdrop} onClick={handleBackdropClick}>
<div className={styles.boardsbarEditDialog}>
<div className={styles.hd}>
<h2>Custom Board List</h2>
<button className={styles.closeButton} onClick={closeBoardsBarEditModal} title='Close' />
</div>
<div className={styles.bd}>
<BoardsBarEditModalForm
key={formKey}
visibleDirectories={visibleDirectories}
showSubscriptionsInBoardsBar={showSubscriptionsInBoardsBar}
setDirectoryVisibility={setDirectoryVisibility}
setShowSubscriptionsInBoardsBar={setShowSubscriptionsInBoardsBar}
closeBoardsBarEditModal={closeBoardsBarEditModal}
subscriptions={subscriptions}
location={location}
/>
</div>
</div>
</div>
);
};
export default BoardsBarEditModal;
@@ -0,0 +1,13 @@
// Types for boardsbar edit modal component
export interface BoardCodeCheckbox {
code: string;
label: string;
checked: boolean;
}
export interface SubscriptionCheckbox {
address: string;
displayName: string;
checked: boolean;
}
@@ -0,0 +1 @@
export { default } from './boardsbar-edit-modal';