From e8eefda48625c63a19cadff3476cb12dbb70f43b Mon Sep 17 00:00:00 2001 From: tassoman Date: Thu, 28 Sep 2023 08:03:40 +0200 Subject: [PATCH 001/100] updated: reply-indicator overflows verbose communications, during reply --- src/features/compose/components/reply-indicator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/compose/components/reply-indicator.tsx b/src/features/compose/components/reply-indicator.tsx index fdc7c359b..3847f4be9 100644 --- a/src/features/compose/components/reply-indicator.tsx +++ b/src/features/compose/components/reply-indicator.tsx @@ -36,7 +36,7 @@ const ReplyIndicator: React.FC = ({ className, status, hideActi } return ( - + Date: Mon, 2 Oct 2023 05:05:20 +0000 Subject: [PATCH 002/100] fix(deps): update dependency @floating-ui/react to ^0.26.0 --- package.json | 2 +- yarn.lock | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b93eb9ce6..cfe53c48b 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.22.15", "@emoji-mart/data": "^1.1.2", - "@floating-ui/react": "^0.25.0", + "@floating-ui/react": "^0.26.0", "@fontsource/inter": "^5.0.0", "@fontsource/roboto-mono": "^5.0.0", "@gamestdio/websocket": "^0.3.2", diff --git a/yarn.lock b/yarn.lock index c732c13a8..838d535f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1407,13 +1407,13 @@ dependencies: "@floating-ui/dom" "^1.5.1" -"@floating-ui/react@^0.25.0": - version "0.25.4" - resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.25.4.tgz#82507e14460aee70f435ad2fd717ea182b6d5c61" - integrity sha512-lWRQ/UiTvSIBxohn0/2HFHEmnmOVRjl7j6XcRJuLH0ls6f/9AyHMWVzkAJFuwx0n9gaEeCmg9VccCSCJzbEJig== +"@floating-ui/react@^0.26.0": + version "0.26.0" + resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.0.tgz#f33317652d382f21ce2584c43bb009154e6b7700" + integrity sha512-W70xgicegogSoy+8Hfmpd/NWEuL26vsatIHkpVydmigJ84YYhs5/GlBCkLcETWajCjD9XKwlHUv6ezwbLLiung== dependencies: "@floating-ui/react-dom" "^2.0.2" - "@floating-ui/utils" "^0.1.1" + "@floating-ui/utils" "^0.1.5" tabbable "^6.0.1" "@floating-ui/utils@^0.1.1": @@ -1421,6 +1421,11 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.2.tgz#b7e9309ccce5a0a40ac482cb894f120dba2b357f" integrity sha512-ou3elfqG/hZsbmF4bxeJhPHIf3G2pm0ujc39hYEZrfVqt7Vk/Zji6CXc3W0pmYM8BW1g40U+akTl9DKZhFhInQ== +"@floating-ui/utils@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.5.tgz#f0bada210a75fdf41101c48ddcc291e1b33b3f47" + integrity sha512-3lClsx2F3ei6hup0LYFbbm+NH87qVTX/6T63IllEFCLjT7XCxmbgBM42sXf8LTZx0CE5VpRRUnISUbqSlsxGSA== + "@fontsource/inter@^5.0.0": version "5.0.8" resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-5.0.8.tgz#61b50cb0eb72b14ae1938d47c4a9a91546d2a50c" From ff55272d590d53771fde284d2381c5c9a8844171 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 2 Oct 2023 10:56:45 -0500 Subject: [PATCH 003/100] Timeline: put ScrollTopButton into a Portal --- src/features/ui/components/timeline.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/features/ui/components/timeline.tsx b/src/features/ui/components/timeline.tsx index a4d7d44e6..051d18122 100644 --- a/src/features/ui/components/timeline.tsx +++ b/src/features/ui/components/timeline.tsx @@ -6,6 +6,7 @@ import { defineMessages } from 'react-intl'; import { dequeueTimeline, scrollTopTimeline } from 'soapbox/actions/timelines'; import ScrollTopButton from 'soapbox/components/scroll-top-button'; import StatusList, { IStatusList } from 'soapbox/components/status-list'; +import { Portal } from 'soapbox/components/ui'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; import { makeGetStatusIds } from 'soapbox/selectors'; @@ -51,12 +52,14 @@ const Timeline: React.FC = ({ return ( <> - + + + Date: Mon, 2 Oct 2023 11:14:36 -0500 Subject: [PATCH 004/100] Notifications: render ScrollTopButton in a Portal --- src/features/notifications/index.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/features/notifications/index.tsx b/src/features/notifications/index.tsx index 794073330..8e62ffd7f 100644 --- a/src/features/notifications/index.tsx +++ b/src/features/notifications/index.tsx @@ -14,7 +14,7 @@ import { getSettings } from 'soapbox/actions/settings'; import PullToRefresh from 'soapbox/components/pull-to-refresh'; import ScrollTopButton from 'soapbox/components/scroll-top-button'; import ScrollableList from 'soapbox/components/scrollable-list'; -import { Column } from 'soapbox/components/ui'; +import { Column, Portal } from 'soapbox/components/ui'; import PlaceholderNotification from 'soapbox/features/placeholder/components/placeholder-notification'; import { useAppDispatch, useAppSelector, useSettings } from 'soapbox/hooks'; @@ -176,11 +176,15 @@ const Notifications = () => { return ( {filterBarContainer} - + + + + + {scrollContainer} From 264082b434fe26a103609c0a22813ef82a06ab7a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 2 Oct 2023 11:22:04 -0500 Subject: [PATCH 005/100] ScrollTopButton: a11y, to ); }; From 4e26adb789f26446ac8e25b900bd6fb5d3dcdd5e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 2 Oct 2023 11:23:04 -0500 Subject: [PATCH 006/100] ScrollTopButton: swap out legacy Icon for UI Icon --- src/components/scroll-top-button.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/scroll-top-button.tsx b/src/components/scroll-top-button.tsx index dd42d10ff..4498e3657 100644 --- a/src/components/scroll-top-button.tsx +++ b/src/components/scroll-top-button.tsx @@ -3,8 +3,7 @@ import throttle from 'lodash/throttle'; import React, { useState, useEffect, useCallback } from 'react'; import { useIntl, MessageDescriptor } from 'react-intl'; -import Icon from 'soapbox/components/icon'; -import { Text } from 'soapbox/components/ui'; +import { Icon, Text } from 'soapbox/components/ui'; import { useSettings } from 'soapbox/hooks'; interface IScrollTopButton { @@ -87,7 +86,10 @@ const ScrollTopButton: React.FC = ({ className='flex cursor-pointer items-center space-x-1.5 whitespace-nowrap rounded-full bg-primary-600 px-4 py-2 text-white transition-transform hover:scale-105 hover:bg-primary-700 active:scale-100' onClick={handleClick} > - + {(count > 0) && ( From 2380a0b667e3f9cafd1fffef285e8b4d1488501f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 2 Oct 2023 11:24:07 -0500 Subject: [PATCH 007/100] ScrollTopButton: render null when not visible --- src/components/scroll-top-button.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/components/scroll-top-button.tsx b/src/components/scroll-top-button.tsx index 4498e3657..0ce00ffc6 100644 --- a/src/components/scroll-top-button.tsx +++ b/src/components/scroll-top-button.tsx @@ -1,4 +1,3 @@ -import clsx from 'clsx'; import throttle from 'lodash/throttle'; import React, { useState, useEffect, useCallback } from 'react'; import { useIntl, MessageDescriptor } from 'react-intl'; @@ -35,10 +34,6 @@ const ScrollTopButton: React.FC = ({ const visible = count > 0 && scrolled; - const classes = clsx('fixed left-1/2 top-20 z-50 -translate-x-1/2', { - 'hidden': !visible, - }); - const getScrollTop = (): number => { return (document.scrollingElement || document.documentElement).scrollTop; }; @@ -80,8 +75,12 @@ const ScrollTopButton: React.FC = ({ maybeUnload(); }, [count]); + if (!visible) { + return null; + } + return ( -
+
); From 460a6e14c328becc54b49164ba934cc43be34ec3 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 2 Oct 2023 11:50:20 -0500 Subject: [PATCH 008/100] ScrollTopButton: make better use of callbacks, keep `scrolledTop` state --- src/components/scroll-top-button.tsx | 39 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/components/scroll-top-button.tsx b/src/components/scroll-top-button.tsx index 0ce00ffc6..4e2437b12 100644 --- a/src/components/scroll-top-button.tsx +++ b/src/components/scroll-top-button.tsx @@ -29,39 +29,40 @@ const ScrollTopButton: React.FC = ({ const intl = useIntl(); const settings = useSettings(); + // Whether we are scrolled past the `threshold`. const [scrolled, setScrolled] = useState(false); - const autoload = settings.get('autoloadTimelines') === true; + // Whether we are scrolled above the `autoloadThreshold`. + const [scrolledTop, setScrolledTop] = useState(true); + const autoload = settings.get('autoloadTimelines') === true; const visible = count > 0 && scrolled; + /** Number of pixels scrolled down from the top of the page. */ const getScrollTop = (): number => { return (document.scrollingElement || document.documentElement).scrollTop; }; - const maybeUnload = () => { - if (autoload && getScrollTop() <= autoloadThreshold) { + /** Unload feed items if scrolled to the top. */ + const maybeUnload = useCallback(() => { + if (autoload && scrolledTop) { onClick(); } - }; + }, [autoload, scrolledTop, onClick]); + /** Set state while scrolling. */ const handleScroll = useCallback(throttle(() => { - maybeUnload(); + const scrollTop = getScrollTop(); - if (getScrollTop() > threshold) { - setScrolled(true); - } else { - setScrolled(false); - } - }, 150, { trailing: true }), [autoload, threshold, autoloadThreshold, onClick]); + setScrolled(scrollTop > threshold); + setScrolledTop(scrollTop <= autoloadThreshold); - const scrollUp = () => { + }, 150, { trailing: true }), [threshold, autoloadThreshold]); + + /** Scroll to top and trigger `onClick`. */ + const handleClick: React.MouseEventHandler = useCallback(() => { window.scrollTo({ top: 0 }); - }; - - const handleClick: React.MouseEventHandler = () => { - setTimeout(scrollUp, 10); onClick(); - }; + }, [onClick]); useEffect(() => { window.addEventListener('scroll', handleScroll); @@ -69,11 +70,11 @@ const ScrollTopButton: React.FC = ({ return () => { window.removeEventListener('scroll', handleScroll); }; - }, [onClick]); + }, [handleScroll]); useEffect(() => { maybeUnload(); - }, [count]); + }, [maybeUnload]); if (!visible) { return null; From 74c231b59be1eac08f47009ef89306b0bee90c6f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 2 Oct 2023 12:22:09 -0500 Subject: [PATCH 009/100] Stop re-rendering the ScrollTopButton so much --- src/features/notifications/index.tsx | 8 ++++---- src/features/ui/components/timeline.tsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/features/notifications/index.tsx b/src/features/notifications/index.tsx index 8e62ffd7f..0d9f7f608 100644 --- a/src/features/notifications/index.tsx +++ b/src/features/notifications/index.tsx @@ -104,13 +104,13 @@ const Notifications = () => { }); }; - const handleDequeueNotifications = () => { + const handleDequeueNotifications = useCallback(() => { dispatch(dequeueNotifications()); - }; + }, []); - const handleRefresh = () => { + const handleRefresh = useCallback(() => { return dispatch(expandNotifications()); - }; + }, []); useEffect(() => { handleDequeueNotifications(); diff --git a/src/features/ui/components/timeline.tsx b/src/features/ui/components/timeline.tsx index 051d18122..4db80bf90 100644 --- a/src/features/ui/components/timeline.tsx +++ b/src/features/ui/components/timeline.tsx @@ -38,9 +38,9 @@ const Timeline: React.FC = ({ const hasMore = useAppSelector(state => state.timelines.get(timelineId)?.hasMore === true); const totalQueuedItemsCount = useAppSelector(state => state.timelines.get(timelineId)?.totalQueuedItemsCount || 0); - const handleDequeueTimeline = () => { + const handleDequeueTimeline = useCallback(() => { dispatch(dequeueTimeline(timelineId, onLoadMore)); - }; + }, []); const handleScrollToTop = useCallback(debounce(() => { dispatch(scrollTopTimeline(timelineId, true)); From 7d0c699fe37622bb4ddc9e765d4987468dc7315b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 2 Oct 2023 13:25:59 -0500 Subject: [PATCH 010/100] ScrollTopButton: delay adding the scroll listener so the feed doesn't jump when navigating back --- src/components/scroll-top-button.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/scroll-top-button.tsx b/src/components/scroll-top-button.tsx index 4e2437b12..84f23969e 100644 --- a/src/components/scroll-top-button.tsx +++ b/src/components/scroll-top-button.tsx @@ -32,7 +32,7 @@ const ScrollTopButton: React.FC = ({ // Whether we are scrolled past the `threshold`. const [scrolled, setScrolled] = useState(false); // Whether we are scrolled above the `autoloadThreshold`. - const [scrolledTop, setScrolledTop] = useState(true); + const [scrolledTop, setScrolledTop] = useState(false); const autoload = settings.get('autoloadTimelines') === true; const visible = count > 0 && scrolled; @@ -65,7 +65,12 @@ const ScrollTopButton: React.FC = ({ }, [onClick]); useEffect(() => { - window.addEventListener('scroll', handleScroll); + // Delay adding the scroll listener so navigating back doesn't + // unload feed items before the feed is rendered. + setTimeout(() => { + window.addEventListener('scroll', handleScroll); + handleScroll(); + }, 250); return () => { window.removeEventListener('scroll', handleScroll); From 645ce60a5fc53749b4ccd8dbed8952cf51ba4686 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 2 Oct 2023 13:54:02 -0500 Subject: [PATCH 011/100] eslint: use semicolon TypeScript delimeter --- .eslintrc.cjs | 12 +- scripts/translationRunner.ts | 16 +-- src/actions/admin.ts | 4 +- src/actions/compose.ts | 96 +++++++-------- src/actions/events.ts | 8 +- src/actions/export-data.ts | 4 +- src/actions/filters.ts | 2 +- src/actions/import-data.ts | 6 +- src/actions/me.ts | 4 +- src/actions/onboarding.ts | 4 +- src/actions/push-notifications/registerer.ts | 8 +- src/actions/reports.ts | 6 +- src/actions/rules.ts | 6 +- src/actions/settings.ts | 8 +- src/actions/streaming.ts | 22 ++-- src/actions/timelines.ts | 20 +-- src/api/hooks/accounts/useAccount.ts | 2 +- src/api/hooks/accounts/useAccountList.ts | 2 +- src/api/hooks/accounts/useAccountLookup.ts | 2 +- src/api/hooks/accounts/useFollow.ts | 6 +- src/api/hooks/accounts/useRelationship.ts | 2 +- src/api/hooks/groups/useCreateGroup.ts | 14 +-- src/api/hooks/groups/useGroupValidation.ts | 4 +- src/api/hooks/groups/useUpdateGroup.ts | 14 +-- src/api/hooks/streaming/useCommunityStream.ts | 4 +- src/api/hooks/streaming/usePublicStream.ts | 2 +- src/api/hooks/streaming/useRemoteStream.ts | 4 +- src/components/__mocks__/react-inlinesvg.tsx | 2 +- src/components/account-search.tsx | 4 +- src/components/account.tsx | 58 ++++----- src/components/animated-number.tsx | 4 +- .../announcements/announcement-content.tsx | 2 +- src/components/announcements/announcement.tsx | 8 +- src/components/announcements/emoji.tsx | 6 +- src/components/announcements/reaction.tsx | 12 +- .../announcements/reactions-bar.tsx | 10 +- src/components/attachment-thumbs.tsx | 6 +- src/components/authorize-reject-buttons.tsx | 20 +-- src/components/autosuggest-account-input.tsx | 20 +-- src/components/autosuggest-emoji.tsx | 2 +- src/components/autosuggest-input.tsx | 34 +++--- src/components/autosuggest-location.tsx | 2 +- src/components/autosuggest-textarea.tsx | 34 +++--- src/components/avatar-stack.tsx | 4 +- src/components/badge.tsx | 4 +- src/components/big-card.tsx | 6 +- src/components/birthday-input.tsx | 24 ++-- src/components/birthday-panel.tsx | 2 +- src/components/blurhash.tsx | 10 +- src/components/copyable-input.tsx | 2 +- src/components/display-name.tsx | 6 +- src/components/domain.tsx | 2 +- .../dropdown-menu/dropdown-menu-item.tsx | 28 ++--- .../dropdown-menu/dropdown-menu.tsx | 20 +-- src/components/error-boundary.tsx | 10 +- src/components/event-preview.tsx | 8 +- src/components/extended-video-player.tsx | 16 +-- src/components/fork-awesome-icon.tsx | 6 +- src/components/group-card.tsx | 2 +- src/components/groups/group-avatar.tsx | 6 +- .../groups/popover/group-popover.tsx | 6 +- src/components/hashtag.tsx | 2 +- src/components/helmet.tsx | 2 +- src/components/hoc/group-lookup-hoc.tsx | 10 +- src/components/hover-ref-wrapper.tsx | 8 +- src/components/hover-status-wrapper.tsx | 8 +- src/components/icon-button.tsx | 14 +-- src/components/icon-with-counter.tsx | 8 +- src/components/icon.tsx | 8 +- src/components/list.tsx | 16 +-- src/components/load-gap.tsx | 6 +- src/components/load-more.tsx | 8 +- src/components/location-search.tsx | 2 +- src/components/media-gallery.tsx | 66 +++++----- src/components/missing-indicator.tsx | 2 +- src/components/modal-root.tsx | 8 +- src/components/navlinks.tsx | 2 +- src/components/outline-box.tsx | 4 +- src/components/pending-items-row.tsx | 6 +- src/components/polls/poll-footer.tsx | 6 +- src/components/polls/poll-option.tsx | 16 +-- src/components/polls/poll.tsx | 4 +- src/components/profile-hover-card.tsx | 2 +- src/components/progress-circle.tsx | 8 +- src/components/pull-to-refresh.tsx | 8 +- src/components/pullable.tsx | 2 +- src/components/quoted-status.tsx | 6 +- src/components/radio.tsx | 14 +-- src/components/relative-timestamp.tsx | 10 +- src/components/safe-embed.tsx | 8 +- src/components/scroll-top-button.tsx | 10 +- src/components/scrollable-list.tsx | 48 ++++---- src/components/sidebar-menu.tsx | 10 +- src/components/sidebar-navigation-link.tsx | 12 +- src/components/site-logo.tsx | 4 +- src/components/status-action-bar.tsx | 12 +- src/components/status-action-button.tsx | 20 +-- src/components/status-content.tsx | 12 +- src/components/status-hover-card.tsx | 2 +- src/components/status-list.tsx | 26 ++-- src/components/status-media.tsx | 10 +- src/components/status-reaction-wrapper.tsx | 4 +- src/components/status-reply-mentions.tsx | 4 +- src/components/status.tsx | 34 +++--- .../statuses/sensitive-content-overlay.tsx | 6 +- src/components/statuses/status-info.tsx | 6 +- src/components/still-image.tsx | 16 +-- src/components/thumb-navigation-link.tsx | 14 +-- src/components/tombstone.tsx | 6 +- src/components/translate-button.tsx | 2 +- src/components/ui/accordion/accordion.tsx | 16 +-- src/components/ui/avatar/avatar.tsx | 2 +- src/components/ui/banner/banner.tsx | 6 +- src/components/ui/button/button.tsx | 22 ++-- src/components/ui/button/useButtonStyles.ts | 8 +- src/components/ui/card/card.tsx | 24 ++-- src/components/ui/carousel/carousel.tsx | 10 +- src/components/ui/column/column.tsx | 20 +-- src/components/ui/counter/counter.tsx | 4 +- src/components/ui/datepicker/datepicker.tsx | 2 +- src/components/ui/divider/divider.tsx | 4 +- .../ui/emoji-selector/emoji-selector.tsx | 22 ++-- src/components/ui/emoji/emoji.tsx | 2 +- .../ui/form-actions/form-actions.tsx | 2 +- src/components/ui/form-group/form-group.tsx | 10 +- src/components/ui/form/form.tsx | 6 +- src/components/ui/hstack/hstack.tsx | 18 +-- src/components/ui/icon-button/icon-button.tsx | 10 +- src/components/ui/icon/icon.tsx | 14 +-- src/components/ui/icon/svg-icon.tsx | 8 +- src/components/ui/input/input.tsx | 24 ++-- src/components/ui/layout/layout.tsx | 12 +- src/components/ui/menu/menu.tsx | 4 +- src/components/ui/modal/modal.tsx | 36 +++--- src/components/ui/popover/popover.tsx | 10 +- src/components/ui/portal/portal.tsx | 2 +- .../ui/progress-bar/progress-bar.tsx | 4 +- .../ui/radio-button/radio-button.tsx | 10 +- src/components/ui/select/select.tsx | 2 +- src/components/ui/slider/slider.tsx | 6 +- src/components/ui/spinner/spinner.tsx | 4 +- src/components/ui/stack/stack.tsx | 12 +- src/components/ui/streamfield/streamfield.tsx | 24 ++-- src/components/ui/tabs/tabs.tsx | 34 +++--- src/components/ui/tag-input/tag-input.tsx | 6 +- src/components/ui/tag-input/tag.tsx | 4 +- src/components/ui/text/text.tsx | 28 ++--- src/components/ui/textarea/textarea.tsx | 28 ++--- src/components/ui/toast/toast.tsx | 14 +-- src/components/ui/toggle/toggle.tsx | 2 +- src/components/ui/tooltip/tooltip.tsx | 4 +- src/components/ui/widget/widget.tsx | 16 +-- src/components/upload-progress.tsx | 2 +- src/components/upload.tsx | 12 +- src/components/validation-checkmark.tsx | 4 +- src/components/verification-badge.tsx | 2 +- src/containers/account-container.tsx | 4 +- src/containers/soapbox.tsx | 4 +- src/containers/status-container.tsx | 10 +- src/contexts/chat-context.tsx | 18 +-- src/contexts/stat-context.tsx | 6 +- src/entity-store/__tests__/reducer.test.ts | 4 +- src/entity-store/actions.ts | 2 +- src/entity-store/entities.ts | 16 +-- src/entity-store/hooks/types.ts | 4 +- src/entity-store/hooks/useBatchedEntities.ts | 4 +- src/entity-store/hooks/useCreateEntity.ts | 2 +- src/entity-store/hooks/useEntities.ts | 6 +- src/entity-store/hooks/useEntity.ts | 6 +- src/entity-store/hooks/useEntityActions.ts | 8 +- src/entity-store/reducer.ts | 2 +- src/entity-store/types.ts | 34 +++--- .../account-gallery/components/media-item.tsx | 4 +- src/features/account-gallery/index.tsx | 4 +- .../components/moved-note.tsx | 4 +- src/features/account-timeline/index.tsx | 6 +- src/features/account/components/header.tsx | 2 +- src/features/admin/announcements.tsx | 2 +- src/features/admin/components/dashcounter.tsx | 10 +- .../components/latest-accounts-panel.tsx | 2 +- .../admin/components/report-status.tsx | 4 +- src/features/admin/components/report.tsx | 2 +- .../admin/components/unapproved-account.tsx | 2 +- src/features/admin/moderation-log.tsx | 2 +- src/features/aliases/components/account.tsx | 4 +- src/features/audio/index.tsx | 34 +++--- .../auth-login/components/captcha.tsx | 26 ++-- .../auth-login/components/consumer-button.tsx | 2 +- .../auth-login/components/login-form.tsx | 4 +- .../auth-login/components/otp-auth-form.tsx | 2 +- .../components/registration-form.tsx | 2 +- src/features/auth-token-list/index.tsx | 4 +- src/features/birthdays/account.tsx | 2 +- .../chats/components/chat-composer.tsx | 26 ++-- .../chats/components/chat-list-item.tsx | 4 +- src/features/chats/components/chat-list.tsx | 6 +- .../chats/components/chat-message-list.tsx | 2 +- .../chat-message-reaction-wrapper.tsx | 6 +- .../components/chat-message-reaction.tsx | 6 +- .../chats/components/chat-message.tsx | 4 +- .../chats/components/chat-page/chat-page.tsx | 2 +- .../components/chat-page-settings.tsx | 4 +- .../chat-page/components/welcome.tsx | 4 +- .../chats/components/chat-pane/blankslate.tsx | 2 +- .../chats/components/chat-pending-upload.tsx | 2 +- .../chats/components/chat-search-input.tsx | 6 +- .../components/chat-search/chat-search.tsx | 2 +- .../chats/components/chat-search/results.tsx | 4 +- .../chats/components/chat-textarea.tsx | 8 +- .../chats/components/chat-upload-preview.tsx | 4 +- src/features/chats/components/chat-upload.tsx | 6 +- .../chat-widget/chat-pane-header.tsx | 14 +-- .../components/chat-widget/chat-window.tsx | 2 +- src/features/chats/components/chat.tsx | 6 +- src/features/chats/components/ui/pane.tsx | 8 +- src/features/chats/index.tsx | 4 +- .../components/autosuggest-account.tsx | 2 +- .../components/compose-form-button.tsx | 10 +- .../compose/components/compose-form.tsx | 14 +-- .../compose/components/markdown-button.tsx | 2 +- .../compose/components/poll-button.tsx | 4 +- .../components/polls/duration-selector.tsx | 2 +- .../compose/components/polls/poll-form.tsx | 18 +-- .../compose/components/privacy-dropdown.tsx | 16 +-- .../components/reply-group-indicator.tsx | 2 +- .../compose/components/reply-indicator.tsx | 8 +- .../compose/components/reply-mentions.tsx | 2 +- .../compose/components/schedule-button.tsx | 4 +- .../compose/components/schedule-form.tsx | 2 +- src/features/compose/components/search.tsx | 8 +- .../compose/components/spoiler-button.tsx | 2 +- .../compose/components/spoiler-input.tsx | 2 +- .../components/text-character-counter.tsx | 4 +- .../compose/components/upload-button.tsx | 16 +-- .../compose/components/upload-form.tsx | 2 +- .../compose/components/upload-progress.tsx | 2 +- src/features/compose/components/upload.tsx | 4 +- .../components/visual-character-counter.tsx | 4 +- src/features/compose/components/warning.tsx | 2 +- .../containers/quoted-status-container.tsx | 2 +- .../compose/containers/warning-container.tsx | 2 +- src/features/compose/editor/index.tsx | 24 ++-- .../compose/editor/nodes/emoji-node.tsx | 6 +- .../editor/plugins/autosuggest-plugin.tsx | 18 +-- .../compose/editor/plugins/focus-plugin.tsx | 2 +- .../compose/editor/plugins/state-plugin.tsx | 4 +- src/features/compose/editor/utils/rect.ts | 12 +- .../conversations/components/conversation.tsx | 6 +- .../components/crypto-address.tsx | 6 +- .../components/crypto-donate-panel.tsx | 2 +- .../crypto-donate/components/crypto-icon.tsx | 6 +- .../components/detailed-crypto-address.tsx | 6 +- .../crypto-donate/components/site-wallet.tsx | 2 +- .../developers/components/indicator.tsx | 4 +- src/features/developers/developers-menu.tsx | 6 +- .../directory/components/account-card.tsx | 2 +- .../edit-profile/components/avatar-picker.tsx | 10 +- .../edit-profile/components/header-picker.tsx | 10 +- src/features/edit-profile/index.tsx | 46 +++---- src/features/embedded-status/index.tsx | 4 +- .../components/emoji-picker-dropdown.tsx | 12 +- src/features/emoji/data.ts | 40 +++--- src/features/emoji/index.ts | 20 +-- src/features/emoji/mapping.ts | 6 +- src/features/emoji/search.ts | 4 +- .../event/components/event-action-button.tsx | 4 +- src/features/event/components/event-date.tsx | 2 +- .../event/components/event-header.tsx | 2 +- src/features/event/event-discussion.tsx | 6 +- src/features/event/event-information.tsx | 2 +- .../events/components/event-carousel.tsx | 6 +- .../export-data/components/csv-exporter.tsx | 10 +- src/features/favourited-statuses/index.tsx | 4 +- .../components/instance-restrictions.tsx | 6 +- .../components/restricted-instance.tsx | 2 +- .../feed-suggestions/feed-suggestions.tsx | 8 +- src/features/filters/edit-filter.tsx | 12 +- .../components/account-authorize.tsx | 2 +- src/features/followers/index.tsx | 4 +- src/features/following/index.tsx | 4 +- src/features/forms/index.tsx | 114 +++++++++--------- .../group/components/group-action-button.tsx | 2 +- .../group/components/group-header-image.tsx | 4 +- .../group/components/group-header.tsx | 2 +- .../group/components/group-member-count.tsx | 2 +- .../components/group-member-list-item.tsx | 6 +- .../group/components/group-options-button.tsx | 2 +- .../group/components/group-privacy.tsx | 2 +- .../group/components/group-relationship.tsx | 2 +- .../group/components/group-tag-list-item.tsx | 6 +- .../group/components/group-tags-field.tsx | 10 +- src/features/group/edit-group.tsx | 4 +- src/features/group/group-blocked-members.tsx | 6 +- src/features/group/group-gallery.tsx | 2 +- src/features/group/group-members.tsx | 2 +- .../group/group-membership-requests.tsx | 8 +- src/features/group/group-tag-timeline.tsx | 4 +- src/features/group/group-tags.tsx | 2 +- src/features/group/group-timeline.tsx | 2 +- src/features/group/manage-group.tsx | 2 +- .../components/discover/group-grid-item.tsx | 4 +- .../components/discover/group-list-item.tsx | 4 +- .../components/discover/layout-buttons.tsx | 4 +- .../components/discover/search/blankslate.tsx | 4 +- .../discover/search/recent-searches.tsx | 2 +- .../components/discover/search/results.tsx | 2 +- .../components/discover/search/search.tsx | 4 +- .../components/discover/tag-list-item.tsx | 2 +- .../groups/components/group-link-preview.tsx | 2 +- src/features/groups/components/tab-bar.tsx | 2 +- src/features/groups/tag.tsx | 2 +- src/features/hashtag-timeline/index.tsx | 4 +- .../import-data/components/csv-importer.tsx | 10 +- .../landing-timeline/components/logo-text.tsx | 2 +- src/features/list-adder/components/list.tsx | 2 +- src/features/list-adder/index.tsx | 4 +- .../list-editor/components/account.tsx | 2 +- src/features/list-editor/index.tsx | 4 +- .../mutes/components/group-list-item.tsx | 4 +- .../notifications/components/notification.tsx | 14 +-- .../components/setting-toggle.tsx | 8 +- .../components/placeholder-avatar.tsx | 6 +- .../components/placeholder-display-name.tsx | 6 +- .../components/placeholder-media-gallery.tsx | 4 +- .../components/placeholder-status-content.tsx | 4 +- .../components/placeholder-status.tsx | 2 +- src/features/register-invite/index.tsx | 2 +- .../components/pinned-hosts-picker.tsx | 2 +- src/features/remote-timeline/index.tsx | 4 +- src/features/reply-mentions/account.tsx | 6 +- .../report/components/status-check-box.tsx | 4 +- .../scheduled-status-action-bar.tsx | 2 +- .../components/scheduled-status.tsx | 2 +- src/features/security/mfa/enable-otp-form.tsx | 4 +- .../security/mfa/otp-confirm-form.tsx | 2 +- .../components/color-picker.tsx | 8 +- .../components/color-with-picker.tsx | 6 +- .../components/icon-picker-dropdown.tsx | 4 +- .../components/icon-picker-menu.tsx | 8 +- .../soapbox-config/components/icon-picker.tsx | 4 +- .../components/site-preview.tsx | 2 +- src/features/status/components/card.tsx | 16 +-- .../status/components/detailed-status.tsx | 10 +- .../components/status-interaction-bar.tsx | 10 +- .../status/components/thread-status.tsx | 10 +- src/features/status/components/thread.tsx | 12 +- .../containers/quoted-status-container.tsx | 2 +- src/features/status/index.tsx | 8 +- .../theme-editor/components/color.tsx | 4 +- .../theme-editor/components/palette.tsx | 8 +- src/features/theme-editor/index.tsx | 14 +-- src/features/ui/components/action-button.tsx | 6 +- .../ui/components/background-shapes.tsx | 2 +- .../ui/components/bundle-column-error.tsx | 2 +- .../ui/components/bundle-modal-error.tsx | 4 +- src/features/ui/components/bundle.tsx | 20 +-- src/features/ui/components/columns-area.tsx | 4 +- .../ui/components/group-media-panel.tsx | 2 +- src/features/ui/components/image-loader.tsx | 12 +- .../ui/components/instance-info-panel.tsx | 2 +- .../components/instance-moderation-panel.tsx | 2 +- src/features/ui/components/link-footer.tsx | 8 +- src/features/ui/components/modal-root.tsx | 6 +- .../account-moderation-modal.tsx | 4 +- .../account-moderation-modal/badge-input.tsx | 4 +- .../staff-role-picker.tsx | 2 +- .../ui/components/modals/actions-modal.tsx | 8 +- .../ui/components/modals/birthdays-modal.tsx | 2 +- .../ui/components/modals/boost-modal.tsx | 6 +- .../modals/compare-history-modal.tsx | 4 +- .../ui/components/modals/component-modal.tsx | 8 +- .../compose-event-modal.tsx | 8 +- .../compose-event-modal/upload-button.tsx | 4 +- .../ui/components/modals/compose-modal.tsx | 8 +- .../components/modals/confirmation-modal.tsx | 20 +-- .../ui/components/modals/dislikes-modal.tsx | 4 +- .../modals/edit-announcement-modal.tsx | 2 +- .../modals/edit-federation-modal.tsx | 4 +- .../ui/components/modals/embed-modal.tsx | 4 +- .../ui/components/modals/event-map-modal.tsx | 4 +- .../modals/event-participants-modal.tsx | 4 +- .../modals/familiar-followers-modal.tsx | 4 +- .../ui/components/modals/favourites-modal.tsx | 4 +- .../ui/components/modals/hotkeys-modal.tsx | 2 +- .../ui/components/modals/join-event-modal.tsx | 2 +- .../components/modals/landing-page-modal.tsx | 2 +- .../manage-group-modal/create-group-modal.tsx | 2 +- .../steps/confirmation-step.tsx | 8 +- .../manage-group-modal/steps/details-step.tsx | 4 +- .../manage-group-modal/steps/privacy-step.tsx | 4 +- .../ui/components/modals/media-modal.tsx | 10 +- .../ui/components/modals/mentions-modal.tsx | 4 +- .../modals/missing-description-modal.tsx | 6 +- .../ui/components/modals/reactions-modal.tsx | 12 +- .../ui/components/modals/reblogs-modal.tsx | 4 +- .../modals/reply-mentions-modal.tsx | 4 +- .../modals/report-modal/report-modal.tsx | 2 +- .../report-modal/steps/confirmation-step.tsx | 2 +- .../report-modal/steps/other-actions-step.tsx | 2 +- .../modals/report-modal/steps/reason-step.tsx | 2 +- .../components/modals/unauthorized-modal.tsx | 8 +- .../ui/components/modals/video-modal.tsx | 10 +- .../components/panels/account-note-panel.tsx | 2 +- src/features/ui/components/pending-status.tsx | 10 +- .../ui/components/pinned-accounts-panel.tsx | 4 +- src/features/ui/components/poll-preview.tsx | 2 +- .../ui/components/profile-dropdown.tsx | 18 +-- .../components/profile-familiar-followers.tsx | 2 +- src/features/ui/components/profile-field.tsx | 2 +- .../ui/components/profile-fields-panel.tsx | 2 +- .../ui/components/profile-info-panel.tsx | 4 +- .../ui/components/profile-media-panel.tsx | 2 +- src/features/ui/components/profile-stats.tsx | 4 +- .../ui/components/subscription-button.tsx | 2 +- src/features/ui/components/theme-selector.tsx | 4 +- src/features/ui/components/timeline.tsx | 4 +- src/features/ui/components/trends-panel.tsx | 2 +- src/features/ui/components/user-panel.tsx | 8 +- .../ui/components/who-to-follow-panel.tsx | 2 +- src/features/ui/components/zoomable-image.tsx | 8 +- src/features/ui/index.tsx | 4 +- src/features/ui/util/global-hotkeys.tsx | 4 +- src/features/ui/util/react-router-helpers.tsx | 24 ++-- src/features/video/index.tsx | 32 ++--- src/hooks/forms/useImageField.ts | 4 +- src/hooks/useDimensions.ts | 2 +- src/hooks/useLocale.ts | 4 +- src/middleware/sounds.ts | 4 +- src/normalizers/status.ts | 2 +- src/pages/admin-page.tsx | 2 +- src/pages/chats-page.tsx | 2 +- src/pages/default-page.tsx | 2 +- src/pages/empty-page.tsx | 2 +- src/pages/event-page.tsx | 6 +- src/pages/events-page.tsx | 2 +- src/pages/group-page.tsx | 6 +- src/pages/groups-page.tsx | 2 +- src/pages/groups-pending-page.tsx | 2 +- src/pages/home-page.tsx | 2 +- src/pages/landing-page.tsx | 2 +- src/pages/manage-groups-page.tsx | 2 +- src/pages/profile-page.tsx | 6 +- src/pages/remote-instance-page.tsx | 6 +- src/pages/search-page.tsx | 2 +- src/pages/status-page.tsx | 2 +- src/queries/accounts.ts | 48 ++++---- src/queries/chats.ts | 52 ++++---- src/queries/embed.ts | 20 +-- src/queries/suggestions.ts | 10 +- src/reducers/accounts.ts | 2 +- src/reducers/admin.ts | 16 +-- src/reducers/chats.ts | 2 +- src/reducers/contexts.ts | 4 +- src/reducers/onboarding.ts | 2 +- src/reducers/rules.ts | 12 +- src/reducers/sidebar.ts | 2 +- src/reducers/statuses.ts | 12 +- src/schemas/status.ts | 6 +- src/selectors/index.ts | 6 +- src/service-worker/sw.ts | 36 +++--- src/storage/kv-store.ts | 2 +- src/stream.ts | 14 +-- src/toast.tsx | 10 +- src/types/colors.ts | 8 +- src/types/entities.ts | 4 +- src/types/nostr.ts | 10 +- src/types/window.d.ts | 2 +- src/utils/__tests__/queries.test.ts | 4 +- src/utils/badges.ts | 4 +- src/utils/chats.ts | 2 +- src/utils/colors.ts | 2 +- src/utils/errors.ts | 4 +- src/utils/favicon-service.ts | 8 +- src/utils/features.ts | 8 +- src/utils/legacy.ts | 14 +-- src/utils/queries.ts | 8 +- src/utils/resize-image.ts | 14 +-- src/utils/sounds.ts | 4 +- .../index.d.ts | 30 ++--- 479 files changed, 1928 insertions(+), 1938 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 4359767c2..29a8d474a 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -258,17 +258,7 @@ module.exports = { alphabetize: { order: 'asc' }, }, ], - '@typescript-eslint/member-delimiter-style': [ - 'error', - { - multiline: { - delimiter: 'none', - }, - singleline: { - delimiter: 'comma', - }, - }, - ], + '@typescript-eslint/member-delimiter-style': 'error', 'promise/catch-or-return': 'error', diff --git a/scripts/translationRunner.ts b/scripts/translationRunner.ts index 89c179e2e..d4557408e 100644 --- a/scripts/translationRunner.ts +++ b/scripts/translationRunner.ts @@ -8,8 +8,8 @@ import yargs from 'yargs'; type Validator = (language: string) => void; interface LanguageResult { - language: string - error: any + language: string; + error: any; } const RFC5646_REGEXP = /^[a-z]{2,3}(?:-(?:x|[A-Za-z]{2,4}))*$/; @@ -164,8 +164,8 @@ const extractedMessages = extractedMessagesFiles.reduce((acc, messageFile) => { }, [] as ExtractedDescriptor[]); interface Translation { - language: string - data: Record + language: string; + data: Record; } const translations: Translation[] = languages.map((language: string) => { @@ -188,10 +188,10 @@ function pushIfUnique(arr: T[], newItem: T): void { } interface Problem { - language: string - id: ExtractedDescriptor['id'] - severity: 'error' | 'warning' - type: string + language: string; + id: ExtractedDescriptor['id']; + severity: 'error' | 'warning'; + type: string; } const problems: Problem[] = translations.reduce((acc, translation) => { diff --git a/src/actions/admin.ts b/src/actions/admin.ts index 613615140..433cd1e00 100644 --- a/src/actions/admin.ts +++ b/src/actions/admin.ts @@ -201,7 +201,7 @@ const fetchReports = (params: Record = {}) => } }; -const patchMastodonReports = (reports: { id: string, state: string }[]) => +const patchMastodonReports = (reports: { id: string; state: string }[]) => (dispatch: AppDispatch, getState: () => RootState) => Promise.all(reports.map(({ id, state }) => api(getState) .post(`/api/v1/admin/reports/${id}/${state === 'resolved' ? 'reopen' : 'resolve'}`) @@ -212,7 +212,7 @@ const patchMastodonReports = (reports: { id: string, state: string }[]) => }), )); -const patchPleromaReports = (reports: { id: string, state: string }[]) => +const patchPleromaReports = (reports: { id: string; state: string }[]) => (dispatch: AppDispatch, getState: () => RootState) => api(getState) .patch('/api/v1/pleroma/admin/reports', { reports }) diff --git a/src/actions/compose.ts b/src/actions/compose.ts index fef615386..b781b99b5 100644 --- a/src/actions/compose.ts +++ b/src/actions/compose.ts @@ -101,15 +101,15 @@ const messages = defineMessages({ }); interface ComposeSetStatusAction { - type: typeof COMPOSE_SET_STATUS - id: string - status: Status - rawText: string - explicitAddressing: boolean - spoilerText?: string - contentType?: string | false - v: ReturnType - withRedraft?: boolean + type: typeof COMPOSE_SET_STATUS; + id: string; + status: Status; + rawText: string; + explicitAddressing: boolean; + spoilerText?: string; + contentType?: string | false; + v: ReturnType; + withRedraft?: boolean; } const setComposeToStatus = (status: Status, rawText: string, spoilerText?: string, contentType?: string | false, withRedraft?: boolean) => @@ -139,12 +139,12 @@ const changeCompose = (composeId: string, text: string) => ({ }); interface ComposeReplyAction { - type: typeof COMPOSE_REPLY - id: string - status: Status - account: Account - explicitAddressing: boolean - preserveSpoilers: boolean + type: typeof COMPOSE_REPLY; + id: string; + status: Status; + account: Account; + explicitAddressing: boolean; + preserveSpoilers: boolean; } const replyCompose = (status: Status) => @@ -176,11 +176,11 @@ const cancelReplyCompose = () => ({ }); interface ComposeQuoteAction { - type: typeof COMPOSE_QUOTE - id: string - status: Status - account: Account | undefined - explicitAddressing: boolean + type: typeof COMPOSE_QUOTE; + id: string; + status: Status; + account: Account | undefined; + explicitAddressing: boolean; } const quoteCompose = (status: Status) => @@ -220,9 +220,9 @@ const resetCompose = (composeId = 'compose-modal') => ({ }); interface ComposeMentionAction { - type: typeof COMPOSE_MENTION - id: string - account: Account + type: typeof COMPOSE_MENTION; + id: string; + account: Account; } const mentionCompose = (account: Account) => @@ -238,9 +238,9 @@ const mentionCompose = (account: Account) => }; interface ComposeDirectAction { - type: typeof COMPOSE_DIRECT - id: string - account: Account + type: typeof COMPOSE_DIRECT; + id: string; + account: Account; } const directCompose = (account: Account) => @@ -593,11 +593,11 @@ const fetchComposeSuggestions = (composeId: string, token: string) => }; interface ComposeSuggestionsReadyAction { - type: typeof COMPOSE_SUGGESTIONS_READY - id: string - token: string - emojis?: Emoji[] - accounts?: APIEntity[] + type: typeof COMPOSE_SUGGESTIONS_READY; + id: string; + token: string; + emojis?: Emoji[]; + accounts?: APIEntity[]; } const readyComposeSuggestionsEmojis = (composeId: string, token: string, emojis: Emoji[]) => ({ @@ -615,12 +615,12 @@ const readyComposeSuggestionsAccounts = (composeId: string, token: string, accou }); interface ComposeSuggestionSelectAction { - type: typeof COMPOSE_SUGGESTION_SELECT - id: string - position: number - token: string | null - completion: string - path: Array + type: typeof COMPOSE_SUGGESTION_SELECT; + id: string; + position: number; + token: string | null; + completion: string; + path: Array; } const selectComposeSuggestion = (composeId: string, position: number, token: string | null, suggestion: AutoSuggestion, path: Array) => @@ -774,9 +774,9 @@ const openComposeWithText = (composeId: string, text = '') => }; interface ComposeAddToMentionsAction { - type: typeof COMPOSE_ADD_TO_MENTIONS - id: string - account: string + type: typeof COMPOSE_ADD_TO_MENTIONS; + id: string; + account: string; } const addToMentions = (composeId: string, accountId: string) => @@ -795,9 +795,9 @@ const addToMentions = (composeId: string, accountId: string) => }; interface ComposeRemoveFromMentionsAction { - type: typeof COMPOSE_REMOVE_FROM_MENTIONS - id: string - account: string + type: typeof COMPOSE_REMOVE_FROM_MENTIONS; + id: string; + account: string; } const removeFromMentions = (composeId: string, accountId: string) => @@ -816,11 +816,11 @@ const removeFromMentions = (composeId: string, accountId: string) => }; interface ComposeEventReplyAction { - type: typeof COMPOSE_EVENT_REPLY - id: string - status: Status - account: Account - explicitAddressing: boolean + type: typeof COMPOSE_EVENT_REPLY; + id: string; + status: Status; + account: Account; + explicitAddressing: boolean; } const eventDiscussionCompose = (composeId: string, status: Status) => diff --git a/src/actions/events.ts b/src/actions/events.ts index 6d62d4585..ae291a185 100644 --- a/src/actions/events.ts +++ b/src/actions/events.ts @@ -545,10 +545,10 @@ const cancelEventCompose = () => ({ }); interface EventFormSetAction { - type: typeof EVENT_FORM_SET - status: ReducerStatus - text: string - location: Record + type: typeof EVENT_FORM_SET; + status: ReducerStatus; + text: string; + location: Record; } const editEvent = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { diff --git a/src/actions/export-data.ts b/src/actions/export-data.ts index 519725ea2..b5bf805de 100644 --- a/src/actions/export-data.ts +++ b/src/actions/export-data.ts @@ -34,8 +34,8 @@ type ExportDataActions = { | typeof EXPORT_BLOCKS_FAIL | typeof EXPORT_MUTES_REQUEST | typeof EXPORT_MUTES_SUCCESS - | typeof EXPORT_MUTES_FAIL - error?: any + | typeof EXPORT_MUTES_FAIL; + error?: any; } function fileExport(content: string, fileName: string) { diff --git a/src/actions/filters.ts b/src/actions/filters.ts index ee3508682..fe7026876 100644 --- a/src/actions/filters.ts +++ b/src/actions/filters.ts @@ -33,7 +33,7 @@ const messages = defineMessages({ removed: { id: 'filters.removed', defaultMessage: 'Filter deleted.' }, }); -type FilterKeywords = { keyword: string, whole_word: boolean }[]; +type FilterKeywords = { keyword: string; whole_word: boolean }[]; const fetchFiltersV1 = () => (dispatch: AppDispatch, getState: () => RootState) => { diff --git a/src/actions/import-data.ts b/src/actions/import-data.ts index 023529453..c518d1a16 100644 --- a/src/actions/import-data.ts +++ b/src/actions/import-data.ts @@ -27,9 +27,9 @@ type ImportDataActions = { | typeof IMPORT_BLOCKS_FAIL | typeof IMPORT_MUTES_REQUEST | typeof IMPORT_MUTES_SUCCESS - | typeof IMPORT_MUTES_FAIL - error?: any - config?: string + | typeof IMPORT_MUTES_FAIL; + error?: any; + config?: string; } const messages = defineMessages({ diff --git a/src/actions/me.ts b/src/actions/me.ts index bc4073b70..4e02a6e62 100644 --- a/src/actions/me.ts +++ b/src/actions/me.ts @@ -104,8 +104,8 @@ const patchMeRequest = () => ({ }); interface MePatchSuccessAction { - type: typeof ME_PATCH_SUCCESS - me: APIEntity + type: typeof ME_PATCH_SUCCESS; + me: APIEntity; } const patchMeSuccess = (me: APIEntity) => diff --git a/src/actions/onboarding.ts b/src/actions/onboarding.ts index ff12bd074..f75a89c4b 100644 --- a/src/actions/onboarding.ts +++ b/src/actions/onboarding.ts @@ -4,11 +4,11 @@ const ONBOARDING_END = 'ONBOARDING_END'; const ONBOARDING_LOCAL_STORAGE_KEY = 'soapbox:onboarding'; type OnboardingStartAction = { - type: typeof ONBOARDING_START + type: typeof ONBOARDING_START; } type OnboardingEndAction = { - type: typeof ONBOARDING_END + type: typeof ONBOARDING_END; } export type OnboardingActions = OnboardingStartAction | OnboardingEndAction diff --git a/src/actions/push-notifications/registerer.ts b/src/actions/push-notifications/registerer.ts index 2ea7fbab8..48a0bc8fd 100644 --- a/src/actions/push-notifications/registerer.ts +++ b/src/actions/push-notifications/registerer.ts @@ -37,8 +37,8 @@ const subscribe = (registration: ServiceWorkerRegistration, getState: () => Root }); const unsubscribe = ({ registration, subscription }: { - registration: ServiceWorkerRegistration - subscription: PushSubscription | null + registration: ServiceWorkerRegistration; + subscription: PushSubscription | null; }) => subscription ? subscription.unsubscribe().then(() => registration) : new Promise(r => r(registration)); @@ -82,8 +82,8 @@ const register = () => .then(getPushSubscription) // @ts-ignore .then(({ registration, subscription }: { - registration: ServiceWorkerRegistration - subscription: PushSubscription | null + registration: ServiceWorkerRegistration; + subscription: PushSubscription | null; }) => { if (subscription !== null) { // We have a subscription, check if it is still valid diff --git a/src/actions/reports.ts b/src/actions/reports.ts index be6a60ed8..4ad65a21e 100644 --- a/src/actions/reports.ts +++ b/src/actions/reports.ts @@ -29,9 +29,9 @@ enum ReportableEntities { } type ReportedEntity = { - status?: Status - chatMessage?: ChatMessage - group?: Group + status?: Status; + chatMessage?: ChatMessage; + group?: Group; } const initReport = (entityType: ReportableEntities, account: Account, entities?: ReportedEntity) => (dispatch: AppDispatch) => { diff --git a/src/actions/rules.ts b/src/actions/rules.ts index b5b3b90a4..242d0bbda 100644 --- a/src/actions/rules.ts +++ b/src/actions/rules.ts @@ -7,12 +7,12 @@ const RULES_FETCH_REQUEST = 'RULES_FETCH_REQUEST'; const RULES_FETCH_SUCCESS = 'RULES_FETCH_SUCCESS'; type RulesFetchRequestAction = { - type: typeof RULES_FETCH_REQUEST + type: typeof RULES_FETCH_REQUEST; } type RulesFetchRequestSuccessAction = { - type: typeof RULES_FETCH_SUCCESS - payload: Rule[] + type: typeof RULES_FETCH_SUCCESS; + payload: Rule[]; } export type RulesActions = RulesFetchRequestAction | RulesFetchRequestSuccessAction diff --git a/src/actions/settings.ts b/src/actions/settings.ts index 57db0e438..ae6a3ec35 100644 --- a/src/actions/settings.ts +++ b/src/actions/settings.ts @@ -19,7 +19,7 @@ const FE_NAME = 'soapbox_fe'; /** Options when changing/saving settings. */ type SettingOpts = { /** Whether to display an alert when settings are saved. */ - showAlert?: boolean + showAlert?: boolean; } const saveSuccessMessage = defineMessage({ id: 'settings.save.success', defaultMessage: 'Your preferences have been saved!' }); @@ -183,9 +183,9 @@ const getSettings = createSelector([ }); interface SettingChangeAction { - type: typeof SETTING_CHANGE - path: string[] - value: any + type: typeof SETTING_CHANGE; + path: string[]; + value: any; } const changeSettingImmediate = (path: string[], value: any, opts?: SettingOpts) => diff --git a/src/actions/streaming.ts b/src/actions/streaming.ts index e72b20239..9557757ae 100644 --- a/src/actions/streaming.ts +++ b/src/actions/streaming.ts @@ -65,8 +65,8 @@ const updateChatQuery = (chat: IChat) => { }; interface TimelineStreamOpts { - statContext?: IStatContext - enabled?: boolean + statContext?: IStatContext; + enabled?: boolean; } const connectTimelineStream = ( @@ -192,17 +192,17 @@ function followStateToRelationship(followState: string) { } interface FollowUpdate { - state: 'follow_pending' | 'follow_accept' | 'follow_reject' + state: 'follow_pending' | 'follow_accept' | 'follow_reject'; follower: { - id: string - follower_count: number - following_count: number - } + id: string; + follower_count: number; + following_count: number; + }; following: { - id: string - follower_count: number - following_count: number - } + id: string; + follower_count: number; + following_count: number; + }; } function updateFollowRelationships(update: FollowUpdate) { diff --git a/src/actions/timelines.ts b/src/actions/timelines.ts index 1de17863e..03198652f 100644 --- a/src/actions/timelines.ts +++ b/src/actions/timelines.ts @@ -110,11 +110,11 @@ const dequeueTimeline = (timelineId: string, expandFunc?: (lastStatusId: string) }; interface TimelineDeleteAction { - type: typeof TIMELINE_DELETE - id: string - accountId: string - references: ImmutableMap - reblogOf: unknown + type: typeof TIMELINE_DELETE; + id: string; + accountId: string; + references: ImmutableMap; + reblogOf: unknown; } const deleteFromTimelines = (id: string) => @@ -193,14 +193,14 @@ const expandTimeline = (timelineId: string, path: string, params: Record { diff --git a/src/api/hooks/accounts/useAccount.ts b/src/api/hooks/accounts/useAccount.ts index e7f8b8fea..865b149ad 100644 --- a/src/api/hooks/accounts/useAccount.ts +++ b/src/api/hooks/accounts/useAccount.ts @@ -10,7 +10,7 @@ import { type Account, accountSchema } from 'soapbox/schemas'; import { useRelationship } from './useRelationship'; interface UseAccountOpts { - withRelationship?: boolean + withRelationship?: boolean; } function useAccount(accountId?: string, opts: UseAccountOpts = {}) { diff --git a/src/api/hooks/accounts/useAccountList.ts b/src/api/hooks/accounts/useAccountList.ts index cb82153e0..ec769224b 100644 --- a/src/api/hooks/accounts/useAccountList.ts +++ b/src/api/hooks/accounts/useAccountList.ts @@ -8,7 +8,7 @@ import { useRelationships } from './useRelationships'; import type { EntityFn } from 'soapbox/entity-store/hooks/types'; interface useAccountListOpts { - enabled?: boolean + enabled?: boolean; } function useAccountList(listKey: string[], entityFn: EntityFn, opts: useAccountListOpts = {}) { diff --git a/src/api/hooks/accounts/useAccountLookup.ts b/src/api/hooks/accounts/useAccountLookup.ts index ed73617aa..7aed05f98 100644 --- a/src/api/hooks/accounts/useAccountLookup.ts +++ b/src/api/hooks/accounts/useAccountLookup.ts @@ -10,7 +10,7 @@ import { type Account, accountSchema } from 'soapbox/schemas'; import { useRelationship } from './useRelationship'; interface UseAccountLookupOpts { - withRelationship?: boolean + withRelationship?: boolean; } function useAccountLookup(acct: string | undefined, opts: UseAccountLookupOpts = {}) { diff --git a/src/api/hooks/accounts/useFollow.ts b/src/api/hooks/accounts/useFollow.ts index 3d81182be..60282c4ac 100644 --- a/src/api/hooks/accounts/useFollow.ts +++ b/src/api/hooks/accounts/useFollow.ts @@ -6,9 +6,9 @@ import { useApi } from 'soapbox/hooks/useApi'; import { relationshipSchema } from 'soapbox/schemas'; interface FollowOpts { - reblogs?: boolean - notify?: boolean - languages?: string[] + reblogs?: boolean; + notify?: boolean; + languages?: string[]; } function useFollow() { diff --git a/src/api/hooks/accounts/useRelationship.ts b/src/api/hooks/accounts/useRelationship.ts index e0793108b..6424a2876 100644 --- a/src/api/hooks/accounts/useRelationship.ts +++ b/src/api/hooks/accounts/useRelationship.ts @@ -6,7 +6,7 @@ import { useApi } from 'soapbox/hooks'; import { type Relationship, relationshipSchema } from 'soapbox/schemas'; interface UseRelationshipOpts { - enabled?: boolean + enabled?: boolean; } function useRelationship(accountId: string | undefined, opts: UseRelationshipOpts = {}) { diff --git a/src/api/hooks/groups/useCreateGroup.ts b/src/api/hooks/groups/useCreateGroup.ts index 302374946..e80417856 100644 --- a/src/api/hooks/groups/useCreateGroup.ts +++ b/src/api/hooks/groups/useCreateGroup.ts @@ -4,13 +4,13 @@ import { useApi } from 'soapbox/hooks/useApi'; import { groupSchema } from 'soapbox/schemas'; interface CreateGroupParams { - display_name?: string - note?: string - avatar?: File - header?: File - group_visibility?: 'members_only' | 'everyone' - discoverable?: boolean - tags?: string[] + display_name?: string; + note?: string; + avatar?: File; + header?: File; + group_visibility?: 'members_only' | 'everyone'; + discoverable?: boolean; + tags?: string[]; } function useCreateGroup() { diff --git a/src/api/hooks/groups/useGroupValidation.ts b/src/api/hooks/groups/useGroupValidation.ts index bfcd3bbb0..ffa35eb6f 100644 --- a/src/api/hooks/groups/useGroupValidation.ts +++ b/src/api/hooks/groups/useGroupValidation.ts @@ -4,8 +4,8 @@ import { useApi } from 'soapbox/hooks/useApi'; import { useFeatures } from 'soapbox/hooks/useFeatures'; type Validation = { - error: string - message: string + error: string; + message: string; } const ValidationKeys = { diff --git a/src/api/hooks/groups/useUpdateGroup.ts b/src/api/hooks/groups/useUpdateGroup.ts index b4ec0aa54..129849514 100644 --- a/src/api/hooks/groups/useUpdateGroup.ts +++ b/src/api/hooks/groups/useUpdateGroup.ts @@ -4,13 +4,13 @@ import { useApi } from 'soapbox/hooks/useApi'; import { groupSchema } from 'soapbox/schemas'; interface UpdateGroupParams { - display_name?: string - note?: string - avatar?: File | '' - header?: File | '' - group_visibility?: string - discoverable?: boolean - tags?: string[] + display_name?: string; + note?: string; + avatar?: File | ''; + header?: File | ''; + group_visibility?: string; + discoverable?: boolean; + tags?: string[]; } function useUpdateGroup(groupId: string) { diff --git a/src/api/hooks/streaming/useCommunityStream.ts b/src/api/hooks/streaming/useCommunityStream.ts index 5db097ae5..ce00d4674 100644 --- a/src/api/hooks/streaming/useCommunityStream.ts +++ b/src/api/hooks/streaming/useCommunityStream.ts @@ -1,8 +1,8 @@ import { useTimelineStream } from './useTimelineStream'; interface UseCommunityStreamOpts { - onlyMedia?: boolean - enabled?: boolean + onlyMedia?: boolean; + enabled?: boolean; } function useCommunityStream({ onlyMedia, enabled }: UseCommunityStreamOpts = {}) { diff --git a/src/api/hooks/streaming/usePublicStream.ts b/src/api/hooks/streaming/usePublicStream.ts index eb189c996..7a6f7f61d 100644 --- a/src/api/hooks/streaming/usePublicStream.ts +++ b/src/api/hooks/streaming/usePublicStream.ts @@ -1,7 +1,7 @@ import { useTimelineStream } from './useTimelineStream'; interface UsePublicStreamOpts { - onlyMedia?: boolean + onlyMedia?: boolean; } function usePublicStream({ onlyMedia }: UsePublicStreamOpts = {}) { diff --git a/src/api/hooks/streaming/useRemoteStream.ts b/src/api/hooks/streaming/useRemoteStream.ts index f67f99083..6de560163 100644 --- a/src/api/hooks/streaming/useRemoteStream.ts +++ b/src/api/hooks/streaming/useRemoteStream.ts @@ -1,8 +1,8 @@ import { useTimelineStream } from './useTimelineStream'; interface UseRemoteStreamOpts { - instance: string - onlyMedia?: boolean + instance: string; + onlyMedia?: boolean; } function useRemoteStream({ instance, onlyMedia }: UseRemoteStreamOpts) { diff --git a/src/components/__mocks__/react-inlinesvg.tsx b/src/components/__mocks__/react-inlinesvg.tsx index 1317dcbcb..f9a7cd892 100644 --- a/src/components/__mocks__/react-inlinesvg.tsx +++ b/src/components/__mocks__/react-inlinesvg.tsx @@ -1,7 +1,7 @@ import React from 'react'; interface IInlineSVG { - loader?: JSX.Element + loader?: JSX.Element; } const InlineSVG: React.FC = ({ loader }): JSX.Element => { diff --git a/src/components/account-search.tsx b/src/components/account-search.tsx index cbaab0f18..4daffd70b 100644 --- a/src/components/account-search.tsx +++ b/src/components/account-search.tsx @@ -12,9 +12,9 @@ const messages = defineMessages({ interface IAccountSearch { /** Callback when a searched account is chosen. */ - onSelected: (accountId: string) => void + onSelected: (accountId: string) => void; /** Override the default placeholder of the input. */ - placeholder?: string + placeholder?: string; } /** Input to search for accounts. */ diff --git a/src/components/account.tsx b/src/components/account.tsx index 652a35dc0..ad4766ecd 100644 --- a/src/components/account.tsx +++ b/src/components/account.tsx @@ -17,8 +17,8 @@ import type { StatusApprovalStatus } from 'soapbox/normalizers/status'; import type { Account as AccountSchema } from 'soapbox/schemas'; interface IInstanceFavicon { - account: AccountSchema - disabled?: boolean + account: AccountSchema; + disabled?: boolean; } const messages = defineMessages({ @@ -57,9 +57,9 @@ const InstanceFavicon: React.FC = ({ account, disabled }) => { }; interface IProfilePopper { - condition: boolean - wrapper: (children: React.ReactNode) => React.ReactNode - children: React.ReactNode + condition: boolean; + wrapper: (children: React.ReactNode) => React.ReactNode; + children: React.ReactNode; } const ProfilePopper: React.FC = ({ condition, wrapper, children }) => { @@ -71,31 +71,31 @@ const ProfilePopper: React.FC = ({ condition, wrapper, children }; export interface IAccount { - account: AccountSchema - action?: React.ReactElement - actionAlignment?: 'center' | 'top' - actionIcon?: string - actionTitle?: string + account: AccountSchema; + action?: React.ReactElement; + actionAlignment?: 'center' | 'top'; + actionIcon?: string; + actionTitle?: string; /** Override other actions for specificity like mute/unmute. */ - actionType?: 'muting' | 'blocking' | 'follow_request' - avatarSize?: number - hidden?: boolean - hideActions?: boolean - id?: string - onActionClick?: (account: any) => void - showProfileHoverCard?: boolean - timestamp?: string - timestampUrl?: string - futureTimestamp?: boolean - withAccountNote?: boolean - withDate?: boolean - withLinkToProfile?: boolean - withRelationship?: boolean - showEdit?: boolean - approvalStatus?: StatusApprovalStatus - emoji?: string - emojiUrl?: string - note?: string + actionType?: 'muting' | 'blocking' | 'follow_request'; + avatarSize?: number; + hidden?: boolean; + hideActions?: boolean; + id?: string; + onActionClick?: (account: any) => void; + showProfileHoverCard?: boolean; + timestamp?: string; + timestampUrl?: string; + futureTimestamp?: boolean; + withAccountNote?: boolean; + withDate?: boolean; + withLinkToProfile?: boolean; + withRelationship?: boolean; + showEdit?: boolean; + approvalStatus?: StatusApprovalStatus; + emoji?: string; + emojiUrl?: string; + note?: string; } const Account = ({ diff --git a/src/components/animated-number.tsx b/src/components/animated-number.tsx index 199a8b4db..e7dd82462 100644 --- a/src/components/animated-number.tsx +++ b/src/components/animated-number.tsx @@ -15,8 +15,8 @@ const obfuscatedCount = (count: number) => { }; interface IAnimatedNumber { - value: number - obfuscate?: boolean + value: number; + obfuscate?: boolean; } const AnimatedNumber: React.FC = ({ value, obfuscate }) => { diff --git a/src/components/announcements/announcement-content.tsx b/src/components/announcements/announcement-content.tsx index 459f88e64..f4265d1fd 100644 --- a/src/components/announcements/announcement-content.tsx +++ b/src/components/announcements/announcement-content.tsx @@ -4,7 +4,7 @@ import { useHistory } from 'react-router-dom'; import type { Announcement as AnnouncementEntity, Mention as MentionEntity } from 'soapbox/types/entities'; interface IAnnouncementContent { - announcement: AnnouncementEntity + announcement: AnnouncementEntity; } const AnnouncementContent: React.FC = ({ announcement }) => { diff --git a/src/components/announcements/announcement.tsx b/src/components/announcements/announcement.tsx index 62d5a1170..ea96b37fd 100644 --- a/src/components/announcements/announcement.tsx +++ b/src/components/announcements/announcement.tsx @@ -11,10 +11,10 @@ import type { Map as ImmutableMap } from 'immutable'; import type { Announcement as AnnouncementEntity } from 'soapbox/types/entities'; interface IAnnouncement { - announcement: AnnouncementEntity - addReaction: (id: string, name: string) => void - removeReaction: (id: string, name: string) => void - emojiMap: ImmutableMap> + announcement: AnnouncementEntity; + addReaction: (id: string, name: string) => void; + removeReaction: (id: string, name: string) => void; + emojiMap: ImmutableMap>; } const Announcement: React.FC = ({ announcement, addReaction, removeReaction, emojiMap }) => { diff --git a/src/components/announcements/emoji.tsx b/src/components/announcements/emoji.tsx index 0059e02b7..b3dd0fcc5 100644 --- a/src/components/announcements/emoji.tsx +++ b/src/components/announcements/emoji.tsx @@ -7,9 +7,9 @@ import { joinPublicPath } from 'soapbox/utils/static'; import type { Map as ImmutableMap } from 'immutable'; interface IEmoji { - emoji: string - emojiMap: ImmutableMap> - hovered: boolean + emoji: string; + emojiMap: ImmutableMap>; + hovered: boolean; } const Emoji: React.FC = ({ emoji, emojiMap, hovered }) => { diff --git a/src/components/announcements/reaction.tsx b/src/components/announcements/reaction.tsx index 0d7bd973f..2985514a5 100644 --- a/src/components/announcements/reaction.tsx +++ b/src/components/announcements/reaction.tsx @@ -10,12 +10,12 @@ import type { Map as ImmutableMap } from 'immutable'; import type { AnnouncementReaction } from 'soapbox/types/entities'; interface IReaction { - announcementId: string - reaction: AnnouncementReaction - emojiMap: ImmutableMap> - addReaction: (id: string, name: string) => void - removeReaction: (id: string, name: string) => void - style: React.CSSProperties + announcementId: string; + reaction: AnnouncementReaction; + emojiMap: ImmutableMap>; + addReaction: (id: string, name: string) => void; + removeReaction: (id: string, name: string) => void; + style: React.CSSProperties; } const Reaction: React.FC = ({ announcementId, reaction, addReaction, removeReaction, emojiMap, style }) => { diff --git a/src/components/announcements/reactions-bar.tsx b/src/components/announcements/reactions-bar.tsx index 55b72c59a..97324b89e 100644 --- a/src/components/announcements/reactions-bar.tsx +++ b/src/components/announcements/reactions-bar.tsx @@ -12,11 +12,11 @@ import type { Emoji, NativeEmoji } from 'soapbox/features/emoji'; import type { AnnouncementReaction } from 'soapbox/types/entities'; interface IReactionsBar { - announcementId: string - reactions: ImmutableList - emojiMap: ImmutableMap> - addReaction: (id: string, name: string) => void - removeReaction: (id: string, name: string) => void + announcementId: string; + reactions: ImmutableList; + emojiMap: ImmutableMap>; + addReaction: (id: string, name: string) => void; + removeReaction: (id: string, name: string) => void; } const ReactionsBar: React.FC = ({ announcementId, reactions, addReaction, removeReaction, emojiMap }) => { diff --git a/src/components/attachment-thumbs.tsx b/src/components/attachment-thumbs.tsx index 25b4bec00..1ec694667 100644 --- a/src/components/attachment-thumbs.tsx +++ b/src/components/attachment-thumbs.tsx @@ -9,9 +9,9 @@ import type { List as ImmutableList } from 'immutable'; import type { Attachment } from 'soapbox/types/entities'; interface IAttachmentThumbs { - media: ImmutableList - onClick?(): void - sensitive?: boolean + media: ImmutableList; + onClick?(): void; + sensitive?: boolean; } const AttachmentThumbs = (props: IAttachmentThumbs) => { diff --git a/src/components/authorize-reject-buttons.tsx b/src/components/authorize-reject-buttons.tsx index 5dfb37a31..aa6a39ab7 100644 --- a/src/components/authorize-reject-buttons.tsx +++ b/src/components/authorize-reject-buttons.tsx @@ -5,9 +5,9 @@ import { FormattedMessage } from 'react-intl'; import { HStack, IconButton, Text } from 'soapbox/components/ui'; interface IAuthorizeRejectButtons { - onAuthorize(): Promise | unknown - onReject(): Promise | unknown - countdown?: number + onAuthorize(): Promise | unknown; + onReject(): Promise | unknown; + countdown?: number; } /** Buttons to approve or reject a pending item, usually an account. */ @@ -126,7 +126,7 @@ const AuthorizeRejectButtons: React.FC = ({ onAuthorize }; interface IActionEmblem { - text: React.ReactNode + text: React.ReactNode; } const ActionEmblem: React.FC = ({ text }) => { @@ -140,12 +140,12 @@ const ActionEmblem: React.FC = ({ text }) => { }; interface IAuthorizeRejectButton { - theme: 'primary' | 'danger' - icon: string - action(): void - isLoading?: boolean - disabled?: boolean - style: React.CSSProperties + theme: 'primary' | 'danger'; + icon: string; + action(): void; + isLoading?: boolean; + disabled?: boolean; + style: React.CSSProperties; } const AuthorizeRejectButton: React.FC = ({ theme, icon, action, isLoading, style, disabled }) => { diff --git a/src/components/autosuggest-account-input.tsx b/src/components/autosuggest-account-input.tsx index d191aec15..1b7c05499 100644 --- a/src/components/autosuggest-account-input.tsx +++ b/src/components/autosuggest-account-input.tsx @@ -12,16 +12,16 @@ import type { InputThemes } from 'soapbox/components/ui/input/input'; const noOp = () => { }; interface IAutosuggestAccountInput { - onChange: React.ChangeEventHandler - onSelected: (accountId: string) => void - autoFocus?: boolean - value: string - limit?: number - className?: string - autoSelect?: boolean - menu?: Menu - onKeyDown?: React.KeyboardEventHandler - theme?: InputThemes + onChange: React.ChangeEventHandler; + onSelected: (accountId: string) => void; + autoFocus?: boolean; + value: string; + limit?: number; + className?: string; + autoSelect?: boolean; + menu?: Menu; + onKeyDown?: React.KeyboardEventHandler; + theme?: InputThemes; } const AutosuggestAccountInput: React.FC = ({ diff --git a/src/components/autosuggest-emoji.tsx b/src/components/autosuggest-emoji.tsx index 4f4471ecf..26f507b72 100644 --- a/src/components/autosuggest-emoji.tsx +++ b/src/components/autosuggest-emoji.tsx @@ -7,7 +7,7 @@ import { joinPublicPath } from 'soapbox/utils/static'; import type { Emoji } from 'soapbox/features/emoji'; interface IAutosuggestEmoji { - emoji: Emoji + emoji: Emoji; } const AutosuggestEmoji: React.FC = ({ emoji }) => { diff --git a/src/components/autosuggest-input.tsx b/src/components/autosuggest-input.tsx index 074acfef4..1846b02d0 100644 --- a/src/components/autosuggest-input.tsx +++ b/src/components/autosuggest-input.tsx @@ -17,23 +17,23 @@ import type { Emoji } from 'soapbox/features/emoji'; export type AutoSuggestion = string | Emoji; export interface IAutosuggestInput extends Pick, 'onChange' | 'onKeyUp' | 'onKeyDown'> { - value: string - suggestions: ImmutableList - disabled?: boolean - placeholder?: string - onSuggestionSelected: (tokenStart: number, lastToken: string | null, suggestion: AutoSuggestion) => void - onSuggestionsClearRequested: () => void - onSuggestionsFetchRequested: (token: string) => void - autoFocus: boolean - autoSelect: boolean - className?: string - id?: string - searchTokens: string[] - maxLength?: number - menu?: Menu - renderSuggestion?: React.FC<{ id: string }> - hidePortal?: boolean - theme?: InputThemes + value: string; + suggestions: ImmutableList; + disabled?: boolean; + placeholder?: string; + onSuggestionSelected: (tokenStart: number, lastToken: string | null, suggestion: AutoSuggestion) => void; + onSuggestionsClearRequested: () => void; + onSuggestionsFetchRequested: (token: string) => void; + autoFocus: boolean; + autoSelect: boolean; + className?: string; + id?: string; + searchTokens: string[]; + maxLength?: number; + menu?: Menu; + renderSuggestion?: React.FC<{ id: string }>; + hidePortal?: boolean; + theme?: InputThemes; } export default class AutosuggestInput extends ImmutablePureComponent { diff --git a/src/components/autosuggest-location.tsx b/src/components/autosuggest-location.tsx index 23ea09807..e94158bb0 100644 --- a/src/components/autosuggest-location.tsx +++ b/src/components/autosuggest-location.tsx @@ -19,7 +19,7 @@ export const ADDRESS_ICONS: Record = { }; interface IAutosuggestLocation { - id: string + id: string; } const AutosuggestLocation: React.FC = ({ id }) => { diff --git a/src/components/autosuggest-textarea.tsx b/src/components/autosuggest-textarea.tsx index e0be3c958..bc19c15e5 100644 --- a/src/components/autosuggest-textarea.tsx +++ b/src/components/autosuggest-textarea.tsx @@ -14,23 +14,23 @@ import type { List as ImmutableList } from 'immutable'; import type { Emoji } from 'soapbox/features/emoji'; interface IAutosuggesteTextarea { - id?: string - value: string - suggestions: ImmutableList - disabled: boolean - placeholder: string - onSuggestionSelected: (tokenStart: number, token: string | null, value: string | undefined) => void - onSuggestionsClearRequested: () => void - onSuggestionsFetchRequested: (token: string | number) => void - onChange: React.ChangeEventHandler - onKeyUp?: React.KeyboardEventHandler - onKeyDown?: React.KeyboardEventHandler - onPaste: (files: FileList) => void - autoFocus: boolean - onFocus: () => void - onBlur?: () => void - condensed?: boolean - children: React.ReactNode + id?: string; + value: string; + suggestions: ImmutableList; + disabled: boolean; + placeholder: string; + onSuggestionSelected: (tokenStart: number, token: string | null, value: string | undefined) => void; + onSuggestionsClearRequested: () => void; + onSuggestionsFetchRequested: (token: string | number) => void; + onChange: React.ChangeEventHandler; + onKeyUp?: React.KeyboardEventHandler; + onKeyDown?: React.KeyboardEventHandler; + onPaste: (files: FileList) => void; + autoFocus: boolean; + onFocus: () => void; + onBlur?: () => void; + condensed?: boolean; + children: React.ReactNode; } class AutosuggestTextarea extends ImmutablePureComponent { diff --git a/src/components/avatar-stack.tsx b/src/components/avatar-stack.tsx index b7dbb050d..e9193d344 100644 --- a/src/components/avatar-stack.tsx +++ b/src/components/avatar-stack.tsx @@ -11,8 +11,8 @@ import type { Account } from 'soapbox/types/entities'; const getAccount = makeGetAccount(); interface IAvatarStack { - accountIds: ImmutableOrderedSet - limit?: number + accountIds: ImmutableOrderedSet; + limit?: number; } const AvatarStack: React.FC = ({ accountIds, limit = 3 }) => { diff --git a/src/components/badge.tsx b/src/components/badge.tsx index fac7487e0..eeb03e38b 100644 --- a/src/components/badge.tsx +++ b/src/components/badge.tsx @@ -2,8 +2,8 @@ import clsx from 'clsx'; import React from 'react'; interface IBadge { - title: React.ReactNode - slug: string + title: React.ReactNode; + slug: string; } /** Badge to display on a user's profile. */ const Badge: React.FC = ({ title, slug }) => { diff --git a/src/components/big-card.tsx b/src/components/big-card.tsx index 2fcb7078a..895ac1298 100644 --- a/src/components/big-card.tsx +++ b/src/components/big-card.tsx @@ -3,9 +3,9 @@ import React from 'react'; import { Card, CardBody, Stack, Text } from 'soapbox/components/ui'; interface IBigCard { - title: React.ReactNode - subtitle?: React.ReactNode - children: React.ReactNode + title: React.ReactNode; + subtitle?: React.ReactNode; + children: React.ReactNode; } const BigCard: React.FC = ({ title, subtitle, children }) => { diff --git a/src/components/birthday-input.tsx b/src/components/birthday-input.tsx index 410f3d202..a35e85055 100644 --- a/src/components/birthday-input.tsx +++ b/src/components/birthday-input.tsx @@ -15,9 +15,9 @@ const messages = defineMessages({ }); interface IBirthdayInput { - value?: string - onChange: (value: string) => void - required?: boolean + value?: string; + onChange: (value: string) => void; + required?: boolean; } const BirthdayInput: React.FC = ({ value, onChange, required }) => { @@ -56,15 +56,15 @@ const BirthdayInput: React.FC = ({ value, onChange, required }) nextYearButtonDisabled, date, }: { - decreaseMonth(): void - increaseMonth(): void - prevMonthButtonDisabled: boolean - nextMonthButtonDisabled: boolean - decreaseYear(): void - increaseYear(): void - prevYearButtonDisabled: boolean - nextYearButtonDisabled: boolean - date: Date + decreaseMonth(): void; + increaseMonth(): void; + prevMonthButtonDisabled: boolean; + nextMonthButtonDisabled: boolean; + decreaseYear(): void; + increaseYear(): void; + prevYearButtonDisabled: boolean; + nextYearButtonDisabled: boolean; + date: Date; }) => { return (
diff --git a/src/components/birthday-panel.tsx b/src/components/birthday-panel.tsx index bcfe5d073..f1f31d94e 100644 --- a/src/components/birthday-panel.tsx +++ b/src/components/birthday-panel.tsx @@ -15,7 +15,7 @@ const timeToMidnight = () => { }; interface IBirthdayPanel { - limit: number + limit: number; } const BirthdayPanel = ({ limit }: IBirthdayPanel) => { diff --git a/src/components/blurhash.tsx b/src/components/blurhash.tsx index d0e0f43b9..c492b0e05 100644 --- a/src/components/blurhash.tsx +++ b/src/components/blurhash.tsx @@ -3,18 +3,18 @@ import React, { useRef, useEffect } from 'react'; interface IBlurhash { /** Hash to render */ - hash: string | null | undefined + hash: string | null | undefined; /** Width of the blurred region in pixels. Defaults to 32. */ - width?: number + width?: number; /** Height of the blurred region in pixels. Defaults to width. */ - height?: number + height?: number; /** * Whether dummy mode is enabled. If enabled, nothing is rendered * and canvas left untouched. */ - dummy?: boolean + dummy?: boolean; /** className of the canvas element. */ - className?: string + className?: string; } /** diff --git a/src/components/copyable-input.tsx b/src/components/copyable-input.tsx index 57f4db55d..2422c8276 100644 --- a/src/components/copyable-input.tsx +++ b/src/components/copyable-input.tsx @@ -5,7 +5,7 @@ import { Button, HStack, Input } from './ui'; interface ICopyableInput { /** Text to be copied. */ - value: string + value: string; } /** An input with copy abilities. */ diff --git a/src/components/display-name.tsx b/src/components/display-name.tsx index 9610ae8b6..bb6c8816b 100644 --- a/src/components/display-name.tsx +++ b/src/components/display-name.tsx @@ -11,9 +11,9 @@ import VerificationBadge from './verification-badge'; import type { Account } from 'soapbox/schemas'; interface IDisplayName { - account: Pick - withSuffix?: boolean - children?: React.ReactNode + account: Pick; + withSuffix?: boolean; + children?: React.ReactNode; } const DisplayName: React.FC = ({ account, children, withSuffix = true }) => { diff --git a/src/components/domain.tsx b/src/components/domain.tsx index 61470ced5..99ae9f50b 100644 --- a/src/components/domain.tsx +++ b/src/components/domain.tsx @@ -12,7 +12,7 @@ const messages = defineMessages({ }); interface IDomain { - domain: string + domain: string; } const Domain: React.FC = ({ domain }) => { diff --git a/src/components/dropdown-menu/dropdown-menu-item.tsx b/src/components/dropdown-menu/dropdown-menu-item.tsx index 0b27823d7..8c74e652a 100644 --- a/src/components/dropdown-menu/dropdown-menu-item.tsx +++ b/src/components/dropdown-menu/dropdown-menu-item.tsx @@ -5,23 +5,23 @@ import { useHistory } from 'react-router-dom'; import { Counter, Icon } from '../ui'; export interface MenuItem { - action?: React.EventHandler - active?: boolean - count?: number - destructive?: boolean - href?: string - icon?: string - meta?: string - middleClick?(event: React.MouseEvent): void - target?: React.HTMLAttributeAnchorTarget - text: string - to?: string + action?: React.EventHandler; + active?: boolean; + count?: number; + destructive?: boolean; + href?: string; + icon?: string; + meta?: string; + middleClick?(event: React.MouseEvent): void; + target?: React.HTMLAttributeAnchorTarget; + text: string; + to?: string; } interface IDropdownMenuItem { - index: number - item: MenuItem | null - onClick?(): void + index: number; + item: MenuItem | null; + onClick?(): void; } const DropdownMenuItem = ({ index, item, onClick }: IDropdownMenuItem) => { diff --git a/src/components/dropdown-menu/dropdown-menu.tsx b/src/components/dropdown-menu/dropdown-menu.tsx index 48fff7398..5af959cd7 100644 --- a/src/components/dropdown-menu/dropdown-menu.tsx +++ b/src/components/dropdown-menu/dropdown-menu.tsx @@ -18,16 +18,16 @@ import type { Status } from 'soapbox/types/entities'; export type Menu = Array; interface IDropdownMenu { - children?: React.ReactElement - disabled?: boolean - items: Menu - onClose?: () => void - onOpen?: () => void - onShiftClick?: React.EventHandler - placement?: Placement - src?: string - status?: Status - title?: string + children?: React.ReactElement; + disabled?: boolean; + items: Menu; + onClose?: () => void; + onOpen?: () => void; + onShiftClick?: React.EventHandler; + placement?: Placement; + src?: string; + status?: Status; + title?: string; } const listenerOptions = supportsPassiveEvents ? { passive: true } : false; diff --git a/src/components/error-boundary.tsx b/src/components/error-boundary.tsx index d6c3e9c28..8c45cea1e 100644 --- a/src/components/error-boundary.tsx +++ b/src/components/error-boundary.tsx @@ -27,14 +27,14 @@ const mapStateToProps = (state: RootState) => { }; interface Props extends ReturnType { - children: React.ReactNode + children: React.ReactNode; } type State = { - hasError: boolean - error: any - componentStack: any - browser?: Bowser.Parser.Parser + hasError: boolean; + error: any; + componentStack: any; + browser?: Bowser.Parser.Parser; } class ErrorBoundary extends React.PureComponent { diff --git a/src/components/event-preview.tsx b/src/components/event-preview.tsx index 9cfd1da81..4226bd5ca 100644 --- a/src/components/event-preview.tsx +++ b/src/components/event-preview.tsx @@ -19,10 +19,10 @@ const messages = defineMessages({ }); interface IEventPreview { - status: StatusEntity - className?: string - hideAction?: boolean - floatingAction?: boolean + status: StatusEntity; + className?: string; + hideAction?: boolean; + floatingAction?: boolean; } const EventPreview: React.FC = ({ status, className, hideAction, floatingAction = true }) => { diff --git a/src/components/extended-video-player.tsx b/src/components/extended-video-player.tsx index f31041bcf..e0512588c 100644 --- a/src/components/extended-video-player.tsx +++ b/src/components/extended-video-player.tsx @@ -3,14 +3,14 @@ import React, { useEffect, useRef } from 'react'; import { isIOS } from 'soapbox/is-mobile'; interface IExtendedVideoPlayer { - src: string - alt?: string - width?: number - height?: number - time?: number - controls?: boolean - muted?: boolean - onClick?: () => void + src: string; + alt?: string; + width?: number; + height?: number; + time?: number; + controls?: boolean; + muted?: boolean; + onClick?: () => void; } const ExtendedVideoPlayer: React.FC = ({ src, alt, time, controls, muted, onClick }) => { diff --git a/src/components/fork-awesome-icon.tsx b/src/components/fork-awesome-icon.tsx index 7a836da7b..09d5c8edb 100644 --- a/src/components/fork-awesome-icon.tsx +++ b/src/components/fork-awesome-icon.tsx @@ -9,9 +9,9 @@ import clsx from 'clsx'; import React from 'react'; export interface IForkAwesomeIcon extends React.HTMLAttributes { - id: string - className?: string - fixedWidth?: boolean + id: string; + className?: string; + fixedWidth?: boolean; } const ForkAwesomeIcon: React.FC = ({ id, className, fixedWidth, ...rest }) => { diff --git a/src/components/group-card.tsx b/src/components/group-card.tsx index 84e771902..e95914efa 100644 --- a/src/components/group-card.tsx +++ b/src/components/group-card.tsx @@ -11,7 +11,7 @@ import { HStack, Stack, Text } from './ui'; import type { Group as GroupEntity } from 'soapbox/types/entities'; interface IGroupCard { - group: GroupEntity + group: GroupEntity; } const GroupCard: React.FC = ({ group }) => { diff --git a/src/components/groups/group-avatar.tsx b/src/components/groups/group-avatar.tsx index 9b3213bb9..13b40d237 100644 --- a/src/components/groups/group-avatar.tsx +++ b/src/components/groups/group-avatar.tsx @@ -8,9 +8,9 @@ import { Avatar } from '../ui'; import type { Group } from 'soapbox/schemas'; interface IGroupAvatar { - group: Group - size: number - withRing?: boolean + group: Group; + size: number; + withRing?: boolean; } const GroupAvatar = (props: IGroupAvatar) => { diff --git a/src/components/groups/popover/group-popover.tsx b/src/components/groups/popover/group-popover.tsx index 245420af6..6aa214cd9 100644 --- a/src/components/groups/popover/group-popover.tsx +++ b/src/components/groups/popover/group-popover.tsx @@ -11,9 +11,9 @@ import GroupAvatar from '../group-avatar'; import type { Group } from 'soapbox/schemas'; interface IGroupPopoverContainer { - children: React.ReactElement> - isEnabled: boolean - group: Group + children: React.ReactElement>; + isEnabled: boolean; + group: Group; } const messages = defineMessages({ diff --git a/src/components/hashtag.tsx b/src/components/hashtag.tsx index 41b2c07a6..1963c4f04 100644 --- a/src/components/hashtag.tsx +++ b/src/components/hashtag.tsx @@ -10,7 +10,7 @@ import { HStack, Stack, Text } from './ui'; import type { Tag } from 'soapbox/types/entities'; interface IHashtag { - hashtag: Tag + hashtag: Tag; } const Hashtag: React.FC = ({ hashtag }) => { diff --git a/src/components/helmet.tsx b/src/components/helmet.tsx index c7eef876a..ee2ce3317 100644 --- a/src/components/helmet.tsx +++ b/src/components/helmet.tsx @@ -16,7 +16,7 @@ const getNotifTotals = (state: RootState): number => { }; interface IHelmet { - children: React.ReactNode + children: React.ReactNode; } const Helmet: React.FC = ({ children }) => { diff --git a/src/components/hoc/group-lookup-hoc.tsx b/src/components/hoc/group-lookup-hoc.tsx index 07c2d475b..570ec2e69 100644 --- a/src/components/hoc/group-lookup-hoc.tsx +++ b/src/components/hoc/group-lookup-hoc.tsx @@ -7,15 +7,15 @@ import { Layout } from '../ui'; interface IGroupLookup { params: { - groupSlug: string - } + groupSlug: string; + }; } interface IMaybeGroupLookup { params?: { - groupSlug?: string - groupId?: string - } + groupSlug?: string; + groupId?: string; + }; } function GroupLookupHoc(Component: React.ComponentType<{ params: { groupId: string } }>) { diff --git a/src/components/hover-ref-wrapper.tsx b/src/components/hover-ref-wrapper.tsx index d116e5d13..8d0aa1ecd 100644 --- a/src/components/hover-ref-wrapper.tsx +++ b/src/components/hover-ref-wrapper.tsx @@ -15,10 +15,10 @@ const showProfileHoverCard = debounce((dispatch, ref, accountId) => { }, 600); interface IHoverRefWrapper { - accountId: string - inline?: boolean - className?: string - children: React.ReactNode + accountId: string; + inline?: boolean; + className?: string; + children: React.ReactNode; } /** Makes a profile hover card appear when the wrapped element is hovered. */ diff --git a/src/components/hover-status-wrapper.tsx b/src/components/hover-status-wrapper.tsx index c2d2c4eda..685dbf3d9 100644 --- a/src/components/hover-status-wrapper.tsx +++ b/src/components/hover-status-wrapper.tsx @@ -14,10 +14,10 @@ const showStatusHoverCard = debounce((dispatch, ref, statusId) => { }, 300); interface IHoverStatusWrapper { - statusId: any - inline: boolean - className?: string - children: React.ReactNode + statusId: any; + inline: boolean; + className?: string; + children: React.ReactNode; } /** Makes a status hover card appear when the wrapped element is hovered. */ diff --git a/src/components/icon-button.tsx b/src/components/icon-button.tsx index f4da2c2c5..4eb0dccca 100644 --- a/src/components/icon-button.tsx +++ b/src/components/icon-button.tsx @@ -4,13 +4,13 @@ import React from 'react'; import Icon from 'soapbox/components/icon'; interface IIconButton extends Pick, 'className' | 'disabled' | 'onClick' | 'onKeyDown' | 'onKeyPress' | 'onKeyUp' | 'onMouseDown' | 'onMouseEnter' | 'onMouseLeave' | 'tabIndex' | 'title'> { - active?: boolean - expanded?: boolean - iconClassName?: string - pressed?: boolean - size?: number - src: string - text?: React.ReactNode + active?: boolean; + expanded?: boolean; + iconClassName?: string; + pressed?: boolean; + size?: number; + src: string; + text?: React.ReactNode; } const IconButton: React.FC = ({ diff --git a/src/components/icon-with-counter.tsx b/src/components/icon-with-counter.tsx index dbc17d9d2..5cac95290 100644 --- a/src/components/icon-with-counter.tsx +++ b/src/components/icon-with-counter.tsx @@ -4,10 +4,10 @@ import Icon, { IIcon } from 'soapbox/components/icon'; import { Counter } from 'soapbox/components/ui'; interface IIconWithCounter extends React.HTMLAttributes { - count: number - countMax?: number - icon?: string - src?: string + count: number; + countMax?: number; + icon?: string; + src?: string; } const IconWithCounter: React.FC = ({ icon, count, countMax, ...rest }) => { diff --git a/src/components/icon.tsx b/src/components/icon.tsx index 421d937dd..8ab8fe309 100644 --- a/src/components/icon.tsx +++ b/src/components/icon.tsx @@ -8,10 +8,10 @@ import React from 'react'; import InlineSVG from 'react-inlinesvg'; // eslint-disable-line no-restricted-imports export interface IIcon extends React.HTMLAttributes { - src: string - id?: string - alt?: string - className?: string + src: string; + id?: string; + alt?: string; + className?: string; } /** diff --git a/src/components/list.tsx b/src/components/list.tsx index 06a202535..303690593 100644 --- a/src/components/list.tsx +++ b/src/components/list.tsx @@ -8,7 +8,7 @@ import { SelectDropdown } from '../features/forms'; import { Icon, HStack, Select } from './ui'; interface IList { - children: React.ReactNode + children: React.ReactNode; } const List: React.FC = ({ children }) => ( @@ -16,13 +16,13 @@ const List: React.FC = ({ children }) => ( ); interface IListItem { - label: React.ReactNode - hint?: React.ReactNode - to?: string - onClick?(): void - onSelect?(): void - isSelected?: boolean - children?: React.ReactNode + label: React.ReactNode; + hint?: React.ReactNode; + to?: string; + onClick?(): void; + onSelect?(): void; + isSelected?: boolean; + children?: React.ReactNode; } const ListItem: React.FC = ({ label, hint, children, to, onClick, onSelect, isSelected }) => { diff --git a/src/components/load-gap.tsx b/src/components/load-gap.tsx index 2836fb14f..3ccdf4f81 100644 --- a/src/components/load-gap.tsx +++ b/src/components/load-gap.tsx @@ -8,9 +8,9 @@ const messages = defineMessages({ }); interface ILoadGap { - disabled?: boolean - maxId: string - onClick: (id: string) => void + disabled?: boolean; + maxId: string; + onClick: (id: string) => void; } const LoadGap: React.FC = ({ disabled, maxId, onClick }) => { diff --git a/src/components/load-more.tsx b/src/components/load-more.tsx index 878adda7c..c356751b9 100644 --- a/src/components/load-more.tsx +++ b/src/components/load-more.tsx @@ -4,10 +4,10 @@ import { FormattedMessage } from 'react-intl'; import { Button } from 'soapbox/components/ui'; interface ILoadMore { - onClick: React.MouseEventHandler - disabled?: boolean - visible?: boolean - className?: string + onClick: React.MouseEventHandler; + disabled?: boolean; + visible?: boolean; + className?: string; } const LoadMore: React.FC = ({ onClick, disabled, visible = true, className }) => { diff --git a/src/components/location-search.tsx b/src/components/location-search.tsx index df0622f2f..d2d8d4f29 100644 --- a/src/components/location-search.tsx +++ b/src/components/location-search.tsx @@ -18,7 +18,7 @@ const messages = defineMessages({ }); interface ILocationSearch { - onSelected: (locationId: string) => void + onSelected: (locationId: string) => void; } const LocationSearch: React.FC = ({ onSelected }) => { diff --git a/src/components/media-gallery.tsx b/src/components/media-gallery.tsx index 0c26b26f6..0d8e48683 100644 --- a/src/components/media-gallery.tsx +++ b/src/components/media-gallery.tsx @@ -19,21 +19,21 @@ const ATTACHMENT_LIMIT = 4; const MAX_FILENAME_LENGTH = 45; interface Dimensions { - w: Property.Width | number - h: Property.Height | number - t?: Property.Top - r?: Property.Right - b?: Property.Bottom - l?: Property.Left - float?: Property.Float - pos?: Property.Position + w: Property.Width | number; + h: Property.Height | number; + t?: Property.Top; + r?: Property.Right; + b?: Property.Bottom; + l?: Property.Left; + float?: Property.Float; + pos?: Property.Position; } interface SizeData { - style: React.CSSProperties - itemsDimensions: Dimensions[] - size: number - width: number + style: React.CSSProperties; + itemsDimensions: Dimensions[]; + size: number; + width: number; } const withinLimits = (aspectRatio: number) => { @@ -48,16 +48,16 @@ const shouldLetterbox = (attachment: Attachment): boolean => { }; interface IItem { - attachment: Attachment - standalone?: boolean - index: number - size: number - onClick: (index: number) => void - displayWidth?: number - visible: boolean - dimensions: Dimensions - last?: boolean - total: number + attachment: Attachment; + standalone?: boolean; + index: number; + size: number; + onClick: (index: number) => void; + displayWidth?: number; + visible: boolean; + dimensions: Dimensions; + last?: boolean; + total: number; } const Item: React.FC = ({ @@ -275,17 +275,17 @@ const Item: React.FC = ({ }; export interface IMediaGallery { - sensitive?: boolean - media: ImmutableList - height?: number - onOpenMedia: (media: ImmutableList, index: number) => void - defaultWidth?: number - cacheWidth?: (width: number) => void - visible?: boolean - onToggleVisibility?: () => void - displayMedia?: string - compact?: boolean - className?: string + sensitive?: boolean; + media: ImmutableList; + height?: number; + onOpenMedia: (media: ImmutableList, index: number) => void; + defaultWidth?: number; + cacheWidth?: (width: number) => void; + visible?: boolean; + onToggleVisibility?: () => void; + displayMedia?: string; + compact?: boolean; + className?: string; } const MediaGallery: React.FC = (props) => { diff --git a/src/components/missing-indicator.tsx b/src/components/missing-indicator.tsx index 02c383d01..69bade6c2 100644 --- a/src/components/missing-indicator.tsx +++ b/src/components/missing-indicator.tsx @@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl'; import { Card, CardBody, Stack, Text } from './ui'; interface MissingIndicatorProps { - nested?: boolean + nested?: boolean; } const MissingIndicator = ({ nested = false }: MissingIndicatorProps): JSX.Element => ( diff --git a/src/components/modal-root.tsx b/src/components/modal-root.tsx index d8c8725a1..024f59ebc 100644 --- a/src/components/modal-root.tsx +++ b/src/components/modal-root.tsx @@ -37,10 +37,10 @@ export const checkEventComposeContent = (compose?: ReturnType void - onClose: (type?: ModalType) => void - type: ModalType - children: React.ReactNode + onCancel?: () => void; + onClose: (type?: ModalType) => void; + type: ModalType; + children: React.ReactNode; } const ModalRoot: React.FC = ({ children, onCancel, onClose, type }) => { diff --git a/src/components/navlinks.tsx b/src/components/navlinks.tsx index 4e0804326..4670f8015 100644 --- a/src/components/navlinks.tsx +++ b/src/components/navlinks.tsx @@ -5,7 +5,7 @@ import { Text } from 'soapbox/components/ui'; import { useSettings, useSoapboxConfig } from 'soapbox/hooks'; interface INavlinks { - type: string + type: string; } const Navlinks: React.FC = ({ type }) => { diff --git a/src/components/outline-box.tsx b/src/components/outline-box.tsx index 6994e6bd1..0f56c21ae 100644 --- a/src/components/outline-box.tsx +++ b/src/components/outline-box.tsx @@ -2,8 +2,8 @@ import clsx from 'clsx'; import React from 'react'; interface IOutlineBox extends React.HTMLAttributes { - children: React.ReactNode - className?: string + children: React.ReactNode; + className?: string; } /** Wraps children in a container with an outline. */ diff --git a/src/components/pending-items-row.tsx b/src/components/pending-items-row.tsx index 4fbf236cd..f86be15d3 100644 --- a/src/components/pending-items-row.tsx +++ b/src/components/pending-items-row.tsx @@ -7,11 +7,11 @@ import { HStack, Icon, Text } from 'soapbox/components/ui'; interface IPendingItemsRow { /** Path to navigate the user when clicked. */ - to: string + to: string; /** Number of pending items. */ - count: number + count: number; /** Size of the icon. */ - size?: 'md' | 'lg' + size?: 'md' | 'lg'; } const PendingItemsRow: React.FC = ({ to, count, size = 'md' }) => { diff --git a/src/components/polls/poll-footer.tsx b/src/components/polls/poll-footer.tsx index 1994c1e76..9df4215d0 100644 --- a/src/components/polls/poll-footer.tsx +++ b/src/components/polls/poll-footer.tsx @@ -16,9 +16,9 @@ const messages = defineMessages({ }); interface IPollFooter { - poll: PollEntity - showResults: boolean - selected: Selected + poll: PollEntity; + showResults: boolean; + selected: Selected; } const PollFooter: React.FC = ({ poll, showResults, selected }): JSX.Element => { diff --git a/src/components/polls/poll-option.tsx b/src/components/polls/poll-option.tsx index b4c37e11d..f27a81e64 100644 --- a/src/components/polls/poll-option.tsx +++ b/src/components/polls/poll-option.tsx @@ -15,7 +15,7 @@ const messages = defineMessages({ votes: { id: 'poll.votes', defaultMessage: '{votes, plural, one {# vote} other {# votes}}' }, }); -const PollPercentageBar: React.FC<{ percent: number, leading: boolean }> = ({ percent, leading }): JSX.Element => { +const PollPercentageBar: React.FC<{ percent: number; leading: boolean }> = ({ percent, leading }): JSX.Element => { return ( {({ width }) => ( @@ -29,7 +29,7 @@ const PollPercentageBar: React.FC<{ percent: number, leading: boolean }> = ({ pe }; interface IPollOptionText extends IPollOption { - percent: number + percent: number; } const PollOptionText: React.FC = ({ poll, option, index, active, onToggle }) => { @@ -95,12 +95,12 @@ const PollOptionText: React.FC = ({ poll, option, index, active }; interface IPollOption { - poll: PollEntity - option: PollOptionEntity - index: number - showResults?: boolean - active: boolean - onToggle: (value: number) => void + poll: PollEntity; + option: PollOptionEntity; + index: number; + showResults?: boolean; + active: boolean; + onToggle: (value: number) => void; } const PollOption: React.FC = (props): JSX.Element | null => { diff --git a/src/components/polls/poll.tsx b/src/components/polls/poll.tsx index a925e9597..45336a05c 100644 --- a/src/components/polls/poll.tsx +++ b/src/components/polls/poll.tsx @@ -13,8 +13,8 @@ import PollOption from './poll-option'; export type Selected = Record; interface IPoll { - id: string - status?: string + id: string; + status?: string; } const messages = defineMessages({ diff --git a/src/components/profile-hover-card.tsx b/src/components/profile-hover-card.tsx index 98b9f70e8..f7517430e 100644 --- a/src/components/profile-hover-card.tsx +++ b/src/components/profile-hover-card.tsx @@ -56,7 +56,7 @@ const handleMouseLeave = (dispatch: AppDispatch): React.MouseEventHandler => { }; interface IProfileHoverCard { - visible: boolean + visible: boolean; } /** Popup profile preview that appears when hovering avatars and display names. */ diff --git a/src/components/progress-circle.tsx b/src/components/progress-circle.tsx index af5bf396c..1adb3f46e 100644 --- a/src/components/progress-circle.tsx +++ b/src/components/progress-circle.tsx @@ -2,10 +2,10 @@ import clsx from 'clsx'; import React from 'react'; interface IProgressCircle { - progress: number - radius?: number - stroke?: number - title?: string + progress: number; + radius?: number; + stroke?: number; + title?: string; } const ProgressCircle: React.FC = ({ progress, radius = 12, stroke = 4, title }) => { diff --git a/src/components/pull-to-refresh.tsx b/src/components/pull-to-refresh.tsx index bf033eaa4..829441b37 100644 --- a/src/components/pull-to-refresh.tsx +++ b/src/components/pull-to-refresh.tsx @@ -4,10 +4,10 @@ import PTRComponent from 'react-simple-pull-to-refresh'; import { Spinner } from 'soapbox/components/ui'; interface IPullToRefresh { - onRefresh?: () => Promise - refreshingContent?: JSX.Element | string - pullingContent?: JSX.Element | string - children: React.ReactNode + onRefresh?: () => Promise; + refreshingContent?: JSX.Element | string; + pullingContent?: JSX.Element | string; + children: React.ReactNode; } /** diff --git a/src/components/pullable.tsx b/src/components/pullable.tsx index a8fe6afea..e92a76ea2 100644 --- a/src/components/pullable.tsx +++ b/src/components/pullable.tsx @@ -3,7 +3,7 @@ import React from 'react'; import PullToRefresh from './pull-to-refresh'; interface IPullable { - children: React.ReactNode + children: React.ReactNode; } /** diff --git a/src/components/quoted-status.tsx b/src/components/quoted-status.tsx index 065d41216..a5954eaa8 100644 --- a/src/components/quoted-status.tsx +++ b/src/components/quoted-status.tsx @@ -23,11 +23,11 @@ const messages = defineMessages({ interface IQuotedStatus { /** The quoted status entity. */ - status?: StatusEntity + status?: StatusEntity; /** Callback when cancelled (during compose). */ - onCancel?: Function + onCancel?: Function; /** Whether the status is shown in the post composer. */ - compose?: boolean + compose?: boolean; } /** Status embedded in a quote post. */ diff --git a/src/components/radio.tsx b/src/components/radio.tsx index f81412bb7..2104b9985 100644 --- a/src/components/radio.tsx +++ b/src/components/radio.tsx @@ -3,8 +3,8 @@ import React from 'react'; import List, { ListItem } from './list'; interface IRadioGroup { - onChange: React.ChangeEventHandler - children: React.ReactElement<{ onChange: React.ChangeEventHandler }>[] + onChange: React.ChangeEventHandler; + children: React.ReactElement<{ onChange: React.ChangeEventHandler }>[]; } const RadioGroup = ({ onChange, children }: IRadioGroup) => { @@ -16,11 +16,11 @@ const RadioGroup = ({ onChange, children }: IRadioGroup) => { }; interface IRadioItem { - label: React.ReactNode - hint?: React.ReactNode - value: string - checked: boolean - onChange?: React.ChangeEventHandler + label: React.ReactNode; + hint?: React.ReactNode; + value: string; + checked: boolean; + onChange?: React.ChangeEventHandler; } const RadioItem: React.FC = ({ label, hint, checked = false, onChange, value }) => { diff --git a/src/components/relative-timestamp.tsx b/src/components/relative-timestamp.tsx index 84e747146..c9b2780f2 100644 --- a/src/components/relative-timestamp.tsx +++ b/src/components/relative-timestamp.tsx @@ -113,14 +113,14 @@ const timeRemainingString = (intl: IntlShape, date: Date, now: number) => { }; interface RelativeTimestampProps extends IText { - intl: IntlShape - timestamp: string - year?: number - futureDate?: boolean + intl: IntlShape; + timestamp: string; + year?: number; + futureDate?: boolean; } interface RelativeTimestampState { - now: number + now: number; } /** Displays a timestamp compared to the current time, eg "1m" for one minute ago. */ diff --git a/src/components/safe-embed.tsx b/src/components/safe-embed.tsx index d77d0b3fa..3e2668648 100644 --- a/src/components/safe-embed.tsx +++ b/src/components/safe-embed.tsx @@ -2,13 +2,13 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; interface ISafeEmbed { /** Styles for the outer frame element. */ - className?: string + className?: string; /** Space-separate list of restrictions to ALLOW for the iframe. */ - sandbox?: string + sandbox?: string; /** Unique title for the iframe. */ - title: string + title: string; /** HTML body to embed. */ - html?: string + html?: string; } /** Safely embeds arbitrary HTML content on the page (by putting it in an iframe). */ diff --git a/src/components/scroll-top-button.tsx b/src/components/scroll-top-button.tsx index 84f23969e..58d356c2a 100644 --- a/src/components/scroll-top-button.tsx +++ b/src/components/scroll-top-button.tsx @@ -7,15 +7,15 @@ import { useSettings } from 'soapbox/hooks'; interface IScrollTopButton { /** Callback when clicked, and also when scrolled to the top. */ - onClick: () => void + onClick: () => void; /** Number of unread items. */ - count: number + count: number; /** Message to display in the button (should contain a `{count}` value). */ - message: MessageDescriptor + message: MessageDescriptor; /** Distance from the top of the screen (scrolling down) before the button appears. */ - threshold?: number + threshold?: number; /** Distance from the top of the screen (scrolling up) before the action is triggered. */ - autoloadThreshold?: number + autoloadThreshold?: number; } /** Floating new post counter above timelines, clicked to scroll to top. */ diff --git a/src/components/scrollable-list.tsx b/src/components/scrollable-list.tsx index 78fb7f036..dd5fb7625 100644 --- a/src/components/scrollable-list.tsx +++ b/src/components/scrollable-list.tsx @@ -10,14 +10,14 @@ import { Card, Spinner } from './ui'; /** Custom Viruoso component context. */ type Context = { - itemClassName?: string - listClassName?: string + itemClassName?: string; + listClassName?: string; } /** Scroll position saved in sessionStorage. */ type SavedScrollPosition = { - index: number - offset: number + index: number; + offset: number; } /** Custom Virtuoso Item component representing a single scrollable item. */ @@ -37,48 +37,48 @@ const List: Components['List'] = React.forwardRef((props, interface IScrollableList extends VirtuosoProps { /** Unique key to preserve the scroll position when navigating back. */ - scrollKey?: string + scrollKey?: string; /** Pagination callback when the end of the list is reached. */ - onLoadMore?: () => void + onLoadMore?: () => void; /** Whether the data is currently being fetched. */ - isLoading?: boolean + isLoading?: boolean; /** Whether to actually display the loading state. */ - showLoading?: boolean + showLoading?: boolean; /** Whether we expect an additional page of data. */ - hasMore?: boolean + hasMore?: boolean; /** Additional element to display at the top of the list. */ - prepend?: React.ReactNode + prepend?: React.ReactNode; /** Whether to display the prepended element. */ - alwaysPrepend?: boolean + alwaysPrepend?: boolean; /** Message to display when the list is loaded but empty. */ - emptyMessage?: React.ReactNode + emptyMessage?: React.ReactNode; /** Should the empty message be displayed in a Card */ - emptyMessageCard?: boolean + emptyMessageCard?: boolean; /** Scrollable content. */ - children: Iterable + children: Iterable; /** Callback when the list is scrolled to the top. */ - onScrollToTop?: () => void + onScrollToTop?: () => void; /** Callback when the list is scrolled. */ - onScroll?: () => void + onScroll?: () => void; /** Placeholder component to render while loading. */ - placeholderComponent?: React.ComponentType | React.NamedExoticComponent + placeholderComponent?: React.ComponentType | React.NamedExoticComponent; /** Number of placeholders to render while loading. */ - placeholderCount?: number + placeholderCount?: number; /** * Pull to refresh callback. * @deprecated Put a PTR around the component instead. */ - onRefresh?: () => Promise + onRefresh?: () => Promise; /** Extra class names on the Virtuoso element. */ - className?: string + className?: string; /** Class names on each item container. */ - itemClassName?: string + itemClassName?: string; /** `id` attribute on the Virtuoso element. */ - id?: string + id?: string; /** CSS styles on the Virtuoso element. */ - style?: React.CSSProperties + style?: React.CSSProperties; /** Whether to use the window to scroll the content instead of Virtuoso's container. */ - useWindowScroll?: boolean + useWindowScroll?: boolean; } /** Legacy ScrollableList with Virtuoso for backwards-compatibility. */ diff --git a/src/components/sidebar-menu.tsx b/src/components/sidebar-menu.tsx index 819411133..8b1d43f1a 100644 --- a/src/components/sidebar-menu.tsx +++ b/src/components/sidebar-menu.tsx @@ -43,11 +43,11 @@ const messages = defineMessages({ }); interface ISidebarLink { - href?: string - to?: string - icon: string - text: string | JSX.Element - onClick: React.EventHandler + href?: string; + to?: string; + icon: string; + text: string | JSX.Element; + onClick: React.EventHandler; } const SidebarLink: React.FC = ({ href, to, icon, text, onClick }) => { diff --git a/src/components/sidebar-navigation-link.tsx b/src/components/sidebar-navigation-link.tsx index 108d4376c..224b75e06 100644 --- a/src/components/sidebar-navigation-link.tsx +++ b/src/components/sidebar-navigation-link.tsx @@ -6,17 +6,17 @@ import { Icon, Text } from './ui'; interface ISidebarNavigationLink { /** Notification count, if any. */ - count?: number + count?: number; /** Optional max to cap count (ie: N+) */ - countMax?: number + countMax?: number; /** URL to an SVG icon. */ - icon: string + icon: string; /** Link label. */ - text: React.ReactNode + text: React.ReactNode; /** Route to an internal page. */ - to?: string + to?: string; /** Callback when the link is clicked. */ - onClick?: React.EventHandler + onClick?: React.EventHandler; } /** Desktop sidebar navigation link. */ diff --git a/src/components/site-logo.tsx b/src/components/site-logo.tsx index 9d545a48d..10a769fad 100644 --- a/src/components/site-logo.tsx +++ b/src/components/site-logo.tsx @@ -5,9 +5,9 @@ import { useSoapboxConfig, useSettings, useTheme } from 'soapbox/hooks'; interface ISiteLogo extends React.ComponentProps<'img'> { /** Extra class names for the element. */ - className?: string + className?: string; /** Override theme setting for */ - theme?: 'dark' | 'light' + theme?: 'dark' | 'light'; } /** Display the most appropriate site logo based on the theme and configuration. */ diff --git a/src/components/status-action-bar.tsx b/src/components/status-action-bar.tsx index a42fac13b..8b2009b82 100644 --- a/src/components/status-action-bar.tsx +++ b/src/components/status-action-bar.tsx @@ -108,11 +108,11 @@ const messages = defineMessages({ }); interface IStatusActionBar { - status: Status - withLabels?: boolean - expandable?: boolean - space?: 'sm' | 'md' | 'lg' - statusActionButtonTheme?: 'default' | 'inverse' + status: Status; + withLabels?: boolean; + expandable?: boolean; + space?: 'sm' | 'md' | 'lg'; + statusActionButtonTheme?: 'default' | 'inverse'; } const StatusActionBar: React.FC = ({ @@ -698,7 +698,7 @@ const StatusActionBar: React.FC = ({ const canShare = ('share' in navigator) && (status.visibility === 'public' || status.visibility === 'group'); const spacing: { - [key: string]: React.ComponentProps['space'] + [key: string]: React.ComponentProps['space']; } = { 'sm': 2, 'md': 8, diff --git a/src/components/status-action-button.tsx b/src/components/status-action-button.tsx index 10f952065..3dfe61939 100644 --- a/src/components/status-action-button.tsx +++ b/src/components/status-action-button.tsx @@ -14,7 +14,7 @@ const COLORS = { type Color = keyof typeof COLORS; interface IStatusActionCounter { - count: number + count: number; } /** Action button numerical counter, eg "5" likes. */ @@ -27,15 +27,15 @@ const StatusActionCounter: React.FC = ({ count = 0 }): JSX }; interface IStatusActionButton extends React.ButtonHTMLAttributes { - iconClassName?: string - icon: string - count?: number - active?: boolean - color?: Color - filled?: boolean - emoji?: ImmutableMap - text?: React.ReactNode - theme?: 'default' | 'inverse' + iconClassName?: string; + icon: string; + count?: number; + active?: boolean; + color?: Color; + filled?: boolean; + emoji?: ImmutableMap; + text?: React.ReactNode; + theme?: 'default' | 'inverse'; } const StatusActionButton = React.forwardRef((props, ref): JSX.Element => { diff --git a/src/components/status-content.tsx b/src/components/status-content.tsx index 5fc530769..7c322dcb8 100644 --- a/src/components/status-content.tsx +++ b/src/components/status-content.tsx @@ -18,7 +18,7 @@ const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top) const BIG_EMOJI_LIMIT = 10; interface IReadMoreButton { - onClick: React.MouseEventHandler + onClick: React.MouseEventHandler; } /** Button to expand a truncated status (due to too much content) */ @@ -30,11 +30,11 @@ const ReadMoreButton: React.FC = ({ onClick }) => ( ); interface IStatusContent { - status: Status - onClick?: () => void - collapsable?: boolean - translatable?: boolean - textSize?: Sizes + status: Status; + onClick?: () => void; + collapsable?: boolean; + translatable?: boolean; + textSize?: Sizes; } /** Renders the text content of a status */ diff --git a/src/components/status-hover-card.tsx b/src/components/status-hover-card.tsx index 4f91bff4e..5c31f9b4d 100644 --- a/src/components/status-hover-card.tsx +++ b/src/components/status-hover-card.tsx @@ -15,7 +15,7 @@ import { showStatusHoverCard } from './hover-status-wrapper'; import { Card, CardBody } from './ui'; interface IStatusHoverCard { - visible: boolean + visible: boolean; } /** Popup status preview that appears when hovering reply to */ diff --git a/src/components/status-list.tsx b/src/components/status-list.tsx index d1f6164c3..4f6347593 100644 --- a/src/components/status-list.tsx +++ b/src/components/status-list.tsx @@ -17,31 +17,31 @@ import type { IScrollableList } from 'soapbox/components/scrollable-list'; interface IStatusList extends Omit { /** Unique key to preserve the scroll position when navigating back. */ - scrollKey: string + scrollKey: string; /** List of status IDs to display. */ - statusIds: ImmutableOrderedSet + statusIds: ImmutableOrderedSet; /** Last _unfiltered_ status ID (maxId) for pagination. */ - lastStatusId?: string + lastStatusId?: string; /** Pinned statuses to show at the top of the feed. */ - featuredStatusIds?: ImmutableOrderedSet + featuredStatusIds?: ImmutableOrderedSet; /** Pagination callback when the end of the list is reached. */ - onLoadMore?: (lastStatusId: string) => void + onLoadMore?: (lastStatusId: string) => void; /** Whether the data is currently being fetched. */ - isLoading: boolean + isLoading: boolean; /** Whether the server did not return a complete page. */ - isPartial?: boolean + isPartial?: boolean; /** Whether we expect an additional page of data. */ - hasMore: boolean + hasMore: boolean; /** Message to display when the list is loaded but empty. */ - emptyMessage: React.ReactNode + emptyMessage: React.ReactNode; /** ID of the timeline in Redux. */ - timelineId?: string + timelineId?: string; /** Whether to display a gap or border between statuses in the list. */ - divideType?: 'space' | 'border' + divideType?: 'space' | 'border'; /** Whether to display ads. */ - showAds?: boolean + showAds?: boolean; /** Whether to show group information. */ - showGroup?: boolean + showGroup?: boolean; } /** Feed of statuses, built atop ScrollableList. */ diff --git a/src/components/status-media.tsx b/src/components/status-media.tsx index 99329f297..00975e224 100644 --- a/src/components/status-media.tsx +++ b/src/components/status-media.tsx @@ -15,15 +15,15 @@ import type { Status, Attachment } from 'soapbox/types/entities'; interface IStatusMedia { /** Status entity to render media for. */ - status: Status + status: Status; /** Whether to display compact media. */ - muted?: boolean + muted?: boolean; /** Callback when compact media is clicked. */ - onClick?: () => void + onClick?: () => void; /** Whether or not the media is concealed behind a NSFW banner. */ - showMedia?: boolean + showMedia?: boolean; /** Callback when visibility is toggled (eg clicked through NSFW). */ - onToggleVisibility?: () => void + onToggleVisibility?: () => void; } /** Render media attachments for a status. */ diff --git a/src/components/status-reaction-wrapper.tsx b/src/components/status-reaction-wrapper.tsx index 236653307..f8e6129d1 100644 --- a/src/components/status-reaction-wrapper.tsx +++ b/src/components/status-reaction-wrapper.tsx @@ -8,8 +8,8 @@ import { isUserTouching } from 'soapbox/is-mobile'; import { getReactForStatus } from 'soapbox/utils/emoji-reacts'; interface IStatusReactionWrapper { - statusId: string - children: JSX.Element + statusId: string; + children: JSX.Element; } /** Provides emoji reaction functionality to the underlying button component */ diff --git a/src/components/status-reply-mentions.tsx b/src/components/status-reply-mentions.tsx index e03d0c7f7..2b8e8633d 100644 --- a/src/components/status-reply-mentions.tsx +++ b/src/components/status-reply-mentions.tsx @@ -11,8 +11,8 @@ import { isPubkey } from 'soapbox/utils/nostr'; import type { Account, Status } from 'soapbox/types/entities'; interface IStatusReplyMentions { - status: Status - hoverable?: boolean + status: Status; + hoverable?: boolean; } const StatusReplyMentions: React.FC = ({ status, hoverable = true }) => { diff --git a/src/components/status.tsx b/src/components/status.tsx index d3ef12c1d..c2411e6dc 100644 --- a/src/components/status.tsx +++ b/src/components/status.tsx @@ -30,29 +30,29 @@ import type { } from 'soapbox/types/entities'; // Defined in components/scrollable-list -export type ScrollPosition = { height: number, top: number }; +export type ScrollPosition = { height: number; top: number }; const messages = defineMessages({ reblogged_by: { id: 'status.reblogged_by', defaultMessage: '{name} reposted' }, }); export interface IStatus { - id?: string - avatarSize?: number - status: StatusEntity - onClick?: () => void - muted?: boolean - hidden?: boolean - unread?: boolean - onMoveUp?: (statusId: string, featured?: boolean) => void - onMoveDown?: (statusId: string, featured?: boolean) => void - focusable?: boolean - featured?: boolean - hideActionBar?: boolean - hoverable?: boolean - variant?: 'default' | 'rounded' | 'slim' - showGroup?: boolean - accountAction?: React.ReactElement + id?: string; + avatarSize?: number; + status: StatusEntity; + onClick?: () => void; + muted?: boolean; + hidden?: boolean; + unread?: boolean; + onMoveUp?: (statusId: string, featured?: boolean) => void; + onMoveDown?: (statusId: string, featured?: boolean) => void; + focusable?: boolean; + featured?: boolean; + hideActionBar?: boolean; + hoverable?: boolean; + variant?: 'default' | 'rounded' | 'slim'; + showGroup?: boolean; + accountAction?: React.ReactElement; } const Status: React.FC = (props) => { diff --git a/src/components/statuses/sensitive-content-overlay.tsx b/src/components/statuses/sensitive-content-overlay.tsx index bf5dc7d82..dabb23318 100644 --- a/src/components/statuses/sensitive-content-overlay.tsx +++ b/src/components/statuses/sensitive-content-overlay.tsx @@ -27,9 +27,9 @@ const messages = defineMessages({ }); interface ISensitiveContentOverlay { - status: StatusEntity - onToggleVisibility?(): void - visible?: boolean + status: StatusEntity; + onToggleVisibility?(): void; + visible?: boolean; } const SensitiveContentOverlay = React.forwardRef((props, ref) => { diff --git a/src/components/statuses/status-info.tsx b/src/components/statuses/status-info.tsx index 50fad86b3..619ee5afd 100644 --- a/src/components/statuses/status-info.tsx +++ b/src/components/statuses/status-info.tsx @@ -3,9 +3,9 @@ import React from 'react'; import { HStack, Text } from '../ui'; interface IStatusInfo { - avatarSize: number - icon: React.ReactNode - text: React.ReactNode + avatarSize: number; + icon: React.ReactNode; + text: React.ReactNode; } const StatusInfo = (props: IStatusInfo) => { diff --git a/src/components/still-image.tsx b/src/components/still-image.tsx index 4a3caf01e..68647ff23 100644 --- a/src/components/still-image.tsx +++ b/src/components/still-image.tsx @@ -5,19 +5,19 @@ import { useSettings } from 'soapbox/hooks'; export interface IStillImage { /** Image alt text. */ - alt?: string + alt?: string; /** Extra class names for the outer
container. */ - className?: string + className?: string; /** URL to the image */ - src: string + src: string; /** Extra CSS styles on the outer
element. */ - style?: React.CSSProperties + style?: React.CSSProperties; /** Whether to display the image contained vs filled in its container. */ - letterboxed?: boolean + letterboxed?: boolean; /** Whether to show the file extension in the corner. */ - showExt?: boolean + showExt?: boolean; /** Callback function if the image fails to load */ - onError?(): void + onError?(): void; } /** Renders images on a canvas, only playing GIFs if autoPlayGif is enabled. */ @@ -83,7 +83,7 @@ const StillImage: React.FC = ({ alt, className, src, style, letterb interface IExtensionBadge { /** File extension. */ - ext: string + ext: string; } /** Badge displaying a file extension. */ diff --git a/src/components/thumb-navigation-link.tsx b/src/components/thumb-navigation-link.tsx index 46c3ae4a7..d7d8cd681 100644 --- a/src/components/thumb-navigation-link.tsx +++ b/src/components/thumb-navigation-link.tsx @@ -6,13 +6,13 @@ import IconWithCounter from 'soapbox/components/icon-with-counter'; import { Icon, Text } from 'soapbox/components/ui'; interface IThumbNavigationLink { - count?: number - countMax?: number - src: string - text: string | React.ReactElement - to: string - exact?: boolean - paths?: Array + count?: number; + countMax?: number; + src: string; + text: string | React.ReactElement; + to: string; + exact?: boolean; + paths?: Array; } const ThumbNavigationLink: React.FC = ({ count, countMax, src, text, to, exact, paths }): JSX.Element => { diff --git a/src/components/tombstone.tsx b/src/components/tombstone.tsx index e8efe24c7..faaa84ee0 100644 --- a/src/components/tombstone.tsx +++ b/src/components/tombstone.tsx @@ -5,9 +5,9 @@ import { Text } from 'soapbox/components/ui'; import { HotKeys } from 'soapbox/features/ui/components/hotkeys'; interface ITombstone { - id: string - onMoveUp?: (statusId: string) => void - onMoveDown?: (statusId: string) => void + id: string; + onMoveUp?: (statusId: string) => void; + onMoveDown?: (statusId: string) => void; } /** Represents a deleted item. */ diff --git a/src/components/translate-button.tsx b/src/components/translate-button.tsx index f330cad5e..e74b9b82a 100644 --- a/src/components/translate-button.tsx +++ b/src/components/translate-button.tsx @@ -10,7 +10,7 @@ import { Stack, Button, Text } from './ui'; import type { Account, Status } from 'soapbox/types/entities'; interface ITranslateButton { - status: Status + status: Status; } const TranslateButton: React.FC = ({ status }) => { diff --git a/src/components/ui/accordion/accordion.tsx b/src/components/ui/accordion/accordion.tsx index 842032242..f415dac08 100644 --- a/src/components/ui/accordion/accordion.tsx +++ b/src/components/ui/accordion/accordion.tsx @@ -16,14 +16,14 @@ const messages = defineMessages({ }); interface IAccordion { - headline: React.ReactNode - children?: React.ReactNode - menu?: Menu - expanded?: boolean - onToggle?: (value: boolean) => void - action?: () => void - actionIcon?: string - actionLabel?: string + headline: React.ReactNode; + children?: React.ReactNode; + menu?: Menu; + expanded?: boolean; + onToggle?: (value: boolean) => void; + action?: () => void; + actionIcon?: string; + actionLabel?: string; } /** diff --git a/src/components/ui/avatar/avatar.tsx b/src/components/ui/avatar/avatar.tsx index 762939776..d00f8ebf2 100644 --- a/src/components/ui/avatar/avatar.tsx +++ b/src/components/ui/avatar/avatar.tsx @@ -9,7 +9,7 @@ const AVATAR_SIZE = 42; interface IAvatar extends Pick { /** Width and height of the avatar in pixels. */ - size?: number + size?: number; } /** Round profile avatar for accounts. */ diff --git a/src/components/ui/banner/banner.tsx b/src/components/ui/banner/banner.tsx index da6f7ed03..c0d489fe8 100644 --- a/src/components/ui/banner/banner.tsx +++ b/src/components/ui/banner/banner.tsx @@ -2,9 +2,9 @@ import clsx from 'clsx'; import React from 'react'; interface IBanner { - theme: 'frosted' | 'opaque' - children: React.ReactNode - className?: string + theme: 'frosted' | 'opaque'; + children: React.ReactNode; + className?: string; } /** Displays a sticky full-width banner at the bottom of the screen. */ diff --git a/src/components/ui/button/button.tsx b/src/components/ui/button/button.tsx index e06ffa7d1..412717d3c 100644 --- a/src/components/ui/button/button.tsx +++ b/src/components/ui/button/button.tsx @@ -10,27 +10,27 @@ import type { ButtonSizes, ButtonThemes } from './useButtonStyles'; interface IButton { /** Whether this button expands the width of its container. */ - block?: boolean + block?: boolean; /** Elements inside the
) : ( <> - - setUsername(event.target.value)} - type='text' - placeholder={intl.formatMessage(features.logInWithUsername ? messages.username : messages.email)} - className='max-w-[200px]' - /> + {features.nostrSignup ? ( +
+ +
+ ) : ( + + setUsername(event.target.value)} + type='text' + placeholder={intl.formatMessage(features.logInWithUsername ? messages.username : messages.email)} + className='max-w-[200px]' + /> - setPassword(event.target.value)} - type='password' - placeholder={intl.formatMessage(messages.password)} - className='max-w-[200px]' - /> + setPassword(event.target.value)} + type='password' + placeholder={intl.formatMessage(messages.password)} + className='max-w-[200px]' + /> - - - - - + + + + + + + + )} + +
- - -
- - {isOpen && ( + {(isOpen) && ( diff --git a/src/utils/features.ts b/src/utils/features.ts index f0207bfac..a1224eda2 100644 --- a/src/utils/features.ts +++ b/src/utils/features.ts @@ -685,6 +685,12 @@ const getInstanceFeatures = (instance: Instance) => { */ nostrSign: v.software === DITTO, + /** + * Whether the backend uses Ditto's Nosteric way of registration. + * @see POST /api/v1/accounts + */ + nostrSignup: v.software === DITTO, + /** * Add private notes to accounts. * @see POST /api/v1/accounts/:id/note From 3e6af89a9b0250c76208b3dfd904c56e59b78872 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 4 Oct 2023 18:07:49 -0500 Subject: [PATCH 019/100] Enable Nostr registration --- src/actions/accounts.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/actions/accounts.ts b/src/actions/accounts.ts index 27cf94455..779966596 100644 --- a/src/actions/accounts.ts +++ b/src/actions/accounts.ts @@ -1,5 +1,8 @@ +import { nip19 } from 'nostr-tools'; + import { importEntities } from 'soapbox/entity-store/actions'; import { Entities } from 'soapbox/entity-store/entities'; +import { getPublicKey } from 'soapbox/features/nostr/sign'; import { selectAccount } from 'soapbox/selectors'; import { isLoggedIn } from 'soapbox/utils/auth'; import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features'; @@ -128,9 +131,15 @@ const maybeRedirectLogin = (error: AxiosError, history?: History) => { const noOp = () => new Promise(f => f(undefined)); const createAccount = (params: Record) => - (dispatch: AppDispatch, getState: () => RootState) => { + async (dispatch: AppDispatch, getState: () => RootState) => { + const { instance } = getState(); + const { nostrSignup } = getFeatures(instance); + const pubkey = nostrSignup ? await getPublicKey() : undefined; + dispatch({ type: ACCOUNT_CREATE_REQUEST, params }); - return api(getState, 'app').post('/api/v1/accounts', params).then(({ data: token }) => { + return api(getState, 'app').post('/api/v1/accounts', params, { + headers: pubkey ? { authorization: `Bearer ${nip19.npubEncode(pubkey)}` } : undefined, + }).then(({ data: token }) => { return dispatch({ type: ACCOUNT_CREATE_SUCCESS, params, token }); }).catch(error => { dispatch({ type: ACCOUNT_CREATE_FAIL, error, params }); From eceb08287493e31ab90bfa8d7d7114d34add7297 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 4 Oct 2023 18:12:18 -0500 Subject: [PATCH 020/100] Fix kindSchema --- src/schemas/nostr.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schemas/nostr.ts b/src/schemas/nostr.ts index 41c3290c3..1dcd0c466 100644 --- a/src/schemas/nostr.ts +++ b/src/schemas/nostr.ts @@ -4,7 +4,7 @@ import { z } from 'zod'; /** Schema to validate Nostr hex IDs such as event IDs and pubkeys. */ const nostrIdSchema = z.string().regex(/^[0-9a-f]{64}$/); /** Nostr kinds are positive integers. */ -const kindSchema = z.number().int().positive(); +const kindSchema = z.number().int().nonnegative(); /** Nostr event template schema. */ const eventTemplateSchema = z.object({ From 73f407455eb4202b9487665c03e59cf972783859 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 5 Oct 2023 16:33:59 -0500 Subject: [PATCH 021/100] Sentry: use DSN from SoapboxConfig --- src/containers/soapbox.tsx | 5 +- src/hooks/index.ts | 3 +- src/hooks/useSentry.ts | 50 ++++++++++++++++++++ src/main.tsx | 4 -- src/monitoring.ts | 56 ++++------------------- src/normalizers/soapbox/soapbox-config.ts | 1 + src/sentry.ts | 13 ++++++ 7 files changed, 80 insertions(+), 52 deletions(-) create mode 100644 src/hooks/useSentry.ts create mode 100644 src/sentry.ts diff --git a/src/containers/soapbox.tsx b/src/containers/soapbox.tsx index 253189714..9df5b7a19 100644 --- a/src/containers/soapbox.tsx +++ b/src/containers/soapbox.tsx @@ -28,8 +28,9 @@ import { useAppSelector, useAppDispatch, useOwnAccount, - useSoapboxConfig, + useSentry, useSettings, + useSoapboxConfig, useTheme, useLocale, } from 'soapbox/hooks'; @@ -224,6 +225,8 @@ const SoapboxHead: React.FC = ({ children }) => { 'demetricator': settings.get('demetricator'), }); + useSentry(soapboxConfig.sentryDsn); + return ( <> diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 98d600547..ef77478df 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -19,7 +19,8 @@ export { useOwnAccount } from './useOwnAccount'; export { usePrevious } from './usePrevious'; export { useRefEventHandler } from './useRefEventHandler'; export { useRegistrationStatus } from './useRegistrationStatus'; +export { useSentry } from './useSentry'; export { useSettings } from './useSettings'; export { useSoapboxConfig } from './useSoapboxConfig'; export { useSystemTheme } from './useSystemTheme'; -export { useTheme } from './useTheme'; +export { useTheme } from './useTheme'; \ No newline at end of file diff --git a/src/hooks/useSentry.ts b/src/hooks/useSentry.ts new file mode 100644 index 000000000..2a13b2645 --- /dev/null +++ b/src/hooks/useSentry.ts @@ -0,0 +1,50 @@ +import { useEffect } from 'react'; + +/** Hook to start Sentry. Should only be called once. */ +function useSentry(dsn: string | undefined) { + useEffect(() => { + if (dsn) { + startSentry(dsn).catch(console.error); + } + }, [dsn]); +} + +/** Start Sentry. */ +async function startSentry(dsn: string): Promise { + const [Sentry, { Integrations: Integrations }] = await Promise.all([ + import('@sentry/react'), + import('@sentry/tracing'), + ]); + + Sentry.init({ + dsn, + debug: false, + integrations: [new Integrations.BrowserTracing()], + + // Filter events. + // https://docs.sentry.io/platforms/javascript/configuration/filtering/ + ignoreErrors: [ + // Network errors. + 'AxiosError', + // sw.js couldn't be downloaded. + 'Failed to update a ServiceWorker for scope', + // Useful for try/catch, useless as a Sentry error. + 'AbortError', + // localForage error in FireFox private browsing mode (which doesn't support IndexedDB). + // We only use IndexedDB as a cache, so we can safely ignore the error. + 'No available storage method found', + ], + denyUrls: [ + // Browser extensions. + /extensions\//i, + /^chrome:\/\//i, + /^moz-extension:\/\//i, + ], + + // We recommend adjusting this value in production, or using tracesSampler + // for finer control + tracesSampleRate: 1.0, + }); +} + +export { useSentry }; \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index 74d967b01..33b325dab 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -23,13 +23,9 @@ import './styles/tailwind.css'; import './precheck'; import { default as Soapbox } from './containers/soapbox'; -import * as monitoring from './monitoring'; import ready from './ready'; import { registerSW } from './utils/sw'; -// Sentry -monitoring.start(); - if (BuildConfig.NODE_ENV === 'production') { printConsoleWarning(); registerSW('/sw.js'); diff --git a/src/monitoring.ts b/src/monitoring.ts index ab0d390dc..128b3a5ba 100644 --- a/src/monitoring.ts +++ b/src/monitoring.ts @@ -1,49 +1,13 @@ -import * as BuildConfig from 'soapbox/build-config'; - import type { CaptureContext } from '@sentry/types'; -export const start = (): void => { - Promise.all([ - import('@sentry/react'), - import('@sentry/tracing'), - ]).then(([Sentry, { Integrations: Integrations }]) => { - Sentry.init({ - dsn: BuildConfig.SENTRY_DSN, - environment: BuildConfig.NODE_ENV, - debug: false, - integrations: [new Integrations.BrowserTracing()], +/** Capture the exception and report it to Sentry. */ +async function captureException (exception: any, captureContext?: CaptureContext | undefined): Promise { + try { + const Sentry = await import('@sentry/react'); + Sentry.captureException(exception, captureContext); + } catch (e) { + console.error(e); + } +} - // Filter events. - // https://docs.sentry.io/platforms/javascript/configuration/filtering/ - ignoreErrors: [ - // Network errors. - 'AxiosError', - // sw.js couldn't be downloaded. - 'Failed to update a ServiceWorker for scope', - // Useful for try/catch, useless as a Sentry error. - 'AbortError', - // localForage error in FireFox private browsing mode (which doesn't support IndexedDB). - // We only use IndexedDB as a cache, so we can safely ignore the error. - 'No available storage method found', - ], - denyUrls: [ - // Browser extensions. - /extensions\//i, - /^chrome:\/\//i, - /^moz-extension:\/\//i, - ], - - // We recommend adjusting this value in production, or using tracesSampler - // for finer control - tracesSampleRate: 1.0, - }); - }).catch(console.error); -}; - -export const captureException = (exception: any, captureContext?: CaptureContext | undefined): void => { - import('@sentry/react') - .then(Sentry => { - Sentry.captureException(exception, captureContext); - }) - .catch(console.error); -}; +export { captureException }; diff --git a/src/normalizers/soapbox/soapbox-config.ts b/src/normalizers/soapbox/soapbox-config.ts index 403c92a32..4092f8367 100644 --- a/src/normalizers/soapbox/soapbox-config.ts +++ b/src/normalizers/soapbox/soapbox-config.ts @@ -116,6 +116,7 @@ export const SoapboxConfigRecord = ImmutableRecord({ * On some platforms this can be too blurry without additional configuration. */ mediaPreview: false, + sentryDsn: undefined as string | undefined, }, 'SoapboxConfig'); type SoapboxConfigMap = ImmutableMap; diff --git a/src/sentry.ts b/src/sentry.ts new file mode 100644 index 000000000..ac454501a --- /dev/null +++ b/src/sentry.ts @@ -0,0 +1,13 @@ +import type { CaptureContext } from '@sentry/types'; + +/** Capture the exception in Sentry. */ +async function captureException (exception: any, captureContext?: CaptureContext | undefined): Promise { + try { + const Sentry = await import('@sentry/react'); + Sentry.captureException(exception, captureContext); + } catch (e) { + console.error(e); + } +} + +export { startSentry, captureException }; \ No newline at end of file From e07fa23e0de857ce9a0353c3a3ad392bfdee4fda Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 5 Oct 2023 16:48:20 -0500 Subject: [PATCH 022/100] Let Sentry DSN be configurable in SoapboxConfig --- src/features/soapbox-config/index.tsx | 14 ++++++++++++++ src/sentry.ts | 13 ------------- 2 files changed, 14 insertions(+), 13 deletions(-) delete mode 100644 src/sentry.ts diff --git a/src/features/soapbox-config/index.tsx b/src/features/soapbox-config/index.tsx index 15295e212..ffe91e123 100644 --- a/src/features/soapbox-config/index.tsx +++ b/src/features/soapbox-config/index.tsx @@ -54,6 +54,8 @@ const messages = defineMessages({ tileServerAttributionLabel: { id: 'soapbox_config.tile_server_attribution_label', defaultMessage: 'Map tiles attribution' }, redirectRootNoLoginLabel: { id: 'soapbox_config.redirect_root_no_login_label', defaultMessage: 'Redirect homepage' }, redirectRootNoLoginHint: { id: 'soapbox_config.redirect_root_no_login_hint', defaultMessage: 'Path to redirect the homepage when a user is not logged in.' }, + sentryDsnLabel: { id: 'soapbox_config.sentry_dsn_label', defaultMessage: 'Sentry DSN' }, + sentryDsnHint: { id: 'soapbox_config.sentry_dsn_hint', defaultMessage: 'DSN URL for error reporting. Works with Sentry and GlitchTip.' }, }); type ValueGetter = (e: React.ChangeEvent) => any; @@ -285,6 +287,18 @@ const SoapboxConfig: React.FC = () => { onChange={handleChange(['redirectRootNoLogin'], (e) => e.target.value)} /> + + + e.target.value)} + /> + diff --git a/src/sentry.ts b/src/sentry.ts deleted file mode 100644 index ac454501a..000000000 --- a/src/sentry.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { CaptureContext } from '@sentry/types'; - -/** Capture the exception in Sentry. */ -async function captureException (exception: any, captureContext?: CaptureContext | undefined): Promise { - try { - const Sentry = await import('@sentry/react'); - Sentry.captureException(exception, captureContext); - } catch (e) { - console.error(e); - } -} - -export { startSentry, captureException }; \ No newline at end of file From 5ed8c78c88be322575503cf44b2e719c9f45693f Mon Sep 17 00:00:00 2001 From: Tassoman Date: Sat, 7 Oct 2023 13:26:14 +0000 Subject: [PATCH 023/100] added vertical scroll feature to the autosuggest component --- src/actions/compose.ts | 6 +++--- src/features/compose/editor/plugins/autosuggest-plugin.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/actions/compose.ts b/src/actions/compose.ts index b781b99b5..c71524195 100644 --- a/src/actions/compose.ts +++ b/src/actions/compose.ts @@ -524,7 +524,7 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, composeId, params: { q: token.slice(1), resolve: false, - limit: 4, + limit: 10, }, }).then(response => { dispatch(importFetchedAccounts(response.data)); @@ -538,7 +538,7 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, composeId, const fetchComposeSuggestionsEmojis = (dispatch: AppDispatch, getState: () => RootState, composeId: string, token: string) => { const state = getState(); - const results = emojiSearch(token.replace(':', ''), { maxResults: 5 }, state.custom_emojis); + const results = emojiSearch(token.replace(':', ''), { maxResults: 10 }, state.custom_emojis); dispatch(readyComposeSuggestionsEmojis(composeId, token, results)); }; @@ -565,7 +565,7 @@ const fetchComposeSuggestionsTags = (dispatch: AppDispatch, getState: () => Root }), params: { q: token.slice(1), - limit: 4, + limit: 10, type: 'hashtags', }, }).then(response => { diff --git a/src/features/compose/editor/plugins/autosuggest-plugin.tsx b/src/features/compose/editor/plugins/autosuggest-plugin.tsx index 34b929d13..33ab39b0f 100644 --- a/src/features/compose/editor/plugins/autosuggest-plugin.tsx +++ b/src/features/compose/editor/plugins/autosuggest-plugin.tsx @@ -538,7 +538,7 @@ const AutosuggestPlugin = ({ ? ReactDOM.createPortal(
Date: Sat, 7 Oct 2023 11:50:42 -0500 Subject: [PATCH 024/100] vite: enable sourcemap --- vite.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/vite.config.ts b/vite.config.ts index eb7f9560c..4f93c42d3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -23,6 +23,7 @@ export default defineConfig({ entryFileNames: 'packs/[name]-[hash].js', }, }, + sourcemap: true, }, assetsInclude: ['**/*.oga'], server: { From 6b173f44bef134ca37937bf029d7a31c26246e40 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 7 Oct 2023 15:04:37 -0500 Subject: [PATCH 025/100] AsyncComponents: use React.lazy --- .../emoji/components/emoji-picker.tsx | 6 +- src/features/ui/util/async-components.ts | 811 ++++-------------- 2 files changed, 166 insertions(+), 651 deletions(-) diff --git a/src/features/emoji/components/emoji-picker.tsx b/src/features/emoji/components/emoji-picker.tsx index 695fae2cc..6dd2b8064 100644 --- a/src/features/emoji/components/emoji-picker.tsx +++ b/src/features/emoji/components/emoji-picker.tsx @@ -13,7 +13,7 @@ const getImageURL = (set: string, name: string) => { return joinPublicPath(`/packs/emoji/${name}.svg`); }; -const Picker = (props: any) => { +const Picker: React.FC = (props) => { const ref = useRef(null); useEffect(() => { @@ -25,6 +25,4 @@ const Picker = (props: any) => { return
; }; -export { - Picker, -}; +export default Picker; diff --git a/src/features/ui/util/async-components.ts b/src/features/ui/util/async-components.ts index b5208b3e1..a93048aeb 100644 --- a/src/features/ui/util/async-components.ts +++ b/src/features/ui/util/async-components.ts @@ -1,647 +1,164 @@ -export function AboutPage() { - return import('../../about'); -} - -export function EmojiPicker() { - return import('../../emoji/components/emoji-picker'); -} - -export function Notifications() { - return import('../../notifications'); -} - -export function LandingTimeline() { - return import('../../landing-timeline'); -} - -export function HomeTimeline() { - return import('../../home-timeline'); -} - -export function PublicTimeline() { - return import('../../public-timeline'); -} - -export function RemoteTimeline() { - return import('../../remote-timeline'); -} - -export function CommunityTimeline() { - return import('../../community-timeline'); -} - -export function HashtagTimeline() { - return import('../../hashtag-timeline'); -} - -export function DirectTimeline() { - return import('../../direct-timeline'); -} - -export function Conversations() { - return import('../../conversations'); -} - -export function ListTimeline() { - return import('../../list-timeline'); -} - -export function Lists() { - return import('../../lists'); -} - -export function Bookmarks() { - return import('../../bookmarks'); -} - -export function Status() { - return import('../../status'); -} - -export function PinnedStatuses() { - return import('../../pinned-statuses'); -} - -export function AccountTimeline() { - return import('../../account-timeline'); -} - -export function AccountGallery() { - return import('../../account-gallery'); -} - -export function Followers() { - return import('../../followers'); -} - -export function Following() { - return import('../../following'); -} - -export function FollowRequests() { - return import('../../follow-requests'); -} - -export function GenericNotFound() { - return import('../../generic-not-found'); -} - -export function FavouritedStatuses() { - return import('../../favourited-statuses'); -} - -export function Blocks() { - return import('../../blocks'); -} - -export function DomainBlocks() { - return import('../../domain-blocks'); -} - -export function Mutes() { - return import('../../mutes'); -} - -export function MuteModal() { - return import('../components/modals/mute-modal'); -} - -export function Filters() { - return import('../../filters'); -} - -export function EditFilter() { - return import('../../filters/edit-filter'); -} - -export function ReportModal() { - return import('../components/modals/report-modal/report-modal'); -} - -export function AccountModerationModal() { - return import('../components/modals/account-moderation-modal/account-moderation-modal'); -} - -export function MediaGallery() { - return import('../../../components/media-gallery'); -} - -export function Video() { - return import('../../video'); -} - -export function Audio() { - return import('../../audio'); -} - -export function MediaModal() { - return import('../components/modals/media-modal'); -} - -export function VideoModal() { - return import('../components/modals/video-modal'); -} - -export function BoostModal() { - return import('../components/modals/boost-modal'); -} - -export function ConfirmationModal() { - return import('../components/modals/confirmation-modal'); -} - -export function MissingDescriptionModal() { - return import('../components/modals/missing-description-modal'); -} - -export function ActionsModal() { - return import('../components/modals/actions-modal'); -} - -export function HotkeysModal() { - return import('../components/modals/hotkeys-modal'); -} - -export function ComposeModal() { - return import('../components/modals/compose-modal'); -} - -export function ReplyMentionsModal() { - return import('../components/modals/reply-mentions-modal'); -} - -export function UnauthorizedModal() { - return import('../components/modals/unauthorized-modal'); -} - -export function EditFederationModal() { - return import('../components/modals/edit-federation-modal'); -} - -export function EmbedModal() { - return import('../components/modals/embed-modal'); -} - -export function ComponentModal() { - return import('../components/modals/component-modal'); -} - -export function ReblogsModal() { - return import('../components/modals/reblogs-modal'); -} - -export function FavouritesModal() { - return import('../components/modals/favourites-modal'); -} - -export function DislikesModal() { - return import('../components/modals/dislikes-modal'); -} - -export function ReactionsModal() { - return import('../components/modals/reactions-modal'); -} - -export function MentionsModal() { - return import('../components/modals/mentions-modal'); -} - -export function LandingPageModal() { - return import('../components/modals/landing-page-modal'); -} - -export function BirthdaysModal() { - return import('../components/modals/birthdays-modal'); -} - -export function BirthdayPanel() { - return import('../../../components/birthday-panel'); -} - -export function ListEditor() { - return import('../../list-editor'); -} - -export function ListAdder() { - return import('../../list-adder'); -} - -export function Search() { - return import('../../search'); -} - -export function LoginPage() { - return import('../../auth-login/components/login-page'); -} - -export function ExternalLogin() { - return import('../../external-login'); -} - -export function LogoutPage() { - return import('../../auth-login/components/logout'); -} - -export function RegistrationPage() { - return import('../../auth-login/components/registration-page'); -} - -export function Settings() { - return import('../../settings'); -} - -export function EditProfile() { - return import('../../edit-profile'); -} - -export function EditEmail() { - return import('../../edit-email'); -} - -export function EmailConfirmation() { - return import('../../email-confirmation'); -} - -export function EditPassword() { - return import('../../edit-password'); -} - -export function DeleteAccount() { - return import('../../delete-account'); -} - -export function SoapboxConfig() { - return import('../../soapbox-config'); -} - -export function ExportData() { - return import('../../export-data'); -} - -export function ImportData() { - return import('../../import-data'); -} - -export function Backups() { - return import('../../backups'); -} - -export function PasswordReset() { - return import('../../auth-login/components/password-reset'); -} - -export function PasswordResetConfirm() { - return import('../../auth-login/components/password-reset-confirm'); -} - -export function MfaForm() { - return import('../../security/mfa-form'); -} - -export function ChatIndex() { - return import('../../chats'); -} - -export function ChatWidget() { - return import('../../chats/components/chat-widget/chat-widget'); -} - -export function ServerInfo() { - return import('../../server-info'); -} - -export function Dashboard() { - return import('../../admin'); -} - -export function ModerationLog() { - return import('../../admin/moderation-log'); -} - -export function ThemeEditor() { - return import('../../theme-editor'); -} - -export function UserPanel() { - return import('../components/user-panel'); -} - -export function PromoPanel() { - return import('../components/promo-panel'); -} - -export function SignUpPanel() { - return import('../components/panels/sign-up-panel'); -} - -export function CtaBanner() { - return import('../components/cta-banner'); -} - -export function FundingPanel() { - return import('../components/funding-panel'); -} - -export function TrendsPanel() { - return import('../components/trends-panel'); -} - -export function ProfileInfoPanel() { - return import('../components/profile-info-panel'); -} - -export function ProfileMediaPanel() { - return import('../components/profile-media-panel'); -} - -export function ProfileFieldsPanel() { - return import('../components/profile-fields-panel'); -} - -export function PinnedAccountsPanel() { - return import('../components/pinned-accounts-panel'); -} - -export function InstanceInfoPanel() { - return import('../components/instance-info-panel'); -} - -export function InstanceModerationPanel() { - return import('../components/instance-moderation-panel'); -} - -export function LatestAccountsPanel() { - return import('../../admin/components/latest-accounts-panel'); -} - -export function SidebarMenu() { - return import('../../../components/sidebar-menu'); -} - -export function ModalContainer() { - return import('../containers/modal-container'); -} - -export function ProfileHoverCard() { - return import('soapbox/components/profile-hover-card'); -} - -export function StatusHoverCard() { - return import('soapbox/components/status-hover-card'); -} - -export function CryptoDonate() { - return import('../../crypto-donate'); -} - -export function CryptoDonatePanel() { - return import('../../crypto-donate/components/crypto-donate-panel'); -} - -export function CryptoAddress() { - return import('../../crypto-donate/components/crypto-address'); -} - -export function CryptoDonateModal() { - return import('../components/modals/crypto-donate-modal'); -} - -export function ScheduledStatuses() { - return import('../../scheduled-statuses'); -} - -export function UserIndex() { - return import('../../admin/user-index'); -} - -export function FederationRestrictions() { - return import('../../federation-restrictions'); -} - -export function Aliases() { - return import('../../aliases'); -} - -export function Migration() { - return import('../../migration'); -} - -export function ScheduleForm() { - return import('../../compose/components/schedule-form'); -} - -export function WhoToFollowPanel() { - return import('../components/who-to-follow-panel'); -} - -export function FollowRecommendations() { - return import('../../follow-recommendations'); -} - -export function Directory() { - return import('../../directory'); -} - -export function RegisterInvite() { - return import('../../register-invite'); -} - -export function Share() { - return import('../../share'); -} - -export function NewStatus() { - return import('../../new-status'); -} - -export function IntentionalError() { - return import('../../intentional-error'); -} - -export function Developers() { - return import('../../developers'); -} - -export function CreateApp() { - return import('../../developers/apps/create'); -} - -export function SettingsStore() { - return import('../../developers/settings-store'); -} - -export function TestTimeline() { - return import('../../test-timeline'); -} - -export function ServiceWorkerInfo() { - return import('../../developers/service-worker-info'); -} - -export function DatePicker() { - return import('../../birthdays/date-picker'); -} - -export function OnboardingWizard() { - return import('../../onboarding/onboarding-wizard'); -} - -export function CompareHistoryModal() { - return import('../components/modals/compare-history-modal'); -} - -export function AuthTokenList() { - return import('../../auth-token-list'); -} - -export function FamiliarFollowersModal() { - return import('../components/modals/familiar-followers-modal'); -} - -export function AnnouncementsPanel() { - return import('../../../components/announcements/announcements-panel'); -} - -export function Quotes() { - return import('../../quotes'); -} - -export function ComposeEventModal() { - return import('../components/modals/compose-event-modal/compose-event-modal'); -} - -export function JoinEventModal() { - return import('../components/modals/join-event-modal'); -} - -export function EventHeader() { - return import('../../event/components/event-header'); -} - -export function EventInformation() { - return import('../../event/event-information'); -} - -export function EventDiscussion() { - return import('../../event/event-discussion'); -} - -export function EventMapModal() { - return import('../components/modals/event-map-modal'); -} - -export function EventParticipantsModal() { - return import('../components/modals/event-participants-modal'); -} - -export function Events() { - return import('../../events'); -} - -export function Groups() { - return import('../../groups'); -} - -export function GroupsDiscover() { - return import('../../groups/discover'); -} - -export function GroupsPopular() { - return import('../../groups/popular'); -} - -export function GroupsSuggested() { - return import('../../groups/suggested'); -} - -export function GroupsTag() { - return import('../../groups/tag'); -} - -export function GroupsTags() { - return import('../../groups/tags'); -} - -export function PendingGroupRequests() { - return import('../../groups/pending-requests'); -} - -export function GroupMembers() { - return import('../../group/group-members'); -} - -export function GroupTags() { - return import('../../group/group-tags'); -} - -export function GroupTagTimeline() { - return import('../../group/group-tag-timeline'); -} - -export function GroupTimeline() { - return import('../../group/group-timeline'); -} - -export function ManageGroup() { - return import('../../group/manage-group'); -} - -export function EditGroup() { - return import('../../group/edit-group'); -} - -export function GroupBlockedMembers() { - return import('../../group/group-blocked-members'); -} - -export function GroupMembershipRequests() { - return import('../../group/group-membership-requests'); -} - -export function GroupGallery() { - return import('../../group/group-gallery'); -} - -export function CreateGroupModal() { - return import('../components/modals/manage-group-modal/create-group-modal'); -} - -export function NewGroupPanel() { - return import('../components/panels/new-group-panel'); -} - -export function MyGroupsPanel() { - return import('../components/panels/my-groups-panel'); -} - -export function SuggestedGroupsPanel() { - return import('../components/panels/suggested-groups-panel'); -} - -export function GroupMediaPanel() { - return import('../components/group-media-panel'); -} - -export function NewEventPanel() { - return import('../components/panels/new-event-panel'); -} - -export function Announcements() { - return import('../../admin/announcements'); -} - -export function EditAnnouncementModal() { - return import('../components/modals/edit-announcement-modal'); -} - -export function FollowedTags() { - return import('../../followed-tags'); -} - -export function AccountNotePanel() { - return import('../components/panels/account-note-panel'); -} - -export function ComposeEditor() { - return import('../../compose/editor'); -} +import { lazy } from 'react'; + +export const AboutPage = lazy(() => import('soapbox/features/about')); +export const EmojiPicker = lazy(() => import('soapbox/features/emoji/components/emoji-picker')); +export const Notifications = lazy(() => import('soapbox/features/notifications')); +export const LandingTimeline = lazy(() => import('soapbox/features/landing-timeline')); +export const HomeTimeline = lazy(() => import('soapbox/features/home-timeline')); +export const PublicTimeline = lazy(() => import('soapbox/features/public-timeline')); +export const RemoteTimeline = lazy(() => import('soapbox/features/remote-timeline')); +export const CommunityTimeline = lazy(() => import('soapbox/features/community-timeline')); +export const HashtagTimeline = lazy(() => import('soapbox/features/hashtag-timeline')); +export const DirectTimeline = lazy(() => import('soapbox/features/direct-timeline')); +export const Conversations = lazy(() => import('soapbox/features/conversations')); +export const ListTimeline = lazy(() => import('soapbox/features/list-timeline')); +export const Lists = lazy(() => import('soapbox/features/lists')); +export const Bookmarks = lazy(() => import('soapbox/features/bookmarks')); +export const Status = lazy(() => import('soapbox/features/status')); +export const PinnedStatuses = lazy(() => import('soapbox/features/pinned-statuses')); +export const AccountTimeline = lazy(() => import('soapbox/features/account-timeline')); +export const AccountGallery = lazy(() => import('soapbox/features/account-gallery')); +export const Followers = lazy(() => import('soapbox/features/followers')); +export const Following = lazy(() => import('soapbox/features/following')); +export const FollowRequests = lazy(() => import('soapbox/features/follow-requests')); +export const GenericNotFound = lazy(() => import('soapbox/features/generic-not-found')); +export const FavouritedStatuses = lazy(() => import('soapbox/features/favourited-statuses')); +export const Blocks = lazy(() => import('soapbox/features/blocks')); +export const DomainBlocks = lazy(() => import('soapbox/features/domain-blocks')); +export const Mutes = lazy(() => import('soapbox/features/mutes')); +export const MuteModal = lazy(() => import('soapbox/features/ui/components/modals/mute-modal')); +export const Filters = lazy(() => import('soapbox/features/filters')); +export const EditFilter = lazy(() => import('soapbox/features/filters/edit-filter')); +export const ReportModal = lazy(() => import('soapbox/features/ui/components/modals/report-modal/report-modal')); +export const AccountModerationModal = lazy(() => import('soapbox/features/ui/components/modals/account-moderation-modal/account-moderation-modal')); +export const MediaGallery = lazy(() => import('soapbox/components/media-gallery')); +export const Video = lazy(() => import('soapbox/features/video')); +export const Audio = lazy(() => import('soapbox/features/audio')); +export const MediaModal = lazy(() => import('soapbox/features/ui/components/modals/media-modal')); +export const VideoModal = lazy(() => import('soapbox/features/ui/components/modals/video-modal')); +export const BoostModal = lazy(() => import('soapbox/features/ui/components/modals/boost-modal')); +export const ConfirmationModal = lazy(() => import('soapbox/features/ui/components/modals/confirmation-modal')); +export const MissingDescriptionModal = lazy(() => import('soapbox/features/ui/components/modals/missing-description-modal')); +export const ActionsModal = lazy(() => import('soapbox/features/ui/components/modals/actions-modal')); +export const HotkeysModal = lazy(() => import('soapbox/features/ui/components/modals/hotkeys-modal')); +export const ComposeModal = lazy(() => import('soapbox/features/ui/components/modals/compose-modal')); +export const ReplyMentionsModal = lazy(() => import('soapbox/features/ui/components/modals/reply-mentions-modal')); +export const UnauthorizedModal = lazy(() => import('soapbox/features/ui/components/modals/unauthorized-modal')); +export const EditFederationModal = lazy(() => import('soapbox/features/ui/components/modals/edit-federation-modal')); +export const EmbedModal = lazy(() => import('soapbox/features/ui/components/modals/embed-modal')); +export const ComponentModal = lazy(() => import('soapbox/features/ui/components/modals/component-modal')); +export const ReblogsModal = lazy(() => import('soapbox/features/ui/components/modals/reblogs-modal')); +export const FavouritesModal = lazy(() => import('soapbox/features/ui/components/modals/favourites-modal')); +export const DislikesModal = lazy(() => import('soapbox/features/ui/components/modals/dislikes-modal')); +export const ReactionsModal = lazy(() => import('soapbox/features/ui/components/modals/reactions-modal')); +export const MentionsModal = lazy(() => import('soapbox/features/ui/components/modals/mentions-modal')); +export const LandingPageModal = lazy(() => import('soapbox/features/ui/components/modals/landing-page-modal')); +export const BirthdaysModal = lazy(() => import('soapbox/features/ui/components/modals/birthdays-modal')); +export const BirthdayPanel = lazy(() => import('soapbox/components/birthday-panel')); +export const ListEditor = lazy(() => import('soapbox/features/list-editor')); +export const ListAdder = lazy(() => import('soapbox/features/list-adder')); +export const Search = lazy(() => import('soapbox/features/search')); +export const LoginPage = lazy(() => import('soapbox/features/auth-login/components/login-page')); +export const ExternalLogin = lazy(() => import('soapbox/features/external-login')); +export const LogoutPage = lazy(() => import('soapbox/features/auth-login/components/logout')); +export const RegistrationPage = lazy(() => import('soapbox/features/auth-login/components/registration-page')); +export const Settings = lazy(() => import('soapbox/features/settings')); +export const EditProfile = lazy(() => import('soapbox/features/edit-profile')); +export const EditEmail = lazy(() => import('soapbox/features/edit-email')); +export const EmailConfirmation = lazy(() => import('soapbox/features/email-confirmation')); +export const EditPassword = lazy(() => import('soapbox/features/edit-password')); +export const DeleteAccount = lazy(() => import('soapbox/features/delete-account')); +export const SoapboxConfig = lazy(() => import('soapbox/features/soapbox-config')); +export const ExportData = lazy(() => import('soapbox/features/export-data')); +export const ImportData = lazy(() => import('soapbox/features/import-data')); +export const Backups = lazy(() => import('soapbox/features/backups')); +export const PasswordReset = lazy(() => import('soapbox/features/auth-login/components/password-reset')); +export const PasswordResetConfirm = lazy(() => import('soapbox/features/auth-login/components/password-reset-confirm')); +export const MfaForm = lazy(() => import('soapbox/features/security/mfa-form')); +export const ChatIndex = lazy(() => import('soapbox/features/chats')); +export const ChatWidget = lazy(() => import('soapbox/features/chats/components/chat-widget/chat-widget')); +export const ServerInfo = lazy(() => import('soapbox/features/server-info')); +export const Dashboard = lazy(() => import('soapbox/features/admin')); +export const ModerationLog = lazy(() => import('soapbox/features/admin/moderation-log')); +export const ThemeEditor = lazy(() => import('soapbox/features/theme-editor')); +export const UserPanel = lazy(() => import('soapbox/features/ui/components/user-panel')); +export const PromoPanel = lazy(() => import('soapbox/features/ui/components/promo-panel')); +export const SignUpPanel = lazy(() => import('soapbox/features/ui/components/panels/sign-up-panel')); +export const CtaBanner = lazy(() => import('soapbox/features/ui/components/cta-banner')); +export const FundingPanel = lazy(() => import('soapbox/features/ui/components/funding-panel')); +export const TrendsPanel = lazy(() => import('soapbox/features/ui/components/trends-panel')); +export const ProfileInfoPanel = lazy(() => import('soapbox/features/ui/components/profile-info-panel')); +export const ProfileMediaPanel = lazy(() => import('soapbox/features/ui/components/profile-media-panel')); +export const ProfileFieldsPanel = lazy(() => import('soapbox/features/ui/components/profile-fields-panel')); +export const PinnedAccountsPanel = lazy(() => import('soapbox/features/ui/components/pinned-accounts-panel')); +export const InstanceInfoPanel = lazy(() => import('soapbox/features/ui/components/instance-info-panel')); +export const InstanceModerationPanel = lazy(() => import('soapbox/features/ui/components/instance-moderation-panel')); +export const LatestAccountsPanel = lazy(() => import('soapbox/features/admin/components/latest-accounts-panel')); +export const SidebarMenu = lazy(() => import('soapbox/components/sidebar-menu')); +export const ModalContainer = lazy(() => import('soapbox/features/ui/containers/modal-container')); +export const ProfileHoverCard = lazy(() => import('soapbox/components/profile-hover-card')); +export const StatusHoverCard = lazy(() => import('soapbox/components/status-hover-card')); +export const CryptoDonate = lazy(() => import('soapbox/features/crypto-donate')); +export const CryptoDonatePanel = lazy(() => import('soapbox/features/crypto-donate/components/crypto-donate-panel')); +export const CryptoAddress = lazy(() => import('soapbox/features/crypto-donate/components/crypto-address')); +export const CryptoDonateModal = lazy(() => import('soapbox/features/ui/components/modals/crypto-donate-modal')); +export const ScheduledStatuses = lazy(() => import('soapbox/features/scheduled-statuses')); +export const UserIndex = lazy(() => import('soapbox/features/admin/user-index')); +export const FederationRestrictions = lazy(() => import('soapbox/features/federation-restrictions')); +export const Aliases = lazy(() => import('soapbox/features/aliases')); +export const Migration = lazy(() => import('soapbox/features/migration')); +export const ScheduleForm = lazy(() => import('soapbox/features/compose/components/schedule-form')); +export const WhoToFollowPanel = lazy(() => import('soapbox/features/ui/components/who-to-follow-panel')); +export const FollowRecommendations = lazy(() => import('soapbox/features/follow-recommendations')); +export const Directory = lazy(() => import('soapbox/features/directory')); +export const RegisterInvite = lazy(() => import('soapbox/features/register-invite')); +export const Share = lazy(() => import('soapbox/features/share')); +export const NewStatus = lazy(() => import('soapbox/features/new-status')); +export const IntentionalError = lazy(() => import('soapbox/features/intentional-error')); +export const Developers = lazy(() => import('soapbox/features/developers')); +export const CreateApp = lazy(() => import('soapbox/features/developers/apps/create')); +export const SettingsStore = lazy(() => import('soapbox/features/developers/settings-store')); +export const TestTimeline = lazy(() => import('soapbox/features/test-timeline')); +export const ServiceWorkerInfo = lazy(() => import('soapbox/features/developers/service-worker-info')); +export const DatePicker = lazy(() => import('soapbox/features/birthdays/date-picker')); +export const OnboardingWizard = lazy(() => import('soapbox/features/onboarding/onboarding-wizard')); +export const CompareHistoryModal = lazy(() => import('soapbox/features/ui/components/modals/compare-history-modal')); +export const AuthTokenList = lazy(() => import('soapbox/features/auth-token-list')); +export const FamiliarFollowersModal = lazy(() => import('soapbox/features/ui/components/modals/familiar-followers-modal')); +export const AnnouncementsPanel = lazy(() => import('soapbox/components/announcements/announcements-panel')); +export const Quotes = lazy(() => import('soapbox/features/quotes')); +export const ComposeEventModal = lazy(() => import('soapbox/features/ui/components/modals/compose-event-modal/compose-event-modal')); +export const JoinEventModal = lazy(() => import('soapbox/features/ui/components/modals/join-event-modal')); +export const EventHeader = lazy(() => import('soapbox/features/event/components/event-header')); +export const EventInformation = lazy(() => import('soapbox/features/event/event-information')); +export const EventDiscussion = lazy(() => import('soapbox/features/event/event-discussion')); +export const EventMapModal = lazy(() => import('soapbox/features/ui/components/modals/event-map-modal')); +export const EventParticipantsModal = lazy(() => import('soapbox/features/ui/components/modals/event-participants-modal')); +export const Events = lazy(() => import('soapbox/features/events')); +export const Groups = lazy(() => import('soapbox/features/groups')); +export const GroupsDiscover = lazy(() => import('soapbox/features/groups/discover')); +export const GroupsPopular = lazy(() => import('soapbox/features/groups/popular')); +export const GroupsSuggested = lazy(() => import('soapbox/features/groups/suggested')); +export const GroupsTag = lazy(() => import('soapbox/features/groups/tag')); +export const GroupsTags = lazy(() => import('soapbox/features/groups/tags')); +export const PendingGroupRequests = lazy(() => import('soapbox/features/groups/pending-requests')); +export const GroupMembers = lazy(() => import('soapbox/features/group/group-members')); +export const GroupTags = lazy(() => import('soapbox/features/group/group-tags')); +export const GroupTagTimeline = lazy(() => import('soapbox/features/group/group-tag-timeline')); +export const GroupTimeline = lazy(() => import('soapbox/features/group/group-timeline')); +export const ManageGroup = lazy(() => import('soapbox/features/group/manage-group')); +export const EditGroup = lazy(() => import('soapbox/features/group/edit-group')); +export const GroupBlockedMembers = lazy(() => import('soapbox/features/group/group-blocked-members')); +export const GroupMembershipRequests = lazy(() => import('soapbox/features/group/group-membership-requests')); +export const GroupGallery = lazy(() => import('soapbox/features/group/group-gallery')); +export const CreateGroupModal = lazy(() => import('soapbox/features/ui/components/modals/manage-group-modal/create-group-modal')); +export const NewGroupPanel = lazy(() => import('soapbox/features/ui/components/panels/new-group-panel')); +export const MyGroupsPanel = lazy(() => import('soapbox/features/ui/components/panels/my-groups-panel')); +export const SuggestedGroupsPanel = lazy(() => import('soapbox/features/ui/components/panels/suggested-groups-panel')); +export const GroupMediaPanel = lazy(() => import('soapbox/features/ui/components/group-media-panel')); +export const NewEventPanel = lazy(() => import('soapbox/features/ui/components/panels/new-event-panel')); +export const Announcements = lazy(() => import('soapbox/features/admin/announcements')); +export const EditAnnouncementModal = lazy(() => import('soapbox/features/ui/components/modals/edit-announcement-modal')); +export const FollowedTags = lazy(() => import('soapbox/features/followed-tags')); +export const AccountNotePanel = lazy(() => import('soapbox/features/ui/components/panels/account-note-panel')); +export const ComposeEditor = lazy(() => import('soapbox/features/compose/editor')); From 3a221e8c86ca5f749b18ef0633960a12eb6e9d4d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 7 Oct 2023 15:37:36 -0500 Subject: [PATCH 026/100] Bundle: just wrap it with React.Suspense --- .../components/emoji-picker-dropdown.tsx | 50 +++----- src/features/ui/components/bundle.tsx | 121 +++--------------- .../ui/containers/bundle-container.tsx | 20 +-- src/features/ui/index.tsx | 4 +- src/features/ui/util/react-router-helpers.tsx | 2 +- 5 files changed, 35 insertions(+), 162 deletions(-) diff --git a/src/features/emoji/components/emoji-picker-dropdown.tsx b/src/features/emoji/components/emoji-picker-dropdown.tsx index 379ae9c5b..8917272b3 100644 --- a/src/features/emoji/components/emoji-picker-dropdown.tsx +++ b/src/features/emoji/components/emoji-picker-dropdown.tsx @@ -9,12 +9,10 @@ import { useAppDispatch, useAppSelector, useTheme } from 'soapbox/hooks'; import { RootState } from 'soapbox/store'; import { buildCustomEmojis } from '../../emoji'; -import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components'; +import { EmojiPicker } from '../../ui/util/async-components'; import type { Emoji, CustomEmoji, NativeEmoji } from 'soapbox/features/emoji'; -let EmojiPicker: any; // load asynchronously - export const messages = defineMessages({ emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' }, emoji_pick: { id: 'emoji_button.pick', defaultMessage: 'Pick an emoji…' }, @@ -136,8 +134,6 @@ const EmojiPickerDropdown: React.FC = ({ const customEmojis = useAppSelector((state) => getCustomEmojis(state)); const frequentlyUsedEmojis = useAppSelector((state) => getFrequentlyUsedEmojis(state)); - const [loading, setLoading] = useState(false); - const handlePick = (emoji: any) => { setVisible(false); @@ -210,18 +206,6 @@ const EmojiPickerDropdown: React.FC = ({ } else { document.body.style.overflow = ''; } - - if (!EmojiPicker) { - setLoading(true); - - EmojiPickerAsync().then(EmojiMart => { - EmojiPicker = EmojiMart.Picker; - - setLoading(false); - }).catch(() => { - setLoading(false); - }); - } }, [visible]); useEffect(() => () => { @@ -231,23 +215,21 @@ const EmojiPickerDropdown: React.FC = ({ return ( visible ? ( - {!loading && ( - - )} + ) : null ); diff --git a/src/features/ui/components/bundle.tsx b/src/features/ui/components/bundle.tsx index 58157901d..dc84acf69 100644 --- a/src/features/ui/components/bundle.tsx +++ b/src/features/ui/components/bundle.tsx @@ -1,114 +1,23 @@ -import React from 'react'; +import React, { Suspense } from 'react'; -const emptyComponent = () => null; -const noop = () => { }; - -export interface BundleProps { - fetchComponent: () => Promise; - loading: React.ComponentType; - error: React.ComponentType<{ onRetry: (props?: BundleProps) => void }>; - children: (mod: any) => React.ReactNode; +export interface IBundle> { + fetchComponent: React.LazyExoticComponent; + loading?: React.ComponentType; + error?: React.ComponentType<{ onRetry: (props?: IBundle) => void }>; + children: (component: React.LazyExoticComponent) => React.ReactNode; renderDelay?: number; - onFetch: () => void; - onFetchSuccess: () => void; - onFetchFail: (error: any) => void; -} - -interface BundleState { - mod: any; - forceRender: boolean; + onFetch?: () => void; + onFetchSuccess?: () => void; + onFetchFail?: (error: any) => void; } /** Fetches and renders an async component. */ -class Bundle extends React.PureComponent { - - timeout: NodeJS.Timeout | undefined; - timestamp: Date | undefined; - - static defaultProps = { - loading: emptyComponent, - error: emptyComponent, - renderDelay: 0, - onFetch: noop, - onFetchSuccess: noop, - onFetchFail: noop, - }; - - static cache = new Map; - - state = { - mod: undefined, - forceRender: false, - }; - - componentDidMount() { - this.load(this.props); - } - - UNSAFE_componentWillReceiveProps(nextProps: BundleProps) { - if (nextProps.fetchComponent !== this.props.fetchComponent) { - this.load(nextProps); - } - } - - componentWillUnmount() { - if (this.timeout) { - clearTimeout(this.timeout); - } - } - - load = (props?: BundleProps) => { - const { fetchComponent, onFetch, onFetchSuccess, onFetchFail, renderDelay } = props || this.props; - const cachedMod = Bundle.cache.get(fetchComponent); - - if (fetchComponent === undefined) { - this.setState({ mod: null }); - return Promise.resolve(); - } - - onFetch(); - - if (cachedMod) { - this.setState({ mod: cachedMod.default }); - onFetchSuccess(); - return Promise.resolve(); - } - - this.setState({ mod: undefined }); - - if (renderDelay !== 0) { - this.timestamp = new Date(); - this.timeout = setTimeout(() => this.setState({ forceRender: true }), renderDelay); - } - - return fetchComponent() - .then((mod) => { - Bundle.cache.set(fetchComponent, mod); - this.setState({ mod: mod.default }); - onFetchSuccess(); - }) - .catch((error) => { - this.setState({ mod: null }); - onFetchFail(error); - }); - }; - - render() { - const { loading: Loading, error: Error, children, renderDelay } = this.props; - const { mod, forceRender } = this.state; - const elapsed = this.timestamp ? ((new Date()).getTime() - this.timestamp.getTime()) : renderDelay!; - - if (mod === undefined) { - return (elapsed >= renderDelay! || forceRender) ? : null; - } - - if (mod === null) { - return ; - } - - return children(mod); - } - +function Bundle>({ fetchComponent, loading: Loading, children }: IBundle) { + return ( + : null}> + {children(fetchComponent)} + + ); } export default Bundle; diff --git a/src/features/ui/containers/bundle-container.tsx b/src/features/ui/containers/bundle-container.tsx index 50a8b8629..b94b1bb02 100644 --- a/src/features/ui/containers/bundle-container.tsx +++ b/src/features/ui/containers/bundle-container.tsx @@ -1,21 +1,3 @@ -import { connect } from 'react-redux'; - -import { fetchBundleRequest, fetchBundleSuccess, fetchBundleFail } from 'soapbox/actions/bundles'; - import Bundle from '../components/bundle'; -import type { AppDispatch } from 'soapbox/store'; - -const mapDispatchToProps = (dispatch: AppDispatch) => ({ - onFetch() { - dispatch(fetchBundleRequest()); - }, - onFetchSuccess() { - dispatch(fetchBundleSuccess()); - }, - onFetchFail(error: any) { - dispatch(fetchBundleFail(error)); - }, -}); - -export default connect(null, mapDispatchToProps)(Bundle); +export default Bundle; diff --git a/src/features/ui/index.tsx b/src/features/ui/index.tsx index 0e1f30a08..cbc8590b5 100644 --- a/src/features/ui/index.tsx +++ b/src/features/ui/index.tsx @@ -1,5 +1,5 @@ import clsx from 'clsx'; -import React, { useEffect, useRef } from 'react'; +import React, { lazy, useEffect, useRef } from 'react'; import { Switch, useHistory, useLocation, Redirect } from 'react-router-dom'; import { fetchFollowRequests } from 'soapbox/actions/accounts'; @@ -354,7 +354,7 @@ const SwitchingColumnsArea: React.FC = ({ children }) => - new Promise((_resolve, reject) => reject())} content={children} /> + Promise.reject())} content={children} /> {hasCrypto && } diff --git a/src/features/ui/util/react-router-helpers.tsx b/src/features/ui/util/react-router-helpers.tsx index 911e3b430..02a5fb180 100644 --- a/src/features/ui/util/react-router-helpers.tsx +++ b/src/features/ui/util/react-router-helpers.tsx @@ -17,7 +17,7 @@ type PageProps = { }; interface IWrappedRoute extends RouteProps { - component: (...args: any[]) => any; + component: React.LazyExoticComponent; page?: React.ComponentType; content?: React.ReactNode; componentParams?: Record; From 5b539416d91af1e2e75b1be64ef0bf36b766e74a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 7 Oct 2023 15:38:15 -0500 Subject: [PATCH 027/100] ProfileHoverCard/StatusHoverCard: make `visible` an optional prop --- src/components/profile-hover-card.tsx | 2 +- src/components/status-hover-card.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/profile-hover-card.tsx b/src/components/profile-hover-card.tsx index f7517430e..ddb89e11e 100644 --- a/src/components/profile-hover-card.tsx +++ b/src/components/profile-hover-card.tsx @@ -56,7 +56,7 @@ const handleMouseLeave = (dispatch: AppDispatch): React.MouseEventHandler => { }; interface IProfileHoverCard { - visible: boolean; + visible?: boolean; } /** Popup profile preview that appears when hovering avatars and display names. */ diff --git a/src/components/status-hover-card.tsx b/src/components/status-hover-card.tsx index 5c31f9b4d..08359c61a 100644 --- a/src/components/status-hover-card.tsx +++ b/src/components/status-hover-card.tsx @@ -15,7 +15,7 @@ import { showStatusHoverCard } from './hover-status-wrapper'; import { Card, CardBody } from './ui'; interface IStatusHoverCard { - visible: boolean; + visible?: boolean; } /** Popup status preview that appears when hovering reply to */ From 0f10922c3d156f397d471cebb93fdba6730038c6 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 7 Oct 2023 15:43:34 -0500 Subject: [PATCH 028/100] ProfilePage: fix types of async components --- src/features/ui/components/panels/account-note-panel.tsx | 2 +- src/features/ui/components/pinned-accounts-panel.tsx | 2 +- src/features/ui/components/profile-familiar-followers.tsx | 2 +- src/features/ui/components/profile-fields-panel.tsx | 2 +- src/features/ui/components/profile-info-panel.tsx | 2 +- src/features/ui/components/profile-media-panel.tsx | 3 ++- src/pages/remote-instance-page.tsx | 6 +++--- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/features/ui/components/panels/account-note-panel.tsx b/src/features/ui/components/panels/account-note-panel.tsx index 045fd6083..032039dae 100644 --- a/src/features/ui/components/panels/account-note-panel.tsx +++ b/src/features/ui/components/panels/account-note-panel.tsx @@ -7,8 +7,8 @@ import { submitAccountNote } from 'soapbox/actions/account-notes'; import { HStack, Text, Widget } from 'soapbox/components/ui'; import { useAppDispatch } from 'soapbox/hooks'; +import type { Account as AccountEntity } from 'soapbox/schemas'; import type { AppDispatch } from 'soapbox/store'; -import type { Account as AccountEntity } from 'soapbox/types/entities'; const onSave = debounce( (dispatch: AppDispatch, id: string, value: string, callback: () => void) => diff --git a/src/features/ui/components/pinned-accounts-panel.tsx b/src/features/ui/components/pinned-accounts-panel.tsx index d9aed232b..b0f5e918f 100644 --- a/src/features/ui/components/pinned-accounts-panel.tsx +++ b/src/features/ui/components/pinned-accounts-panel.tsx @@ -9,7 +9,7 @@ import BundleContainer from 'soapbox/features/ui/containers/bundle-container'; import { WhoToFollowPanel } from 'soapbox/features/ui/util/async-components'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; -import type { Account } from 'soapbox/types/entities'; +import type { Account } from 'soapbox/schemas'; interface IPinnedAccountsPanel { account: Account; diff --git a/src/features/ui/components/profile-familiar-followers.tsx b/src/features/ui/components/profile-familiar-followers.tsx index 749e52f93..c80e46b04 100644 --- a/src/features/ui/components/profile-familiar-followers.tsx +++ b/src/features/ui/components/profile-familiar-followers.tsx @@ -12,7 +12,7 @@ import VerificationBadge from 'soapbox/components/verification-badge'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { makeGetAccount } from 'soapbox/selectors'; -import type { Account } from 'soapbox/types/entities'; +import type { Account } from 'soapbox/schemas'; const getAccount = makeGetAccount(); diff --git a/src/features/ui/components/profile-fields-panel.tsx b/src/features/ui/components/profile-fields-panel.tsx index 946cf35a7..fb434a25b 100644 --- a/src/features/ui/components/profile-fields-panel.tsx +++ b/src/features/ui/components/profile-fields-panel.tsx @@ -5,7 +5,7 @@ import { Widget, Stack } from 'soapbox/components/ui'; import ProfileField from './profile-field'; -import type { Account } from 'soapbox/types/entities'; +import type { Account } from 'soapbox/schemas'; interface IProfileFieldsPanel { account: Account; diff --git a/src/features/ui/components/profile-info-panel.tsx b/src/features/ui/components/profile-info-panel.tsx index d44a83d8e..244434b8b 100644 --- a/src/features/ui/components/profile-info-panel.tsx +++ b/src/features/ui/components/profile-info-panel.tsx @@ -15,7 +15,7 @@ import ProfileFamiliarFollowers from './profile-familiar-followers'; import ProfileField from './profile-field'; import ProfileStats from './profile-stats'; -import type { Account } from 'soapbox/types/entities'; +import type { Account } from 'soapbox/schemas'; /** Basically ensure the URL isn't `javascript:alert('hi')` or something like that */ const isSafeUrl = (text: string): boolean => { diff --git a/src/features/ui/components/profile-media-panel.tsx b/src/features/ui/components/profile-media-panel.tsx index f87b12c55..d63b849f3 100644 --- a/src/features/ui/components/profile-media-panel.tsx +++ b/src/features/ui/components/profile-media-panel.tsx @@ -10,7 +10,8 @@ import { getAccountGallery } from 'soapbox/selectors'; import MediaItem from '../../account-gallery/components/media-item'; -import type { Account, Attachment } from 'soapbox/types/entities'; +import type { Account } from 'soapbox/schemas'; +import type { Attachment } from 'soapbox/types/entities'; interface IProfileMediaPanel { account?: Account; diff --git a/src/pages/remote-instance-page.tsx b/src/pages/remote-instance-page.tsx index b5c4e5262..60a61e16d 100644 --- a/src/pages/remote-instance-page.tsx +++ b/src/pages/remote-instance-page.tsx @@ -13,15 +13,15 @@ import { federationRestrictionsDisclosed } from 'soapbox/utils/state'; import { Layout } from '../components/ui'; interface IRemoteInstancePage { - params?: { - instance?: string; + params: { + instance: string; }; children: React.ReactNode; } /** Page for viewing a remote instance timeline. */ const RemoteInstancePage: React.FC = ({ children, params }) => { - const host = params?.instance; + const host = params.instance; const { account } = useOwnAccount(); const disclosed = useAppSelector(federationRestrictionsDisclosed); From 1b213452b7a8f1c060cba84d26bd91268c6e350c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 7 Oct 2023 16:00:42 -0500 Subject: [PATCH 029/100] Fix Bundle type errors, remove bundle error components, remove group slug support --- src/components/hoc/group-lookup-hoc.tsx | 62 ------------------- src/components/hoc/with-hoc.tsx | 11 ---- .../ui/components/bundle-column-error.tsx | 39 ------------ .../ui/components/bundle-modal-error.tsx | 35 ----------- src/features/ui/components/modal-root.tsx | 10 +-- src/features/ui/index.tsx | 24 ------- src/features/ui/util/react-router-helpers.tsx | 6 +- src/pages/event-page.tsx | 2 +- src/pages/group-page.tsx | 3 +- src/pages/remote-instance-page.tsx | 6 +- 10 files changed, 9 insertions(+), 189 deletions(-) delete mode 100644 src/components/hoc/group-lookup-hoc.tsx delete mode 100644 src/components/hoc/with-hoc.tsx delete mode 100644 src/features/ui/components/bundle-column-error.tsx delete mode 100644 src/features/ui/components/bundle-modal-error.tsx diff --git a/src/components/hoc/group-lookup-hoc.tsx b/src/components/hoc/group-lookup-hoc.tsx deleted file mode 100644 index 570ec2e69..000000000 --- a/src/components/hoc/group-lookup-hoc.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; - -import { useGroupLookup } from 'soapbox/api/hooks'; -import ColumnLoading from 'soapbox/features/ui/components/column-loading'; - -import { Layout } from '../ui'; - -interface IGroupLookup { - params: { - groupSlug: string; - }; -} - -interface IMaybeGroupLookup { - params?: { - groupSlug?: string; - groupId?: string; - }; -} - -function GroupLookupHoc(Component: React.ComponentType<{ params: { groupId: string } }>) { - const GroupLookup: React.FC = (props) => { - const { entity: group } = useGroupLookup(props.params.groupSlug); - - if (!group) return ( - <> - - - - - - - ); - - const newProps = { - ...props, - params: { - ...props.params, - id: group.id, - groupId: group.id, - }, - }; - - return ( - - ); - }; - - const MaybeGroupLookup: React.FC = (props) => { - const { params } = props; - - if (params?.groupId) { - return ; - } else { - return ; - } - }; - - return MaybeGroupLookup; -} - -export default GroupLookupHoc; \ No newline at end of file diff --git a/src/components/hoc/with-hoc.tsx b/src/components/hoc/with-hoc.tsx deleted file mode 100644 index d5752a45f..000000000 --- a/src/components/hoc/with-hoc.tsx +++ /dev/null @@ -1,11 +0,0 @@ -type HOC = (Component: React.ComponentType

) => React.ComponentType -type AsyncComponent

= () => Promise<{ default: React.ComponentType

}> - -const withHoc = (asyncComponent: AsyncComponent

, hoc: HOC) => { - return async () => { - const { default: component } = await asyncComponent(); - return { default: hoc(component) }; - }; -}; - -export default withHoc; \ No newline at end of file diff --git a/src/features/ui/components/bundle-column-error.tsx b/src/features/ui/components/bundle-column-error.tsx deleted file mode 100644 index 4653610da..000000000 --- a/src/features/ui/components/bundle-column-error.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { defineMessages, useIntl } from 'react-intl'; - -import { Column, Stack, Text, IconButton } from 'soapbox/components/ui'; - -const messages = defineMessages({ - title: { id: 'bundle_column_error.title', defaultMessage: 'Network error' }, - body: { id: 'bundle_column_error.body', defaultMessage: 'Something went wrong while loading this page.' }, - retry: { id: 'bundle_column_error.retry', defaultMessage: 'Try again' }, -}); - -interface IBundleColumnError { - onRetry: () => void; -} - -const BundleColumnError: React.FC = ({ onRetry }) => { - const intl = useIntl(); - - const handleRetry = () => { - onRetry(); - }; - - return ( - - - - - {intl.formatMessage(messages.body)} - - - ); -}; - -export default BundleColumnError; diff --git a/src/features/ui/components/bundle-modal-error.tsx b/src/features/ui/components/bundle-modal-error.tsx deleted file mode 100644 index 1b33d3d22..000000000 --- a/src/features/ui/components/bundle-modal-error.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import { defineMessages, useIntl } from 'react-intl'; - -import { Modal } from 'soapbox/components/ui'; - -const messages = defineMessages({ - error: { id: 'bundle_modal_error.message', defaultMessage: 'Something went wrong while loading this modal.' }, - retry: { id: 'bundle_modal_error.retry', defaultMessage: 'Try again' }, - close: { id: 'bundle_modal_error.close', defaultMessage: 'Close' }, -}); - -interface IBundleModalError { - onRetry: () => void; - onClose: () => void; -} - -const BundleModalError: React.FC = ({ onRetry, onClose }) => { - const intl = useIntl(); - - const handleRetry = () => { - onRetry(); - }; - - return ( - - ); -}; - -export default BundleModalError; diff --git a/src/features/ui/components/modal-root.tsx b/src/features/ui/components/modal-root.tsx index b4097d220..5dc8bd6ca 100644 --- a/src/features/ui/components/modal-root.tsx +++ b/src/features/ui/components/modal-root.tsx @@ -40,12 +40,10 @@ import { import BundleContainer from '../containers/bundle-container'; -import { BundleProps } from './bundle'; -import BundleModalError from './bundle-modal-error'; import ModalLoading from './modal-loading'; /* eslint sort-keys: "error" */ -const MODAL_COMPONENTS = { +const MODAL_COMPONENTS: Record> = { 'ACCOUNT_MODERATION': AccountModerationModal, 'ACTIONS': ActionsModal, 'BIRTHDAYS': BirthdaysModal, @@ -108,10 +106,6 @@ export default class ModalRoot extends React.PureComponent { return !['MEDIA', 'VIDEO', 'BOOST', 'CONFIRM', 'ACTIONS'].includes(modalId) ? : null; }; - renderError: React.ComponentType<{ onRetry: (props?: BundleProps) => void }> = (props) => { - return ; - }; - onClickClose = (_?: ModalType) => { const { onClose, type } = this.props; onClose(type); @@ -124,7 +118,7 @@ export default class ModalRoot extends React.PureComponent { return ( {visible && ( - + {(SpecificComponent) => } )} diff --git a/src/features/ui/index.tsx b/src/features/ui/index.tsx index cbc8590b5..39db6599b 100644 --- a/src/features/ui/index.tsx +++ b/src/features/ui/index.tsx @@ -15,8 +15,6 @@ import { fetchSuggestionsForTimeline } from 'soapbox/actions/suggestions'; import { expandHomeTimeline } from 'soapbox/actions/timelines'; import { useUserStream } from 'soapbox/api/hooks'; import { useSignerStream } from 'soapbox/api/hooks/nostr/useSignerStream'; -import GroupLookupHoc from 'soapbox/components/hoc/group-lookup-hoc'; -import withHoc from 'soapbox/components/hoc/with-hoc'; import SidebarNavigation from 'soapbox/components/sidebar-navigation'; import ThumbNavigation from 'soapbox/components/thumb-navigation'; import { Layout } from 'soapbox/components/ui'; @@ -147,16 +145,6 @@ import { WrappedRoute } from './util/react-router-helpers'; // Without this it ends up in ~8 very commonly used bundles. import 'soapbox/components/status'; -const GroupTagsSlug = withHoc(GroupTags as any, GroupLookupHoc); -const GroupTagTimelineSlug = withHoc(GroupTagTimeline as any, GroupLookupHoc); -const GroupTimelineSlug = withHoc(GroupTimeline as any, GroupLookupHoc); -const GroupMembersSlug = withHoc(GroupMembers as any, GroupLookupHoc); -const GroupGallerySlug = withHoc(GroupGallery as any, GroupLookupHoc); -const ManageGroupSlug = withHoc(ManageGroup as any, GroupLookupHoc); -const EditGroupSlug = withHoc(EditGroup as any, GroupLookupHoc); -const GroupBlockedMembersSlug = withHoc(GroupBlockedMembers as any, GroupLookupHoc); -const GroupMembershipRequestsSlug = withHoc(GroupMembershipRequests as any, GroupLookupHoc); - interface ISwitchingColumnsArea { children: React.ReactNode; } @@ -310,18 +298,6 @@ const SwitchingColumnsArea: React.FC = ({ children }) => {features.groups && } {features.groups && } - {features.groupsTags && } - {features.groupsTags && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.scheduledStatuses && } diff --git a/src/features/ui/util/react-router-helpers.tsx b/src/features/ui/util/react-router-helpers.tsx index 02a5fb180..e202af4a4 100644 --- a/src/features/ui/util/react-router-helpers.tsx +++ b/src/features/ui/util/react-router-helpers.tsx @@ -4,7 +4,6 @@ import { Redirect, Route, useHistory, RouteProps, RouteComponentProps, match as import { Layout } from 'soapbox/components/ui'; import { useOwnAccount, useSettings } from 'soapbox/hooks'; -import BundleColumnError from '../components/bundle-column-error'; import ColumnForbidden from '../components/column-forbidden'; import ColumnLoading from '../components/column-loading'; import ColumnsArea from '../components/columns-area'; @@ -48,7 +47,7 @@ const WrappedRoute: React.FC = ({ const renderComponent = ({ match }: RouteComponentProps) => { if (Page) { return ( - + {Component => ( @@ -63,7 +62,7 @@ const WrappedRoute: React.FC = ({ } return ( - + {Component => ( @@ -89,7 +88,6 @@ const WrappedRoute: React.FC = ({ const renderLoading = () => renderWithLayout(); const renderForbidden = () => renderWithLayout(); - const renderError = (props: any) => renderWithLayout(); const loginRedirect = () => { const actualUrl = encodeURIComponent(`${history.location.pathname}${history.location.search}`); diff --git a/src/pages/event-page.tsx b/src/pages/event-page.tsx index f10a865fa..a8fd7bb4b 100644 --- a/src/pages/event-page.tsx +++ b/src/pages/event-page.tsx @@ -31,7 +31,7 @@ const EventPage: React.FC = ({ params, children }) => { const history = useHistory(); const statusId = params?.statusId!; - const status = useAppSelector(state => getStatus(state, { id: statusId })); + const status = useAppSelector(state => getStatus(state, { id: statusId }) || undefined); const event = status?.event; diff --git a/src/pages/group-page.tsx b/src/pages/group-page.tsx index 5124076be..fac6ca6a4 100644 --- a/src/pages/group-page.tsx +++ b/src/pages/group-page.tsx @@ -3,7 +3,6 @@ import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { useRouteMatch } from 'react-router-dom'; import { useGroup, useGroupMembershipRequests } from 'soapbox/api/hooks'; -import GroupLookupHoc from 'soapbox/components/hoc/group-lookup-hoc'; import { Column, Icon, Layout, Stack, Text, Tabs } from 'soapbox/components/ui'; import GroupHeader from 'soapbox/features/group/components/group-header'; import LinkFooter from 'soapbox/features/ui/components/link-footer'; @@ -191,4 +190,4 @@ const GroupPage: React.FC = ({ params, children }) => { ); }; -export default GroupLookupHoc(GroupPage as any) as any; +export default GroupPage; diff --git a/src/pages/remote-instance-page.tsx b/src/pages/remote-instance-page.tsx index 60a61e16d..b53844213 100644 --- a/src/pages/remote-instance-page.tsx +++ b/src/pages/remote-instance-page.tsx @@ -13,15 +13,15 @@ import { federationRestrictionsDisclosed } from 'soapbox/utils/state'; import { Layout } from '../components/ui'; interface IRemoteInstancePage { - params: { - instance: string; + params?: { + instance?: string; }; children: React.ReactNode; } /** Page for viewing a remote instance timeline. */ const RemoteInstancePage: React.FC = ({ children, params }) => { - const host = params.instance; + const host = params!.instance!; const { account } = useOwnAccount(); const disclosed = useAppSelector(federationRestrictionsDisclosed); From b02c39da2d5a642763083b257c35f23ad20a233e Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 7 Oct 2023 17:14:45 -0500 Subject: [PATCH 030/100] Delete Bundle component, use Suspense and React.lazy --- src/actions/bundles.ts | 28 ------- src/components/attachment-thumbs.tsx | 27 +++---- src/components/birthday-input.tsx | 25 +++--- src/components/profile-hover-card.tsx | 15 ++-- src/components/status-media.tsx | 78 +++++++++---------- src/containers/soapbox.tsx | 14 ++-- .../chats/components/chat-message.tsx | 24 +++--- .../compose/components/compose-form.tsx | 36 ++++----- .../compose/components/schedule-form.tsx | 31 ++++---- .../containers/schedule-form-container.tsx | 14 ---- .../report/components/status-check-box.tsx | 51 +++++------- src/features/ui/components/bundle.tsx | 23 ------ src/features/ui/components/modal-loading.tsx | 2 - src/features/ui/components/modal-root.tsx | 16 ++-- .../compose-event-modal.tsx | 71 ++++++++--------- .../modals/edit-announcement-modal.tsx | 45 +++++------ .../ui/components/pinned-accounts-panel.tsx | 5 +- src/features/ui/components/profile-field.tsx | 13 +--- .../ui/containers/bundle-container.tsx | 3 - src/features/ui/index.tsx | 26 ++----- src/features/ui/util/react-router-helpers.tsx | 41 ++++------ src/pages/admin-page.tsx | 6 +- src/pages/default-page.tsx | 17 +--- src/pages/event-page.tsx | 21 ++--- src/pages/events-page.tsx | 13 +--- src/pages/group-page.tsx | 19 ++--- src/pages/groups-page.tsx | 23 +----- src/pages/groups-pending-page.tsx | 13 +--- src/pages/home-page.tsx | 39 +++------- src/pages/landing-page.tsx | 13 +--- src/pages/manage-groups-page.tsx | 10 +-- src/pages/profile-page.tsx | 34 ++------ src/pages/remote-instance-page.tsx | 15 +--- src/pages/search-page.tsx | 23 ++---- src/pages/status-page.tsx | 19 ++--- 35 files changed, 275 insertions(+), 578 deletions(-) delete mode 100644 src/actions/bundles.ts delete mode 100644 src/features/compose/containers/schedule-form-container.tsx delete mode 100644 src/features/ui/components/bundle.tsx delete mode 100644 src/features/ui/containers/bundle-container.tsx diff --git a/src/actions/bundles.ts b/src/actions/bundles.ts deleted file mode 100644 index fc5ef9321..000000000 --- a/src/actions/bundles.ts +++ /dev/null @@ -1,28 +0,0 @@ -const BUNDLE_FETCH_REQUEST = 'BUNDLE_FETCH_REQUEST'; -const BUNDLE_FETCH_SUCCESS = 'BUNDLE_FETCH_SUCCESS'; -const BUNDLE_FETCH_FAIL = 'BUNDLE_FETCH_FAIL'; - -const fetchBundleRequest = (skipLoading?: boolean) => ({ - type: BUNDLE_FETCH_REQUEST, - skipLoading, -}); - -const fetchBundleSuccess = (skipLoading?: boolean) => ({ - type: BUNDLE_FETCH_SUCCESS, - skipLoading, -}); - -const fetchBundleFail = (error: any, skipLoading?: boolean) => ({ - type: BUNDLE_FETCH_FAIL, - error, - skipLoading, -}); - -export { - BUNDLE_FETCH_REQUEST, - BUNDLE_FETCH_SUCCESS, - BUNDLE_FETCH_FAIL, - fetchBundleRequest, - fetchBundleSuccess, - fetchBundleFail, -}; diff --git a/src/components/attachment-thumbs.tsx b/src/components/attachment-thumbs.tsx index 1ec694667..c5a017408 100644 --- a/src/components/attachment-thumbs.tsx +++ b/src/components/attachment-thumbs.tsx @@ -1,7 +1,6 @@ -import React from 'react'; +import React, { Suspense } from 'react'; import { openModal } from 'soapbox/actions/modals'; -import Bundle from 'soapbox/features/ui/components/bundle'; import { MediaGallery } from 'soapbox/features/ui/util/async-components'; import { useAppDispatch } from 'soapbox/hooks'; @@ -18,23 +17,21 @@ const AttachmentThumbs = (props: IAttachmentThumbs) => { const { media, onClick, sensitive } = props; const dispatch = useAppDispatch(); - const renderLoading = () =>

; + const fallback =
; const onOpenMedia = (media: ImmutableList, index: number) => dispatch(openModal('MEDIA', { media, index })); return (
- - {(Component: any) => ( - - )} - + + + {onClick && (
diff --git a/src/components/birthday-input.tsx b/src/components/birthday-input.tsx index a35e85055..62043bd51 100644 --- a/src/components/birthday-input.tsx +++ b/src/components/birthday-input.tsx @@ -2,7 +2,6 @@ import React, { useMemo } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import IconButton from 'soapbox/components/icon-button'; -import BundleContainer from 'soapbox/features/ui/containers/bundle-container'; import { DatePicker } from 'soapbox/features/ui/util/async-components'; import { useInstance, useFeatures } from 'soapbox/hooks'; @@ -114,19 +113,17 @@ const BirthdayInput: React.FC = ({ value, onChange, required }) return (
- - {Component => ()} - +
); }; diff --git a/src/components/profile-hover-card.tsx b/src/components/profile-hover-card.tsx index ddb89e11e..9c06dd92c 100644 --- a/src/components/profile-hover-card.tsx +++ b/src/components/profile-hover-card.tsx @@ -12,7 +12,6 @@ import { import { useAccount, usePatronUser } from 'soapbox/api/hooks'; import Badge from 'soapbox/components/badge'; import ActionButton from 'soapbox/features/ui/components/action-button'; -import BundleContainer from 'soapbox/features/ui/containers/bundle-container'; import { UserPanel } from 'soapbox/features/ui/util/async-components'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; import { isLocal } from 'soapbox/utils/accounts'; @@ -112,15 +111,11 @@ export const ProfileHoverCard: React.FC = ({ visible = true } - - {Component => ( - } - badges={badges} - /> - )} - + } + badges={badges} + /> {isLocal(account) ? ( diff --git a/src/components/status-media.tsx b/src/components/status-media.tsx index 00975e224..7147e3a54 100644 --- a/src/components/status-media.tsx +++ b/src/components/status-media.tsx @@ -1,16 +1,14 @@ -import React from 'react'; +import React, { Suspense } from 'react'; import { openModal } from 'soapbox/actions/modals'; import AttachmentThumbs from 'soapbox/components/attachment-thumbs'; import { GroupLinkPreview } from 'soapbox/features/groups/components/group-link-preview'; import PlaceholderCard from 'soapbox/features/placeholder/components/placeholder-card'; import Card from 'soapbox/features/status/components/card'; -import Bundle from 'soapbox/features/ui/components/bundle'; import { MediaGallery, Video, Audio } from 'soapbox/features/ui/util/async-components'; import { useAppDispatch } from 'soapbox/hooks'; import type { List as ImmutableList } from 'immutable'; -import type VideoType from 'soapbox/features/video'; import type { Status, Attachment } from 'soapbox/types/entities'; interface IStatusMedia { @@ -70,54 +68,48 @@ const StatusMedia: React.FC = ({ const video = firstAttachment; media = ( - - {(Component: typeof VideoType) => ( - - )} - + + ); } else if (size === 1 && firstAttachment.type === 'audio') { const attachment = firstAttachment; media = ( - - {(Component: any) => ( - - )} - + + ); } else { media = ( - - {(Component: any) => ( - - )} - + + + ); } } else if (status.spoiler_text.length === 0 && !status.quote && status.card?.group) { diff --git a/src/containers/soapbox.tsx b/src/containers/soapbox.tsx index 9df5b7a19..d60f8eec0 100644 --- a/src/containers/soapbox.tsx +++ b/src/containers/soapbox.tsx @@ -1,6 +1,6 @@ import { QueryClientProvider } from '@tanstack/react-query'; import clsx from 'clsx'; -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, Suspense } from 'react'; import { Toaster } from 'react-hot-toast'; import { IntlProvider } from 'react-intl'; import { Provider } from 'react-redux'; @@ -18,7 +18,6 @@ import Helmet from 'soapbox/components/helmet'; import LoadingScreen from 'soapbox/components/loading-screen'; import { StatProvider } from 'soapbox/contexts/stat-context'; import EmbeddedStatus from 'soapbox/features/embedded-status'; -import BundleContainer from 'soapbox/features/ui/containers/bundle-container'; import { ModalContainer, OnboardingWizard, @@ -87,9 +86,9 @@ const SoapboxMount = () => { /** Render the onboarding flow. */ const renderOnboarding = () => ( - - {(Component) => } - + }> + + ); /** Render the auth layout or UI. */ @@ -127,10 +126,7 @@ const SoapboxMount = () => { {renderBody()} - - {Component => } - - +
diff --git a/src/features/chats/components/chat-message.tsx b/src/features/chats/components/chat-message.tsx index 373be7f12..9b65eabf9 100644 --- a/src/features/chats/components/chat-message.tsx +++ b/src/features/chats/components/chat-message.tsx @@ -10,7 +10,6 @@ import { initReport, ReportableEntities } from 'soapbox/actions/reports'; import DropdownMenu from 'soapbox/components/dropdown-menu'; import { HStack, Icon, Stack, Text } from 'soapbox/components/ui'; import emojify from 'soapbox/features/emoji'; -import Bundle from 'soapbox/features/ui/components/bundle'; import { MediaGallery } from 'soapbox/features/ui/util/async-components'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { ChatKeys, IChat, useChatActions } from 'soapbox/queries/chats'; @@ -22,7 +21,6 @@ import ChatMessageReaction from './chat-message-reaction'; import ChatMessageReactionWrapper from './chat-message-reaction-wrapper/chat-message-reaction-wrapper'; import type { Menu as IMenu } from 'soapbox/components/dropdown-menu'; -import type { IMediaGallery } from 'soapbox/components/media-gallery'; import type { ChatMessage as ChatMessageEntity } from 'soapbox/types/entities'; const messages = defineMessages({ @@ -111,19 +109,15 @@ const ChatMessage = (props: IChatMessage) => { if (!chatMessage.media_attachments.size) return null; return ( - - {(Component: React.FC) => ( - - )} - + ); }; diff --git a/src/features/compose/components/compose-form.tsx b/src/features/compose/components/compose-form.tsx index 849f6cf80..97bb5d958 100644 --- a/src/features/compose/components/compose-form.tsx +++ b/src/features/compose/components/compose-form.tsx @@ -17,14 +17,12 @@ import AutosuggestInput, { AutoSuggestion } from 'soapbox/components/autosuggest import AutosuggestTextarea from 'soapbox/components/autosuggest-textarea'; import { Button, HStack, Stack } from 'soapbox/components/ui'; import EmojiPickerDropdown from 'soapbox/features/emoji/containers/emoji-picker-dropdown-container'; -import Bundle from 'soapbox/features/ui/components/bundle'; -import { ComposeEditor } from 'soapbox/features/ui/util/async-components'; +import { ComposeEditor, ScheduleForm } from 'soapbox/features/ui/util/async-components'; import { useAppDispatch, useAppSelector, useCompose, useDraggedFiles, useFeatures, useInstance, usePrevious } from 'soapbox/hooks'; import { isMobile } from 'soapbox/is-mobile'; import QuotedStatusContainer from '../containers/quoted-status-container'; import ReplyIndicatorContainer from '../containers/reply-indicator-container'; -import ScheduleFormContainer from '../containers/schedule-form-container'; import UploadButtonContainer from '../containers/upload-button-container'; import WarningContainer from '../containers/warning-container'; import { $createEmojiNode } from '../editor/nodes/emoji-node'; @@ -260,7 +258,7 @@ const ComposeForm = ({ id, shouldCondense, autoFocus, clickab ref={spoilerTextRef} /> - + ); @@ -313,23 +311,19 @@ const ComposeForm = ({ id, shouldCondense, autoFocus, clickab {!shouldCondense && !event && !group && }
- - {(Component: any) => ( - - )} - + {composeModifiers}
diff --git a/src/features/compose/components/schedule-form.tsx b/src/features/compose/components/schedule-form.tsx index 074d1b234..aab6b8864 100644 --- a/src/features/compose/components/schedule-form.tsx +++ b/src/features/compose/components/schedule-form.tsx @@ -5,7 +5,6 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { setSchedule, removeSchedule } from 'soapbox/actions/compose'; import IconButton from 'soapbox/components/icon-button'; import { HStack, Stack, Text } from 'soapbox/components/ui'; -import BundleContainer from 'soapbox/features/ui/containers/bundle-container'; import { DatePicker } from 'soapbox/features/ui/util/async-components'; import { useAppDispatch, useCompose } from 'soapbox/hooks'; @@ -55,22 +54,20 @@ const ScheduleForm: React.FC = ({ composeId }) => { - - {Component => ()} - + = (props) => ( - - {Component => } - -); - -export default ScheduleFormContainer; diff --git a/src/features/report/components/status-check-box.tsx b/src/features/report/components/status-check-box.tsx index d8d9c0eb4..b8cb48015 100644 --- a/src/features/report/components/status-check-box.tsx +++ b/src/features/report/components/status-check-box.tsx @@ -6,7 +6,6 @@ import StatusContent from 'soapbox/components/status-content'; import { Toggle } from 'soapbox/components/ui'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; -import Bundle from '../../ui/components/bundle'; import { MediaGallery, Video, Audio } from '../../ui/util/async-components'; interface IStatusCheckBox { @@ -35,22 +34,16 @@ const StatusCheckBox: React.FC = ({ id, disabled }) => { if (video) { media = ( - - {(Component: any) => ( - - )} - +