// ui.jsx — shared atoms: Logo, Avatar, Stars, Chip, primary button etc.
const COLORS = {
bg: '#0B0B0B',
bg2: '#141414',
card: '#1A1A1A',
card2: '#222222',
border: 'rgba(212,175,55,0.14)',
borderSoft: 'rgba(255,255,255,0.06)',
text: '#FFFFFF',
text2: 'rgba(255,255,255,0.65)',
text3: 'rgba(255,255,255,0.4)',
gold: '#D4AF37',
goldDark: '#B6932B',
goldSoft: 'rgba(212,175,55,0.14)',
green: '#34D399',
red: '#F87171',
};
// Logo: T inside shield, gold gradient
function TrustaLogo({ size = 64, mark = true }) {
const id = React.useId();
return (
);
}
function Wordmark({ size = 28, color = '#D4AF37', tracking = 0.32 }) {
return (
TRUSTA
);
}
function Avatar({ src, size = 40, ring = false, verified = false }) {
return (
);
}
function Stars({ value = 5, size = 12, count = true }) {
return (
{[1, 2, 3, 4, 5].map(i => (
))}
);
}
function GoldButton({ children, onClick, disabled, full = true, style = {} }) {
return (
);
}
function GhostButton({ children, onClick, full = true, style = {} }) {
return (
);
}
// Bottom tab bar (5 tabs, center is FAB-style "+")
function TabBar({ active, onChange, t }) {
// Badge unread sur l'onglet Alerts
const [unread, setUnread] = React.useState(() =>
(window.TrustaNotifStore && window.TrustaNotifStore.getUnread()) || 0
);
React.useEffect(() => {
if (!window.TrustaNotifStore) return;
setUnread(window.TrustaNotifStore.getUnread());
return window.TrustaNotifStore.subscribe(setUnread);
}, []);
const Icon = ({ name, fill }) => {
const c = fill;
switch (name) {
case 'home': return ;
case 'chat': return ;
case 'alert': return ;
case 'profile': return ;
}
};
const tabs = [
{ id: 'home', label: t('nav_home') },
{ id: 'chat', label: t('nav_chat') },
{ id: 'post', label: '' }, // FAB
{ id: 'alert', label: t('nav_alerts') },
{ id: 'profile', label: t('nav_profile') },
];
return (
{tabs.map(tab => {
if (tab.id === 'post') {
return (
);
}
const isActive = active === tab.id;
const c = isActive ? COLORS.gold : COLORS.text3;
const showBadge = tab.id === 'alert' && unread > 0;
return (
);
})}
);
}
// Top header (custom, replaces IOSNavBar)
function Header({ left, right, title, sub, dark = true, onBack, big = false }) {
return (
{onBack ? (
) : left}
{right}
);
}
// === HeartButton — favoris (cœur, état synchronisé via TrustaFavStore) ===
function HeartButton({ kind, id, size = 22, style = {}, onChange }) {
const numId = Number(id || 0);
const [fav, setFav] = React.useState(() =>
!!(window.TrustaFavStore && window.TrustaFavStore.isFav(kind, numId))
);
const [busy, setBusy] = React.useState(false);
React.useEffect(() => {
if (!window.TrustaFavStore) return;
const update = () => setFav(window.TrustaFavStore.isFav(kind, numId));
update();
return window.TrustaFavStore.subscribe(update);
}, [kind, numId]);
const handleClick = async (e) => {
if (e) { e.stopPropagation(); e.preventDefault(); }
if (busy || !numId || !window.TrustaFavStore) return;
setBusy(true);
try {
const newVal = await window.TrustaFavStore.toggle(kind, numId);
if (onChange) onChange(newVal);
} catch (err) {
console.error('toggle fav failed', err);
} finally {
setBusy(false);
}
};
const fillColor = fav ? COLORS.gold : 'transparent';
const strokeColor = fav ? COLORS.gold : 'rgba(255,255,255,0.85)';
return (
);
}
window.HeartButton = HeartButton;
window.COLORS = COLORS;
window.TrustaLogo = TrustaLogo;
window.Wordmark = Wordmark;
window.Avatar = Avatar;
window.Stars = Stars;
window.GoldButton = GoldButton;
window.GhostButton = GhostButton;
window.TabBar = TabBar;
window.Header = Header;