Skip to content

Commit ec3fbcb

Browse files
kyttavladimyr
andcommitted
WIP first draft of url builder
Closes #61 Co-authored-by: Dario Vladovic <[email protected]>
1 parent ded589c commit ec3fbcb

File tree

1 file changed

+166
-122
lines changed

1 file changed

+166
-122
lines changed

src/shareon.js

Lines changed: 166 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,193 @@
1+
/**
2+
* @typedef PublishPreset
3+
*
4+
* @property {string} url
5+
* @property {string} [title]
6+
* @property {string} [media]
7+
* @property {string} [text]
8+
* @property {string} [via]
9+
* @property {string} [hashtags]
10+
* @property {string} [fbAppId]
11+
* @property {string} [s2fInstance]
12+
*/
13+
14+
/**
15+
* Creates a URL that a button will point to.
16+
*
17+
* @param {string} baseUrl [description]
18+
* @param {Record<string, string | undefined>} parameters [description]
19+
* @return {string} [description]
20+
*/
21+
const buildUrl = (baseUrl, parameters) => {
22+
const url = new URL(baseUrl);
23+
for (const [parameter, value] of Object.entries(parameters)) {
24+
if (value) {
25+
url.searchParams.append(parameter, value);
26+
}
27+
}
28+
29+
return url.href;
30+
};
31+
132
// prettier-ignore
233
/**
334
* Map of social networks to their respective URL builders.
435
*
536
* The `d` argument of each builder is the object with the page metadata, such
637
* as page title, URL, author name, etc.
738
*
8-
* @type {{ [network: string]: (d: {
9-
* url: string,
10-
* title?: string,
11-
* media?: string,
12-
* text?: string,
13-
* via?: string,
14-
* fbAppId?: string,
15-
* s2fInstance?: string,
16-
* }) => string}}
39+
* @type {{ [network: string]: (d: PublishPreset) => URL}}
1740
*/
1841
const urlBuilderMap = {
19-
facebook: (d) => `https://www.facebook.com/sharer/sharer.php?u=${d.url}${d.hashtags ? `&hashtag=%23${d.hashtags.split('%2C')[0]}` : ''}`,
20-
fediverse: (d) => `https://${d.s2fInstance}/?text=${d.title}%0D%0A${d.url}${d.text ? `%0D%0A%0D%0A${d.text}` : ''}${d.via ? `%0D%0A%0D%0A${d.via}` : ''}`,
21-
email: (d) => `mailto:?subject=${d.title}&body=${d.url}`,
22-
linkedin: (d) => `https://www.linkedin.com/sharing/share-offsite/?url=${d.url}`,
23-
mastodon: (d) => `https://toot.kytta.dev/?text=${d.title}%0D%0A${d.url}${d.text ? `%0D%0A%0D%0A${d.text}` : ''}${d.via ? `%0D%0A%0D%0A${d.via}` : ''}`,
24-
messenger: (d) => `https://www.facebook.com/dialog/send?app_id=${d.fbAppId}&link=${d.url}&redirect_uri=${d.url}`,
25-
odnoklassniki: (d) => `https://connect.ok.ru/offer?url=${d.url}&title=${d.title}${d.media ? `&imageUrl=${d.media}` : ''}`,
26-
pinterest: (d) => `https://pinterest.com/pin/create/button/?url=${d.url}&description=${d.title}${d.media ? `&media=${d.media}` : ''}`,
27-
pocket: (d) => `https://getpocket.com/edit.php?url=${d.url}`,
28-
reddit: (d) => `https://www.reddit.com/submit?title=${d.title}&url=${d.url}`,
29-
teams: (d) => `https://teams.microsoft.com/share?href=${d.url}${d.text ? `&msgText=${d.text}` : ''}`,
30-
telegram: (d) => `https://telegram.me/share/url?url=${d.url}${d.text ? `&text=${d.text}` : ''}`,
31-
tumblr: (d) => `https://www.tumblr.com/widgets/share/tool?posttype=link${d.hashtags ? `&tags=${d.hashtags}` : ''}&title=${d.title}&content=${d.url}&canonicalUrl=${d.url}${d.text ? `&caption=${d.text}` : ''}${d.via ? `&show-via=${d.via}` : ''}`,
32-
twitter: (d) => `https://twitter.com/intent/tweet?url=${d.url}&text=${d.title}${d.via ? `&via=${d.via}` : ''}${d.hashtags ? `&hashtags=${d.hashtags}` : ''}`,
33-
viber: (d) => `viber://forward?text=${d.title}%0D%0A${d.url}${d.text ? `%0D%0A%0D%0A${d.text}` : ''}`,
34-
vkontakte: (d) => `https://vk.com/share.php?url=${d.url}&title=${d.title}${d.media ? `&image=${d.media}` : ''}`,
35-
whatsapp: (d) => `https://wa.me/?text=${d.title}%0D%0A${d.url}${d.text ? `%0D%0A%0D%0A${d.text}` : ''}`,
36-
};
37-
38-
const openUrl = (buttonUrl) => () => {
39-
window.open(buttonUrl, "_blank", "noopener,noreferrer");
42+
facebook: (d) => buildUrl('https://www.facebook.com/sharer/sharer.php', {
43+
u: d.url,
44+
hashtag: d.hashtags?.split(',')[0]
45+
}),
46+
fediverse: (d) => buildUrl(`https://${d.s2fInstance}`, {
47+
text: [d.title, d.url, d.via].filter(Boolean).join('\n\n'),
48+
}),
49+
email: (d) => buildUrl('mailto:', {
50+
subject: d.title,
51+
body: d.url,
52+
}),
53+
linkedin: (d) => buildUrl('https://www.linkedin.com/sharing/share-offsite', {
54+
url: d.url,
55+
}),
56+
mastodon: (d) => buildUrl(`https://${d.s2fInstance}`, {
57+
text: [d.title, d.url, d.via].filter(Boolean).join('\n\n'),
58+
}),
59+
messenger: (d) => buildUrl(`https://www.facebook.com/dialog/send`, {
60+
"app_id": d.fbAppId,
61+
"link": d.url,
62+
"redirect_uri": d.url,
63+
}),
64+
odnoklassniki: (d) => buildUrl('https://connect.ok.ru/offer', {
65+
url: d.url,
66+
title: d.title,
67+
imageUrl: d.media,
68+
}),
69+
pinterest: (d) => buildUrl('https://pinterest.com/pin/create/button', {
70+
url: d.url,
71+
description: d.title,
72+
media: d.media,
73+
}),
74+
pocket: (d) => buildUrl('https://getpocket.com/edit.php', {
75+
url: d.url,
76+
}),
77+
reddit: (d) => buildUrl('https://www.reddit.com/submit', {
78+
title: d.title,
79+
url: d.url
80+
}),
81+
teams: (d) => buildUrl('https://teams.microsoft.com/share', {
82+
href: d.url,
83+
msgText: d.text,
84+
}),
85+
telegram: (d) => buildUrl('https://telegram.me/share/url', {
86+
url: d.url,
87+
text: d.text,
88+
}),
89+
tumblr: (d) => buildUrl('https://www.tumblr.com/widgets/share/tool', {
90+
posttype: 'link',
91+
tags: d.hashtags,
92+
title: d.title,
93+
content: d.url,
94+
canonicalUrl: d.url,
95+
caption: d.text,
96+
"show-via": d.via,
97+
}),
98+
twitter: (d) => buildUrl('https://twitter.com/intent/tweet', {
99+
url: d.url,
100+
text: d.title,
101+
via: d.via,
102+
hashtags: d.hashtags,
103+
}),
104+
viber: (d) => buildUrl('viber://forward', {
105+
text: [d.title, d.url, d.text].filter(Boolean).join('\n\n')
106+
}),
107+
vkontakte: (d) => buildUrl('https://vk.com/share.php', {
108+
url: d.url,
109+
title: d.title,
110+
image: d.media,
111+
}),
112+
whatsapp: (d) => buildUrl('https://wa.me', {
113+
text: [d.title, d.url, d.text].filter(Boolean).join('\n\n')
114+
}),
40115
};
41116

