-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathbackground.ts
155 lines (121 loc) · 3.97 KB
/
background.ts
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
155
/** Translation rule for the link text. */
export interface Rule {
/** Regular expression to match the URL. */
url: string;
/** Regular expression to match the title. */
search: string;
/** Replacement pattern for the matched title. */
replace: string;
};
/** A generic key type to bind some type to ordinary string key. */
export class ConfigKey<T> {
/** Unique identifier of the key */
public id: string;
constructor(id: string) {
this.id = id;
}
}
/** Version of entire configuration state. */
export const keyVersion = new ConfigKey<number>("version");
/** Translation rules. */
export const keyRules = new ConfigKey<Rule[]>("rules");
/** Type-safe wrapper around extension storage. */
export class ConfigStore {
/** Get a value from storage, with fallback value. */
async get<T>(key: ConfigKey<T>, fallback: T): Promise<T> {
// Specify key to to a partial query.
const result = await chrome.storage.sync.get({ [key.id]: fallback });
return result[key.id] as T;
}
/** Set a value in storage. */
async set<T>(key: ConfigKey<T>, value: T) {
// Set is always a partial update.
await chrome.storage.sync.set({ [key.id]: value });
}
};
function delay<T>(t: number, val: T): Promise<T> {
return new Promise((resolve) => setTimeout(resolve, t, val));
}
async function displayBadge(tab: chrome.tabs.Tab): Promise<void> {
await chrome.action.setBadgeText({
text: "Done",
tabId: tab.id,
});
await delay(1500, null);
await chrome.action.setBadgeText({
text: "",
tabId: tab.id,
});
}
async function generateLink(url: string, title: string): Promise<string> {
const store = new ConfigStore();
const rules = await store.get(keyRules, []);
for (const rule of rules) {
const regexUrl = new RegExp(rule.url);
if (!regexUrl.test(url)) continue;
const regexSearch = new RegExp(rule.search);
if (!regexSearch.test(title)) continue;
title = title.replace(regexSearch, rule.replace);
break;
}
return "[" + title + "](" + url + ")";
}
chrome.action.onClicked.addListener(function (tab) {
if (!tab.id || tab.id === chrome.tabs.TAB_ID_NONE) {
return;
}
copyMarkdownLink(tab);
});
async function copyMarkdownLink(tab: chrome.tabs.Tab): Promise<void> {
const link = await generateLink(tab.url!, tab.title!);
// Execute script in the tab to copy the link to clipboard.
// The function and argument must be serializable.
await chrome.scripting.executeScript({
target: { tabId: tab.id! },
func: (...rest): void => {
// `func` required to be () => void, load argument manually.
const link = (rest as unknown[])[0] as string;
// Clipboard is only available in tabs, not in background script.
navigator.clipboard.writeText(link);
},
args: [link],
});
await displayBadge(tab);
}
/** Configuration schema of the extension. */
interface Config {
/** Version of the configuration. */
version: number;
/** Translation rules. */
rules: Rule[];
}
interface MigrateFunc {
(config: Config): Config;
};
async function migration() {
const steps: MigrateFunc[] = [
(config) => {
if (config.version >= 1) {
return config;
}
config.version = 1;
config.rules = [];
return config;
},
];
const store = new ConfigStore();
const initial: Config = {
version: await store.get(keyVersion, 0),
rules: await store.get(keyRules, []),
};
const result = steps.reduce((config, step) => step(config), initial);
await Promise.all([
store.set(keyVersion, result.version),
store.set(keyRules, result.rules),
]);
}
chrome.runtime.onInstalled.addListener(() => {
console.log("Extension started");
migration();
console.log("Migration done");
});