diff --git a/packages/pl-fe/package.json b/packages/pl-fe/package.json index be3eb6695..7979918db 100644 --- a/packages/pl-fe/package.json +++ b/packages/pl-fe/package.json @@ -143,7 +143,6 @@ "type-fest": "^4.0.0", "typescript": "^5.6.2", "util": "^0.12.5", - "uuid": "^10.0.0", "vite": "^5.4.8", "vite-plugin-compile-time": "^0.2.1", "vite-plugin-html": "^3.2.2", @@ -176,7 +175,6 @@ "@types/react-swipeable-views": "^0.13.5", "@types/redux-mock-store": "^1.0.6", "@types/semver": "^7.5.8", - "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.6.0", "@typescript-eslint/parser": "^8.6.0", "eslint": "^8.57.1", diff --git a/packages/pl-fe/src/actions/draft-statuses.ts b/packages/pl-fe/src/actions/draft-statuses.ts index 09acdfae5..a4f786765 100644 --- a/packages/pl-fe/src/actions/draft-statuses.ts +++ b/packages/pl-fe/src/actions/draft-statuses.ts @@ -1,4 +1,3 @@ -import { v4 as uuid } from 'uuid'; import { makeGetAccount } from 'pl-fe/selectors'; import KVStore from 'pl-fe/storage/kv-store'; @@ -34,7 +33,7 @@ const saveDraftStatus = (composeId: string) => const draft = { ...compose.toJS(), - draft_id: compose.draft_id || uuid(), + draft_id: compose.draft_id || crypto.randomUUID(), }; dispatch({ diff --git a/packages/pl-fe/src/components/list.tsx b/packages/pl-fe/src/components/list.tsx index 4d5934d26..0e91bc00f 100644 --- a/packages/pl-fe/src/components/list.tsx +++ b/packages/pl-fe/src/components/list.tsx @@ -1,7 +1,6 @@ import clsx from 'clsx'; import React from 'react'; import { Link } from 'react-router-dom'; -import { v4 as uuidv4 } from 'uuid'; import { SelectDropdown } from '../features/forms'; @@ -29,7 +28,7 @@ interface IListItem { } const ListItem: React.FC = ({ className, label, hint, children, to, href, onClick, onSelect, isSelected, size = 'md' }) => { - const id = uuidv4(); + const id = crypto.randomUUID(); const domId = `list-group-${id}`; const onKeyDown = (e: React.KeyboardEvent) => { diff --git a/packages/pl-fe/src/components/ui/form-group/form-group.tsx b/packages/pl-fe/src/components/ui/form-group/form-group.tsx index df7e2b33c..100cfffe7 100644 --- a/packages/pl-fe/src/components/ui/form-group/form-group.tsx +++ b/packages/pl-fe/src/components/ui/form-group/form-group.tsx @@ -1,5 +1,4 @@ import React, { useMemo } from 'react'; -import { v4 as uuidv4 } from 'uuid'; import Checkbox from '../checkbox/checkbox'; import HStack from '../hstack/hstack'; @@ -21,7 +20,7 @@ interface IFormGroup { /** Input container with label. Renders the child. */ const FormGroup: React.FC = (props) => { const { children, errors = [], labelText, labelTitle, hintText } = props; - const formFieldId: string = useMemo(() => `field-${uuidv4()}`, []); + const formFieldId: string = useMemo(() => `field-${crypto.randomUUID()}`, []); const inputChildren = React.Children.toArray(children); const hasError = errors?.length > 0; diff --git a/packages/pl-fe/src/components/ui/radio-button/radio-button.tsx b/packages/pl-fe/src/components/ui/radio-button/radio-button.tsx index b66b1b077..d6b9c6590 100644 --- a/packages/pl-fe/src/components/ui/radio-button/radio-button.tsx +++ b/packages/pl-fe/src/components/ui/radio-button/radio-button.tsx @@ -1,5 +1,4 @@ import React, { useMemo } from 'react'; -import { v4 as uuidv4 } from 'uuid'; import HStack from '../hstack/hstack'; @@ -15,7 +14,7 @@ interface IRadioButton { * A group for radio input with label. */ const RadioButton: React.FC = ({ name, value, checked, onChange, label }) => { - const formFieldId: string = useMemo(() => `radio-${uuidv4()}`, []); + const formFieldId: string = useMemo(() => `radio-${crypto.randomUUID()}`, []); return ( diff --git a/packages/pl-fe/src/features/auth-login/components/registration-form.tsx b/packages/pl-fe/src/features/auth-login/components/registration-form.tsx index c14274fa9..15a518a88 100644 --- a/packages/pl-fe/src/features/auth-login/components/registration-form.tsx +++ b/packages/pl-fe/src/features/auth-login/components/registration-form.tsx @@ -3,7 +3,6 @@ import debounce from 'lodash/debounce'; import React, { useState, useRef, useCallback } from 'react'; import { useIntl, FormattedMessage, defineMessages } from 'react-intl'; import { Link, useHistory } from 'react-router-dom'; -import { v4 as uuidv4 } from 'uuid'; import { accountLookup } from 'pl-fe/actions/accounts'; import { register, verifyCredentials } from 'pl-fe/actions/auth'; @@ -63,7 +62,7 @@ const RegistrationForm: React.FC = ({ inviteToken }) => { agreement: false, locale: '', }); - const [captchaIdempotencyKey, setCaptchaIdempotencyKey] = useState(uuidv4()); + const [captchaIdempotencyKey, setCaptchaIdempotencyKey] = useState(crypto.randomUUID()); const [usernameUnavailable, setUsernameUnavailable] = useState(false); const [passwordConfirmation, setPasswordConfirmation] = useState(''); const [passwordMismatch, setPasswordMismatch] = useState(false); @@ -227,7 +226,7 @@ const RegistrationForm: React.FC = ({ inviteToken }) => { }; const refreshCaptcha = () => { - setCaptchaIdempotencyKey(uuidv4()); + setCaptchaIdempotencyKey(crypto.randomUUID()); setParams(params => ({ ...params, captcha_solution: '' })); }; diff --git a/packages/pl-fe/src/features/forms/index.tsx b/packages/pl-fe/src/features/forms/index.tsx index ddf42857d..2eae91ef4 100644 --- a/packages/pl-fe/src/features/forms/index.tsx +++ b/packages/pl-fe/src/features/forms/index.tsx @@ -2,7 +2,6 @@ import clsx from 'clsx'; import MultiselectReactDropdown from 'multiselect-react-dropdown'; import React, { useMemo, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { v4 as uuidv4 } from 'uuid'; import { Icon, Select } from '../../components/ui'; @@ -44,7 +43,7 @@ interface ILabelInputContainer { } const LabelInputContainer: React.FC = ({ label, hint, children }) => { - const [id] = useState(uuidv4()); + const [id] = useState(crypto.randomUUID()); const childrenWithProps = React.Children.map(children, child => ( // @ts-ignore: not sure how to get the right type here React.cloneElement(child, { id, key: id }) diff --git a/packages/pl-fe/src/features/theme-editor/index.tsx b/packages/pl-fe/src/features/theme-editor/index.tsx index e7d3482bb..40ddf34d6 100644 --- a/packages/pl-fe/src/features/theme-editor/index.tsx +++ b/packages/pl-fe/src/features/theme-editor/index.tsx @@ -1,6 +1,5 @@ import React, { useRef, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { v4 as uuidv4 } from 'uuid'; import { updatePlFeConfig } from 'pl-fe/actions/admin'; import { getHost } from 'pl-fe/actions/instance'; @@ -51,7 +50,7 @@ const ThemeEditor: React.FC = () => { const [colors, setColors] = useState(plFe.colors.toJS() as any); const [submitting, setSubmitting] = useState(false); - const [resetKey, setResetKey] = useState(uuidv4()); + const [resetKey, setResetKey] = useState(crypto.randomUUID()); const fileInput = useRef(null); @@ -73,7 +72,7 @@ const ThemeEditor: React.FC = () => { }; const setTheme = (theme: any) => { - setResetKey(uuidv4()); + setResetKey(crypto.randomUUID()); setTimeout(() => setColors(theme)); }; diff --git a/packages/pl-fe/src/jest/factory.ts b/packages/pl-fe/src/jest/factory.ts index 49d9be8e3..3cc570b25 100644 --- a/packages/pl-fe/src/jest/factory.ts +++ b/packages/pl-fe/src/jest/factory.ts @@ -17,17 +17,18 @@ import { type Relationship, type Status, } from 'pl-api'; -import { v4 as uuidv4 } from 'uuid'; import type { PartialDeep } from 'type-fest'; +const uuid = crypto.randomUUID; + // TODO: there's probably a better way to create these factory functions. // This looks promising but didn't work on my first attempt: https://github.com/anatine/zod-plugins/tree/main/packages/zod-mock const buildAccount = (props: PartialDeep = {}): Account => accountSchema.parse(Object.assign({ - id: uuidv4(), - url: `https://soapbox.test/users/${uuidv4()}`, + id: uuid(), + url: `https://soapbox.test/users/${uuid()}`, }, props)); const buildCard = (props: PartialDeep = {}): PreviewCard => @@ -37,22 +38,22 @@ const buildCard = (props: PartialDeep = {}): PreviewCard => const buildGroup = (props: PartialDeep = {}): Group => groupSchema.parse(Object.assign({ - id: uuidv4(), + id: uuid(), owner: { - id: uuidv4(), + id: uuid(), }, }, props)); const buildGroupRelationship = (props: PartialDeep = {}): GroupRelationship => groupRelationshipSchema.parse(Object.assign({ - id: uuidv4(), + id: uuid(), }, props)); const buildGroupMember = ( props: PartialDeep = {}, accountProps: PartialDeep = {}, ): GroupMember => groupMemberSchema.parse(Object.assign({ - id: uuidv4(), + id: uuid(), account: buildAccount(accountProps), role: GroupRoles.USER, }, props)); @@ -61,12 +62,12 @@ const buildInstance = (props: PartialDeep = {}) => instanceSchema.pars const buildRelationship = (props: PartialDeep = {}): Relationship => relationshipSchema.parse(Object.assign({ - id: uuidv4(), + id: uuid(), }, props)); const buildStatus = (props: PartialDeep = {}) => statusSchema.parse(Object.assign({ - id: uuidv4(), + id: uuid(), account: buildAccount(), }, props)); diff --git a/packages/pl-fe/src/reducers/compose.ts b/packages/pl-fe/src/reducers/compose.ts index b05081915..c527829af 100644 --- a/packages/pl-fe/src/reducers/compose.ts +++ b/packages/pl-fe/src/reducers/compose.ts @@ -1,6 +1,5 @@ import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord, fromJS } from 'immutable'; import { PLEROMA, type CredentialAccount, type MediaAttachment, type Tag } from 'pl-api'; -import { v4 as uuid } from 'uuid'; import { isNativeEmoji } from 'pl-fe/features/emoji'; import { tagHistory } from 'pl-fe/settings'; @@ -72,6 +71,8 @@ import type { Language } from 'pl-fe/features/preferences'; import type { Account, Status } from 'pl-fe/normalizers'; import type { APIEntity } from 'pl-fe/types/entities'; +const uuid = crypto.randomUUID; + const getResetFileKey = () => Math.floor((Math.random() * 0x10000)); const PollRecord = ImmutableRecord({ diff --git a/packages/pl-fe/yarn.lock b/packages/pl-fe/yarn.lock index 4902ba330..a191a7778 100644 --- a/packages/pl-fe/yarn.lock +++ b/packages/pl-fe/yarn.lock @@ -2885,11 +2885,6 @@ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== -"@types/uuid@^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" - integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== - "@typescript-eslint/eslint-plugin@^5.59.5": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" @@ -11434,11 +11429,6 @@ uuid@3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -uuid@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" - integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== - uuidv4@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/uuidv4/-/uuidv4-2.0.0.tgz#3ec764288f9e9c4e40f8027ad309c2c528be2976"