Skip to main content
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

notificationsGranted
Ref<boolean>
Whether the user has granted browser notification permissions.
notificationsEnabled
Ref<boolean>
Whether browser notifications are currently enabled for the user.

Notifications

notifications
Ref<Notification[]>
Array of in-app notifications. Includes unread notifications and read notifications from the last 7 days.
team_invites
Ref<Array>
Pending invitations to join teams.
tournament_team_invites
Ref<Array>
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>
title
string
required
The notification title
tag
string
required
A unique tag for the notification (replaces existing notifications with same tag)
options
NotificationOptions
required
Standard browser NotificationOptions object (body, icon, etc.)
force
boolean
default:"false"
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

hasNotifications
ComputedRef<boolean>
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)

Build docs developers (and LLMs) love