Merge remote-tracking branch 'origin/main' into drafts
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
commit
8c36432a06
22 changed files with 489 additions and 164 deletions
14
package.json
14
package.json
|
@ -55,12 +55,12 @@
|
|||
"@fontsource/roboto-mono": "^5.0.0",
|
||||
"@fontsource/tajawal": "^5.0.8",
|
||||
"@gamestdio/websocket": "^0.3.2",
|
||||
"@lexical/clipboard": "^0.13.1",
|
||||
"@lexical/hashtag": "^0.13.1",
|
||||
"@lexical/link": "^0.13.1",
|
||||
"@lexical/react": "^0.13.1",
|
||||
"@lexical/selection": "^0.13.1",
|
||||
"@lexical/utils": "^0.13.1",
|
||||
"@lexical/clipboard": "^0.14.2",
|
||||
"@lexical/hashtag": "^0.14.2",
|
||||
"@lexical/link": "^0.14.2",
|
||||
"@lexical/react": "^0.14.2",
|
||||
"@lexical/selection": "^0.14.2",
|
||||
"@lexical/utils": "^0.14.2",
|
||||
"@mkljczk/react-hotkeys": "^1.2.2",
|
||||
"@noble/hashes": "^1.3.3",
|
||||
"@popperjs/core": "^2.11.5",
|
||||
|
@ -131,7 +131,7 @@
|
|||
"intl-pluralrules": "^2.0.0",
|
||||
"isomorphic-dompurify": "^2.3.0",
|
||||
"leaflet": "^1.8.0",
|
||||
"lexical": "^0.13.1",
|
||||
"lexical": "^0.14.2",
|
||||
"line-awesome": "^1.3.0",
|
||||
"localforage": "^1.10.0",
|
||||
"lodash": "^4.7.11",
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { getSettings, changeSetting } from 'soapbox/actions/settings';
|
||||
|
||||
import type { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import type { List as ImmutableList, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import type { AppDispatch, RootState } from 'soapbox/store';
|
||||
|
||||
const getPinnedHosts = (state: RootState) => {
|
||||
const settings = getSettings(state);
|
||||
return settings.getIn(['remote_timeline', 'pinnedHosts']) as ImmutableOrderedSet<string>;
|
||||
return settings.getIn(['remote_timeline', 'pinnedHosts']) as ImmutableList<string> | ImmutableOrderedSet<string>;
|
||||
};
|
||||
|
||||
const pinHost = (host: string) =>
|
||||
|
@ -13,7 +13,7 @@ const pinHost = (host: string) =>
|
|||
const state = getState();
|
||||
const pinnedHosts = getPinnedHosts(state);
|
||||
|
||||
return dispatch(changeSetting(['remote_timeline', 'pinnedHosts'], pinnedHosts.add(host)));
|
||||
return dispatch(changeSetting(['remote_timeline', 'pinnedHosts'], pinnedHosts.toOrderedSet().add(host)));
|
||||
};
|
||||
|
||||
const unpinHost = (host: string) =>
|
||||
|
@ -21,7 +21,7 @@ const unpinHost = (host: string) =>
|
|||
const state = getState();
|
||||
const pinnedHosts = getPinnedHosts(state);
|
||||
|
||||
return dispatch(changeSetting(['remote_timeline', 'pinnedHosts'], pinnedHosts.remove(host)));
|
||||
return dispatch(changeSetting(['remote_timeline', 'pinnedHosts'], pinnedHosts.toOrderedSet().remove(host)));
|
||||
};
|
||||
|
||||
export {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
import { defineMessage } from 'react-intl';
|
||||
import { createSelector } from 'reselect';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
@ -169,7 +169,7 @@ const defaultSettings = ImmutableMap({
|
|||
]),
|
||||
|
||||
remote_timeline: ImmutableMap({
|
||||
pinnedHosts: ImmutableOrderedSet(),
|
||||
pinnedHosts: ImmutableList(),
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
|
@ -3,18 +3,22 @@ import React from 'react';
|
|||
|
||||
interface ISelect extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
||||
children: Iterable<React.ReactNode>;
|
||||
full?: boolean;
|
||||
}
|
||||
|
||||
/** Multiple-select dropdown. */
|
||||
const Select = React.forwardRef<HTMLSelectElement, ISelect>((props, ref) => {
|
||||
const { children, className, ...filteredProps } = props;
|
||||
const { children, className, full = true, ...filteredProps } = props;
|
||||
|
||||
return (
|
||||
<select
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
'w-full truncate rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-primary-500 focus:outline-none focus:ring-primary-500 disabled:opacity-50 black:bg-black sm:text-sm dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:focus:border-primary-500 dark:focus:ring-primary-500',
|
||||
'truncate rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-primary-500 focus:outline-none focus:ring-primary-500 disabled:opacity-50 black:bg-black sm:text-sm dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:focus:border-primary-500 dark:focus:ring-primary-500',
|
||||
className,
|
||||
{
|
||||
'w-full': full,
|
||||
},
|
||||
)}
|
||||
{...filteredProps}
|
||||
>
|
||||
|
|
122
src/features/edit-identity/index.tsx
Normal file
122
src/features/edit-identity/index.tsx
Normal file
|
@ -0,0 +1,122 @@
|
|||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { patchMe } from 'soapbox/actions/me';
|
||||
import List, { ListItem } from 'soapbox/components/list';
|
||||
import { Button, Column, Emoji, HStack, Icon, Input, Tooltip } from 'soapbox/components/ui';
|
||||
import { useNostr } from 'soapbox/contexts/nostr-context';
|
||||
import { useNostrReq } from 'soapbox/features/nostr/hooks/useNostrReq';
|
||||
import { useAppDispatch, useInstance, useOwnAccount } from 'soapbox/hooks';
|
||||
import toast from 'soapbox/toast';
|
||||
|
||||
interface IEditIdentity {
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'settings.edit_identity', defaultMessage: 'Identity' },
|
||||
username: { id: 'edit_profile.fields.nip05_label', defaultMessage: 'Username' },
|
||||
unverified: { id: 'edit_profile.fields.nip05_unverified', defaultMessage: 'Name could not be verified and won\'t be used.' },
|
||||
success: { id: 'edit_profile.success', defaultMessage: 'Your profile has been successfully saved!' },
|
||||
error: { id: 'edit_profile.error', defaultMessage: 'Profile update failed' },
|
||||
});
|
||||
|
||||
/** EditIdentity component. */
|
||||
const EditIdentity: React.FC<IEditIdentity> = () => {
|
||||
const intl = useIntl();
|
||||
const instance = useInstance();
|
||||
const dispatch = useAppDispatch();
|
||||
const { account } = useOwnAccount();
|
||||
const { relay, signer } = useNostr();
|
||||
|
||||
const admin = instance.nostr?.pubkey;
|
||||
const pubkey = account?.nostr?.pubkey;
|
||||
const [username, setUsername] = useState<string>('');
|
||||
|
||||
const { events: labels } = useNostrReq(
|
||||
(admin && pubkey)
|
||||
? [{ kinds: [1985], authors: [admin], '#L': ['nip05'], '#p': [pubkey] }]
|
||||
: [],
|
||||
);
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
const updateNip05 = async (nip05: string): Promise<void> => {
|
||||
if (account.source?.nostr?.nip05 === nip05) return;
|
||||
try {
|
||||
await dispatch(patchMe({ nip05 }));
|
||||
toast.success(intl.formatMessage(messages.success));
|
||||
} catch (e) {
|
||||
toast.error(intl.formatMessage(messages.error));
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
if (!admin || !signer || !relay) return;
|
||||
|
||||
const event = await signer.signEvent({
|
||||
kind: 5950,
|
||||
content: '',
|
||||
tags: [
|
||||
['i', `${username}@${instance.domain}`, 'text'],
|
||||
['p', admin],
|
||||
],
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
|
||||
await relay.event(event);
|
||||
};
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.title)}>
|
||||
<List>
|
||||
{labels.map((label) => {
|
||||
const identifier = label.tags.find(([name]) => name === 'l')?.[1];
|
||||
if (!identifier) return null;
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
key={identifier}
|
||||
label={
|
||||
<HStack alignItems='center' space={2}>
|
||||
<span>{identifier}</span>
|
||||
{(account.source?.nostr?.nip05 === identifier && account.acct !== identifier) && (
|
||||
<Tooltip text={intl.formatMessage(messages.unverified)}>
|
||||
<div>
|
||||
<Emoji className='h-4 w-4' emoji='⚠️' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</HStack>
|
||||
}
|
||||
isSelected={account.source?.nostr?.nip05 === identifier}
|
||||
onSelect={() => updateNip05(identifier)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<ListItem label={<UsernameInput value={username} onChange={(e) => setUsername(e.target.value)} />}>
|
||||
<Button theme='accent' onClick={submit}>Add</Button>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
const UsernameInput: React.FC<React.ComponentProps<typeof Input>> = (props) => {
|
||||
const intl = useIntl();
|
||||
const instance = useInstance();
|
||||
|
||||
return (
|
||||
<Input
|
||||
placeholder={intl.formatMessage(messages.username)}
|
||||
append={(
|
||||
<HStack alignItems='center' space={1} className='rounded p-1 text-sm backdrop-blur'>
|
||||
<Icon className='h-4 w-4' src={require('@tabler/icons/at.svg')} />
|
||||
<span>{instance.domain}</span>
|
||||
</HStack>
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditIdentity;
|
|
@ -325,7 +325,7 @@ const EditProfile: React.FC = () => {
|
|||
type='text'
|
||||
value={data.nip05}
|
||||
onChange={handleTextChange('nip05')}
|
||||
placeholder={intl.formatMessage(messages.nip05Placeholder, { domain: location.host })}
|
||||
placeholder={intl.formatMessage(messages.nip05Placeholder, { domain: instance.domain })}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
|
|
|
@ -5,10 +5,10 @@ import Icon from 'soapbox/components/icon';
|
|||
import { HStack, Stack, Text } from 'soapbox/components/ui';
|
||||
import { useInstance } from 'soapbox/hooks';
|
||||
|
||||
import type { Map as ImmutableMap } from 'immutable';
|
||||
import type { RemoteInstance } from 'soapbox/selectors';
|
||||
|
||||
const hasRestrictions = (remoteInstance: ImmutableMap<string, any>): boolean => {
|
||||
const { accept, reject_deletes, report_removal, ...federation } = remoteInstance.get('federation');
|
||||
const hasRestrictions = (remoteInstance: RemoteInstance): boolean => {
|
||||
const { accept, reject_deletes, report_removal, ...federation } = remoteInstance.federation;
|
||||
return !!Object.values(federation).reduce((acc, value) => Boolean(acc || value), false);
|
||||
};
|
||||
|
||||
|
@ -30,7 +30,7 @@ const Restriction: React.FC<IRestriction> = ({ icon, children }) => {
|
|||
};
|
||||
|
||||
interface IInstanceRestrictions {
|
||||
remoteInstance: ImmutableMap<string, any>;
|
||||
remoteInstance: RemoteInstance;
|
||||
}
|
||||
|
||||
const InstanceRestrictions: React.FC<IInstanceRestrictions> = ({ remoteInstance }) => {
|
||||
|
@ -46,7 +46,7 @@ const InstanceRestrictions: React.FC<IInstanceRestrictions> = ({ remoteInstance
|
|||
followers_only,
|
||||
media_nsfw,
|
||||
media_removal,
|
||||
} = remoteInstance.get('federation').toJS();
|
||||
} = remoteInstance.federation;
|
||||
|
||||
const fullMediaRemoval = media_removal && avatar_removal && banner_removal;
|
||||
const partialMediaRemoval = media_removal || avatar_removal || banner_removal;
|
||||
|
@ -108,10 +108,10 @@ const InstanceRestrictions: React.FC<IInstanceRestrictions> = ({ remoteInstance
|
|||
const renderContent = () => {
|
||||
if (!instance || !remoteInstance) return null;
|
||||
|
||||
const host = remoteInstance.get('host');
|
||||
const host = remoteInstance.host;
|
||||
const siteTitle = instance.title;
|
||||
|
||||
if (remoteInstance.getIn(['federation', 'reject']) === true) {
|
||||
if (remoteInstance.federation.reject === true) {
|
||||
return (
|
||||
<Restriction icon={require('@tabler/icons/shield-x.svg')}>
|
||||
<FormattedMessage
|
||||
|
|
|
@ -27,8 +27,8 @@ const RestrictedInstance: React.FC<IRestrictedInstance> = ({ host }) => {
|
|||
<div>
|
||||
<a href='#' className='flex items-center gap-1 py-2.5 no-underline' onClick={toggleExpanded}>
|
||||
<Icon src={expanded ? require('@tabler/icons/caret-down.svg') : require('@tabler/icons/caret-right.svg')} />
|
||||
<div className={clsx({ 'line-through': remoteInstance.getIn(['federation', 'reject']) })}>
|
||||
{remoteInstance.get('host')}
|
||||
<div className={clsx({ 'line-through': remoteInstance.federation.reject })}>
|
||||
{remoteInstance.host}
|
||||
</div>
|
||||
</a>
|
||||
<div
|
||||
|
|
80
src/features/nostr-relays/components/relay-editor.tsx
Normal file
80
src/features/nostr-relays/components/relay-editor.tsx
Normal file
|
@ -0,0 +1,80 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { HStack, Input, Select } from 'soapbox/components/ui';
|
||||
import Streamfield, { StreamfieldComponent } from 'soapbox/components/ui/streamfield/streamfield';
|
||||
import { useInstance } from 'soapbox/hooks';
|
||||
|
||||
interface IRelayEditor {
|
||||
relays: RelayData[];
|
||||
setRelays: (relays: RelayData[]) => void;
|
||||
}
|
||||
|
||||
const RelayEditor: React.FC<IRelayEditor> = ({ relays, setRelays }) => {
|
||||
const handleAddRelay = (): void => {
|
||||
setRelays([...relays, { url: '' }]);
|
||||
};
|
||||
|
||||
const handleRemoveRelay = (i: number): void => {
|
||||
const newRelays = [...relays];
|
||||
newRelays.splice(i, 1);
|
||||
setRelays(newRelays);
|
||||
};
|
||||
|
||||
return (
|
||||
<Streamfield
|
||||
values={relays}
|
||||
onChange={setRelays}
|
||||
component={RelayField}
|
||||
onAddItem={handleAddRelay}
|
||||
onRemoveItem={handleRemoveRelay}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
interface RelayData {
|
||||
url: string;
|
||||
marker?: 'read' | 'write';
|
||||
}
|
||||
|
||||
const RelayField: StreamfieldComponent<RelayData> = ({ value, onChange }) => {
|
||||
const instance = useInstance();
|
||||
|
||||
const handleChange = (key: string): React.ChangeEventHandler<HTMLInputElement> => {
|
||||
return e => {
|
||||
onChange({ ...value, [key]: e.currentTarget.value });
|
||||
};
|
||||
};
|
||||
|
||||
const handleMarkerChange = (e: React.ChangeEvent<HTMLSelectElement>): void => {
|
||||
onChange({ ...value, marker: (e.currentTarget.value as 'read' | 'write' | '') || undefined });
|
||||
};
|
||||
|
||||
return (
|
||||
<HStack space={2} grow>
|
||||
<Input
|
||||
type='text'
|
||||
outerClassName='w-full grow'
|
||||
value={value.url}
|
||||
onChange={handleChange('url')}
|
||||
placeholder={instance.nostr?.relay ?? `wss://${instance.domain}/relay`}
|
||||
/>
|
||||
|
||||
<Select className='mt-1' full={false} onChange={handleMarkerChange}>
|
||||
<option value='' selected={value.marker === undefined}>
|
||||
<FormattedMessage id='nostr_relays.read_write' defaultMessage='Read & write' />
|
||||
</option>
|
||||
<option value='read' selected={value.marker === 'read'}>
|
||||
<FormattedMessage id='nostr_relays.read_only' defaultMessage='Read-only' />
|
||||
</option>
|
||||
<option value='write' selected={value.marker === 'write'}>
|
||||
<FormattedMessage id='nostr_relays.write_only' defaultMessage='Write-only' />
|
||||
</option>
|
||||
</Select>
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default RelayEditor;
|
||||
|
||||
export type { RelayData };
|
74
src/features/nostr-relays/index.tsx
Normal file
74
src/features/nostr-relays/index.tsx
Normal file
|
@ -0,0 +1,74 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { Button, Column, Form, FormActions, Stack } from 'soapbox/components/ui';
|
||||
import { useNostr } from 'soapbox/contexts/nostr-context';
|
||||
import { useNostrReq } from 'soapbox/features/nostr/hooks/useNostrReq';
|
||||
import { useOwnAccount } from 'soapbox/hooks';
|
||||
|
||||
import RelayEditor, { RelayData } from './components/relay-editor';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'nostr_relays.title', defaultMessage: 'Relays' },
|
||||
});
|
||||
|
||||
const NostrRelays = () => {
|
||||
const intl = useIntl();
|
||||
const { account } = useOwnAccount();
|
||||
const { relay, signer } = useNostr();
|
||||
|
||||
const { events } = useNostrReq(
|
||||
account?.nostr
|
||||
? [{ kinds: [10002], authors: [account?.nostr.pubkey], limit: 1 }]
|
||||
: [],
|
||||
);
|
||||
|
||||
const [relays, setRelays] = useState<RelayData[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
const tags = events[0]?.tags ?? [];
|
||||
const data = tags.map(tag => ({ url: tag[1], marker: tag[2] as 'read' | 'write' | undefined }));
|
||||
setRelays(data);
|
||||
}, [events[0]]);
|
||||
|
||||
const handleSubmit = async (): Promise<void> => {
|
||||
if (!signer || !relay) return;
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
const event = await signer.signEvent({
|
||||
kind: 10002,
|
||||
tags: relays.map(relay => relay.marker ? ['r', relay.url, relay.marker] : ['r', relay.url]),
|
||||
content: '',
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line compat/compat
|
||||
await relay.event(event, { signal: AbortSignal.timeout(1000) });
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.title)}>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Stack space={4}>
|
||||
<RelayEditor relays={relays} setRelays={setRelays} />
|
||||
|
||||
<FormActions>
|
||||
<Button to='/settings' theme='tertiary'>
|
||||
<FormattedMessage id='common.cancel' defaultMessage='Cancel' />
|
||||
</Button>
|
||||
|
||||
<Button theme='primary' type='submit' disabled={isLoading}>
|
||||
<FormattedMessage id='edit_profile.save' defaultMessage='Save' />
|
||||
</Button>
|
||||
</FormActions>
|
||||
</Stack>
|
||||
</Form>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
export default NostrRelays;
|
|
@ -1,14 +1,17 @@
|
|||
import { NostrEvent, NostrFilter } from '@soapbox/nspec';
|
||||
import { NSet, NostrEvent, NostrFilter } from '@soapbox/nspec';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useNostr } from 'soapbox/contexts/nostr-context';
|
||||
import { useForceUpdate } from 'soapbox/hooks/useForceUpdate';
|
||||
|
||||
/** Streams events from the relay for the given filters. */
|
||||
export function useNostrReq(filters: NostrFilter[]): { events: NostrEvent[]; eose: boolean; closed: boolean } {
|
||||
const { relay } = useNostr();
|
||||
|
||||
const [events, setEvents] = useState<NostrEvent[]>([]);
|
||||
const nset = useRef<NSet>(new NSet());
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
const [closed, setClosed] = useState(false);
|
||||
const [eose, setEose] = useState(false);
|
||||
|
||||
|
@ -21,7 +24,8 @@ export function useNostrReq(filters: NostrFilter[]): { events: NostrEvent[]; eos
|
|||
(async () => {
|
||||
for await (const msg of relay.req(value, { signal })) {
|
||||
if (msg[0] === 'EVENT') {
|
||||
setEvents((prev) => [msg[2], ...prev]);
|
||||
nset.current.add(msg[2]);
|
||||
forceUpdate();
|
||||
} else if (msg[0] === 'EOSE') {
|
||||
setEose(true);
|
||||
} else if (msg[0] === 'CLOSED') {
|
||||
|
@ -41,7 +45,7 @@ export function useNostrReq(filters: NostrFilter[]): { events: NostrEvent[]; eos
|
|||
}, [relay, value]);
|
||||
|
||||
return {
|
||||
events,
|
||||
events: [...nset.current],
|
||||
eose,
|
||||
closed,
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ const PinnedHostsPicker: React.FC<IPinnedHostsPicker> = ({ host: activeHost }) =
|
|||
|
||||
return (
|
||||
<HStack className='mb-4' space={2}>
|
||||
{pinnedHosts.map((host: any) => (
|
||||
{pinnedHosts.map((host) => (
|
||||
<Button
|
||||
key={host}
|
||||
to={`/timeline/${host}`}
|
||||
|
|
|
@ -20,6 +20,8 @@ const messages = defineMessages({
|
|||
configureMfa: { id: 'settings.configure_mfa', defaultMessage: 'Configure MFA' },
|
||||
deleteAccount: { id: 'settings.delete_account', defaultMessage: 'Delete Account' },
|
||||
editProfile: { id: 'settings.edit_profile', defaultMessage: 'Edit Profile' },
|
||||
editIdentity: { id: 'settings.edit_identity', defaultMessage: 'Identity' },
|
||||
editRelays: { id: 'nostr_relays.title', defaultMessage: 'Relays' },
|
||||
exportData: { id: 'column.export_data', defaultMessage: 'Export data' },
|
||||
importData: { id: 'navigation_bar.import_data', defaultMessage: 'Import data' },
|
||||
mfaDisabled: { id: 'mfa.disabled', defaultMessage: 'Disabled' },
|
||||
|
@ -65,6 +67,12 @@ const Settings = () => {
|
|||
<ListItem label={intl.formatMessage(messages.editProfile)} to='/settings/profile'>
|
||||
<span className='max-w-full truncate'>{displayName}</span>
|
||||
</ListItem>
|
||||
{features.nip05 && (
|
||||
<ListItem label={intl.formatMessage(messages.editIdentity)} to='/settings/identity'>
|
||||
<span className='max-w-full truncate'>{account?.source?.nostr?.nip05}</span>
|
||||
</ListItem>
|
||||
)}
|
||||
{features.nostr && <ListItem label={intl.formatMessage(messages.editRelays)} to='/settings/relays' />}
|
||||
</List>
|
||||
</CardBody>
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ const InstanceInfoPanel: React.FC<IInstanceInfoPanel> = ({ host }) => {
|
|||
|
||||
return (
|
||||
<Widget
|
||||
title={remoteInstance.get('host')}
|
||||
title={remoteInstance.host}
|
||||
onActionClick={handlePinHost}
|
||||
actionIcon={pinned ? require('@tabler/icons/pinned-off.svg') : require('@tabler/icons/pin.svg')}
|
||||
actionTitle={intl.formatMessage(pinned ? messages.unpinHost : messages.pinHost, { host })}
|
||||
|
|
|
@ -33,7 +33,7 @@ const EditFederationModal: React.FC<IEditFederationModal> = ({ host, onClose })
|
|||
const [data, setData] = useState<Record<string, any>>({});
|
||||
|
||||
useEffect(() => {
|
||||
setData(remoteInstance.get('federation') as Record<string, any>);
|
||||
setData(remoteInstance.federation);
|
||||
}, [remoteInstance]);
|
||||
|
||||
const handleDataChange = (key: string): React.ChangeEventHandler<HTMLInputElement> => {
|
||||
|
|
|
@ -138,7 +138,9 @@ import {
|
|||
ExternalLogin,
|
||||
LandingTimeline,
|
||||
BookmarkFolders,
|
||||
EditIdentity,
|
||||
Domains,
|
||||
NostrRelays,
|
||||
DraftStatuses,
|
||||
} from './util/async-components';
|
||||
import GlobalHotkeys from './util/global-hotkeys';
|
||||
|
@ -309,11 +311,13 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = ({ children }) =>
|
|||
<WrappedRoute path='/draft_statuses' page={DefaultPage} component={DraftStatuses} content={children} />
|
||||
|
||||
<WrappedRoute path='/settings/profile' page={DefaultPage} component={EditProfile} content={children} />
|
||||
{features.nip05 && <WrappedRoute path='/settings/identity' page={DefaultPage} component={EditIdentity} content={children} />}
|
||||
{features.exportData && <WrappedRoute path='/settings/export' page={DefaultPage} component={ExportData} content={children} />}
|
||||
{features.importData && <WrappedRoute path='/settings/import' page={DefaultPage} component={ImportData} content={children} />}
|
||||
{features.accountAliases && <WrappedRoute path='/settings/aliases' page={DefaultPage} component={Aliases} content={children} />}
|
||||
{features.accountMoving && <WrappedRoute path='/settings/migration' page={DefaultPage} component={Migration} content={children} />}
|
||||
{features.backups && <WrappedRoute path='/settings/backups' page={DefaultPage} component={Backups} content={children} />}
|
||||
<WrappedRoute path='/settings/relays' page={DefaultPage} component={NostrRelays} content={children} />
|
||||
<WrappedRoute path='/settings/email' page={DefaultPage} component={EditEmail} content={children} />
|
||||
<WrappedRoute path='/settings/password' page={DefaultPage} component={EditPassword} content={children} />
|
||||
<WrappedRoute path='/settings/account' page={DefaultPage} component={DeleteAccount} content={children} />
|
||||
|
|
|
@ -167,6 +167,8 @@ export const NostrLoginModal = lazy(() => import('soapbox/features/ui/components
|
|||
export const BookmarkFolders = lazy(() => import('soapbox/features/bookmark-folders'));
|
||||
export const EditBookmarkFolderModal = lazy(() => import('soapbox/features/ui/components/modals/edit-bookmark-folder-modal'));
|
||||
export const SelectBookmarkFolderModal = lazy(() => import('soapbox/features/ui/components/modals/select-bookmark-folder-modal'));
|
||||
export const EditIdentity = lazy(() => import('soapbox/features/edit-identity'));
|
||||
export const Domains = lazy(() => import('soapbox/features/admin/domains'));
|
||||
export const EditDomainModal = lazy(() => import('soapbox/features/ui/components/modals/edit-domain-modal'));
|
||||
export const NostrRelays = lazy(() => import('soapbox/features/nostr-relays'));
|
||||
export const DraftStatuses = lazy(() => import('soapbox/features/draft-statuses'));
|
||||
|
|
11
src/hooks/useForceUpdate.ts
Normal file
11
src/hooks/useForceUpdate.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
|
||||
export function useForceUpdate(): () => void {
|
||||
const [, setState] = useState(false);
|
||||
|
||||
const forceUpdate = useCallback(() => {
|
||||
setState(prevState => !prevState);
|
||||
}, []);
|
||||
|
||||
return forceUpdate;
|
||||
}
|
|
@ -641,6 +641,7 @@
|
|||
"edit_profile.fields.meta_fields_label": "Profile fields",
|
||||
"edit_profile.fields.nip05_label": "Username",
|
||||
"edit_profile.fields.nip05_placeholder": "user@{domain}",
|
||||
"edit_profile.fields.nip05_unverified": "Name could not be verified and won't be used.",
|
||||
"edit_profile.fields.stranger_notifications_label": "Block notifications from strangers",
|
||||
"edit_profile.fields.website_label": "Website",
|
||||
"edit_profile.fields.website_placeholder": "Display a Link",
|
||||
|
@ -1123,6 +1124,10 @@
|
|||
"new_group_panel.title": "Create Group",
|
||||
"nostr_extension.found": "<link>Sign in</link> with browser extension.",
|
||||
"nostr_extension.not_found": "Browser extension not found.",
|
||||
"nostr_relays.read_only": "Read-only",
|
||||
"nostr_relays.read_write": "Read & write",
|
||||
"nostr_relays.title": "Relays",
|
||||
"nostr_relays.write_only": "Write-only",
|
||||
"nostr_signup.key-add.title": "Import Key",
|
||||
"nostr_signup.key.title": "You need a key to continue",
|
||||
"nostr_signup.keygen.title": "Your new key",
|
||||
|
@ -1385,6 +1390,7 @@
|
|||
"settings.change_password": "Change Password",
|
||||
"settings.configure_mfa": "Configure MFA",
|
||||
"settings.delete_account": "Delete Account",
|
||||
"settings.edit_identity": "Identity",
|
||||
"settings.edit_profile": "Edit Profile",
|
||||
"settings.messages.label": "Allow users to start a new chat with you",
|
||||
"settings.mutes": "Mutes",
|
||||
|
|
|
@ -2,6 +2,7 @@ import {
|
|||
Map as ImmutableMap,
|
||||
List as ImmutableList,
|
||||
OrderedSet as ImmutableOrderedSet,
|
||||
Record as ImmutableRecord,
|
||||
fromJS,
|
||||
} from 'immutable';
|
||||
import { createSelector } from 'reselect';
|
||||
|
@ -305,7 +306,7 @@ const getRemoteInstanceFavicon = (state: RootState, host: string) => {
|
|||
return account?.pleroma?.favicon;
|
||||
};
|
||||
|
||||
type HostFederation = {
|
||||
export type HostFederation = {
|
||||
[key in keyof MRFSimple]: boolean;
|
||||
};
|
||||
|
||||
|
@ -328,19 +329,25 @@ export const makeGetHosts = () => {
|
|||
});
|
||||
};
|
||||
|
||||
export const makeGetRemoteInstance = () => {
|
||||
return createSelector([
|
||||
export const RemoteInstanceRecord = ImmutableRecord({
|
||||
host: '',
|
||||
favicon: null as string | null,
|
||||
federation: null as unknown as HostFederation,
|
||||
});
|
||||
|
||||
export type RemoteInstance = ReturnType<typeof RemoteInstanceRecord>;
|
||||
|
||||
export const makeGetRemoteInstance = () =>
|
||||
createSelector([
|
||||
(_state: RootState, host: string) => host,
|
||||
getRemoteInstanceFavicon,
|
||||
getRemoteInstanceFederation,
|
||||
], (host, favicon, federation) => {
|
||||
return ImmutableMap({
|
||||
], (host, favicon, federation) =>
|
||||
RemoteInstanceRecord({
|
||||
host,
|
||||
favicon,
|
||||
federation,
|
||||
});
|
||||
});
|
||||
};
|
||||
}));
|
||||
|
||||
type ColumnQuery = { type: string; prefix?: string };
|
||||
|
||||
|
|
|
@ -757,6 +757,9 @@ const getInstanceFeatures = (instance: Instance) => {
|
|||
*/
|
||||
nip05: v.software === DITTO,
|
||||
|
||||
/** Has a Nostr relay. */
|
||||
nostr: !!instance.nostr?.relay,
|
||||
|
||||
/**
|
||||
* Ability to sign Nostr events over websocket.
|
||||
* @see GET /api/v1/streaming?stream=nostr
|
||||
|
|
248
yarn.lock
248
yarn.lock
|
@ -1685,160 +1685,160 @@
|
|||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@lexical/clipboard@0.13.1", "@lexical/clipboard@^0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/clipboard/-/clipboard-0.13.1.tgz#ca132306129974ea2c9e51d6a8637f8fcffcdb3d"
|
||||
integrity sha512-gMSbVeqb7S+XAi/EMMlwl+FCurLPugN2jAXcp5k5ZaUd7be8B+iupbYdoKkjt4qBhxmvmfe9k46GoC0QOPl/nw==
|
||||
"@lexical/clipboard@0.14.2", "@lexical/clipboard@^0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/clipboard/-/clipboard-0.14.2.tgz#4638acdc80816500e87096646a98f24a34b94fa9"
|
||||
integrity sha512-WevZZ+VPpkvcgZZXxjHY2lc3+2kw+UA6q19MWdZA2y4NVQyeDmjxQp5uxdUWGTKTBzt22vlzeQ2jbtKz4wY3/g==
|
||||
dependencies:
|
||||
"@lexical/html" "0.13.1"
|
||||
"@lexical/list" "0.13.1"
|
||||
"@lexical/selection" "0.13.1"
|
||||
"@lexical/utils" "0.13.1"
|
||||
"@lexical/html" "0.14.2"
|
||||
"@lexical/list" "0.14.2"
|
||||
"@lexical/selection" "0.14.2"
|
||||
"@lexical/utils" "0.14.2"
|
||||
|
||||
"@lexical/code@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/code/-/code-0.13.1.tgz#e13688390582a4b63a639daff1f16bcb82aa854d"
|
||||
integrity sha512-QK77r3QgEtJy96ahYXNgpve8EY64BQgBSnPDOuqVrLdl92nPzjqzlsko2OZldlrt7gjXcfl9nqfhZ/CAhStfOg==
|
||||
"@lexical/code@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/code/-/code-0.14.2.tgz#e23443275b342e245684cc4e1889130edd746cbc"
|
||||
integrity sha512-XByweRm8flv/2nwwLbnZwskl2y6jWuDFuKB/8ywvSenMsGyr7OSg5ALJrsFuX5KUo9mEGGpaiM8UVeMoeaqMgg==
|
||||
dependencies:
|
||||
"@lexical/utils" "0.13.1"
|
||||
"@lexical/utils" "0.14.2"
|
||||
prismjs "^1.27.0"
|
||||
|
||||
"@lexical/dragon@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/dragon/-/dragon-0.13.1.tgz#32ba02bff4d8f02a6317d874671ee0b0a2dcdc53"
|
||||
integrity sha512-aNlqfif4//jW7gOxbBgdrbDovU6m3EwQrUw+Y/vqRkY+sWmloyAUeNwCPH1QP3Q5cvfolzOeN5igfBljsFr+1g==
|
||||
"@lexical/dragon@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/dragon/-/dragon-0.14.2.tgz#493af45501550227b0274ff8b7307a2d9ba0c38d"
|
||||
integrity sha512-yzYJ3HD/goZZW5+ckYGCidtQ5zwrT08yDPhNgFUh9eni2ePQ9b56x2Bko01urocIkEqA0d6VEtfoO+hAOHiq6Q==
|
||||
|
||||
"@lexical/hashtag@0.13.1", "@lexical/hashtag@^0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/hashtag/-/hashtag-0.13.1.tgz#eb273c199a0115ec0f0191c2449e97f512360f2e"
|
||||
integrity sha512-Dl0dUG4ZXNjYYuAUR0GMGpLGsA+cps2/ln3xEmy28bZR0sKkjXugsu2QOIxZjYIPBewDrXzPcvK8md45cMYoSg==
|
||||
"@lexical/hashtag@0.14.2", "@lexical/hashtag@^0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/hashtag/-/hashtag-0.14.2.tgz#7ae794480079c702e707319e7b588aec39b2b060"
|
||||
integrity sha512-H2z71iGX3n0ZB45y5uoI7aPwVjClo01df480IrTecKxr4aAKbK5LhFQWOphiUXBiv4O1sn+Xa88f8IpbfqQ5Vg==
|
||||
dependencies:
|
||||
"@lexical/utils" "0.13.1"
|
||||
"@lexical/utils" "0.14.2"
|
||||
|
||||
"@lexical/history@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/history/-/history-0.13.1.tgz#3bb54716dc69779d3b35894bd72637a7fc2ed284"
|
||||
integrity sha512-cZXt30MalEEiRaflE9tHeGYnwT1xSDjXLsf9M409DSU9POJyZ1fsULJrG1tWv2uFQOhwal33rve9+MatUlITrg==
|
||||
"@lexical/history@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/history/-/history-0.14.2.tgz#740a6dffd0c5250ada12ce13f514d0c3f7df960b"
|
||||
integrity sha512-k0gNIKCDeyIWEHIJKHZFfGVEN4yPID8tfuR4/R1M12WUwnga/X3regxPSMV8LE1SWbCz6iiQuaeZozxOJ3AlRw==
|
||||
dependencies:
|
||||
"@lexical/utils" "0.13.1"
|
||||
"@lexical/utils" "0.14.2"
|
||||
|
||||
"@lexical/html@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/html/-/html-0.13.1.tgz#e56035d0c6528ffb932390e0d3d357c82f69253a"
|
||||
integrity sha512-XkZrnCSHIUavtpMol6aG8YsJ5KqC9hMxEhAENf3HTGi3ocysCByyXOyt1EhEYpjJvgDG4wRqt25xGDbLjj1/sA==
|
||||
"@lexical/html@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/html/-/html-0.14.2.tgz#8b8f0d81d0afdea9865ef9fba814705daabbbf23"
|
||||
integrity sha512-5uL0wSfS9H5/HNeCM4QaJMekoL1w4D81361RlC2ppKt1diSzLiWOITX1qElaTcnDJBGez5mv1ZNiRTutYOPV4Q==
|
||||
dependencies:
|
||||
"@lexical/selection" "0.13.1"
|
||||
"@lexical/utils" "0.13.1"
|
||||
"@lexical/selection" "0.14.2"
|
||||
"@lexical/utils" "0.14.2"
|
||||
|
||||
"@lexical/link@0.13.1", "@lexical/link@^0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/link/-/link-0.13.1.tgz#f1c4c12c828c0251e5d7fb4fb336f2d62380fc57"
|
||||
integrity sha512-7E3B2juL2UoMj2n+CiyFZ7tlpsdViAoIE7MpegXwfe/VQ66wFwk/VxGTa/69ng2EoF7E0kh+SldvGQDrWAWb1g==
|
||||
"@lexical/link@0.14.2", "@lexical/link@^0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/link/-/link-0.14.2.tgz#78c220a81475505b17fb8fa6c5baff08a3ed3b5b"
|
||||
integrity sha512-XD4VdxtBm9Yx5vk2hDEDKY1BjgNVdfmxQHo6Y/kyImAHhGRiBWa6V1+l55qfgcjPW3tN2QY/gSKDCPQGk7vKJw==
|
||||
dependencies:
|
||||
"@lexical/utils" "0.13.1"
|
||||
"@lexical/utils" "0.14.2"
|
||||
|
||||
"@lexical/list@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/list/-/list-0.13.1.tgz#461cb989157bdf4a43eaa8596fdb09df60d114ee"
|
||||
integrity sha512-6U1pmNZcKLuOWiWRML8Raf9zSEuUCMlsOye82niyF6I0rpPgYo5UFghAAbGISDsyqzM1B2L4BgJ6XrCk/dJptg==
|
||||
"@lexical/list@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/list/-/list-0.14.2.tgz#b1d70e95e9b6a22f22b47f11ed7b667b1eeacdd2"
|
||||
integrity sha512-74MVHcYtTC5Plj+GGRV08uk9qbI1AaKc37NGLe3T08aVBqzqxXl1qZK9BhrM2mqTVXB98ZnOXkBk+07vke+b0Q==
|
||||
dependencies:
|
||||
"@lexical/utils" "0.13.1"
|
||||
"@lexical/utils" "0.14.2"
|
||||
|
||||
"@lexical/mark@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/mark/-/mark-0.13.1.tgz#084bb49a8bc1c5c5a4ed5c5d4a20c98ea85ec8b1"
|
||||
integrity sha512-dW27PW8wWDOKFqXTBUuUfV+umU0KfwvXGkPUAxRJrvwUWk5RKaS48LhgbNlQ5BfT84Q8dSiQzvbaa6T40t9a3A==
|
||||
"@lexical/mark@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/mark/-/mark-0.14.2.tgz#0f264d7f287c380e99a118eeba84a4bb94abc403"
|
||||
integrity sha512-8G1p2tuUkymWXvWgUUShZp5AgYIODUZrBYDpGsCBNkXuSdGagOirS5LhKeiT/68BnrPzGrlnCdmomnI/kMxh6w==
|
||||
dependencies:
|
||||
"@lexical/utils" "0.13.1"
|
||||
"@lexical/utils" "0.14.2"
|
||||
|
||||
"@lexical/markdown@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/markdown/-/markdown-0.13.1.tgz#1fd2efcacff4ce733682a8161a3f3d78dba37503"
|
||||
integrity sha512-6tbdme2h5Zy/M88loVQVH5G0Nt7VMR9UUkyiSaicyBRDOU2OHacaXEp+KSS/XuF+d7TA+v/SzyDq8HS77cO1wA==
|
||||
"@lexical/markdown@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/markdown/-/markdown-0.14.2.tgz#26ae02482708632bc2e0e4a662c39a443f8e3bdc"
|
||||
integrity sha512-U5P8ceEhiQqEKyy3dx4ldVBdoajquVndrZ4TvS6HJs8jeqOH49sLMvbKtNpXPL1plGvvwAjutQzEUdY+ifpGgw==
|
||||
dependencies:
|
||||
"@lexical/code" "0.13.1"
|
||||
"@lexical/link" "0.13.1"
|
||||
"@lexical/list" "0.13.1"
|
||||
"@lexical/rich-text" "0.13.1"
|
||||
"@lexical/text" "0.13.1"
|
||||
"@lexical/utils" "0.13.1"
|
||||
"@lexical/code" "0.14.2"
|
||||
"@lexical/link" "0.14.2"
|
||||
"@lexical/list" "0.14.2"
|
||||
"@lexical/rich-text" "0.14.2"
|
||||
"@lexical/text" "0.14.2"
|
||||
"@lexical/utils" "0.14.2"
|
||||
|
||||
"@lexical/offset@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/offset/-/offset-0.13.1.tgz#f37417822aef3dc81580d4abb96e43ba9d547225"
|
||||
integrity sha512-j/RZcztJ7dyTrfA2+C3yXDzWDXV+XmMpD5BYeQCEApaHvlo20PHt1BISk7RcrnQW8PdzGvpKblRWf//c08LS9w==
|
||||
"@lexical/offset@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/offset/-/offset-0.14.2.tgz#7cbade3817b55e305013a61698465df9f4e8b3ab"
|
||||
integrity sha512-bv6M+HITGNDuXvIELB1NLobRKoxtP1JPG3zDhKGPnLyjzFeHE5FZ6QbGc9y8ltrhUafVmxgzW/Es0IJXTsGf7Q==
|
||||
|
||||
"@lexical/overflow@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/overflow/-/overflow-0.13.1.tgz#42c036dc3ad3eb929fda5aa0a00a725b74f72669"
|
||||
integrity sha512-Uw34j+qG2UJRCIR+bykfFMduFk7Pc4r/kNt8N1rjxGuGXAsreTVch1iOhu7Ev6tJgkURsduKuaJCAi7iHnKl7g==
|
||||
"@lexical/overflow@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/overflow/-/overflow-0.14.2.tgz#63482f6941b0f21bf79639e36fd5711052e84adf"
|
||||
integrity sha512-2eHirK9GmGr7juMjeF54fZ8xPwdMMzni+FXC4NVMDWzzM3yjK3BCBK6AUJsU3o4Y4bV3mUvZp55x9Ys5lX3zOw==
|
||||
|
||||
"@lexical/plain-text@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/plain-text/-/plain-text-0.13.1.tgz#e7e713029443c30facce27b34836bf604cf92c0f"
|
||||
integrity sha512-4j5KAsMKUvJ8LhVDSS4zczbYXzdfmgYSAVhmqpSnJtud425Nk0TAfpUBLFoivxZB7KMoT1LGWQZvd47IvJPvtA==
|
||||
"@lexical/plain-text@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/plain-text/-/plain-text-0.14.2.tgz#498306c83bbd5be24f6c6935670e77247763d866"
|
||||
integrity sha512-vHvJ+Dy+SpzZ0hVN3r+DONHoSn0WzuK6htwsV0IyaMyEeypxnFs6FB/znyWmoU+X2EAhDCu5O9npLDWmrJjCdQ==
|
||||
|
||||
"@lexical/react@^0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/react/-/react-0.13.1.tgz#6c35bf43e24560d2ca3aa2c6ff607ef37de87bac"
|
||||
integrity sha512-Sy6EL230KAb0RZsZf1dZrRrc3+rvCDQWltcd8C/cqBUYlxsLYCW9s4f3RB2werngD/PtLYbBB48SYXNkIALITA==
|
||||
"@lexical/react@^0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/react/-/react-0.14.2.tgz#2a6b812f0e07cba05b02ff2b8bdfb9b4afc05873"
|
||||
integrity sha512-HCsZv5yExA/8h7Ul6y5NcqUAGctL79A7qSz7YSlrwfw8EYyUHXW5LzeoFTWSl12SKs600PM+I8jLGXu72N6kfQ==
|
||||
dependencies:
|
||||
"@lexical/clipboard" "0.13.1"
|
||||
"@lexical/code" "0.13.1"
|
||||
"@lexical/dragon" "0.13.1"
|
||||
"@lexical/hashtag" "0.13.1"
|
||||
"@lexical/history" "0.13.1"
|
||||
"@lexical/link" "0.13.1"
|
||||
"@lexical/list" "0.13.1"
|
||||
"@lexical/mark" "0.13.1"
|
||||
"@lexical/markdown" "0.13.1"
|
||||
"@lexical/overflow" "0.13.1"
|
||||
"@lexical/plain-text" "0.13.1"
|
||||
"@lexical/rich-text" "0.13.1"
|
||||
"@lexical/selection" "0.13.1"
|
||||
"@lexical/table" "0.13.1"
|
||||
"@lexical/text" "0.13.1"
|
||||
"@lexical/utils" "0.13.1"
|
||||
"@lexical/yjs" "0.13.1"
|
||||
"@lexical/clipboard" "0.14.2"
|
||||
"@lexical/code" "0.14.2"
|
||||
"@lexical/dragon" "0.14.2"
|
||||
"@lexical/hashtag" "0.14.2"
|
||||
"@lexical/history" "0.14.2"
|
||||
"@lexical/link" "0.14.2"
|
||||
"@lexical/list" "0.14.2"
|
||||
"@lexical/mark" "0.14.2"
|
||||
"@lexical/markdown" "0.14.2"
|
||||
"@lexical/overflow" "0.14.2"
|
||||
"@lexical/plain-text" "0.14.2"
|
||||
"@lexical/rich-text" "0.14.2"
|
||||
"@lexical/selection" "0.14.2"
|
||||
"@lexical/table" "0.14.2"
|
||||
"@lexical/text" "0.14.2"
|
||||
"@lexical/utils" "0.14.2"
|
||||
"@lexical/yjs" "0.14.2"
|
||||
react-error-boundary "^3.1.4"
|
||||
|
||||
"@lexical/rich-text@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/rich-text/-/rich-text-0.13.1.tgz#8251e81a3985a4d76bef027cf6c0dc90c661e4ec"
|
||||
integrity sha512-HliB9Ync06mv9DBg/5j0lIsTJp+exLHlaLJe+n8Zq1QNTzZzu2LsIT/Crquk50In7K/cjtlaQ/d5RB0LkjMHYg==
|
||||
"@lexical/rich-text@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/rich-text/-/rich-text-0.14.2.tgz#d2ef208b5f5b0fb4021c61a00187ba8da016ce69"
|
||||
integrity sha512-KXxeVfzHw4volw5Tm/TXGCDH+OCvFm1QU17LK3ToAulN2M+j0745yxpudtRfcl96fIbB9THY2toAZpJ9YbMTNQ==
|
||||
|
||||
"@lexical/selection@0.13.1", "@lexical/selection@^0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/selection/-/selection-0.13.1.tgz#466d7cd0ee1b04680bd949112f1f5cb6a6618efa"
|
||||
integrity sha512-Kt9eSwjxPznj7yzIYipu9yYEgmRJhHiq3DNxHRxInYcZJWWNNHum2xKyxwwcN8QYBBzgfPegfM/geqQEJSV1lQ==
|
||||
"@lexical/selection@0.14.2", "@lexical/selection@^0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/selection/-/selection-0.14.2.tgz#39d6c0db233790eec81125653503e20deadc13a9"
|
||||
integrity sha512-M122XXGEiBgaxEhL63d+su0pPri67/GlFIwGC/j3D0TN4Giyt/j0ToHhqvlIF6TfuXlBusIYbSuJ19ny12lCEg==
|
||||
|
||||
"@lexical/table@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/table/-/table-0.13.1.tgz#814d3b8a2afb821aff151c92cce831809f9d67a1"
|
||||
integrity sha512-VQzgkfkEmnvn6C64O/kvl0HI3bFoBh3WA/U67ALw+DS11Mb5CKjbt0Gzm/258/reIxNMpshjjicpWMv9Miwauw==
|
||||
"@lexical/table@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/table/-/table-0.14.2.tgz#a28e0e24dfc8c43ac4ccf53dd33c1292969740d6"
|
||||
integrity sha512-iwsZ5AqkM7RGyU38daK0XgpC8DG0TlEqEYsXhOLjCpAERY/+bgfdjxP8YWtUV5eIgHX0yY7FkqCUZUJSEcbUeA==
|
||||
dependencies:
|
||||
"@lexical/utils" "0.13.1"
|
||||
"@lexical/utils" "0.14.2"
|
||||
|
||||
"@lexical/text@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/text/-/text-0.13.1.tgz#12104d42da7a707a19853679f3a88e8ed6ce8084"
|
||||
integrity sha512-NYy3TZKt3qzReDwN2Rr5RxyFlg84JjXP2JQGMrXSSN7wYe73ysQIU6PqdVrz4iZkP+w34F3pl55dJ24ei3An9w==
|
||||
"@lexical/text@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/text/-/text-0.14.2.tgz#5a6fdd78ac3ec62baa23bbb3466046b7b18f2dfb"
|
||||
integrity sha512-5N1Bwr75EGdP/4izQRzoxyKpozy+0YixGJOo3OoldlX9/j+Sf5XiCFj3Ifgfl5dygZ0oITQk4duHpzjPHez59Q==
|
||||
|
||||
"@lexical/utils@0.13.1", "@lexical/utils@^0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/utils/-/utils-0.13.1.tgz#f2a72f71c859933781294830b38b25b5b33122a9"
|
||||
integrity sha512-AtQQKzYymkbOaQxaBXjRBS8IPxF9zWQnqwHTUTrJqJ4hX71aIQd/thqZbfQETAFJfC8pNBZw5zpxN6yPHk23dQ==
|
||||
"@lexical/utils@0.14.2", "@lexical/utils@^0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/utils/-/utils-0.14.2.tgz#7ecb2e16f2a395c96be6de0128bcb7de54589872"
|
||||
integrity sha512-IGknsaSyQbBJYKJJrrjNPaZuQPsJmFqGrCmNR6DcQNenWrFnmAliQPFA7HbszwRSxOFTo/BCAsIgXRQob6RjOQ==
|
||||
dependencies:
|
||||
"@lexical/list" "0.13.1"
|
||||
"@lexical/selection" "0.13.1"
|
||||
"@lexical/table" "0.13.1"
|
||||
"@lexical/list" "0.14.2"
|
||||
"@lexical/selection" "0.14.2"
|
||||
"@lexical/table" "0.14.2"
|
||||
|
||||
"@lexical/yjs@0.13.1":
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/yjs/-/yjs-0.13.1.tgz#2a71ae3c4b3cc5c660bbe66d537eb0cbf3c7c1b6"
|
||||
integrity sha512-4GbqQM+PwNTV59AZoNrfTe/0rLjs+cX6Y6yAdZSRPBwr5L3JzYeU1TTcFCVQTtsE7KF8ddVP8sD7w9pi8rOWLA==
|
||||
"@lexical/yjs@0.14.2":
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/@lexical/yjs/-/yjs-0.14.2.tgz#30226dfb51a44195e7dfb1bf064198f80c629c07"
|
||||
integrity sha512-0AKwQYRx7KvAKTdizoAXd9XZhkTO6l3LmHRvsu70YAHVZUEb9tVQBM7lp/dtBBQF0SII/7uKwrluX6Vi/apWYA==
|
||||
dependencies:
|
||||
"@lexical/offset" "0.13.1"
|
||||
"@lexical/offset" "0.14.2"
|
||||
|
||||
"@mdn/browser-compat-data@^5.2.34", "@mdn/browser-compat-data@^5.3.13":
|
||||
version "5.3.16"
|
||||
|
@ -6183,10 +6183,10 @@ levn@^0.4.1:
|
|||
prelude-ls "^1.2.1"
|
||||
type-check "~0.4.0"
|
||||
|
||||
lexical@^0.13.1:
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/lexical/-/lexical-0.13.1.tgz#0abffe9bc05a7a9da8a6128ea478bf08c11654db"
|
||||
integrity sha512-jaqRYzVEfBKbX4FwYpd/g+MyOjRaraAel0iQsTrwvx3hyN0bswUZuzb6H6nGlFSjcdrc77wKpyKwoWj4aUd+Bw==
|
||||
lexical@^0.14.2:
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/lexical/-/lexical-0.14.2.tgz#592e0e16ef415a4423be7ff65ac995fc4da40c90"
|
||||
integrity sha512-Uxe0jD2T4XY/+WKiVgnV6OH/GmsF1I0YStcSuMR3Alfhnv5MEYuCa482zo+S5zOPjB1x9j/b+TOLtZEMArwELw==
|
||||
|
||||
li@^1.3.0:
|
||||
version "1.3.0"
|
||||
|
|
Loading…
Reference in a new issue