feature: improved embeds
This commit is contained in:
parent
609699e4b0
commit
f2066310d4
5 changed files with 757 additions and 45 deletions
|
@ -185,10 +185,9 @@
|
|||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 640px;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 20%;
|
||||
}
|
||||
|
||||
#talk .embed-container.iframe {
|
||||
}
|
||||
|
||||
#talk .embed-container .embed-actions-container {
|
||||
|
@ -208,14 +207,23 @@
|
|||
}
|
||||
|
||||
#talk .embed-container.short {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 7.5%;
|
||||
}
|
||||
|
||||
#talk .embed-container.letterbox {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 33%;
|
||||
}
|
||||
|
||||
#talk .embed-container.square {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 50%;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<style>
|
||||
<!-- #include file="./talk.css" -->
|
||||
</style>
|
||||
<script src="/js/external/mimetypes.js" type="text/javascript"></script>
|
||||
<script src="/js/external/punycode.js" type="text/javascript"></script>
|
||||
<script src="/tabs/talk/talk.js" type="text/javascript"></script>
|
||||
<div class="sidebar resizable">
|
||||
|
|
|
@ -1,35 +1,37 @@
|
|||
const URL_MATCHING_REGEX =
|
||||
/(?:(?<protocol>[a-zA-Z]+):\/\/)?(?:(?<auth>(?<username>\S.+)\:(?<password>.+))\@)?(?<host>(?:(?<hostname>[-a-zA-Z0-9\.]+)\.)?(?<domain>[-a-zA-Z0-9]+?\.(?<tld>[-a-zA-Z0-9]{2,64}))(?:\:(?<port>[0-9]{1,6}))?)\b(?<path>[-a-zA-Z0-9@:%_{}\[\]<>\(\)\+.~&\/="]*)(?:\?(?<query>[a-zA-Z0-9!$%&<>()*+,-\.\/\:\;\=\?\@_~"]+))?(?:#(?<hash>[a-zA-Z0-9!$&'()*+,-\.\/\:\;\=\?\@_~"]*?))?/gm;
|
||||
// wow https://stackoverflow.com/questions/3891641/regex-test-only-works-every-other-time
|
||||
// watch out for places we need to set `lastIndex` ... :frown:
|
||||
|
||||
const VIDEO_ID_EXTRACTOR = /ggit/gi;
|
||||
const URL_MATCHING_REGEX =
|
||||
/(?:(?<protocol>[a-zA-Z]+):\/\/)?(?:(?<auth>(?<username>\S.+)\:(?<password>.+))\@)?(?<host>(?:(?<hostname>[-a-zA-Z0-9\.]+)\.)?(?<domain>[-a-zA-Z0-9]+?\.(?<tld>[-a-zA-Z0-9]{2,64}))(?:\:(?<port>[0-9]{1,6}))?)\b(?<path>[-a-zA-Z0-9@:%_{}\[\]<>\(\)\+.~&\/="]*?(?<extension>\.[^\.?/#"]+)?)(?:\?(?<query>[a-zA-Z0-9!$%&<>()*+,-\.\/\:\;\=\?\@_~"]+))?(?:#(?<hash>[a-zA-Z0-9!$&'()*+,-\.\/\:\;\=\?\@_~"]*?))?(?:$|\s)/gim;
|
||||
|
||||
const VIDEO_ID_EXTRACTOR =
|
||||
/(?<video_domain>vimeo\.com|youtu(?:be\.com|\.be|be\.googleapis\.com))(?:\/(?<action>video|embed|watch|v))?.*(?:(?:\/|v=)(?<video_id>[A-Za-z0-9._%-]*))\S*/gi;
|
||||
const SPOTIFY_EXTRACTOR =
|
||||
/(?<protocol>(?:spotify:.*|https?))?(?:\:(?:\/\/)?)?(?<host>(?:(?<subdomain>.+)\.)?(?<domain>spotify\.com))?.*?\/?(?<item_type>(?:album|artist|episode|playlist|tracks?))?\/?(?<item_id>[a-zA-Z0-9]{22})/gi;
|
||||
/^\/(?<item_type>(?:album|artist|episode|playlist|tracks?))\/?(?<item_id>[a-zA-Z0-9]{22})/gi;
|
||||
const TIDAL_EXTRACTOR =
|
||||
/(?<protocol>(?:tidal:track:|https?))?(?::\/\/)?(?<host>(?:(?<subdomain>.+)\.)?(?<domain>tidal\.com|tidalhi\.fi)).*?\/(?<item_type>(?:album|artist|tracks?))\/(?<item_id>[0-9]+)/gi;
|
||||
/^\/(?:(?<action>.*?)\/)?(?<item_type>(?:album|artist|episode|playlist|tracks?))\/(?<item_id>[0-9]+)/gi;
|
||||
|
||||
const URL_MATCH_HANDLERS = [
|
||||
// Tidal
|
||||
(match) => {
|
||||
const original_url = match[0];
|
||||
(link_info) => {
|
||||
const is_tidal_link = ["tidal.com", "tidalhi.fi"].includes(link_info.domain?.toLowerCase());
|
||||
|
||||
if (!is_tidal_link) {
|
||||
return;
|
||||
}
|
||||
|
||||
// wow https://stackoverflow.com/questions/3891641/regex-test-only-works-every-other-time
|
||||
TIDAL_EXTRACTOR.lastIndex = 0;
|
||||
|
||||
const {
|
||||
groups: { item_type, item_id },
|
||||
} = TIDAL_EXTRACTOR.exec(original_url) ?? { groups: {} };
|
||||
groups: { action, item_type, item_id },
|
||||
} = TIDAL_EXTRACTOR.exec(link_info.path ?? "") ?? { groups: {} };
|
||||
|
||||
console.dir({
|
||||
original_url,
|
||||
item_type,
|
||||
item_id,
|
||||
});
|
||||
if (!(item_type && item_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="embed-container ${item_type === "album" ? "square" : "short"} tidal rounded">
|
||||
<div class="embed-container iframe ${item_type.toLowerCase().indexOf("track") === 0 ? "short" : "square"} tidal">
|
||||
<div class="embed-actions-container">
|
||||
<button class="icon plus" onclick="console.log(\"close\");"/>
|
||||
<button class="icon talk" onclick="console.log(\"stop\");"/>
|
||||
|
@ -45,21 +47,25 @@ const URL_MATCH_HANDLERS = [
|
|||
},
|
||||
|
||||
// Spotify
|
||||
(match) => {
|
||||
const original_url = match[0];
|
||||
(link_info) => {
|
||||
const is_spotify_link = ["spotify.com"].includes(link_info.domain?.toLowerCase());
|
||||
|
||||
if (!is_spotify_link) {
|
||||
return;
|
||||
}
|
||||
|
||||
SPOTIFY_EXTRACTOR.lastIndex = 0;
|
||||
|
||||
const {
|
||||
groups: { host, subdomain, item_type, item_id },
|
||||
} = SPOTIFY_EXTRACTOR.exec(original_url) ?? { groups: {} };
|
||||
groups: { item_type, item_id },
|
||||
} = SPOTIFY_EXTRACTOR.exec(link_info.path ?? "") ?? { groups: {} };
|
||||
|
||||
if (!item_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="embed-container ${["album", "artist"].includes(item_type) ? "square" : "short"} spotify rounded">
|
||||
<div class="embed-container iframe ${!item_type || item_type.toLowerCase().indexOf("track") === 0 ? "short" : "square"} spotify rounded">
|
||||
<div class="embed-actions-container">
|
||||
<button class="icon plus" onclick="console.log(\"close\");"/>
|
||||
<button class="icon talk" onclick="console.log(\"stop\");"/>
|
||||
|
@ -73,21 +79,27 @@ const URL_MATCH_HANDLERS = [
|
|||
},
|
||||
|
||||
// YouTube
|
||||
(match) => {
|
||||
const original_url = match[0];
|
||||
(link_info) => {
|
||||
const is_youtube_link = ["youtube.com", "youtu.be", "youtube.googleapis.com"].includes(
|
||||
link_info.domain?.toLowerCase(),
|
||||
);
|
||||
|
||||
if (!is_youtube_link) {
|
||||
return;
|
||||
}
|
||||
|
||||
VIDEO_ID_EXTRACTOR.lastIndex = 0;
|
||||
|
||||
const {
|
||||
groups: { domain, video_id },
|
||||
} = VIDEO_ID_EXTRACTOR.exec(original_url) ?? { groups: {} };
|
||||
groups: { video_domain, action, video_id },
|
||||
} = VIDEO_ID_EXTRACTOR.exec(link_info.url) ?? { groups: {} };
|
||||
|
||||
if (domain?.toLowerCase().indexOf("youtu") === -1 || !video_id) {
|
||||
if (!video_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="embed-container letterbox youtube">
|
||||
<div class="embed-container iframe letterbox youtube">
|
||||
<div class="embed-actions-container">
|
||||
<button class="icon plus" onclick="console.log(\"close\");"/>
|
||||
<button class="icon talk" onclick="console.log(\"stop\");"/>
|
||||
|
@ -104,21 +116,25 @@ const URL_MATCH_HANDLERS = [
|
|||
},
|
||||
|
||||
// Vimeo
|
||||
(match) => {
|
||||
const original_url = match[0];
|
||||
(link_info) => {
|
||||
const is_vimeo_link = ["vimeo.com"].includes(link_info.domain?.toLowerCase());
|
||||
|
||||
if (!is_vimeo_link) {
|
||||
return;
|
||||
}
|
||||
|
||||
VIDEO_ID_EXTRACTOR.lastIndex = 0;
|
||||
|
||||
const {
|
||||
groups: { domain, video_id },
|
||||
} = VIDEO_ID_EXTRACTOR.exec(original_url) ?? { groups: {} };
|
||||
groups: { video_domain, action, video_id },
|
||||
} = VIDEO_ID_EXTRACTOR.exec(link_info.url) ?? { groups: {} };
|
||||
|
||||
if (domain?.toLowerCase().indexOf("vimeo") === -1 || !video_id) {
|
||||
if (!video_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="embed-container letterbox vimeo">
|
||||
<div class="embed-container iframe letterbox vimeo">
|
||||
<div class="embed-actions-container">
|
||||
<button class="icon plus" onclick="console.log(\"close\");"/>
|
||||
<button class="icon talk" onclick="console.log(\"stop\");"/>
|
||||
|
@ -134,24 +150,61 @@ const URL_MATCH_HANDLERS = [
|
|||
},
|
||||
|
||||
// linkify generic url
|
||||
(match) => {
|
||||
(link_info) => {
|
||||
// TODO: punycoding if something has unicode?
|
||||
// const punycode = get_punycode();
|
||||
// const punycoded_url = punycode.encode(match[0]);
|
||||
const groups = match.groups ?? {};
|
||||
|
||||
return `<a href="${groups.protocol ?? "http"}://${groups.host ?? ""}${groups.path ? `/${groups.path}` : ""}${groups.query ? `?${groups.query}` : ""}${groups.hash ? `#${groups.hash}` : ""}">${match[0]}</a>`;
|
||||
if (typeof link_info.extension === "string") {
|
||||
const mime_types = get_mime_types(link_info.extension);
|
||||
if (mime_types.length) {
|
||||
if (mime_types.includes("image/gif")) {
|
||||
return `<div class="embed-container image gif"><img src="${link_info.url}" alt="A gif from ${link_info.domain}" /></div>`;
|
||||
}
|
||||
|
||||
if (mime_types.includes("video/mp4")) {
|
||||
return `<div class="embed-container image gif"><video autoplay muted loop><source src="${link_info.url}" type="video/mp4">Your browser does not support video embeds.</video></div>`;
|
||||
}
|
||||
|
||||
if (mime_types[0].indexOf("image") === 0) {
|
||||
return `<div class="embed-container image"><img src="${link_info.url}" alt="An image from ${link_info.domain}" /></div>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return `<a href="${link_info.url}">${link_info.url}</a>`;
|
||||
},
|
||||
];
|
||||
|
||||
function message_text_to_html(input) {
|
||||
let html_message = input;
|
||||
let match;
|
||||
|
||||
URL_MATCHING_REGEX.lastIndex = 0;
|
||||
while ((match = URL_MATCHING_REGEX.exec(input)) !== null) {
|
||||
const url = match[0];
|
||||
|
||||
const {
|
||||
groups: { protocol, host, hostname, domain, tld, path, extension, query, hash },
|
||||
} = match;
|
||||
|
||||
for (const handler of URL_MATCH_HANDLERS) {
|
||||
const result = handler(match);
|
||||
const result = handler({
|
||||
url,
|
||||
protocol,
|
||||
host,
|
||||
hostname,
|
||||
domain,
|
||||
tld,
|
||||
path,
|
||||
extension,
|
||||
query,
|
||||
hash,
|
||||
});
|
||||
|
||||
if (typeof result === "string") {
|
||||
html_message = html_message.replace(match[0], result);
|
||||
html_message = html_message.replace(url, result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -274,7 +327,7 @@ async function poll_for_new_events() {
|
|||
signal: room_polling_request_abort_controller.signal,
|
||||
})
|
||||
.then(async (new_events_response) => {
|
||||
const new_events = (await new_events_response.json()).reverse();
|
||||
const new_events = ((await new_events_response.json()) ?? []).reverse();
|
||||
await append_room_events(new_events.toReversed());
|
||||
poll_for_new_events(room_id);
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue