mirror of
https://github.com/mastodon/mastodon.git
synced 2024-11-20 03:25:17 +01:00
Converted app/javascript/mastodon/utils/ folder to TypeScript (#27895)
This commit is contained in:
parent
cdc7894243
commit
1142f4c79e
@ -1,10 +0,0 @@
|
|||||||
import ready from '../ready';
|
|
||||||
|
|
||||||
export let assetHost = '';
|
|
||||||
|
|
||||||
ready(() => {
|
|
||||||
const cdnHost = document.querySelector('meta[name=cdn-host]');
|
|
||||||
if (cdnHost) {
|
|
||||||
assetHost = cdnHost.content || '';
|
|
||||||
}
|
|
||||||
});
|
|
13
app/javascript/mastodon/utils/config.ts
Normal file
13
app/javascript/mastodon/utils/config.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import ready from '../ready';
|
||||||
|
|
||||||
|
export let assetHost = '';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
ready(() => {
|
||||||
|
const cdnHost = document.querySelector<HTMLMetaElement>(
|
||||||
|
'meta[name=cdn-host]',
|
||||||
|
);
|
||||||
|
if (cdnHost) {
|
||||||
|
assetHost = cdnHost.content || '';
|
||||||
|
}
|
||||||
|
});
|
@ -1,6 +0,0 @@
|
|||||||
// NB: This function can still return unsafe HTML
|
|
||||||
export const unescapeHTML = (html) => {
|
|
||||||
const wrapper = document.createElement('div');
|
|
||||||
wrapper.innerHTML = html.replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n').replace(/<[^>]*>/g, '');
|
|
||||||
return wrapper.textContent;
|
|
||||||
};
|
|
9
app/javascript/mastodon/utils/html.ts
Normal file
9
app/javascript/mastodon/utils/html.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// NB: This function can still return unsafe HTML
|
||||||
|
export const unescapeHTML = (html: string) => {
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.innerHTML = html
|
||||||
|
.replace(/<br\s*\/?>/g, '\n')
|
||||||
|
.replace(/<\/p><p>/g, '\n\n')
|
||||||
|
.replace(/<[^>]*>/g, '');
|
||||||
|
return wrapper.textContent;
|
||||||
|
};
|
@ -1,13 +1,23 @@
|
|||||||
// Copied from emoji-mart for consistency with emoji picker and since
|
// Copied from emoji-mart for consistency with emoji picker and since
|
||||||
// they don't export the icons in the package
|
// they don't export the icons in the package
|
||||||
export const loupeIcon = (
|
export const loupeIcon = (
|
||||||
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' width='13' height='13'>
|
<svg
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
viewBox='0 0 20 20'
|
||||||
|
width='13'
|
||||||
|
height='13'
|
||||||
|
>
|
||||||
<path d='M12.9 14.32a8 8 0 1 1 1.41-1.41l5.35 5.33-1.42 1.42-5.33-5.34zM8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12z' />
|
<path d='M12.9 14.32a8 8 0 1 1 1.41-1.41l5.35 5.33-1.42 1.42-5.33-5.34zM8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12z' />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const deleteIcon = (
|
export const deleteIcon = (
|
||||||
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' width='13' height='13'>
|
<svg
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
viewBox='0 0 20 20'
|
||||||
|
width='13'
|
||||||
|
height='13'
|
||||||
|
>
|
||||||
<path d='M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z' />
|
<path d='M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z' />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
@ -1,30 +0,0 @@
|
|||||||
// Handles browser quirks, based on
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API/Using_the_Notifications_API
|
|
||||||
|
|
||||||
const checkNotificationPromise = () => {
|
|
||||||
try {
|
|
||||||
// eslint-disable-next-line promise/valid-params, promise/catch-or-return
|
|
||||||
Notification.requestPermission().then();
|
|
||||||
} catch(e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePermission = (permission, callback) => {
|
|
||||||
// Whatever the user answers, we make sure Chrome stores the information
|
|
||||||
if(!('permission' in Notification)) {
|
|
||||||
Notification.permission = permission;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(Notification.permission);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const requestNotificationPermission = (callback) => {
|
|
||||||
if (checkNotificationPromise()) {
|
|
||||||
Notification.requestPermission().then((permission) => handlePermission(permission, callback)).catch(console.warn);
|
|
||||||
} else {
|
|
||||||
Notification.requestPermission((permission) => handlePermission(permission, callback));
|
|
||||||
}
|
|
||||||
};
|
|
13
app/javascript/mastodon/utils/notifications.ts
Normal file
13
app/javascript/mastodon/utils/notifications.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Tries Notification.requestPermission, console warning instead of rejecting on error.
|
||||||
|
* @param callback Runs with the permission result on completion.
|
||||||
|
*/
|
||||||
|
export const requestNotificationPermission = async (
|
||||||
|
callback: NotificationPermissionCallback,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
callback(await Notification.requestPermission());
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error);
|
||||||
|
}
|
||||||
|
};
|
@ -1,8 +1,8 @@
|
|||||||
import PropTypes from "prop-types";
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { __RouterContext } from "react-router";
|
import { __RouterContext } from 'react-router';
|
||||||
|
|
||||||
import hoistStatics from "hoist-non-react-statics";
|
import hoistStatics from 'hoist-non-react-statics';
|
||||||
|
|
||||||
export const WithRouterPropTypes = {
|
export const WithRouterPropTypes = {
|
||||||
match: PropTypes.object.isRequired,
|
match: PropTypes.object.isRequired,
|
||||||
@ -16,31 +16,37 @@ export const WithOptionalRouterPropTypes = {
|
|||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface OptionalRouterProps {
|
||||||
|
ref: unknown;
|
||||||
|
wrappedComponentRef: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
// This is copied from https://github.com/remix-run/react-router/blob/v5.3.4/packages/react-router/modules/withRouter.js
|
// This is copied from https://github.com/remix-run/react-router/blob/v5.3.4/packages/react-router/modules/withRouter.js
|
||||||
// but does not fail if called outside of a React Router context
|
// but does not fail if called outside of a React Router context
|
||||||
export function withOptionalRouter(Component) {
|
export function withOptionalRouter<
|
||||||
const displayName = `withRouter(${Component.displayName || Component.name})`;
|
ComponentType extends React.ComponentType<OptionalRouterProps>,
|
||||||
const C = props => {
|
>(Component: ComponentType) {
|
||||||
|
const displayName = `withRouter(${Component.displayName ?? Component.name})`;
|
||||||
|
const C = (props: React.ComponentProps<ComponentType>) => {
|
||||||
const { wrappedComponentRef, ...remainingProps } = props;
|
const { wrappedComponentRef, ...remainingProps } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<__RouterContext.Consumer>
|
<__RouterContext.Consumer>
|
||||||
{context => {
|
{(context) => {
|
||||||
if(context)
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
if (context) {
|
||||||
return (
|
return (
|
||||||
|
// @ts-expect-error - Dynamic covariant generic components are tough to type.
|
||||||
<Component
|
<Component
|
||||||
{...remainingProps}
|
{...remainingProps}
|
||||||
{...context}
|
{...context}
|
||||||
ref={wrappedComponentRef}
|
ref={wrappedComponentRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
return (
|
// @ts-expect-error - Dynamic covariant generic components are tough to type.
|
||||||
<Component
|
return <Component {...remainingProps} ref={wrappedComponentRef} />;
|
||||||
{...remainingProps}
|
}
|
||||||
ref={wrappedComponentRef}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
</__RouterContext.Consumer>
|
</__RouterContext.Consumer>
|
||||||
);
|
);
|
||||||
@ -53,8 +59,8 @@ export function withOptionalRouter(Component) {
|
|||||||
wrappedComponentRef: PropTypes.oneOfType([
|
wrappedComponentRef: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.func,
|
PropTypes.func,
|
||||||
PropTypes.object
|
PropTypes.object,
|
||||||
])
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
return hoistStatics(C, Component);
|
return hoistStatics(C, Component);
|
@ -1,11 +1,7 @@
|
|||||||
import { isMobile } from '../is_mobile';
|
import { isMobile } from '../is_mobile';
|
||||||
|
|
||||||
/** @type {number | null} */
|
let cachedScrollbarWidth: number | null = null;
|
||||||
let cachedScrollbarWidth = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
const getActualScrollbarWidth = () => {
|
const getActualScrollbarWidth = () => {
|
||||||
const outer = document.createElement('div');
|
const outer = document.createElement('div');
|
||||||
outer.style.visibility = 'hidden';
|
outer.style.visibility = 'hidden';
|
||||||
@ -16,20 +12,19 @@ const getActualScrollbarWidth = () => {
|
|||||||
outer.appendChild(inner);
|
outer.appendChild(inner);
|
||||||
|
|
||||||
const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
|
const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
|
||||||
outer.parentNode.removeChild(outer);
|
outer.remove();
|
||||||
|
|
||||||
return scrollbarWidth;
|
return scrollbarWidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
export const getScrollbarWidth = () => {
|
export const getScrollbarWidth = () => {
|
||||||
if (cachedScrollbarWidth !== null) {
|
if (cachedScrollbarWidth !== null) {
|
||||||
return cachedScrollbarWidth;
|
return cachedScrollbarWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollbarWidth = isMobile(window.innerWidth) ? 0 : getActualScrollbarWidth();
|
const scrollbarWidth = isMobile(window.innerWidth)
|
||||||
|
? 0
|
||||||
|
: getActualScrollbarWidth();
|
||||||
cachedScrollbarWidth = scrollbarWidth;
|
cachedScrollbarWidth = scrollbarWidth;
|
||||||
|
|
||||||
return scrollbarWidth;
|
return scrollbarWidth;
|
@ -161,6 +161,7 @@
|
|||||||
"@types/object-assign": "^4.0.30",
|
"@types/object-assign": "^4.0.30",
|
||||||
"@types/prop-types": "^15.7.5",
|
"@types/prop-types": "^15.7.5",
|
||||||
"@types/punycode": "^2.1.0",
|
"@types/punycode": "^2.1.0",
|
||||||
|
"@types/rails__ujs": "^6.0.4",
|
||||||
"@types/react": "^18.2.7",
|
"@types/react": "^18.2.7",
|
||||||
"@types/react-dom": "^18.2.4",
|
"@types/react-dom": "^18.2.4",
|
||||||
"@types/react-helmet": "^6.1.6",
|
"@types/react-helmet": "^6.1.6",
|
||||||
|
@ -2317,6 +2317,7 @@ __metadata:
|
|||||||
"@types/object-assign": "npm:^4.0.30"
|
"@types/object-assign": "npm:^4.0.30"
|
||||||
"@types/prop-types": "npm:^15.7.5"
|
"@types/prop-types": "npm:^15.7.5"
|
||||||
"@types/punycode": "npm:^2.1.0"
|
"@types/punycode": "npm:^2.1.0"
|
||||||
|
"@types/rails__ujs": "npm:^6.0.4"
|
||||||
"@types/react": "npm:^18.2.7"
|
"@types/react": "npm:^18.2.7"
|
||||||
"@types/react-dom": "npm:^18.2.4"
|
"@types/react-dom": "npm:^18.2.4"
|
||||||
"@types/react-helmet": "npm:^6.1.6"
|
"@types/react-helmet": "npm:^6.1.6"
|
||||||
@ -3349,6 +3350,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/rails__ujs@npm:^6.0.4":
|
||||||
|
version: 6.0.4
|
||||||
|
resolution: "@types/rails__ujs@npm:6.0.4"
|
||||||
|
checksum: 7477cb03a0e1339b9cd5c8ac4a197a153e2ff48742b2f527c5a39dcdf80f01493011e368483290d3717662c63066fada3ab203a335804cbb3573cf575f37007e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/range-parser@npm:*":
|
"@types/range-parser@npm:*":
|
||||||
version: 1.2.7
|
version: 1.2.7
|
||||||
resolution: "@types/range-parser@npm:1.2.7"
|
resolution: "@types/range-parser@npm:1.2.7"
|
||||||
|
Loading…
Reference in New Issue
Block a user