~james/core

core/web/src/lib/components/notifications/notification.ts -rw-r--r-- 5.3 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 15 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import { isDid } from "@atcute/lexicons/syntax";
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 { profileIdentifier } from "#lib/api/identity.ts";
import type {
	NotificationCategory,
	NotificationSummary,
	NotificationType
} from "#lib/components/notifications/types.ts";

type IconComponent = Component<SvelteHTMLElements["svg"]>;

export const SOCIAL_TYPES: NotificationType[] = ["repo_starred", "followed"];

const CATEGORIES: Record<NotificationType, NotificationCategory> = {
	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<NotificationType, IconComponent> = {
	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
// TicketStatePill, so merges land on the same token here.
const TONES: Record<NotificationType, string> = {
	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<NotificationType, string> = {
	repo_starred: "starred",
	followed: "followed you",
	issue_created: "opened an issue",
	issue_commented: "commented on an issue",
	issue_closed: "closed an issue",
	issue_reopen: "reopened an issue",
	issue_assigned: "assigned you to an issue",
	issue_unassigned: "unassigned you from an issue",
	pull_created: "created a PR",
	pull_commented: "commented on a PR",
	pull_merged: "merged a PR",
	pull_closed: "closed a PR",
	pull_reopen: "reopened a PR",
	pull_assigned: "assigned you to a PR",
	pull_unassigned: "unassigned you from a PR",
	user_mentioned: "mentioned you"
};

export const notificationCategory = (type: NotificationType): NotificationCategory =>
	CATEGORIES[type];

export const notificationIcon = (type: NotificationType): IconComponent => ICONS[type];

export const notificationTone = (type: NotificationType): string => TONES[type];

const basePhrase = (notification: NotificationSummary): string => {
	if (notification.type === "user_mentioned" && notification.subject) {
		return notification.subject.kind === "issue"
			? "mentioned you on an issue"
			: "mentioned you on a pull request";
	}
	return PHRASES[notification.type];
};

const preposition = (type: NotificationType): string => {
	if (type === "repo_starred") return "";
	return type === "user_mentioned" ? " in" : " on";
};

export const notificationPhrase = (
	notification: NotificationSummary,
	leadingRepo = false
): string => {
	const phrase = basePhrase(notification);
	return leadingRepo && notification.repo ? phrase + preposition(notification.type) : phrase;
};

export const notificationTarget = (
	notification: NotificationSummary,
	includingOwner = true
): string => {
	const repo = notification.repo;
	if (!repo) return "";
	return includingOwner ? `${repo.ownerHandle}/${repo.name}` : repo.name;
};

export const notificationHref = (notification: NotificationSummary): string => {
	if (notification.type === "followed")
		return `/${profileIdentifier(notification.actor.handle, notification.actor.did)}`;
	const repo = notification.repo;
	if (!repo) return `/${profileIdentifier(notification.actor.handle, notification.actor.did)}`;
	// did-addressed repos have no name to route on; the did is their address, and
	// issues/pulls under them can't be addressed either
	if (isDid(repo.name)) return `/${repo.name}`;
	const base = `/${repo.ownerHandle}/${repo.name}`;
	const subject = notification.subject;
	if (!subject) return base;
	return `${base}/${subject.kind === "issue" ? "issues" : "pulls"}/${subject.number}`;
};