mirror of
https://github.com/glitch-soc/mastodon.git
synced 2026-03-29 03:00:33 +02:00
Profile editing: Add warning for links (#38148)
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
} from '@/mastodon/store';
|
||||
import { isUrlWithoutProtocol } from '@/mastodon/utils/checks';
|
||||
|
||||
import { ConfirmationModal } from '../../ui/components/confirmation_modals';
|
||||
import type { DialogModalProps } from '../../ui/components/dialog_modal';
|
||||
@@ -48,7 +49,7 @@ const messages = defineMessages({
|
||||
},
|
||||
editValueHint: {
|
||||
id: 'account_edit.field_edit_modal.value_hint',
|
||||
defaultMessage: 'E.g. “example.me”',
|
||||
defaultMessage: 'E.g. “https://example.me”',
|
||||
},
|
||||
limitHeader: {
|
||||
id: 'account_edit.field_edit_modal.limit_header',
|
||||
@@ -109,6 +110,10 @@ export const EditFieldModal: FC<DialogModalProps & { fieldKey?: string }> = ({
|
||||
);
|
||||
return hasLink && hasEmoji;
|
||||
}, [customEmojiCodes, newLabel, newValue]);
|
||||
const hasLinkWithoutProtocol = useMemo(
|
||||
() => isUrlWithoutProtocol(newValue),
|
||||
[newValue],
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const handleSave = useCallback(() => {
|
||||
@@ -175,6 +180,19 @@ export const EditFieldModal: FC<DialogModalProps & { fieldKey?: string }> = ({
|
||||
/>
|
||||
</Callout>
|
||||
)}
|
||||
|
||||
{hasLinkWithoutProtocol && (
|
||||
<Callout variant='warning'>
|
||||
<FormattedMessage
|
||||
id='account_edit.field_edit_modal.url_warning'
|
||||
defaultMessage='To add a link, please include {protocol} at the beginning.'
|
||||
description='{protocol} is https://'
|
||||
values={{
|
||||
protocol: <code>https://</code>,
|
||||
}}
|
||||
/>
|
||||
</Callout>
|
||||
)}
|
||||
</ConfirmationModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -173,7 +173,8 @@
|
||||
"account_edit.field_edit_modal.link_emoji_warning": "We recommend against the use of custom emoji in combination with urls. Custom fields containing both will display as text only instead of as a link, in order to prevent user confusion.",
|
||||
"account_edit.field_edit_modal.name_hint": "E.g. “Personal website”",
|
||||
"account_edit.field_edit_modal.name_label": "Label",
|
||||
"account_edit.field_edit_modal.value_hint": "E.g. “example.me”",
|
||||
"account_edit.field_edit_modal.url_warning": "To add a link, please include {protocol} at the beginning.",
|
||||
"account_edit.field_edit_modal.value_hint": "E.g. “https://example.me”",
|
||||
"account_edit.field_edit_modal.value_label": "Value",
|
||||
"account_edit.field_reorder_modal.drag_cancel": "Dragging was cancelled. Field \"{item}\" was dropped.",
|
||||
"account_edit.field_reorder_modal.drag_end": "Field \"{item}\" was dropped.",
|
||||
|
||||
21
app/javascript/mastodon/utils/checks.test.ts
Normal file
21
app/javascript/mastodon/utils/checks.test.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { isUrlWithoutProtocol } from './checks';
|
||||
|
||||
describe('isUrlWithoutProtocol', () => {
|
||||
test.concurrent.each([
|
||||
['example.com', true],
|
||||
['sub.domain.co.uk', true],
|
||||
['example', false], // No dot
|
||||
['example..com', false], // Consecutive dots
|
||||
['example.com.', false], // Trailing dot
|
||||
['example.c', false], // TLD too short
|
||||
['example.123', false], // Numeric TLDs are not valid
|
||||
['example.com/path', true], // Paths are allowed
|
||||
['example.com?query=string', true], // Query strings are allowed
|
||||
['example.com#fragment', true], // Fragments are allowed
|
||||
['example .com', false], // Spaces are not allowed
|
||||
['example://com', false], // Protocol inside the string is not allowed
|
||||
['example.com^', false], // Invalid characters not allowed
|
||||
])('should return %s for input "%s"', (input, expected) => {
|
||||
expect(isUrlWithoutProtocol(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -9,3 +9,29 @@ export function isValidUrl(
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the input string is probably a URL without a protocol. Note this is not full URL validation,
|
||||
* and is mostly used to detect link-like inputs.
|
||||
* @see https://www.xjavascript.com/blog/check-if-a-javascript-string-is-a-url/
|
||||
* @param input The input string to check
|
||||
*/
|
||||
export function isUrlWithoutProtocol(input: string): boolean {
|
||||
if (!input.length || input.includes(' ') || input.includes('://')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(`http://${input}`);
|
||||
const { host } = url;
|
||||
return (
|
||||
host !== '' && // Host is not empty
|
||||
host.includes('.') && // Host contains at least one dot
|
||||
!host.endsWith('.') && // No trailing dot
|
||||
!host.includes('..') && // No consecutive dots
|
||||
/\.[\w]{2,}$/.test(host) // TLD is at least 2 characters
|
||||
);
|
||||
} catch {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user