42117
const init = () => {
43-
const shareonContainers = document.querySelectorAll(".shareon");
44-
45118
// iterate over <div class="shareon">
46-
for (const container of shareonContainers) {
119+
for (const container of document.querySelectorAll(".shareon")) {
47120
// iterate over children of <div class="shareon">
48121
for (const child of container.children) {
49-
if (child) {
50-
const classListLength = child.classList.length;
122+
if (!child) continue;
51123

52-
// iterate over classes of the child element
53-
for (let k = 0; k < classListLength; k += 1) {
54-
const cls = child.classList.item(k);
124+
// iterate over classes of the child element
125+
for (const cls of child.classList) {
126+
// if it's "Copy URL"
127+
if (cls === "copy-url") {
128+
child.addEventListener("click", () => {
129+
const url =
130+
child.dataset.url ||
131+
container.dataset.url ||
132+
window.location.href;
133+
navigator.clipboard.writeText(url);
134+
child.classList.add("done");
135+
setTimeout(() => {
136+
child.classList.remove("done");
137+
}, 1000);
138+
});
139+
break;
140+
}
55141

56-
// if it's "Copy URL"
57-
if (cls === "copy-url") {
58-
child.addEventListener("click", () => {
59-
const url =
60-
child.dataset.url ||
61-
container.dataset.url ||
62-
window.location.href;
63-
navigator.clipboard.writeText(url);
64-
child.classList.add("done");
65-
setTimeout(() => {
66-
child.classList.remove("done");
67-
}, 1000);
68-
});
69-
}
142+
// if it's "Print"
143+
if (cls === "print") {
144+
child.addEventListener("click", () => {
145+
window.print();
146+
});
147+
break;
148+
}
149+
150+
// if it's "Web Share"
151+
if (cls === "web-share") {
152+
const data = {
153+
title:
154+
child.dataset.title || container.dataset.title || document.title,
155+
text: child.dataset.text || container.dataset.text || "",
156+
url:
157+
child.dataset.url ||
158+
container.dataset.url ||
159+
window.location.href,
160+
};
70161

71-
// if it's "Print"
72-
if (cls === "print") {
162+
if (navigator.canShare?.(data)) {
73163
child.addEventListener("click", () => {
74-
window.print();
164+
navigator.share(data);
75165
});
166+
} else {
167+
child.style.display = "none";
76168
}
169+
}
77170

78-
// if it's "Web Share"
79-
if (cls === "web-share") {
80-
const data = {
81-
title:
82-
child.dataset.title ||
83-
container.dataset.title ||
84-
document.title,
85-
text: child.dataset.text || container.dataset.text || "",
86-
url:
87-
child.dataset.url ||
88-
container.dataset.url ||
89-
window.location.href,
90-
};
91-
92-
if (navigator.canShare && navigator.canShare(data)) {
93-
child.addEventListener("click", () => {
94-
navigator.share(data);
95-
});
96-
} else {
97-
child.style.display = "none";
98-
}
99-
}
100-
101-
// if it's one of the networks
102-
if (Object.prototype.hasOwnProperty.call(urlBuilderMap, cls)) {
103-
const preset = {
104-
url: encodeURIComponent(
105-
child.dataset.url ||
106-
container.dataset.url ||
107-
window.location.href,
108-
),
109-
title: encodeURIComponent(
110-
child.dataset.title ||
111-
container.dataset.title ||
112-
document.title,
113-
),
114-
media: encodeURIComponent(
115-
child.dataset.media || container.dataset.media || "",
116-
),
117-
text: encodeURIComponent(
118-
child.dataset.text || container.dataset.text || "",
119-
),
120-
via: encodeURIComponent(
121-
child.dataset.via || container.dataset.via || "",
122-
),
123-
hashtags: encodeURIComponent(
124-
child.dataset.hashtags || container.dataset.hashtags || "",
125-
),
126-
fbAppId: encodeURIComponent(
127-
child.dataset.fbAppId || container.dataset.fbAppId || "",
128-
),
129-
s2fInstance: encodeURIComponent(
130-
child.dataset.s2fInstance ||
131-
container.dataset.s2fInstance ||
132-
"s2f.kytta.dev",
133-
),
134-
};
135-
const url = urlBuilderMap[cls](preset);
136-
137-
if (child.tagName.toLowerCase() === "a") {
138-
child.setAttribute("href", url);
139-
child.setAttribute("rel", "noopener noreferrer");
140-
child.setAttribute("target", "_blank");
141-
} else {
142-
child.addEventListener("click", openUrl(url));
143-
}
171+
// if it's one of the networks
172+
if (Object.prototype.hasOwnProperty.call(urlBuilderMap, cls)) {
173+
const url = urlBuilderMap[cls]({
174+
url: window.location.href,
175+
title: document.title,
176+
s2fInstance: "s2f.kytta.dev",
177+
...container.dataset,
178+
...child.dataset,
179+
});
144180

145-
break; // once a network is detected we don't want to check further
181+
if (child.tagName.toLowerCase() === "a") {
182+
child.setAttribute("href", url);
183+
child.setAttribute("rel", "noopener noreferrer");
184+
child.setAttribute("target", "_blank");
185+
} else {
186+
child.addEventListener("click", () => {
187+
window.open(url, "_blank", "noopener,noreferrer");
188+
});
146189
}
190+
break; // once a network is detected we don't want to check further
147191
}
148192
}
149193
}

0 commit comments

Comments
 (0)