The useNotificationStore is a Pinia store that manages in-app notifications, browser notifications, team invites, and tournament team invites for the 5Stack platform.
Overview
This store handles:
- In-app notification management
- Browser (system) notifications
- Team invitations
- Tournament team invitations
- Notification permissions
Import
import { useNotificationStore } from '~/stores/NotificationStore';
Usage
const notificationStore = useNotificationStore();
// Check if there are unread notifications
if (notificationStore.hasNotifications) {
console.log('You have new notifications');
}
// Send a browser notification
await notificationStore.sendNotification(
'Match Found',
'match-ready',
{ body: 'Your match is ready to start!' }
);
State
Notification Settings
Whether the user has granted browser notification permissions.
Whether browser notifications are currently enabled for the user.
Notifications
Array of in-app notifications. Includes unread notifications and read notifications from the last 7 days.
Pending invitations to join teams.
Pending invitations to join tournament teams.
Methods
setupNotifications()
Requests browser notification permission from the user.
async setupNotifications(): Promise<void>
Example:
await notificationStore.setupNotifications();
if (notificationStore.notificationsGranted) {
console.log('Notifications enabled');
}
sendNotification()
Sends a browser notification to the user.
async sendNotification(
title: string,
tag: string,
options: NotificationOptions,
force?: boolean
): Promise<void>
A unique tag for the notification (replaces existing notifications with same tag)
options
NotificationOptions
required
Standard browser NotificationOptions object (body, icon, etc.)
If true, shows notification even when the page is visible
Behavior:
- Only sends notifications if
notificationsEnabled is true
- By default, doesn’t send if the page is visible (unless
force is true)
- Only sends if browser permission is granted
- Automatically adds the 5Stack icon to the notification
Example:
await notificationStore.sendNotification(
'Match Ready',
'match-confirmation',
{
body: 'Your match has been confirmed. Click to join!',
requireInteraction: true,
tag: 'match-123',
}
);
Computed Properties
Returns true if there are:
- Unread in-app notifications, OR
- Pending team invites, OR
- Pending tournament team invites
Notification Type
type Notification = {
id: string;
title: string;
message: string;
steam_id: string;
type: string;
entity_id: string;
is_read: boolean;
created_at: string;
actions?: Array<{
graphql: {
type: string;
action: string;
selection: Record<string, any>;
variables?: Record<string, any>;
};
}>;
};
Real-time Subscriptions
The store automatically subscribes to:
- In-app notifications (unread + last 7 days of read)
- Team invitations
- Tournament team invitations
Subscriptions are initialized automatically when the user is authenticated.
Example: Notification Badge
const notificationStore = useNotificationStore();
const notificationCount = computed(() => {
let count = 0;
// Count unread notifications
count += notificationStore.notifications.filter(n => !n.is_read).length;
// Count team invites
count += notificationStore.team_invites.length;
// Count tournament team invites
count += notificationStore.tournament_team_invites.length;
return count;
});
const showBadge = computed(() => notificationStore.hasNotifications);
Example: Team Invites List
const notificationStore = useNotificationStore();
const teamInvitesList = computed(() => {
return notificationStore.team_invites.map(invite => ({
id: invite.id,
teamName: invite.team.name,
invitedBy: invite.invited_by.name,
invitedByAvatar: invite.invited_by.avatar_url,
createdAt: new Date(invite.created_at),
}));
});
Example: Match Found Notification
const notificationStore = useNotificationStore();
function notifyMatchFound(matchId: string, confirmedCount: number, totalPlayers: number) {
notificationStore.sendNotification(
'Match Found!',
`match-${matchId}`,
{
body: `${confirmedCount}/${totalPlayers} players confirmed. Click to accept!`,
requireInteraction: true,
tag: matchId,
},
true // Force notification even if page is visible
);
}
Example: Notification Actions
const notificationStore = useNotificationStore();
const notificationsWithActions = computed(() => {
return notificationStore.notifications
.filter(n => !n.is_read && n.actions && n.actions.length > 0)
.map(notification => ({
id: notification.id,
title: notification.title,
message: notification.message,
type: notification.type,
actions: notification.actions?.map(action => ({
type: action.graphql.type,
action: action.graphql.action,
})),
}));
});
Notification Retention
The store queries notifications with the following retention policy:
- All unread notifications
- Read notifications from the last 7 days
- Deleted notifications are excluded
Browser Notification Best Practices
- Call
setupNotifications() in response to user action (e.g., button click)
- Use unique
tag values to prevent notification spam
- Set
requireInteraction: true for important notifications (e.g., match confirmations)
- Use
force: true sparingly, only for critical notifications
- The store automatically prevents notifications when the page is visible (unless forced)