Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/components/Announcements/Announcements.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,38 @@ button.sendButton:hover {
display: block;
}

.tabIconWrapper {
position: relative;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
}

.scheduleBadge {
position: absolute;
top: -6px;
right: -10px;
background: #f55151;
color: #fff;
border-radius: 999px;
font-size: 0.65rem;
font-weight: 700;
padding: 2px 6px;
min-width: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 2px solid #fff;
line-height: 1;
}

.tabNavItem.dark .scheduleBadge {
border-color: #14233a;
}

.tabLabel {
font-size: 0.75rem;
text-align: center;
Expand Down
22 changes: 16 additions & 6 deletions src/components/Announcements/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
import { faFacebook, faLinkedin, faMedium } from '@fortawesome/free-brands-svg-icons';
import ReactTooltip from 'react-tooltip';
import EmailPanel from './platforms/email';
import MyspaceAutoPoster from './platforms/myspace';
import PlatformScheduleBadge from './platforms/PlatformScheduleBadge';

function Announcements({ title, email: initialEmail }) {
const [activeTab, setActiveTab] = useState('email');
Expand Down Expand Up @@ -104,15 +106,19 @@ function Announcements({ title, email: initialEmail }) {
onClick={() => setActiveTab(id)}
aria-selected={activeTab === id}
>
<div className={styles.tabIcon}>
<div className={styles.tabIconWrapper}>
{customIconSrc ? (
<img src={customIconSrc} alt={`${label} icon`} className={styles.tabIcon} />
) : (
<FontAwesomeIcon
icon={icon}
className={styles.tabIcon}
style={{ width: '100%', height: '100%', color: getIconColor(id) }}
/>
)}
{id === 'myspace' && (
<PlatformScheduleBadge platform="myspace" className={styles.scheduleBadge} />
)}
</div>
<div className={styles.tabLabel}>{label}</div>
</NavLink>
Expand Down Expand Up @@ -165,11 +171,15 @@ function Announcements({ title, email: initialEmail }) {
'slashdot',
'blogger',
'truthsocial',
].map(platform => (
<TabPane tabId={platform} key={platform}>
<SocialMediaComposer platform={platform} />
</TabPane>
))}
].map(platform => {
const PlatformComposer =
platform === 'myspace' ? MyspaceAutoPoster : SocialMediaComposer;
return (
<TabPane tabId={platform} key={platform}>
<PlatformComposer platform={platform} />
</TabPane>
);
})}
</TabContent>
</div>
</div>
Expand Down
59 changes: 59 additions & 0 deletions src/components/Announcements/platforms/PlatformScheduleBadge.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useEffect, useState } from 'react';

const scheduleCounts = new Map();
const listeners = new Map();

const getScheduleCount = platform => scheduleCounts.get(platform) || 0;

const notify = (platform, count) => {
const platformListeners = listeners.get(platform);
if (!platformListeners) return;
platformListeners.forEach(listener => {
try {
listener(count);
} catch (error) {
// eslint-disable-next-line no-console
console.error('PlatformScheduleBadge listener error', error);
}
});
};

export const setPlatformScheduleCount = (platform, count) => {
if (!platform) return;
const normalizedCount = Math.max(0, Number.isFinite(count) ? count : 0);
const nextCount = Math.round(normalizedCount);
scheduleCounts.set(platform, nextCount);
notify(platform, nextCount);
};

const subscribeToScheduleCount = (platform, listener) => {
if (!listeners.has(platform)) listeners.set(platform, new Set());
const platformListeners = listeners.get(platform);
platformListeners.add(listener);
return () => {
platformListeners.delete(listener);
if (platformListeners.size === 0) {
listeners.delete(platform);
}
};
};

export const usePlatformScheduleCount = platform => {
const [count, setCount] = useState(() => getScheduleCount(platform));

useEffect(() => {
setCount(getScheduleCount(platform));
return subscribeToScheduleCount(platform, setCount);
}, [platform]);

return count;
};

const PlatformScheduleBadge = ({ platform, className }) => {
const count = usePlatformScheduleCount(platform);
if (!count) return null;
const displayValue = count > 99 ? '99+' : count;
return <span className={className}>{displayValue}</span>;
};

export default PlatformScheduleBadge;
Loading
Loading