diff --git a/src/pages/http-route.jsx b/src/pages/http-route.jsx index 5729e9c2..f3189f1a 100644 --- a/src/pages/http-route.jsx +++ b/src/pages/http-route.jsx @@ -1,34 +1,73 @@ -import { useLayoutEffect } from 'preact/hooks'; +import { useLayoutEffect, useState } from 'preact/hooks'; import { useLocation } from 'react-router-dom'; import Link from '../components/link'; -import getInstanceStatusURL from '../utils/get-instance-status-url'; +import Loader from '../components/loader'; +import { api } from '../utils/api'; +import getInstanceStatusURL, { + getInstanceStatusObject, +} from '../utils/get-instance-status-url'; export default function HttpRoute() { const location = useLocation(); const url = location.pathname.replace(/^\//, ''); - const statusURL = getInstanceStatusURL(url); + const statusObject = getInstanceStatusObject(url); + // const statusURL = getInstanceStatusURL(url); + const statusURL = statusObject?.instance + ? `/${statusObject.instance}/s/${statusObject.id}` + : null; + const [uiState, setUIState] = useState('loading'); useLayoutEffect(() => { - if (statusURL) { - setTimeout(() => { - window.location.hash = statusURL + '?view=full'; - }, 300); - } + setUIState('loading'); + (async () => { + const { instance, id } = statusObject; + const { masto } = api({ instance }); + + // Check if status returns 200 + try { + const status = await masto.v1.statuses.$select(id).fetch(); + if (status) { + window.location.hash = statusURL + '?view=full'; + return; + } + } catch (e) {} + + // Fallback to search + { + const { masto: currentMasto, instance: currentInstance } = api(); + const result = await currentMasto.v2.search.fetch({ + q: url, + type: 'statuses', + limit: 1, + resolve: true, + }); + if (result.statuses.length) { + const status = result.statuses[0]; + window.location.hash = `/${currentInstance}/s/${status.id}?view=full`; + } else { + // Fallback to original URL, which will probably show error + window.location.hash = statusURL + '?view=full'; + } + } + })(); }, [statusURL]); return (
- {statusURL ? ( + {uiState === 'loading' ? ( <> -

Redirecting…

+ +

Resolving…

- {statusURL} + + {url} +

) : ( <> -

Unable to process URL

+

Unable to resolve URL

{url} diff --git a/src/utils/get-instance-status-url.js b/src/utils/get-instance-status-url.js index 28c810ff..ebbcaa94 100644 --- a/src/utils/get-instance-status-url.js +++ b/src/utils/get-instance-status-url.js @@ -1,19 +1,36 @@ -export const statusRegex = /\/@([^@\/]+)@?([^\/]+)?\/([^\/]+)\/?$/i; -export const statusNoteRegex = /\/notes\/([^\/]+)\/?$/i; -function getInstanceStatusURL(url) { +// export const statusRegex = /\/@([^@\/]+)@?([^\/]+)?\/([^\/]+)\/?$/i; +// export const statusNoteRegex = /\/notes\/([^\/]+)\/?$/i; + +const statusPostRegexes = [ + /^\/@[^@\/]+\/(?:statuses|posts)\/([^\/]+)/i, // GoToSocial, Takahe + /\/notes\/([^\/]+)/i, // Misskey, Firefish + /^\/(?:notice|objects)\/([a-z0-9-]+)/i, // Pleroma + /\/@[^@\/]+@?[^\/]+?\/([^\/]+)/i, // Mastodon +]; + +export function getInstanceStatusObject(url) { // Regex /:username/:id, where username = @username or @username@domain, id = anything const { hostname, pathname } = new URL(url); - const [, username, domain, id] = pathname.match(statusRegex) || []; - - if (id) { - return `/${hostname}/s/${id}`; + // const [, username, domain, id] = pathname.match(statusRegex) || []; + for (const regex of statusPostRegexes) { + const [, id] = pathname.match(regex) || []; + console.log(pathname, regex, id); + if (id) { + return { + instance: hostname, + id, + }; + } } + return null; +} - const [, noteId] = pathname.match(statusNoteRegex) || []; - - if (noteId) { - return `/${hostname}/s/${noteId}`; +function getInstanceStatusURL(url) { + const { instance, id } = getInstanceStatusObject(url); + if (instance && id) { + return `/${instance}/s/${id}`; } + return null; } export default getInstanceStatusURL;