Transform ticker into Matrix notification system

- Extended TickerItem interface with priority, actionable, notification, and invitation types
- Added Matrix-specific notification functions:
  - addSessionInvitation() - for session invites
  - addMessageNotification() - for new messages
  - addSessionJoined/Left() - for user activity
  - addConnectionStatus() - for Matrix connection state
  - addSessionWaiting() - for sessions awaiting participants
- Updated default items to show Matrix notifications instead of system stats
- Added demo notification system in AppLayout with realistic Matrix scenarios
- Focused route notifications on Matrix-related sections only
- Creates authentic collaborative workspace notification experience
This commit is contained in:
spencrmartin
2025-12-15 10:43:48 -05:00
parent 0fafc0f489
commit 3790f68134
2 changed files with 112 additions and 41 deletions
+33 -34
View File
@@ -328,9 +328,34 @@ const AppLayoutContent: React.FC<AppLayoutProps> = ({ setIsGoosehintsModalOpen }
return () => window.removeEventListener('open-sidecar-localhost', handler);
}, [sidecar]);
// Add ticker updates based on navigation changes and system events
// Add Matrix-related notifications and demo content
useEffect(() => {
// Simulate Matrix notifications for demo purposes
const demoNotifications = [
() => ticker.addSessionInvitation('project-gamma', 'david'),
() => ticker.addMessageNotification('eve', 'design-team', 2),
() => ticker.addSessionJoined('frank', 'standup-meeting'),
() => ticker.addSessionWaiting('code-review-session', 3),
() => ticker.addConnectionStatus('connected'),
];
// Add initial demo notification
setTimeout(() => {
const randomNotification = demoNotifications[Math.floor(Math.random() * demoNotifications.length)];
randomNotification();
}, 2000);
// Add periodic demo notifications
const interval = setInterval(() => {
const randomNotification = demoNotifications[Math.floor(Math.random() * demoNotifications.length)];
randomNotification();
}, 15000); // Every 15 seconds
return () => clearInterval(interval);
}, []); // Removed ticker from dependencies
// Add route change notifications
useEffect(() => {
// Update ticker when route changes
const routeNames: Record<string, string> = {
'/': 'hub',
'/pair': 'chat',
@@ -344,41 +369,15 @@ const AppLayoutContent: React.FC<AppLayoutProps> = ({ setIsGoosehintsModalOpen }
};
const currentRoute = routeNames[location.pathname] || 'unknown';
ticker.addItem({
text: `section: ${currentRoute}`,
type: 'info'
});
}, [location.pathname]); // Removed ticker from dependencies
// Add ticker updates for sidecar changes
useEffect(() => {
if (sidecar?.activeViews.length) {
// Only add route notifications for Matrix-related sections
if (currentRoute === 'sessions' || currentRoute === 'peers' || currentRoute === 'channels') {
ticker.addItem({
text: `sidecars active: ${sidecar.activeViews.length}`,
type: 'success'
text: `navigated to ${currentRoute}`,
type: 'info'
});
}
}, [sidecar?.activeViews.length]); // Removed ticker from dependencies
// Add periodic system status updates
useEffect(() => {
const interval = setInterval(() => {
const memoryUsage = Math.floor(Math.random() * 30) + 40; // Mock memory usage 40-70%
const activeConnections = Math.floor(Math.random() * 5) + 1; // Mock 1-6 connections
ticker.addItem({
text: `mem: ${memoryUsage}%`,
type: memoryUsage > 60 ? 'warning' : 'info'
});
ticker.addItem({
text: `connections: ${activeConnections}`,
type: 'neutral'
});
}, 30000); // Update every 30 seconds
return () => clearInterval(interval);
}, []); // Removed ticker from dependencies
}, [location.pathname]); // Removed ticker from dependencies
// Determine layout direction based on navigation position (only for push mode)
const isHorizontalNav = navigationPosition === 'top' || navigationPosition === 'bottom';
@@ -4,8 +4,10 @@ import { cn } from '../utils';
interface TickerItem {
id: string;
text: string;
type?: 'info' | 'warning' | 'error' | 'success' | 'neutral';
type?: 'info' | 'warning' | 'error' | 'success' | 'neutral' | 'notification' | 'invitation';
timestamp?: Date;
priority?: 'low' | 'medium' | 'high';
actionable?: boolean;
}
interface NotificationTickerProps {
@@ -16,12 +18,12 @@ interface NotificationTickerProps {
}
const defaultItems: TickerItem[] = [
{ id: '1', text: 'goose ai agent online', type: 'success' },
{ id: '2', text: 'system status: operational', type: 'info' },
{ id: '3', text: 'extensions loaded: 12', type: 'info' },
{ id: '4', text: 'last sync: 2 min ago', type: 'neutral' },
{ id: '5', text: 'memory usage: 45%', type: 'info' },
{ id: '6', text: 'active sessions: 3', type: 'success' },
{ id: '1', text: 'matrix: connected', type: 'success', priority: 'high' },
{ id: '2', text: 'session invitation from alice: project-alpha', type: 'invitation', priority: 'high', actionable: true },
{ id: '3', text: 'new message from bob in dev-team', type: 'notification', priority: 'medium', actionable: true },
{ id: '4', text: 'charlie joined project-beta', type: 'success', priority: 'low' },
{ id: '5', text: 'session waiting: code-review (2 participants)', type: 'invitation', priority: 'high', actionable: true },
{ id: '6', text: '3 new messages from team in general', type: 'notification', priority: 'medium', actionable: true },
];
export const NotificationTicker: React.FC<NotificationTickerProps> = ({
@@ -212,12 +214,82 @@ export const useNotificationTicker = () => {
});
}, [addItem]);
// Matrix-specific notification functions
const addSessionInvitation = useCallback((sessionName: string, inviterName: string) => {
addItem({
text: `session invitation from ${inviterName.toLowerCase()}: ${sessionName.toLowerCase()}`,
type: 'invitation',
priority: 'high',
actionable: true,
});
}, [addItem]);
const addMessageNotification = useCallback((senderName: string, sessionName: string, messageCount: number = 1) => {
const messageText = messageCount === 1
? `new message from ${senderName.toLowerCase()} in ${sessionName.toLowerCase()}`
: `${messageCount} new messages from ${senderName.toLowerCase()} in ${sessionName.toLowerCase()}`;
addItem({
text: messageText,
type: 'notification',
priority: 'medium',
actionable: true,
});
}, [addItem]);
const addSessionJoined = useCallback((userName: string, sessionName: string) => {
addItem({
text: `${userName.toLowerCase()} joined ${sessionName.toLowerCase()}`,
type: 'success',
priority: 'low',
});
}, [addItem]);
const addSessionLeft = useCallback((userName: string, sessionName: string) => {
addItem({
text: `${userName.toLowerCase()} left ${sessionName.toLowerCase()}`,
type: 'neutral',
priority: 'low',
});
}, [addItem]);
const addConnectionStatus = useCallback((status: 'connected' | 'disconnected' | 'reconnecting') => {
const statusMessages = {
connected: { text: 'matrix: connected', type: 'success' as const },
disconnected: { text: 'matrix: disconnected', type: 'error' as const },
reconnecting: { text: 'matrix: reconnecting...', type: 'warning' as const },
};
const { text, type } = statusMessages[status];
addItem({
text,
type,
priority: 'high',
});
}, [addItem]);
const addSessionWaiting = useCallback((sessionName: string, participantCount: number) => {
addItem({
text: `session waiting: ${sessionName.toLowerCase()} (${participantCount} participants)`,
type: 'invitation',
priority: 'high',
actionable: true,
});
}, [addItem]);
return {
items,
addItem,
removeItem,
clearItems,
updateSystemStatus,
// Matrix-specific functions
addSessionInvitation,
addMessageNotification,
addSessionJoined,
addSessionLeft,
addConnectionStatus,
addSessionWaiting,
};
};