import type { Component } from "svelte"; import type { SvelteHTMLElements } from "svelte/elements"; import AtSign from "$icon/at-sign"; import Ban from "$icon/ban"; import CircleDot from "$icon/circle-dot"; import GitMerge from "$icon/git-merge"; import GitPullRequestClosed from "$icon/git-pull-request-closed"; import GitPullRequestCreate from "$icon/git-pull-request-create"; import MessageSquare from "$icon/message-square"; import Star from "$icon/star"; import UserPlus from "$icon/user-plus"; import UserRoundMinus from "$icon/user-round-minus"; import UserRoundArrowForward from "$lib/components/icons/UserRoundArrowForward.svelte"; import type { NotificationCategory, NotificationGroups, NotificationSummary, NotificationType } from "./types"; type IconComponent = Component; export const SOCIAL_TYPES: NotificationType[] = ["repo_starred", "followed"]; const CATEGORIES: Record = { repo_starred: "social", followed: "social", issue_created: "work", issue_commented: "work", issue_closed: "work", issue_reopen: "work", issue_assigned: "work", issue_unassigned: "work", pull_created: "work", pull_commented: "work", pull_merged: "work", pull_closed: "work", pull_reopen: "work", pull_assigned: "work", pull_unassigned: "work", user_mentioned: "work" }; const ICONS: Record = { repo_starred: Star, followed: UserPlus, issue_created: CircleDot, issue_commented: MessageSquare, issue_closed: Ban, issue_reopen: CircleDot, issue_assigned: UserRoundArrowForward, issue_unassigned: UserRoundMinus, pull_created: GitPullRequestCreate, pull_commented: MessageSquare, pull_merged: GitMerge, pull_closed: GitPullRequestClosed, pull_reopen: GitPullRequestCreate, pull_assigned: UserRoundArrowForward, pull_unassigned: UserRoundMinus, user_mentioned: AtSign }; // the design system has no purple, and a merged pull already reads as info on // PullStatePill, so merges land on the same token here. const TONES: Record = { repo_starred: "text-foreground-warning", followed: "text-foreground-muted", issue_created: "text-foreground-success", issue_commented: "text-foreground-muted", issue_closed: "text-foreground-muted", issue_reopen: "text-foreground-success", issue_assigned: "text-foreground-info", issue_unassigned: "text-foreground-muted", pull_created: "text-foreground-success", pull_commented: "text-foreground-muted", pull_merged: "text-foreground-info", pull_closed: "text-foreground-danger", pull_reopen: "text-foreground-success", pull_assigned: "text-foreground-info", pull_unassigned: "text-foreground-muted", user_mentioned: "text-foreground-info" }; const PHRASES: Record = { repo_starred: "starred", followed: "followed you", issue_created: "opened an issue on", issue_commented: "commented on an issue on", issue_closed: "closed an issue on", issue_reopen: "reopened an issue on", issue_assigned: "assigned you to an issue on", issue_unassigned: "unassigned you from an issue on", pull_created: "created a PR on", pull_commented: "commented on a PR on", pull_merged: "merged a PR on", pull_closed: "closed a PR on", pull_reopen: "reopened a PR on", pull_assigned: "assigned you to a PR on", pull_unassigned: "unassigned you from a PR on", user_mentioned: "mentioned you in" }; export const notificationCategory = (type: NotificationType): NotificationCategory => CATEGORIES[type]; export const notificationIcon = (type: NotificationType): IconComponent => ICONS[type]; export const notificationTone = (type: NotificationType): string => TONES[type]; export const notificationPhrase = (notification: NotificationSummary): string => { if (notification.type === "user_mentioned" && notification.subject) { return notification.subject.kind === "issue" ? "mentioned you on an issue in" : "mentioned you on a pull request in"; } return PHRASES[notification.type]; }; // the subject of the phrase, which the appview prints as owner/name after it export const notificationTarget = (notification: NotificationSummary): string => notification.repo ? `${notification.repo.ownerHandle}/${notification.repo.name}` : ""; export const notificationHref = (notification: NotificationSummary): string => { if (notification.type === "followed") return `/${notification.actor.handle}`; const repo = notification.repo; if (!repo) return `/${notification.actor.handle}`; const base = `/${repo.ownerHandle}/${repo.name}`; const subject = notification.subject; if (!subject) return base; return `${base}/${subject.kind === "issue" ? "issues" : "pulls"}/${subject.uri}`; }; // today, the six days before it, then everything older, same buckets the go page // builds in GroupNotificationsByDate export const groupNotificationsByDate = ( notifications: NotificationSummary[], now: Date = new Date() ): NotificationGroups => { const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime(); const weekStart = todayStart - 6 * 24 * 60 * 60 * 1000; const groups: NotificationGroups = { today: [], thisWeek: [], older: [] }; for (const notification of notifications) { const created = Date.parse(notification.createdAt); if (created >= todayStart) groups.today.push(notification); else if (created >= weekStart) groups.thisWeek.push(notification); else groups.older.push(notification); } return groups; }; export const isEmpty = (groups: NotificationGroups): boolean => groups.today.length === 0 && groups.thisWeek.length === 0 && groups.older.length === 0;