You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vizality/renderer/src/builtins/attributes/modules/components/Anchor.js

128 lines
4.3 KiB

/**
* Patches anchor links, giving them additional or alternate functionality.
* @module Anchor
* @memberof Builtin.Enhancements.Components
*/
import { getModule, FluxDispatcher } from '@vizality/webpack';
import { Regexes, Protocols } from '@vizality/constants';
import { findInReactTree } from '@vizality/util/react';
import { patch, unpatch } from '@vizality/patcher';
export const labels = [ 'Components', 'Anchor' ];
export default builtin => {
const Anchor = getModule(m => m.default?.displayName === 'Anchor');
const { anchor } = getModule('anchor', 'anchorUnderlineOnHover');
const vizalityRegex = new RegExp(`${Regexes.DISCORD}/vizality`, 'i');
const vizalityProtocolRegex = new RegExp(`^(${Protocols.BASE})`, 'i');
const userRegex = new RegExp(`${Regexes.DISCORD}/users/`, 'i');
const discordRegex = new RegExp(`${Regexes.DISCORD}`, 'i');
patch('vz-attributes-anchors', Anchor, 'default', (_, res) => {
try {
const props = findInReactTree(res, r => r.className?.includes(anchor) && r.href);
if (!props) {
return;
}
/**
*
*/
if (props.href?.startsWith('#')) {
props.onClick = evt => {
try {
evt.preventDefault();
/**
* This is bad, but currently it's the only way I really know how to do it.
* We're adding a timeout here so that we can give the new route page elements
* (topOfElement and scroller) time to load into the DOM.
*/
return setTimeout(() => {
const topOfElement = document.querySelector(`#${props.href.substring(1)}`)?.offsetTop;
const scroller = document.querySelector('.vz-dashboard-scroller');
/**
* If the elements don't exist at the check, just load the page normally as if
* there was no hash.
*/
if (!topOfElement || !scroller) {
return;
}
/**
* Try to set the scroller position to the hash element.
*/
return scroller.scroll({ top: topOfElement - 80, behavior: 'smooth' });
}, 250);
} catch (err) {
return builtin.error(builtin._labels.concat(labels), err);
}
};
}
/**
* Force Vizality route links to open within the app.
*/
if (vizalityRegex.test(props.href)) {
const route = props.href?.replace(discordRegex, '');
props.onClick = evt => {
try {
evt.preventDefault();
vizality.api.routes.navigateTo(route);
} catch (err) {
return builtin.error(builtin._labels.concat(labels), err);
}
};
}
/**
* Force Vizality protocol links to open within the app.
*/
if (vizalityProtocolRegex.test(props.href)) {
const route = props.href?.replace(vizalityProtocolRegex, '');
props.onClick = evt => {
try {
evt.preventDefault();
vizality.api.routes.navigateTo(`/vizality/${route}`);
} catch (err) {
return builtin.error(builtin._labels.concat(labels), err);
}
};
}
/*
* Force user links to open in user profile modals within the app.
*/
if (userRegex.test(props.href)) {
const userId = props.href?.replace(userRegex, '');
props.onClick = async evt => {
try {
evt.preventDefault();
if (!userId) {
return;
}
const user = await $discord.users.fetchUser(userId);
if (!user) {
return vizality.api.notifications.sendToast({
id: 'VIZALITY_USER_NOT_FOUND',
header: 'User Not Found',
content: 'That user was unable to be located.',
icon: 'PersonRemove'
});
}
return FluxDispatcher.dispatch({
type: 'USER_PROFILE_MODAL_OPEN',
userId
});
} catch (err) {
return builtin.error(builtin._labels.concat(labels), err);
}
};
}
} catch (err) {
return builtin.error(builtin._labels.concat(labels), err);
}
});
return () => unpatch('vz-attributes-anchors');
};