From 47492bac9aed9c68178aac5df153ded606eae1b8 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 25 Mar 2024 11:19:23 -0500 Subject: [PATCH 01/18] EditProfile: use instance.domain in nip05 placeholder --- src/features/edit-profile/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/edit-profile/index.tsx b/src/features/edit-profile/index.tsx index 03fe961aa..a01fbe816 100644 --- a/src/features/edit-profile/index.tsx +++ b/src/features/edit-profile/index.tsx @@ -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 })} /> )} From bd552ab084f2c42f02072691fb16016797ffa7a5 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 25 Mar 2024 11:20:02 -0500 Subject: [PATCH 02/18] Add EditIdentity page (wip) --- src/features/edit-identity/index.tsx | 64 ++++++++++++++++++++++++ src/features/settings/index.tsx | 4 ++ src/features/ui/index.tsx | 2 + src/features/ui/util/async-components.ts | 1 + 4 files changed, 71 insertions(+) create mode 100644 src/features/edit-identity/index.tsx diff --git a/src/features/edit-identity/index.tsx b/src/features/edit-identity/index.tsx new file mode 100644 index 000000000..2a8854fc8 --- /dev/null +++ b/src/features/edit-identity/index.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { defineMessages, useIntl } from 'react-intl'; + +import List, { ListItem } from 'soapbox/components/list'; +import { Button, Column, HStack, Icon, Input } from 'soapbox/components/ui'; +import { useInstance, useOwnAccount } from 'soapbox/hooks'; + +interface IEditIdentity { +} + +const messages = defineMessages({ + title: { id: 'settings.edit_identity', defaultMessage: 'Identity' }, +}); + +const identifiers = [ + 'alex@alexgleason.me', + 'lunk@alexgleason.me', + 'yolo@alexgleason.me', +]; + +/** EditIdentity component. */ +const EditIdentity: React.FC = () => { + const intl = useIntl(); + const { account } = useOwnAccount(); + + if (!account) return null; + + return ( + + + {identifiers.map((identifier) => ( + { /* TODO */ }} + /> + ))} + }> + + + + + ); +}; + +const UsernameInput: React.FC> = (props) => { + const instance = useInstance(); + + return ( + + + {instance.domain} + + )} + {...props} + /> + ); +}; + +export default EditIdentity; \ No newline at end of file diff --git a/src/features/settings/index.tsx b/src/features/settings/index.tsx index 434445c4e..b426a0f96 100644 --- a/src/features/settings/index.tsx +++ b/src/features/settings/index.tsx @@ -20,6 +20,7 @@ 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' }, 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 +66,9 @@ const Settings = () => { {displayName} + + {account?.source?.nostr?.nip05} + diff --git a/src/features/ui/index.tsx b/src/features/ui/index.tsx index 4d7fdf793..8cae1fce5 100644 --- a/src/features/ui/index.tsx +++ b/src/features/ui/index.tsx @@ -137,6 +137,7 @@ import { ExternalLogin, LandingTimeline, BookmarkFolders, + EditIdentity, } from './util/async-components'; import GlobalHotkeys from './util/global-hotkeys'; import { WrappedRoute } from './util/react-router-helpers'; @@ -305,6 +306,7 @@ const SwitchingColumnsArea: React.FC = ({ children }) => {features.scheduledStatuses && } + {features.exportData && } {features.importData && } {features.accountAliases && } diff --git a/src/features/ui/util/async-components.ts b/src/features/ui/util/async-components.ts index 4660eb047..e5de03050 100644 --- a/src/features/ui/util/async-components.ts +++ b/src/features/ui/util/async-components.ts @@ -167,3 +167,4 @@ 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')); \ No newline at end of file From fb3af2f74b40e2780e2667e058444097289b593f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 25 Mar 2024 11:57:27 -0500 Subject: [PATCH 03/18] UsernameInput: reuse i18n message from nip05 profile field --- src/features/edit-identity/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/features/edit-identity/index.tsx b/src/features/edit-identity/index.tsx index 2a8854fc8..0b26631d2 100644 --- a/src/features/edit-identity/index.tsx +++ b/src/features/edit-identity/index.tsx @@ -10,6 +10,7 @@ interface IEditIdentity { const messages = defineMessages({ title: { id: 'settings.edit_identity', defaultMessage: 'Identity' }, + username: { id: 'edit_profile.fields.nip05_label', defaultMessage: 'Username' }, }); const identifiers = [ @@ -36,7 +37,7 @@ const EditIdentity: React.FC = () => { onSelect={() => { /* TODO */ }} /> ))} - }> + }> @@ -45,11 +46,12 @@ const EditIdentity: React.FC = () => { }; const UsernameInput: React.FC> = (props) => { + const intl = useIntl(); const instance = useInstance(); return ( From 51978c83f089813f45f5a9a48f9e547a862fd5b1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 25 Mar 2024 12:43:36 -0500 Subject: [PATCH 04/18] EditIdentity: save nip05 to profile --- src/features/edit-identity/index.tsx | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/features/edit-identity/index.tsx b/src/features/edit-identity/index.tsx index 0b26631d2..8efcdd1fd 100644 --- a/src/features/edit-identity/index.tsx +++ b/src/features/edit-identity/index.tsx @@ -1,9 +1,11 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; +import { patchMe } from 'soapbox/actions/me'; import List, { ListItem } from 'soapbox/components/list'; import { Button, Column, HStack, Icon, Input } from 'soapbox/components/ui'; -import { useInstance, useOwnAccount } from 'soapbox/hooks'; +import { useAppDispatch, useInstance, useOwnAccount } from 'soapbox/hooks'; +import toast from 'soapbox/toast'; interface IEditIdentity { } @@ -11,6 +13,8 @@ interface IEditIdentity { const messages = defineMessages({ title: { id: 'settings.edit_identity', defaultMessage: 'Identity' }, username: { id: 'edit_profile.fields.nip05_label', defaultMessage: 'Username' }, + success: { id: 'edit_profile.success', defaultMessage: 'Your profile has been successfully saved!' }, + error: { id: 'edit_profile.error', defaultMessage: 'Profile update failed' }, }); const identifiers = [ @@ -22,10 +26,20 @@ const identifiers = [ /** EditIdentity component. */ const EditIdentity: React.FC = () => { const intl = useIntl(); + const dispatch = useAppDispatch(); const { account } = useOwnAccount(); if (!account) return null; + const updateNip05 = async (nip05: string): Promise => { + try { + await dispatch(patchMe({ nip05 })); + toast.success(intl.formatMessage(messages.success)); + } catch (e) { + toast.error(intl.formatMessage(messages.error)); + } + }; + return ( @@ -33,8 +47,8 @@ const EditIdentity: React.FC = () => { { /* TODO */ }} + isSelected={account.source?.nostr?.nip05 === identifier} + onSelect={() => updateNip05(identifier)} /> ))} }> From 4370a772a6ee0f92eab916681f7b0987be6d2247 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 25 Mar 2024 13:10:48 -0500 Subject: [PATCH 05/18] EditIdentity: add exclamation emoji for unverified name --- src/features/edit-identity/index.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/features/edit-identity/index.tsx b/src/features/edit-identity/index.tsx index 8efcdd1fd..8263e0ca9 100644 --- a/src/features/edit-identity/index.tsx +++ b/src/features/edit-identity/index.tsx @@ -3,7 +3,7 @@ import { defineMessages, useIntl } from 'react-intl'; import { patchMe } from 'soapbox/actions/me'; import List, { ListItem } from 'soapbox/components/list'; -import { Button, Column, HStack, Icon, Input } from 'soapbox/components/ui'; +import { Button, Column, Emoji, HStack, Icon, Input, Tooltip } from 'soapbox/components/ui'; import { useAppDispatch, useInstance, useOwnAccount } from 'soapbox/hooks'; import toast from 'soapbox/toast'; @@ -13,6 +13,7 @@ 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' }, }); @@ -32,6 +33,7 @@ const EditIdentity: React.FC = () => { if (!account) return null; const updateNip05 = async (nip05: string): Promise => { + if (account.source?.nostr?.nip05 === nip05) return; try { await dispatch(patchMe({ nip05 })); toast.success(intl.formatMessage(messages.success)); @@ -46,7 +48,18 @@ const EditIdentity: React.FC = () => { {identifiers.map((identifier) => ( + {identifier} + {(account.source?.nostr?.nip05 === identifier && account.acct !== identifier) && ( + +
+ +
+
+ )} + + } isSelected={account.source?.nostr?.nip05 === identifier} onSelect={() => updateNip05(identifier)} /> From c02f23c322c1dafd0ecc34d226a59f6b2d9622fc Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 27 Mar 2024 09:52:07 -0500 Subject: [PATCH 06/18] EditIdentity: pull identifiers from Nostr --- src/features/edit-identity/index.tsx | 84 ++++++++++++++++++---------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/src/features/edit-identity/index.tsx b/src/features/edit-identity/index.tsx index 8263e0ca9..8cd48db22 100644 --- a/src/features/edit-identity/index.tsx +++ b/src/features/edit-identity/index.tsx @@ -1,9 +1,11 @@ -import React from 'react'; +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'; @@ -18,17 +20,22 @@ const messages = defineMessages({ error: { id: 'edit_profile.error', defaultMessage: 'Profile update failed' }, }); -const identifiers = [ - 'alex@alexgleason.me', - 'lunk@alexgleason.me', - 'yolo@alexgleason.me', -]; - /** EditIdentity component. */ const EditIdentity: React.FC = () => { const intl = useIntl(); + const instance = useInstance(); const dispatch = useAppDispatch(); const { account } = useOwnAccount(); + const { relay, signer } = useNostr(); + + const admin = instance.nostr?.pubkey; + const [username, setUsername] = useState(''); + + const { events: labels } = useNostrReq( + admin + ? [{ kinds: [1985], authors: [admin], '#L': ['nip05'] }] + : [], + ); if (!account) return null; @@ -42,30 +49,51 @@ const EditIdentity: React.FC = () => { } }; + 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 ( - {identifiers.map((identifier) => ( - - {identifier} - {(account.source?.nostr?.nip05 === identifier && account.acct !== identifier) && ( - -
- -
-
- )} - - } - isSelected={account.source?.nostr?.nip05 === identifier} - onSelect={() => updateNip05(identifier)} - /> - ))} - }> - + {labels.map((label) => { + const identifier = label.tags.find(([name]) => name === 'l')?.[1]; + if (!identifier) return null; + + return ( + + {identifier} + {(account.source?.nostr?.nip05 === identifier && account.acct !== identifier) && ( + +
+ +
+
+ )} + + } + isSelected={account.source?.nostr?.nip05 === identifier} + onSelect={() => updateNip05(identifier)} + /> + ); + })} + setUsername(e.target.value)} />}> +
From 37816b0a8c8d57c09b8b6761378e4568ee8dfe28 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 27 Mar 2024 09:53:45 -0500 Subject: [PATCH 07/18] EditIdentity: only pull current user's pubkey --- src/features/edit-identity/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/features/edit-identity/index.tsx b/src/features/edit-identity/index.tsx index 8cd48db22..c5dc68ab3 100644 --- a/src/features/edit-identity/index.tsx +++ b/src/features/edit-identity/index.tsx @@ -29,11 +29,12 @@ const EditIdentity: React.FC = () => { const { relay, signer } = useNostr(); const admin = instance.nostr?.pubkey; + const pubkey = account?.nostr?.pubkey; const [username, setUsername] = useState(''); const { events: labels } = useNostrReq( - admin - ? [{ kinds: [1985], authors: [admin], '#L': ['nip05'] }] + (admin && pubkey) + ? [{ kinds: [1985], authors: [admin], '#L': ['nip05'], '#p': [pubkey] }] : [], ); From bd46f1230b7c813efebc64e6c3685b0e19240c82 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 27 Mar 2024 15:19:00 -0500 Subject: [PATCH 08/18] useNostrReq: deduplicate events with NSet --- src/features/nostr/hooks/useNostrReq.ts | 12 ++++++++---- src/hooks/useForceUpdate.ts | 11 +++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 src/hooks/useForceUpdate.ts diff --git a/src/features/nostr/hooks/useNostrReq.ts b/src/features/nostr/hooks/useNostrReq.ts index 654c814f3..72e1187ac 100644 --- a/src/features/nostr/hooks/useNostrReq.ts +++ b/src/features/nostr/hooks/useNostrReq.ts @@ -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([]); + const nset = useRef(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, }; diff --git a/src/hooks/useForceUpdate.ts b/src/hooks/useForceUpdate.ts new file mode 100644 index 000000000..50a84a468 --- /dev/null +++ b/src/hooks/useForceUpdate.ts @@ -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; +} From 70fea445baac2ea3ea996f3b54fb6fd146b310c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Wed, 27 Mar 2024 22:57:54 +0100 Subject: [PATCH 09/18] Upgrade lexical to 1.14.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- package.json | 14 +-- yarn.lock | 248 +++++++++++++++++++++++++-------------------------- 2 files changed, 131 insertions(+), 131 deletions(-) diff --git a/package.json b/package.json index bde744cfc..2ffe7dfce 100644 --- a/package.json +++ b/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", diff --git a/yarn.lock b/yarn.lock index 13c73713a..c4ed986bf 100644 --- a/yarn.lock +++ b/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" From aea4c216896feefed85a8b7f4e33e84d97926ece Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 27 Mar 2024 17:52:34 -0500 Subject: [PATCH 10/18] Feature-gate EditIdentity screen --- src/features/settings/index.tsx | 8 +++++--- src/features/ui/index.tsx | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/features/settings/index.tsx b/src/features/settings/index.tsx index b426a0f96..f04f311a8 100644 --- a/src/features/settings/index.tsx +++ b/src/features/settings/index.tsx @@ -66,9 +66,11 @@ const Settings = () => { {displayName} - - {account?.source?.nostr?.nip05} - + {features.nip05 && ( + + {account?.source?.nostr?.nip05} + + )}
diff --git a/src/features/ui/index.tsx b/src/features/ui/index.tsx index 527b3d915..b206a647b 100644 --- a/src/features/ui/index.tsx +++ b/src/features/ui/index.tsx @@ -307,7 +307,7 @@ const SwitchingColumnsArea: React.FC = ({ children }) => {features.scheduledStatuses && } - + {features.nip05 && } {features.exportData && } {features.importData && } {features.accountAliases && } From 06beaa54de9d1a3dc68c6c2d9307f9340928088b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 28 Mar 2024 13:48:38 -0500 Subject: [PATCH 11/18] yarn i18n --- src/locales/en.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/locales/en.json b/src/locales/en.json index 3bd8d6740..f1189955b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -634,6 +634,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", @@ -1376,6 +1377,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", From 66a304a3d27846f6ef7d9e30762d8d28c69bff46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 29 Mar 2024 23:57:36 +0100 Subject: [PATCH 12/18] Fix: Cannot Pin/Unpin Instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../components/instance-restrictions.tsx | 14 +++++------ .../components/restricted-instance.tsx | 4 ++-- .../ui/components/instance-info-panel.tsx | 2 +- .../modals/edit-federation-modal.tsx | 2 +- src/selectors/index.ts | 23 ++++++++++++------- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/features/federation-restrictions/components/instance-restrictions.tsx b/src/features/federation-restrictions/components/instance-restrictions.tsx index bb0b2c2ec..344dc766d 100644 --- a/src/features/federation-restrictions/components/instance-restrictions.tsx +++ b/src/features/federation-restrictions/components/instance-restrictions.tsx @@ -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): 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 = ({ icon, children }) => { }; interface IInstanceRestrictions { - remoteInstance: ImmutableMap; + remoteInstance: RemoteInstance; } const InstanceRestrictions: React.FC = ({ remoteInstance }) => { @@ -46,7 +46,7 @@ const InstanceRestrictions: React.FC = ({ 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 = ({ 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 ( = ({ host }) => {
-
- {remoteInstance.get('host')} +
+ {remoteInstance.host}
= ({ host }) => { return ( = ({ host, onClose }) const [data, setData] = useState>({}); useEffect(() => { - setData(remoteInstance.get('federation') as Record); + setData(remoteInstance.federation); }, [remoteInstance]); const handleDataChange = (key: string): React.ChangeEventHandler => { diff --git a/src/selectors/index.ts b/src/selectors/index.ts index 6d0809db8..6bd738e0e 100644 --- a/src/selectors/index.ts +++ b/src/selectors/index.ts @@ -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; + +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 }; From 77c41728e60f8cb387cba1aca1802196ce7b1b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 1 Apr 2024 15:20:54 +0200 Subject: [PATCH 13/18] Fix: Cannot Pin/Unpin Instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- src/actions/remote-timeline.ts | 8 ++++---- src/actions/settings.ts | 4 ++-- .../remote-timeline/components/pinned-hosts-picker.tsx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/actions/remote-timeline.ts b/src/actions/remote-timeline.ts index cb21126b9..ac1277aec 100644 --- a/src/actions/remote-timeline.ts +++ b/src/actions/remote-timeline.ts @@ -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; + return settings.getIn(['remote_timeline', 'pinnedHosts']) as ImmutableList | ImmutableOrderedSet; }; 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 { diff --git a/src/actions/settings.ts b/src/actions/settings.ts index ae6a3ec35..ae26af50a 100644 --- a/src/actions/settings.ts +++ b/src/actions/settings.ts @@ -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(), }), }); diff --git a/src/features/remote-timeline/components/pinned-hosts-picker.tsx b/src/features/remote-timeline/components/pinned-hosts-picker.tsx index 2777aa6e3..16cd45481 100644 --- a/src/features/remote-timeline/components/pinned-hosts-picker.tsx +++ b/src/features/remote-timeline/components/pinned-hosts-picker.tsx @@ -16,7 +16,7 @@ const PinnedHostsPicker: React.FC = ({ host: activeHost }) = return ( - {pinnedHosts.map((host: any) => ( + {pinnedHosts.map((host) => ( + + + + + + + ); +}; + +export default NostrRelays; diff --git a/src/features/ui/index.tsx b/src/features/ui/index.tsx index b206a647b..5574196ab 100644 --- a/src/features/ui/index.tsx +++ b/src/features/ui/index.tsx @@ -139,6 +139,7 @@ import { BookmarkFolders, EditIdentity, Domains, + NostrRelays, } from './util/async-components'; import GlobalHotkeys from './util/global-hotkeys'; import { WrappedRoute } from './util/react-router-helpers'; @@ -313,6 +314,7 @@ const SwitchingColumnsArea: React.FC = ({ children }) => {features.accountAliases && } {features.accountMoving && } {features.backups && } + diff --git a/src/features/ui/util/async-components.ts b/src/features/ui/util/async-components.ts index b6dff63f3..c9fae695d 100644 --- a/src/features/ui/util/async-components.ts +++ b/src/features/ui/util/async-components.ts @@ -170,3 +170,4 @@ export const SelectBookmarkFolderModal = lazy(() => import('soapbox/features/ui/ 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')); \ No newline at end of file From bc6082d3f213abe77cc288b5b64a722eb30a40a2 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 1 Apr 2024 13:19:19 -0500 Subject: [PATCH 15/18] RelayEditor: allow setting the marker --- src/components/ui/select/select.tsx | 8 +++++-- .../nostr-relays/components/relay-editor.tsx | 21 +++++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/components/ui/select/select.tsx b/src/components/ui/select/select.tsx index b67ebe975..b74f8195e 100644 --- a/src/components/ui/select/select.tsx +++ b/src/components/ui/select/select.tsx @@ -3,18 +3,22 @@ import React from 'react'; interface ISelect extends React.SelectHTMLAttributes { children: Iterable; + full?: boolean; } /** Multiple-select dropdown. */ const Select = React.forwardRef((props, ref) => { - const { children, className, ...filteredProps } = props; + const { children, className, full = true, ...filteredProps } = props; return ( + + ); }; From 20f60e763c17a57c463e947412b13b2bcd13b494 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 2 Apr 2024 01:54:55 -0500 Subject: [PATCH 16/18] edit relays: hook up data --- src/features/nostr-relays/index.tsx | 35 ++++++++++++++++++++++++++--- src/features/settings/index.tsx | 2 ++ src/utils/features.ts | 3 +++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/features/nostr-relays/index.tsx b/src/features/nostr-relays/index.tsx index 2921390e7..e92f2dabe 100644 --- a/src/features/nostr-relays/index.tsx +++ b/src/features/nostr-relays/index.tsx @@ -1,7 +1,10 @@ -import React, { useState } from 'react'; +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'; @@ -11,13 +14,39 @@ const messages = defineMessages({ 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([]); const [isLoading, setIsLoading] = useState(false); - const handleSubmit = (): void => { + useEffect(() => { + const tags = events[0]?.tags ?? []; + const data = tags.map(tag => ({ url: tag[1], marker: tag[2] as 'read' | 'write' | undefined })); + setRelays(data); + }, [events]); + + const handleSubmit = async (): Promise => { + if (!signer || !relay) return; + setIsLoading(true); - // Save relays + + 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); }; diff --git a/src/features/settings/index.tsx b/src/features/settings/index.tsx index f04f311a8..f2e53b867 100644 --- a/src/features/settings/index.tsx +++ b/src/features/settings/index.tsx @@ -21,6 +21,7 @@ const messages = defineMessages({ 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' }, @@ -71,6 +72,7 @@ const Settings = () => { {account?.source?.nostr?.nip05} )} + {features.nostr && } diff --git a/src/utils/features.ts b/src/utils/features.ts index 37c7af53b..8c42da8f4 100644 --- a/src/utils/features.ts +++ b/src/utils/features.ts @@ -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 From 569f7e5b3621de3215cfab1f9d2768a40fd6a527 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 2 Apr 2024 01:56:29 -0500 Subject: [PATCH 17/18] yarn i18n --- src/locales/en.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/locales/en.json b/src/locales/en.json index f1189955b..83851b3bc 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1115,6 +1115,10 @@ "new_group_panel.title": "Create Group", "nostr_extension.found": "Sign in 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", From a17128c268439da0f44c39f057c4b26c584e567f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 2 Apr 2024 14:41:59 -0500 Subject: [PATCH 18/18] edit relays: fix useEffect --- src/features/nostr-relays/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/nostr-relays/index.tsx b/src/features/nostr-relays/index.tsx index e92f2dabe..e3842f2dc 100644 --- a/src/features/nostr-relays/index.tsx +++ b/src/features/nostr-relays/index.tsx @@ -30,7 +30,7 @@ const NostrRelays = () => { const tags = events[0]?.tags ?? []; const data = tags.map(tag => ({ url: tag[1], marker: tag[2] as 'read' | 'write' | undefined })); setRelays(data); - }, [events]); + }, [events[0]]); const handleSubmit = async (): Promise => { if (!signer || !relay) return;