From 7cc98b72939a2c464a989d3869582762181d2049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 24 Aug 2024 15:01:17 +0200 Subject: [PATCH] Rename pages to layouts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- src/actions/accounts.ts | 5 +- src/actions/importer/index.ts | 3 +- src/actions/notifications.ts | 2 +- src/api/hooks/statuses/useBookmarkFolder.ts | 4 +- src/components/hover-ref-wrapper.tsx | 5 +- src/components/hover-status-wrapper.tsx | 5 +- src/components/profile-hover-card.tsx | 5 +- src/components/status-hover-card.tsx | 5 +- src/entity-store/hooks/useChangeEntity.ts | 3 +- .../components/registration-form.tsx | 2 +- .../floating-text-format-toolbar-plugin.tsx | 4 +- .../compose/editor/plugins/state-plugin.tsx | 3 +- .../security/mfa/otp-confirm-form.tsx | 5 +- src/features/status/components/thread.tsx | 2 +- .../ui/components/modals/video-modal.tsx | 1 - src/features/ui/index.tsx | 234 +++++++++--------- src/features/ui/util/react-router-helpers.tsx | 10 +- .../admin-layout.tsx} | 10 +- .../chats-layout.tsx} | 6 +- .../default-layout.tsx} | 6 +- .../empty-layout.tsx} | 6 +- .../event-layout.tsx} | 6 +- .../events-layout.tsx} | 8 +- .../external-login-layout.tsx} | 6 +- .../group-layout.tsx} | 8 +- .../groups-layout.tsx} | 8 +- .../home-page.tsx => layouts/home-layout.tsx} | 6 +- .../landing-layout.tsx} | 6 +- .../manage-groups-layout.tsx} | 8 +- .../profile-layout.tsx} | 8 +- .../remote-instance-layout.tsx} | 8 +- .../search-layout.tsx} | 6 +- .../status-layout.tsx} | 6 +- src/normalizers/chat-message.ts | 2 +- src/reducers/backups.ts | 5 +- src/reducers/contexts.ts | 5 +- src/reducers/dropdown-menu.ts | 5 +- src/reducers/relationships.ts | 10 +- src/reducers/security.ts | 5 +- src/reducers/status-lists.ts | 5 +- src/reducers/statuses.ts | 5 +- src/reducers/timelines.ts | 10 +- src/reducers/trending-statuses.ts | 5 +- src/reducers/user-lists.ts | 8 +- src/selectors/index.ts | 2 +- src/types/entities.ts | 5 +- src/utils/emoji-reacts.test.ts | 104 ++++---- src/utils/favicon-service.ts | 2 +- src/utils/notification.ts | 2 +- src/utils/resize-image.ts | 48 +--- tailwind.config.ts | 3 +- tailwind/colors.ts | 2 +- 52 files changed, 271 insertions(+), 372 deletions(-) rename src/{pages/admin-page.tsx => layouts/admin-layout.tsx} (62%) rename src/{pages/chats-page.tsx => layouts/chats-layout.tsx} (63%) rename src/{pages/default-page.tsx => layouts/default-layout.tsx} (87%) rename src/{pages/empty-page.tsx => layouts/empty-layout.tsx} (61%) rename src/{pages/event-page.tsx => layouts/event-layout.tsx} (95%) rename src/{pages/events-page.tsx => layouts/events-layout.tsx} (81%) rename src/{pages/external-login-page.tsx => layouts/external-login-layout.tsx} (86%) rename src/{pages/group-page.tsx => layouts/group-layout.tsx} (94%) rename src/{pages/groups-page.tsx => layouts/groups-layout.tsx} (77%) rename src/{pages/home-page.tsx => layouts/home-layout.tsx} (96%) rename src/{pages/landing-page.tsx => layouts/landing-layout.tsx} (86%) rename src/{pages/manage-groups-page.tsx => layouts/manage-groups-layout.tsx} (72%) rename src/{pages/profile-page.tsx => layouts/profile-layout.tsx} (95%) rename src/{pages/remote-instance-page.tsx => layouts/remote-instance-layout.tsx} (81%) rename src/{pages/search-page.tsx => layouts/search-layout.tsx} (87%) rename src/{pages/status-page.tsx => layouts/status-layout.tsx} (87%) diff --git a/src/actions/accounts.ts b/src/actions/accounts.ts index e3823f9d1..70254c482 100644 --- a/src/actions/accounts.ts +++ b/src/actions/accounts.ts @@ -7,10 +7,7 @@ import { isLoggedIn } from 'soapbox/utils/auth'; import { getClient, type PlfeResponse } from '../api'; -import { - importFetchedAccount, - importFetchedAccounts, -} from './importer'; +import { importFetchedAccount, importFetchedAccounts } from './importer'; import type { Map as ImmutableMap } from 'immutable'; import type { MinifiedStatus } from 'soapbox/reducers/statuses'; diff --git a/src/actions/importer/index.ts b/src/actions/importer/index.ts index 87301720b..bf245172c 100644 --- a/src/actions/importer/index.ts +++ b/src/actions/importer/index.ts @@ -1,9 +1,8 @@ -import { type Account as BaseAccount, type Group, type Poll, type Status as BaseStatus } from 'pl-api'; - import { importEntities } from 'soapbox/entity-store/actions'; import { Entities } from 'soapbox/entity-store/entities'; import { normalizeAccount, normalizeGroup } from 'soapbox/normalizers'; +import type { Account as BaseAccount, Group, Poll, Status as BaseStatus } from 'pl-api'; import type { AppDispatch } from 'soapbox/store'; const ACCOUNT_IMPORT = 'ACCOUNT_IMPORT'; diff --git a/src/actions/notifications.ts b/src/actions/notifications.ts index 521f9c400..74846dd3f 100644 --- a/src/actions/notifications.ts +++ b/src/actions/notifications.ts @@ -1,6 +1,5 @@ import IntlMessageFormat from 'intl-messageformat'; import 'intl-pluralrules'; -import { type Account, type Notification as BaseNotification, type PaginatedResponse, type Status } from 'pl-api'; import { defineMessages } from 'react-intl'; import { getClient } from 'soapbox/api'; @@ -23,6 +22,7 @@ import { import { saveMarker } from './markers'; import { getSettings, saveSettings } from './settings'; +import type { Account, Notification as BaseNotification, PaginatedResponse, Status } from 'pl-api'; import type { AppDispatch, RootState } from 'soapbox/store'; const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE' as const; diff --git a/src/api/hooks/statuses/useBookmarkFolder.ts b/src/api/hooks/statuses/useBookmarkFolder.ts index c87a395ba..f2a1ec54b 100644 --- a/src/api/hooks/statuses/useBookmarkFolder.ts +++ b/src/api/hooks/statuses/useBookmarkFolder.ts @@ -1,11 +1,11 @@ -import { type BookmarkFolder } from 'pl-api'; - import { Entities } from 'soapbox/entity-store/entities'; import { selectEntity } from 'soapbox/entity-store/selectors'; import { useAppSelector } from 'soapbox/hooks'; import { useBookmarkFolders } from './useBookmarkFolders'; +import type{ BookmarkFolder } from 'pl-api'; + const useBookmarkFolder = (folderId?: string) => { const { isError, diff --git a/src/components/hover-ref-wrapper.tsx b/src/components/hover-ref-wrapper.tsx index ea242c369..1c35221f0 100644 --- a/src/components/hover-ref-wrapper.tsx +++ b/src/components/hover-ref-wrapper.tsx @@ -3,10 +3,7 @@ import debounce from 'lodash/debounce'; import React, { useRef } from 'react'; import { fetchAccount } from 'soapbox/actions/accounts'; -import { - openProfileHoverCard, - closeProfileHoverCard, -} from 'soapbox/actions/profile-hover-card'; +import { openProfileHoverCard, closeProfileHoverCard } from 'soapbox/actions/profile-hover-card'; import { useAppDispatch } from 'soapbox/hooks'; import { isMobile } from 'soapbox/is-mobile'; diff --git a/src/components/hover-status-wrapper.tsx b/src/components/hover-status-wrapper.tsx index a5e9bc715..44e5016a0 100644 --- a/src/components/hover-status-wrapper.tsx +++ b/src/components/hover-status-wrapper.tsx @@ -3,10 +3,7 @@ import debounce from 'lodash/debounce'; import React, { useRef } from 'react'; import { useDispatch } from 'react-redux'; -import { - openStatusHoverCard, - closeStatusHoverCard, -} from 'soapbox/actions/status-hover-card'; +import { openStatusHoverCard, closeStatusHoverCard } from 'soapbox/actions/status-hover-card'; import { isMobile } from 'soapbox/is-mobile'; const showStatusHoverCard = debounce((dispatch, ref, statusId) => { diff --git a/src/components/profile-hover-card.tsx b/src/components/profile-hover-card.tsx index 6aefcefec..a75b17b92 100644 --- a/src/components/profile-hover-card.tsx +++ b/src/components/profile-hover-card.tsx @@ -5,10 +5,7 @@ import { usePopper } from 'react-popper'; import { useHistory } from 'react-router-dom'; import { fetchRelationships } from 'soapbox/actions/accounts'; -import { - closeProfileHoverCard, - updateProfileHoverCard, -} from 'soapbox/actions/profile-hover-card'; +import { closeProfileHoverCard, updateProfileHoverCard } from 'soapbox/actions/profile-hover-card'; import { useAccount } from 'soapbox/api/hooks'; import Badge from 'soapbox/components/badge'; import ActionButton from 'soapbox/features/ui/components/action-button'; diff --git a/src/components/status-hover-card.tsx b/src/components/status-hover-card.tsx index ae5826d10..f43c08997 100644 --- a/src/components/status-hover-card.tsx +++ b/src/components/status-hover-card.tsx @@ -4,10 +4,7 @@ import { useIntl } from 'react-intl'; import { usePopper } from 'react-popper'; import { useHistory } from 'react-router-dom'; -import { - closeStatusHoverCard, - updateStatusHoverCard, -} from 'soapbox/actions/status-hover-card'; +import { closeStatusHoverCard, updateStatusHoverCard } from 'soapbox/actions/status-hover-card'; import { fetchStatus } from 'soapbox/actions/statuses'; import StatusContainer from 'soapbox/containers/status-container'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; diff --git a/src/entity-store/hooks/useChangeEntity.ts b/src/entity-store/hooks/useChangeEntity.ts index 886081340..f67140ae2 100644 --- a/src/entity-store/hooks/useChangeEntity.ts +++ b/src/entity-store/hooks/useChangeEntity.ts @@ -1,9 +1,10 @@ import { importEntities } from 'soapbox/entity-store/actions'; import { Entities } from 'soapbox/entity-store/entities'; -import { type Entity } from 'soapbox/entity-store/types'; import { useAppDispatch } from 'soapbox/hooks/useAppDispatch'; import { useGetState } from 'soapbox/hooks/useGetState'; +import type { Entity } from 'soapbox/entity-store/types'; + type ChangeEntityFn = (entity: TEntity) => TEntity const useChangeEntity = (entityType: Entities) => { diff --git a/src/features/auth-login/components/registration-form.tsx b/src/features/auth-login/components/registration-form.tsx index 5e261fc0e..2271b66da 100644 --- a/src/features/auth-login/components/registration-form.tsx +++ b/src/features/auth-login/components/registration-form.tsx @@ -70,7 +70,7 @@ const RegistrationForm: React.FC = ({ inviteToken }) => { const controller = useRef(new AbortController()); const onInputChange: React.ChangeEventHandler = e => { - setParams(params => ({ ...params, [e.target.name]: e.target.value })); + setParams(params => ({ ...params, [e.target.name]: e.target.value })); }; const onUsernameChange: React.ChangeEventHandler = e => { diff --git a/src/features/compose/editor/plugins/floating-text-format-toolbar-plugin.tsx b/src/features/compose/editor/plugins/floating-text-format-toolbar-plugin.tsx index 64c119e99..4908ac035 100644 --- a/src/features/compose/editor/plugins/floating-text-format-toolbar-plugin.tsx +++ b/src/features/compose/editor/plugins/floating-text-format-toolbar-plugin.tsx @@ -20,9 +20,7 @@ import { $isHeadingNode, HeadingTagType, } from '@lexical/rich-text'; -import { - $setBlocksType, -} from '@lexical/selection'; +import { $setBlocksType } from '@lexical/selection'; import { $findMatchingParent, $getNearestNodeOfType, mergeRegister } from '@lexical/utils'; import clsx from 'clsx'; import { diff --git a/src/features/compose/editor/plugins/state-plugin.tsx b/src/features/compose/editor/plugins/state-plugin.tsx index 1c7596729..1d97dac6d 100644 --- a/src/features/compose/editor/plugins/state-plugin.tsx +++ b/src/features/compose/editor/plugins/state-plugin.tsx @@ -1,6 +1,5 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { $createRemarkExport } from '@mkljczk/lexical-remark'; -import { type LanguageIdentificationModel } from 'fasttext.wasm.js/dist/models/language-identification/common.js'; import { $getRoot } from 'lexical'; import debounce from 'lodash/debounce'; import { useCallback, useEffect } from 'react'; @@ -11,6 +10,8 @@ import { fetchStatus } from 'soapbox/actions/statuses'; import { useAppDispatch, useFeatures } from 'soapbox/hooks'; import { getStatusIdsFromLinksInContent } from 'soapbox/utils/status'; +import type { LanguageIdentificationModel } from 'fasttext.wasm.js/dist/models/language-identification/common.js'; + let lidModel: LanguageIdentificationModel; interface IStatePlugin { diff --git a/src/features/security/mfa/otp-confirm-form.tsx b/src/features/security/mfa/otp-confirm-form.tsx index f0df740e7..69d6bc21f 100644 --- a/src/features/security/mfa/otp-confirm-form.tsx +++ b/src/features/security/mfa/otp-confirm-form.tsx @@ -3,10 +3,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useIntl, FormattedMessage, defineMessages } from 'react-intl'; import { useHistory } from 'react-router-dom'; -import { - setupMfa, - confirmMfa, -} from 'soapbox/actions/mfa'; +import { setupMfa, confirmMfa } from 'soapbox/actions/mfa'; import { Button, Form, FormActions, FormGroup, Input, Stack, Text } from 'soapbox/components/ui'; import { useAppDispatch } from 'soapbox/hooks'; import toast from 'soapbox/toast'; diff --git a/src/features/status/components/thread.tsx b/src/features/status/components/thread.tsx index 98b4e6f3a..395295446 100644 --- a/src/features/status/components/thread.tsx +++ b/src/features/status/components/thread.tsx @@ -4,7 +4,6 @@ import { List as ImmutableList, OrderedSet as ImmutableOrderedSet } from 'immuta import React, { useEffect, useRef } from 'react'; import { useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; -import { type VirtuosoHandle } from 'react-virtuoso'; import { type ComposeReplyAction, mentionCompose, replyCompose } from 'soapbox/actions/compose'; import { reblog, toggleFavourite, unreblog } from 'soapbox/actions/interactions'; @@ -25,6 +24,7 @@ import { textForScreenReader } from 'soapbox/utils/status'; import DetailedStatus from './detailed-status'; import ThreadStatus from './thread-status'; +import type { VirtuosoHandle } from 'react-virtuoso'; import type { Account, Status } from 'soapbox/normalizers'; import type { SelectedStatus } from 'soapbox/selectors'; diff --git a/src/features/ui/components/modals/video-modal.tsx b/src/features/ui/components/modals/video-modal.tsx index ded3236a2..3955d6855 100644 --- a/src/features/ui/components/modals/video-modal.tsx +++ b/src/features/ui/components/modals/video-modal.tsx @@ -10,7 +10,6 @@ import type { BaseModalProps } from '../modal-root'; import type { MediaAttachment } from 'pl-api'; import type { Account } from 'soapbox/normalizers'; - type VideoModalProps = { media: MediaAttachment; statusId: string; diff --git a/src/features/ui/index.tsx b/src/features/ui/index.tsx index 79d6aa1f0..f282f9dfb 100644 --- a/src/features/ui/index.tsx +++ b/src/features/ui/index.tsx @@ -18,22 +18,22 @@ import SidebarNavigation from 'soapbox/components/sidebar-navigation'; import ThumbNavigation from 'soapbox/components/thumb-navigation'; import { Layout } from 'soapbox/components/ui'; import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig, useFeatures, useDraggedFiles, useInstance, useLoggedIn } from 'soapbox/hooks'; -import AdminPage from 'soapbox/pages/admin-page'; -import ChatsPage from 'soapbox/pages/chats-page'; -import DefaultPage from 'soapbox/pages/default-page'; -import EmptyPage from 'soapbox/pages/empty-page'; -import EventPage from 'soapbox/pages/event-page'; -import EventsPage from 'soapbox/pages/events-page'; -import ExternalLoginPage from 'soapbox/pages/external-login-page'; -import GroupPage from 'soapbox/pages/group-page'; -import GroupsPage from 'soapbox/pages/groups-page'; -import HomePage from 'soapbox/pages/home-page'; -import LandingPage from 'soapbox/pages/landing-page'; -import ManageGroupsPage from 'soapbox/pages/manage-groups-page'; -import ProfilePage from 'soapbox/pages/profile-page'; -import RemoteInstancePage from 'soapbox/pages/remote-instance-page'; -import SearchPage from 'soapbox/pages/search-page'; -import StatusPage from 'soapbox/pages/status-page'; +import AdminLayout from 'soapbox/layouts/admin-layout'; +import ChatsLayout from 'soapbox/layouts/chats-layout'; +import DefaultLayout from 'soapbox/layouts/default-layout'; +import EmptyLayout from 'soapbox/layouts/empty-layout'; +import EventLayout from 'soapbox/layouts/event-layout'; +import EventsLayout from 'soapbox/layouts/events-layout'; +import ExternalLoginLayout from 'soapbox/layouts/external-login-layout'; +import GroupLayout from 'soapbox/layouts/group-layout'; +import GroupsLayout from 'soapbox/layouts/groups-layout'; +import HomeLayout from 'soapbox/layouts/home-layout'; +import LandingLayout from 'soapbox/layouts/landing-layout'; +import ManageGroupsLayout from 'soapbox/layouts/manage-groups-layout'; +import ProfileLayout from 'soapbox/layouts/profile-layout'; +import RemoteInstanceLayout from 'soapbox/layouts/remote-instance-layout'; +import SearchLayout from 'soapbox/layouts/search-layout'; +import StatusLayout from 'soapbox/layouts/status-layout'; import { getVapidKey } from 'soapbox/utils/auth'; import { isStandalone } from 'soapbox/utils/state'; @@ -160,24 +160,24 @@ const SwitchingColumnsArea: React.FC = ({ children }) => {standalone && } - + {isLoggedIn ? ( - + ) : ( - + )} {/* NOTE: we cannot nest routes in a fragment https://stackoverflow.com/a/68637108 */} - {features.federating && } - {features.federating && } - {features.bubbleTimeline && } - {features.federating && } + {features.federating && } + {features.federating && } + {features.bubbleTimeline && } + {features.federating && } - {features.conversations && } + {features.conversations && } {features.conversations && } {/* Mastodon web routes */} @@ -195,7 +195,7 @@ const SwitchingColumnsArea: React.FC = ({ children }) => - + @@ -226,120 +226,120 @@ const SwitchingColumnsArea: React.FC = ({ children }) => - + - {features.lists && } - {features.lists && } - {features.bookmarks && } - {features.bookmarks && } - + {features.lists && } + {features.lists && } + {features.bookmarks && } + {features.bookmarks && } + - + - - {features.suggestions && } - {features.profileDirectory && } - {features.events && } + + {features.suggestions && } + {features.profileDirectory && } + {features.events && } - {features.chats && } - {features.chats && } - {features.chats && } - {features.chats && } + {features.chats && } + {features.chats && } + {features.chats && } + {features.chats && } - - - {features.federating && } - - {(features.filters || features.filtersV2) && } - {(features.filters || features.filtersV2) && } - {(features.filters || features.filtersV2) && } - {(features.followedHashtagsList) && } - - - - - - - - - - - {features.events && } - {features.events && } + + + {features.federating && } + + {(features.filters || features.filtersV2) && } + {(features.filters || features.filtersV2) && } + {(features.filters || features.filtersV2) && } + {(features.followedHashtagsList) && } + + + + + + + + + + + {features.events && } + {features.events && } - + - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } - {features.groups && } + {features.groups && } + {features.groups && } + {features.groups && } + {features.groups && } + {features.groups && } + {features.groups && } + {features.groups && } + {features.groups && } + {features.groups && } - - - {features.scheduledStatuses && } - + + + {features.scheduledStatuses && } + - + - - {features.exportData && } - {(features.importBlocks || features.importFollows || features.importMutes) && } - {features.manageAccountAliases && } - {features.accountMoving && } - {features.accountBackups && } - - - - - - - + + {features.exportData && } + {(features.importBlocks || features.importFollows || features.importMutes) && } + {features.manageAccountAliases && } + {features.accountMoving && } + {features.accountBackups && } + + + + + + + - - - - - - - - {features.adminAnnouncements && } - {features.domains && } - {features.adminRules && } - + + + + + + + + {features.adminAnnouncements && } + {features.domains && } + {features.adminRules && } + - - - - - - Promise.reject(new TypeError('Failed to fetch dynamically imported module: TEST')))} content={children} /> - + + + + + + Promise.reject(new TypeError('Failed to fetch dynamically imported module: TEST')))} content={children} /> + - {hasCrypto && } - {features.federating && } + {hasCrypto && } + {features.federating && } - + - + {(features.accountCreation && instance.registrations.enabled) && ( - + )} - - - - - + + + + + - + ); }; diff --git a/src/features/ui/util/react-router-helpers.tsx b/src/features/ui/util/react-router-helpers.tsx index 6c5256af7..87720af61 100644 --- a/src/features/ui/util/react-router-helpers.tsx +++ b/src/features/ui/util/react-router-helpers.tsx @@ -9,14 +9,14 @@ import ColumnForbidden from '../components/column-forbidden'; import ColumnLoading from '../components/column-loading'; import ErrorColumn from '../components/error-column'; -type PageProps = { +type LayoutProps = { params?: MatchType['params']; children: React.ReactNode; }; interface IWrappedRoute extends RouteProps { component: React.LazyExoticComponent; - page: React.ComponentType; + layout: React.ComponentType; content?: React.ReactNode; componentParams?: Record; publicRoute?: boolean; @@ -27,7 +27,7 @@ interface IWrappedRoute extends RouteProps { const WrappedRoute: React.FC = ({ component: Component, - page: Page, + layout: Layout, content, componentParams = {}, publicRoute = false, @@ -44,11 +44,11 @@ const WrappedRoute: React.FC = ({ const renderComponent = ({ match }: RouteComponentProps) => ( }> - + {content} - + ); diff --git a/src/pages/admin-page.tsx b/src/layouts/admin-layout.tsx similarity index 62% rename from src/pages/admin-page.tsx rename to src/layouts/admin-layout.tsx index 90daa9ac2..86206f7cf 100644 --- a/src/pages/admin-page.tsx +++ b/src/layouts/admin-layout.tsx @@ -1,17 +1,15 @@ import React from 'react'; import { Layout } from 'soapbox/components/ui'; -import { - LatestAccountsPanel, -} from 'soapbox/features/ui/util/async-components'; +import { LatestAccountsPanel } from 'soapbox/features/ui/util/async-components'; import LinkFooter from '../features/ui/components/link-footer'; -interface IAdminPage { +interface IAdminLayout { children: React.ReactNode; } -const AdminPage: React.FC = ({ children }) => ( +const AdminLayout: React.FC = ({ children }) => ( <> {children} @@ -24,4 +22,4 @@ const AdminPage: React.FC = ({ children }) => ( ); -export { AdminPage as default }; +export { AdminLayout as default }; diff --git a/src/pages/chats-page.tsx b/src/layouts/chats-layout.tsx similarity index 63% rename from src/pages/chats-page.tsx rename to src/layouts/chats-layout.tsx index 753118373..45b057b34 100644 --- a/src/pages/chats-page.tsx +++ b/src/layouts/chats-layout.tsx @@ -1,14 +1,14 @@ import React from 'react'; -interface IChatsPage { +interface IChatsLayout { children: React.ReactNode; } /** Custom layout for chats on desktop. */ -const ChatsPage: React.FC = ({ children }) => ( +const ChatsLayout: React.FC = ({ children }) => (
{children}
); -export { ChatsPage as default }; +export { ChatsLayout as default }; diff --git a/src/pages/default-page.tsx b/src/layouts/default-layout.tsx similarity index 87% rename from src/pages/default-page.tsx rename to src/layouts/default-layout.tsx index 391840d9d..b62a41457 100644 --- a/src/pages/default-page.tsx +++ b/src/layouts/default-layout.tsx @@ -11,11 +11,11 @@ import { useAppSelector, useFeatures } from 'soapbox/hooks'; import { Layout } from '../components/ui'; -interface IDefaultPage { +interface IDefaultLayout { children: React.ReactNode; } -const DefaultPage: React.FC = ({ children }) => { +const DefaultLayout: React.FC = ({ children }) => { const me = useAppSelector(state => state.me); const features = useFeatures(); @@ -45,4 +45,4 @@ const DefaultPage: React.FC = ({ children }) => { ); }; -export { DefaultPage as default }; +export { DefaultLayout as default }; diff --git a/src/pages/empty-page.tsx b/src/layouts/empty-layout.tsx similarity index 61% rename from src/pages/empty-page.tsx rename to src/layouts/empty-layout.tsx index 93f2a8483..948508817 100644 --- a/src/pages/empty-page.tsx +++ b/src/layouts/empty-layout.tsx @@ -2,11 +2,11 @@ import React from 'react'; import { Layout } from '../components/ui'; -interface IEmptyPage { +interface IEmptyLayout { children: React.ReactNode; } -const EmptyPage: React.FC = ({ children }) => ( +const EmptyLayout: React.FC = ({ children }) => ( <> {children} @@ -16,4 +16,4 @@ const EmptyPage: React.FC = ({ children }) => ( ); -export { EmptyPage as default }; +export { EmptyLayout as default }; diff --git a/src/pages/event-page.tsx b/src/layouts/event-layout.tsx similarity index 95% rename from src/pages/event-page.tsx rename to src/layouts/event-layout.tsx index 31dbdd84f..102211db9 100644 --- a/src/pages/event-page.tsx +++ b/src/layouts/event-layout.tsx @@ -17,14 +17,14 @@ import { makeGetStatus } from 'soapbox/selectors'; const getStatus = makeGetStatus(); -interface IEventPage { +interface IEventLayout { params?: { statusId?: string; }; children: React.ReactNode; } -const EventPage: React.FC = ({ params, children }) => { +const EventLayout: React.FC = ({ params, children }) => { const me = useAppSelector(state => state.me); const features = useFeatures(); @@ -96,4 +96,4 @@ const EventPage: React.FC = ({ params, children }) => { ); }; -export { EventPage as default }; +export { EventLayout as default }; diff --git a/src/pages/events-page.tsx b/src/layouts/events-layout.tsx similarity index 81% rename from src/pages/events-page.tsx rename to src/layouts/events-layout.tsx index 710a19913..c1d4fb1fe 100644 --- a/src/pages/events-page.tsx +++ b/src/layouts/events-layout.tsx @@ -9,12 +9,12 @@ import { } from 'soapbox/features/ui/util/async-components'; import { useFeatures } from 'soapbox/hooks'; -interface IEventsPage { +interface IEventsLayout { children: React.ReactNode; } -/** Page to display events list. */ -const EventsPage: React.FC = ({ children }) => { +/** Layout to display events list. */ +const EventsLayout: React.FC = ({ children }) => { const features = useFeatures(); return ( @@ -37,4 +37,4 @@ const EventsPage: React.FC = ({ children }) => { ); }; -export { EventsPage as default }; +export { EventsLayout as default }; diff --git a/src/pages/external-login-page.tsx b/src/layouts/external-login-layout.tsx similarity index 86% rename from src/pages/external-login-page.tsx rename to src/layouts/external-login-layout.tsx index aa4fc748d..aa9a5a49f 100644 --- a/src/pages/external-login-page.tsx +++ b/src/layouts/external-login-layout.tsx @@ -12,11 +12,11 @@ import { isStandalone } from 'soapbox/utils/state'; import { Layout } from '../components/ui'; -interface IExternalLoginPage { +interface IExternalLoginLayout { children: React.ReactNode; } -const ExternalLoginPage: React.FC = ({ children }) => { +const ExternalLoginLayout: React.FC = ({ children }) => { const me = useAppSelector(state => state.me); const features = useFeatures(); const standalone = useAppSelector(isStandalone); @@ -47,4 +47,4 @@ const ExternalLoginPage: React.FC = ({ children }) => { ); }; -export { ExternalLoginPage as default }; +export { ExternalLoginLayout as default }; diff --git a/src/pages/group-page.tsx b/src/layouts/group-layout.tsx similarity index 94% rename from src/pages/group-page.tsx rename to src/layouts/group-layout.tsx index 0068f02e9..a29dd6f69 100644 --- a/src/pages/group-page.tsx +++ b/src/layouts/group-layout.tsx @@ -19,7 +19,7 @@ const messages = defineMessages({ media: { id: 'group.tabs.media', defaultMessage: 'Media' }, }); -interface IGroupPage { +interface IGroupLayout { params?: { groupId?: string; }; @@ -44,8 +44,8 @@ const PrivacyBlankslate = () => ( ); -/** Page to display a group. */ -const GroupPage: React.FC = ({ params, children }) => { +/** Layout to display a group. */ +const GroupLayout: React.FC = ({ params, children }) => { const intl = useIntl(); const match = useRouteMatch(); const { account: me } = useOwnAccount(); @@ -122,4 +122,4 @@ const GroupPage: React.FC = ({ params, children }) => { ); }; -export { GroupPage as default }; +export { GroupLayout as default }; diff --git a/src/pages/groups-page.tsx b/src/layouts/groups-layout.tsx similarity index 77% rename from src/pages/groups-page.tsx rename to src/layouts/groups-layout.tsx index af75027d1..78805cf3f 100644 --- a/src/pages/groups-page.tsx +++ b/src/layouts/groups-layout.tsx @@ -4,12 +4,12 @@ import { Column, Layout } from 'soapbox/components/ui'; import LinkFooter from 'soapbox/features/ui/components/link-footer'; import { MyGroupsPanel, NewGroupPanel } from 'soapbox/features/ui/util/async-components'; -interface IGroupsPage { +interface IGroupsLayout { children: React.ReactNode; } -/** Page to display groups. */ -const GroupsPage: React.FC = ({ children }) => ( +/** Layout to display groups. */ +const GroupsLayout: React.FC = ({ children }) => ( <> @@ -28,4 +28,4 @@ const GroupsPage: React.FC = ({ children }) => ( ); -export { GroupsPage as default }; +export { GroupsLayout as default }; diff --git a/src/pages/home-page.tsx b/src/layouts/home-layout.tsx similarity index 96% rename from src/pages/home-page.tsx rename to src/layouts/home-layout.tsx index b0dc9401f..0f52c378e 100644 --- a/src/pages/home-page.tsx +++ b/src/layouts/home-layout.tsx @@ -21,11 +21,11 @@ import { useIsMobile } from 'soapbox/hooks/useIsMobile'; import { Avatar, Card, CardBody, HStack, Layout } from '../components/ui'; import ComposeForm from '../features/compose/components/compose-form'; -interface IHomePage { +interface IHomeLayout { children: React.ReactNode; } -const HomePage: React.FC = ({ children }) => { +const HomeLayout: React.FC = ({ children }) => { const intl = useIntl(); const dispatch = useAppDispatch(); @@ -114,4 +114,4 @@ const HomePage: React.FC = ({ children }) => { ); }; -export { HomePage as default }; +export { HomeLayout as default }; diff --git a/src/pages/landing-page.tsx b/src/layouts/landing-layout.tsx similarity index 86% rename from src/pages/landing-page.tsx rename to src/layouts/landing-layout.tsx index cd5994aa6..b88cf8448 100644 --- a/src/pages/landing-page.tsx +++ b/src/layouts/landing-layout.tsx @@ -10,11 +10,11 @@ import { useAppSelector, useFeatures } from 'soapbox/hooks'; import { Layout } from '../components/ui'; -interface ILandingPage { +interface ILandingLayout { children: React.ReactNode; } -const LandingPage: React.FC = ({ children }) => { +const LandingLayout: React.FC = ({ children }) => { const me = useAppSelector(state => state.me); const features = useFeatures(); @@ -41,4 +41,4 @@ const LandingPage: React.FC = ({ children }) => { ); }; -export { LandingPage as default }; +export { LandingLayout as default }; diff --git a/src/pages/manage-groups-page.tsx b/src/layouts/manage-groups-layout.tsx similarity index 72% rename from src/pages/manage-groups-page.tsx rename to src/layouts/manage-groups-layout.tsx index 1c95514bb..bb159011b 100644 --- a/src/pages/manage-groups-page.tsx +++ b/src/layouts/manage-groups-layout.tsx @@ -4,12 +4,12 @@ import { Layout } from 'soapbox/components/ui'; import LinkFooter from 'soapbox/features/ui/components/link-footer'; import { MyGroupsPanel, NewGroupPanel } from 'soapbox/features/ui/util/async-components'; -interface IGroupsPage { +interface IGroupsLayout { children: React.ReactNode; } -/** Page to display groups. */ -const ManageGroupsPage: React.FC = ({ children }) => ( +/** Layout to display groups. */ +const ManageGroupsLayout: React.FC = ({ children }) => ( <> {children} @@ -23,4 +23,4 @@ const ManageGroupsPage: React.FC = ({ children }) => ( ); -export { ManageGroupsPage as default }; +export { ManageGroupsLayout as default }; diff --git a/src/pages/profile-page.tsx b/src/layouts/profile-layout.tsx similarity index 95% rename from src/pages/profile-page.tsx rename to src/layouts/profile-layout.tsx index 4a2ee2b20..62360d625 100644 --- a/src/pages/profile-page.tsx +++ b/src/layouts/profile-layout.tsx @@ -19,15 +19,15 @@ import { import { useAppSelector, useFeatures, useSoapboxConfig } from 'soapbox/hooks'; import { getAcct } from 'soapbox/utils/accounts'; -interface IProfilePage { +interface IProfileLayout { params?: { username?: string; }; children: React.ReactNode; } -/** Page to display a user's profile. */ -const ProfilePage: React.FC = ({ params, children }) => { +/** Layout to display a user's profile. */ +const ProfileLayout: React.FC = ({ params, children }) => { const history = useHistory(); const username = params?.username || ''; @@ -129,4 +129,4 @@ const ProfilePage: React.FC = ({ params, children }) => { ); }; -export { ProfilePage as default }; +export { ProfileLayout as default }; diff --git a/src/pages/remote-instance-page.tsx b/src/layouts/remote-instance-layout.tsx similarity index 81% rename from src/pages/remote-instance-page.tsx rename to src/layouts/remote-instance-layout.tsx index 1d19af148..83ccea88e 100644 --- a/src/pages/remote-instance-page.tsx +++ b/src/layouts/remote-instance-layout.tsx @@ -11,15 +11,15 @@ import { federationRestrictionsDisclosed } from 'soapbox/utils/state'; import { Layout } from '../components/ui'; -interface IRemoteInstancePage { +interface IRemoteInstanceLayout { params?: { instance?: string; }; children: React.ReactNode; } -/** Page for viewing a remote instance timeline. */ -const RemoteInstancePage: React.FC = ({ children, params }) => { +/** Layout for viewing a remote instance timeline. */ +const RemoteInstanceLayout: React.FC = ({ children, params }) => { const host = params!.instance!; const { account } = useOwnAccount(); @@ -43,4 +43,4 @@ const RemoteInstancePage: React.FC = ({ children, params }) ); }; -export { RemoteInstancePage as default }; +export { RemoteInstanceLayout as default }; diff --git a/src/pages/search-page.tsx b/src/layouts/search-layout.tsx similarity index 87% rename from src/pages/search-page.tsx rename to src/layouts/search-layout.tsx index 362f3656c..2d1851702 100644 --- a/src/pages/search-page.tsx +++ b/src/layouts/search-layout.tsx @@ -11,11 +11,11 @@ import { useAppSelector, useFeatures } from 'soapbox/hooks'; import { Layout } from '../components/ui'; -interface ISearchPage { +interface ISearchLayout { children: React.ReactNode; } -const SearchPage: React.FC = ({ children }) => { +const SearchLayout: React.FC = ({ children }) => { const me = useAppSelector(state => state.me); const features = useFeatures(); @@ -48,4 +48,4 @@ const SearchPage: React.FC = ({ children }) => { ); }; -export { SearchPage as default }; +export { SearchLayout as default }; diff --git a/src/pages/status-page.tsx b/src/layouts/status-layout.tsx similarity index 87% rename from src/pages/status-page.tsx rename to src/layouts/status-layout.tsx index 5464050c8..1c38ffde5 100644 --- a/src/pages/status-page.tsx +++ b/src/layouts/status-layout.tsx @@ -11,11 +11,11 @@ import { useAppSelector, useFeatures } from 'soapbox/hooks'; import { Layout } from '../components/ui'; -interface IStatusPage { +interface IStatusLayout { children: React.ReactNode; } -const StatusPage: React.FC = ({ children }) => { +const StatusLayout: React.FC = ({ children }) => { const me = useAppSelector(state => state.me); const features = useFeatures(); @@ -45,4 +45,4 @@ const StatusPage: React.FC = ({ children }) => { ); }; -export { StatusPage as default }; +export { StatusLayout as default }; diff --git a/src/normalizers/chat-message.ts b/src/normalizers/chat-message.ts index c4ed781d6..7299b5094 100644 --- a/src/normalizers/chat-message.ts +++ b/src/normalizers/chat-message.ts @@ -1,4 +1,4 @@ -import { type ChatMessage as BaseChatMessage } from 'pl-api'; +import type { ChatMessage as BaseChatMessage } from 'pl-api'; const normalizeChatMessage = (chatMessage: BaseChatMessage & { pending?: boolean; deleting?: boolean }) => ({ type: 'message' as const, diff --git a/src/reducers/backups.ts b/src/reducers/backups.ts index 6c404ebd9..4279b9ff7 100644 --- a/src/reducers/backups.ts +++ b/src/reducers/backups.ts @@ -1,9 +1,6 @@ import { Map as ImmutableMap } from 'immutable'; -import { - BACKUPS_FETCH_SUCCESS, - BACKUPS_CREATE_SUCCESS, -} from '../actions/backups'; +import { BACKUPS_FETCH_SUCCESS, BACKUPS_CREATE_SUCCESS } from '../actions/backups'; import type { Backup } from 'pl-api'; import type { AnyAction } from 'redux'; diff --git a/src/reducers/contexts.ts b/src/reducers/contexts.ts index 5c01f7b3b..119d50bfd 100644 --- a/src/reducers/contexts.ts +++ b/src/reducers/contexts.ts @@ -6,10 +6,7 @@ import { import { STATUS_IMPORT, STATUSES_IMPORT } from 'soapbox/actions/importer'; -import { - ACCOUNT_BLOCK_SUCCESS, - ACCOUNT_MUTE_SUCCESS, -} from '../actions/accounts'; +import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from '../actions/accounts'; import { CONTEXT_FETCH_SUCCESS, STATUS_CREATE_REQUEST, diff --git a/src/reducers/dropdown-menu.ts b/src/reducers/dropdown-menu.ts index 47d1d5b28..17e80318c 100644 --- a/src/reducers/dropdown-menu.ts +++ b/src/reducers/dropdown-menu.ts @@ -1,9 +1,6 @@ import { Record as ImmutableRecord } from 'immutable'; -import { - DROPDOWN_MENU_OPEN, - DROPDOWN_MENU_CLOSE, -} from '../actions/dropdown-menu'; +import { DROPDOWN_MENU_OPEN, DROPDOWN_MENU_CLOSE } from '../actions/dropdown-menu'; import type { AnyAction } from 'redux'; diff --git a/src/reducers/relationships.ts b/src/reducers/relationships.ts index f5f6812a6..4409729e3 100644 --- a/src/reducers/relationships.ts +++ b/src/reducers/relationships.ts @@ -13,14 +13,8 @@ import { ACCOUNT_REMOVE_FROM_FOLLOWERS_SUCCESS, RELATIONSHIPS_FETCH_SUCCESS, } from '../actions/accounts'; -import { - DOMAIN_BLOCK_SUCCESS, - DOMAIN_UNBLOCK_SUCCESS, -} from '../actions/domain-blocks'; -import { - ACCOUNT_IMPORT, - ACCOUNTS_IMPORT, -} from '../actions/importer'; +import { DOMAIN_BLOCK_SUCCESS, DOMAIN_UNBLOCK_SUCCESS } from '../actions/domain-blocks'; +import { ACCOUNT_IMPORT, ACCOUNTS_IMPORT } from '../actions/importer'; import type { AnyAction } from 'redux'; import type { APIEntity } from 'soapbox/types/entities'; diff --git a/src/reducers/security.ts b/src/reducers/security.ts index e8127dd90..ef5a691f1 100644 --- a/src/reducers/security.ts +++ b/src/reducers/security.ts @@ -5,10 +5,7 @@ import { MFA_CONFIRM_SUCCESS, MFA_DISABLE_SUCCESS, } from '../actions/mfa'; -import { - FETCH_TOKENS_SUCCESS, - REVOKE_TOKEN_SUCCESS, -} from '../actions/security'; +import { FETCH_TOKENS_SUCCESS, REVOKE_TOKEN_SUCCESS } from '../actions/security'; import type { OauthToken } from 'pl-api'; import type { AnyAction } from 'redux'; diff --git a/src/reducers/status-lists.ts b/src/reducers/status-lists.ts index 233451dff..84e3bdca5 100644 --- a/src/reducers/status-lists.ts +++ b/src/reducers/status-lists.ts @@ -54,10 +54,7 @@ import { UNPIN_SUCCESS, type InteractionsAction, } from '../actions/interactions'; -import { - PINNED_STATUSES_FETCH_SUCCESS, - type PinStatusesAction, -} from '../actions/pin-statuses'; +import { PINNED_STATUSES_FETCH_SUCCESS, type PinStatusesAction } from '../actions/pin-statuses'; import { SCHEDULED_STATUSES_FETCH_REQUEST, SCHEDULED_STATUSES_FETCH_SUCCESS, diff --git a/src/reducers/statuses.ts b/src/reducers/statuses.ts index 5234ca2f3..b10e02d0c 100644 --- a/src/reducers/statuses.ts +++ b/src/reducers/statuses.ts @@ -4,10 +4,7 @@ import omit from 'lodash/omit'; import { normalizeStatus, normalizeTranslation, Status as StatusRecord } from 'soapbox/normalizers'; import { simulateEmojiReact, simulateUnEmojiReact } from 'soapbox/utils/emoji-reacts'; -import { - EMOJI_REACT_REQUEST, - UNEMOJI_REACT_REQUEST, -} from '../actions/emoji-reacts'; +import { EMOJI_REACT_REQUEST, UNEMOJI_REACT_REQUEST } from '../actions/emoji-reacts'; import { EVENT_JOIN_REQUEST, EVENT_JOIN_FAIL, diff --git a/src/reducers/timelines.ts b/src/reducers/timelines.ts index 3fe91faba..50f6fd823 100644 --- a/src/reducers/timelines.ts +++ b/src/reducers/timelines.ts @@ -6,15 +6,9 @@ import { } from 'immutable'; import sample from 'lodash/sample'; -import { - ACCOUNT_BLOCK_SUCCESS, - ACCOUNT_MUTE_SUCCESS, -} from '../actions/accounts'; +import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from '../actions/accounts'; import { PIN_SUCCESS, UNPIN_SUCCESS } from '../actions/interactions'; -import { - STATUS_CREATE_REQUEST, - STATUS_CREATE_SUCCESS, -} from '../actions/statuses'; +import { STATUS_CREATE_REQUEST, STATUS_CREATE_SUCCESS } from '../actions/statuses'; import { TIMELINE_UPDATE, TIMELINE_DELETE, diff --git a/src/reducers/trending-statuses.ts b/src/reducers/trending-statuses.ts index 12ec0b7a6..59a55a81b 100644 --- a/src/reducers/trending-statuses.ts +++ b/src/reducers/trending-statuses.ts @@ -1,9 +1,6 @@ import { OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord } from 'immutable'; -import { - TRENDING_STATUSES_FETCH_REQUEST, - TRENDING_STATUSES_FETCH_SUCCESS, -} from 'soapbox/actions/trending-statuses'; +import { TRENDING_STATUSES_FETCH_REQUEST, TRENDING_STATUSES_FETCH_SUCCESS } from 'soapbox/actions/trending-statuses'; import type { Status } from 'pl-api'; import type { AnyAction } from 'redux'; diff --git a/src/reducers/user-lists.ts b/src/reducers/user-lists.ts index 5885a336f..a278d3a78 100644 --- a/src/reducers/user-lists.ts +++ b/src/reducers/user-lists.ts @@ -34,9 +34,7 @@ import { EVENT_PARTICIPATION_REQUEST_AUTHORIZE_SUCCESS, EVENT_PARTICIPATION_REQUEST_REJECT_SUCCESS, } from 'soapbox/actions/events'; -import { - FAMILIAR_FOLLOWERS_FETCH_SUCCESS, -} from 'soapbox/actions/familiar-followers'; +import { FAMILIAR_FOLLOWERS_FETCH_SUCCESS } from 'soapbox/actions/familiar-followers'; import { GROUP_BLOCKS_FETCH_REQUEST, GROUP_BLOCKS_FETCH_SUCCESS, @@ -51,9 +49,7 @@ import { DISLIKES_FETCH_SUCCESS, REACTIONS_FETCH_SUCCESS, } from 'soapbox/actions/interactions'; -import { - NOTIFICATIONS_UPDATE, -} from 'soapbox/actions/notifications'; +import { NOTIFICATIONS_UPDATE } from 'soapbox/actions/notifications'; import type { Account, Notification, PaginatedResponse } from 'pl-api'; import type { APIEntity } from 'soapbox/types/entities'; diff --git a/src/selectors/index.ts b/src/selectors/index.ts index 6b6956adc..df7eb5f49 100644 --- a/src/selectors/index.ts +++ b/src/selectors/index.ts @@ -9,7 +9,6 @@ import { createSelector } from 'reselect'; import { getLocale, getSettings } from 'soapbox/actions/settings'; import { Entities } from 'soapbox/entity-store/entities'; -import { type MRFSimple } from 'soapbox/schemas/pleroma'; import { getDomain } from 'soapbox/utils/accounts'; import { validId } from 'soapbox/utils/auth'; import ConfigDB from 'soapbox/utils/config-db'; @@ -20,6 +19,7 @@ import type { EntityStore } from 'soapbox/entity-store/types'; import type { Account, Group, Notification } from 'soapbox/normalizers'; import type { MinifiedNotification } from 'soapbox/reducers/notifications'; import type { MinifiedStatus } from 'soapbox/reducers/statuses'; +import type { MRFSimple } from 'soapbox/schemas/pleroma'; import type { RootState } from 'soapbox/store'; const normalizeId = (id: any): string => typeof id === 'string' ? id : typeof id === 'object' ? normalizeId(id.id) : ''; diff --git a/src/types/entities.ts b/src/types/entities.ts index 259710b40..8bd7704d9 100644 --- a/src/types/entities.ts +++ b/src/types/entities.ts @@ -1,7 +1,4 @@ -import { - AdminAccountRecord, - AdminReportRecord, -} from 'soapbox/normalizers'; +import { AdminAccountRecord, AdminReportRecord } from 'soapbox/normalizers'; type AdminAccount = ReturnType; type AdminReport = ReturnType; diff --git a/src/utils/emoji-reacts.test.ts b/src/utils/emoji-reacts.test.ts index af2c5c34b..5bd0253e1 100644 --- a/src/utils/emoji-reacts.test.ts +++ b/src/utils/emoji-reacts.test.ts @@ -24,23 +24,23 @@ const ALLOWED_EMOJI = ImmutableList([ describe('sortEmoji', () => { describe('with an unsorted list of emoji', () => { const emojiReacts = ImmutableList([ - { 'count': 7, 'me': true, 'name': '😃' }, - { 'count': 7, 'me': true, 'name': '😯' }, - { 'count': 3, 'me': true, 'name': '😢' }, - { 'count': 1, 'me': true, 'name': '😡' }, + { 'count': 7, 'me': true, 'name': '😃' }, + { 'count': 7, 'me': true, 'name': '😯' }, + { 'count': 3, 'me': true, 'name': '😢' }, + { 'count': 1, 'me': true, 'name': '😡' }, { 'count': 20, 'me': true, 'name': '👍' }, - { 'count': 7, 'me': true, 'name': '😂' }, + { 'count': 7, 'me': true, 'name': '😂' }, { 'count': 15, 'me': true, 'name': '❤' }, ].map((react) => emojiReactionSchema.parse(react))); it('sorts the emoji by count', () => { expect(sortEmoji(emojiReacts, ALLOWED_EMOJI)).toEqual(fromJS([ { 'count': 20, 'me': true, 'name': '👍' }, { 'count': 15, 'me': true, 'name': '❤' }, - { 'count': 7, 'me': true, 'name': '😯' }, - { 'count': 7, 'me': true, 'name': '😂' }, - { 'count': 7, 'me': true, 'name': '😃' }, - { 'count': 3, 'me': true, 'name': '😢' }, - { 'count': 1, 'me': true, 'name': '😡' }, + { 'count': 7, 'me': true, 'name': '😯' }, + { 'count': 7, 'me': true, 'name': '😂' }, + { 'count': 7, 'me': true, 'name': '😃' }, + { 'count': 3, 'me': true, 'name': '😢' }, + { 'count': 1, 'me': true, 'name': '😡' }, ])); }); }); @@ -54,13 +54,13 @@ describe('mergeEmojiFavourites', () => { const emojiReacts = ImmutableList([ { 'count': 20, 'me': false, 'name': '👍', 'url': undefined }, { 'count': 15, 'me': false, 'name': '❤', 'url': undefined }, - { 'count': 7, 'me': false, 'name': '😯', 'url': undefined }, + { 'count': 7, 'me': false, 'name': '😯', 'url': undefined }, ].map((react) => emojiReactionSchema.parse(react))); it('combines 👍 reacts with favourites', () => { expect(mergeEmojiFavourites(emojiReacts, favouritesCount, favourited)).toEqual(fromJS([ - { 'count': 32, 'me': true, 'name': '👍', 'url': undefined }, + { 'count': 32, 'me': true, 'name': '👍', 'url': undefined }, { 'count': 15, 'me': false, 'name': '❤', 'url': undefined }, - { 'count': 7, 'me': false, 'name': '😯', 'url': undefined }, + { 'count': 7, 'me': false, 'name': '😯', 'url': undefined }, ])); }); }); @@ -68,19 +68,19 @@ describe('mergeEmojiFavourites', () => { describe('without existing 👍 reacts', () => { const emojiReacts = ImmutableList([ { 'count': 15, 'me': false, 'name': '❤' }, - { 'count': 7, 'me': false, 'name': '😯' }, + { 'count': 7, 'me': false, 'name': '😯' }, ].map((react) => emojiReactionSchema.parse(react))); it('adds 👍 reacts to the map equaling favourite count', () => { expect(mergeEmojiFavourites(emojiReacts, favouritesCount, favourited)).toEqual(fromJS([ { 'count': 15, 'me': false, 'name': '❤' }, - { 'count': 7, 'me': false, 'name': '😯' }, - { 'count': 12, 'me': true, 'name': '👍' }, + { 'count': 7, 'me': false, 'name': '😯' }, + { 'count': 12, 'me': true, 'name': '👍' }, ])); }); it('does not add 👍 reacts when there are no favourites', () => { expect(mergeEmojiFavourites(emojiReacts, 0, false)).toEqual(fromJS([ - { 'count': 15, 'me': false, 'name': '❤' }, - { 'count': 7, 'me': false, 'name': '😯' }, + { 'count': 15, 'me': false, 'name': '❤' }, + { 'count': 7, 'me': false, 'name': '😯' }, ])); }); }); @@ -89,29 +89,29 @@ describe('mergeEmojiFavourites', () => { describe('reduceEmoji', () => { describe('with a clusterfuck of emoji', () => { const emojiReacts = ImmutableList([ - { 'count': 1, 'me': false, 'name': '😡' }, - { 'count': 1, 'me': true, 'name': '🔪' }, - { 'count': 7, 'me': true, 'name': '😯' }, - { 'count': 3, 'me': false, 'name': '😢' }, - { 'count': 1, 'me': true, 'name': '🌵' }, - { 'count': 20, 'me': true, 'name': '👍' }, - { 'count': 7, 'me': false, 'name': '😂' }, - { 'count': 15, 'me': true, 'name': '❤' }, - { 'count': 1, 'me': false, 'name': '👀' }, - { 'count': 1, 'me': false, 'name': '🍩' }, + { 'count': 1, 'me': false, 'name': '😡' }, + { 'count': 1, 'me': true, 'name': '🔪' }, + { 'count': 7, 'me': true, 'name': '😯' }, + { 'count': 3, 'me': false, 'name': '😢' }, + { 'count': 1, 'me': true, 'name': '🌵' }, + { 'count': 20, 'me': true, 'name': '👍' }, + { 'count': 7, 'me': false, 'name': '😂' }, + { 'count': 15, 'me': true, 'name': '❤' }, + { 'count': 1, 'me': false, 'name': '👀' }, + { 'count': 1, 'me': false, 'name': '🍩' }, ].map((react) => emojiReactionSchema.parse(react))); it('sorts, filters, and combines emoji and favourites', () => { expect(reduceEmoji(emojiReacts, 7, true, ALLOWED_EMOJI)).toEqual(fromJS([ - { 'count': 27, 'me': true, 'name': '👍' }, - { 'count': 15, 'me': true, 'name': '❤' }, - { 'count': 7, 'me': true, 'name': '😯' }, - { 'count': 7, 'me': false, 'name': '😂' }, - { 'count': 3, 'me': false, 'name': '😢' }, - { 'count': 1, 'me': false, 'name': '😡' }, - { 'count': 1, 'me': true, 'name': '🔪' }, - { 'count': 1, 'me': true, 'name': '🌵' }, - { 'count': 1, 'me': false, 'name': '👀' }, - { 'count': 1, 'me': false, 'name': '🍩' }, + { 'count': 27, 'me': true, 'name': '👍' }, + { 'count': 15, 'me': true, 'name': '❤' }, + { 'count': 7, 'me': true, 'name': '😯' }, + { 'count': 7, 'me': false, 'name': '😂' }, + { 'count': 3, 'me': false, 'name': '😢' }, + { 'count': 1, 'me': false, 'name': '😡' }, + { 'count': 1, 'me': true, 'name': '🔪' }, + { 'count': 1, 'me': true, 'name': '🌵' }, + { 'count': 1, 'me': false, 'name': '👀' }, + { 'count': 1, 'me': false, 'name': '🍩' }, ])); }); }); @@ -124,9 +124,9 @@ describe('getReactForStatus', () => { pleroma: { emoji_reactions: [ { 'count': 20, 'me': false, 'name': '👍' }, - { 'count': 15, 'me': true, 'name': '❤' }, - { 'count': 7, 'me': true, 'name': '😯' }, - { 'count': 7, 'me': false, 'name': '😂' }, + { 'count': 15, 'me': true, 'name': '❤' }, + { 'count': 7, 'me': true, 'name': '😯' }, + { 'count': 7, 'me': false, 'name': '😂' }, ], }, })); @@ -145,10 +145,10 @@ describe('getReactForStatus', () => { it('returns undefined when a status has no valid reacts (or favourites)', () => { const status = normalizeStatus(fromJS([ - { 'count': 1, 'me': true, 'name': '🔪' }, - { 'count': 1, 'me': true, 'name': '🌵' }, - { 'count': 1, 'me': false, 'name': '👀' }, - { 'count': 1, 'me': false, 'name': '🍩' }, + { 'count': 1, 'me': true, 'name': '🔪' }, + { 'count': 1, 'me': true, 'name': '🌵' }, + { 'count': 1, 'me': false, 'name': '👀' }, + { 'count': 1, 'me': false, 'name': '🍩' }, ])); expect(getReactForStatus(status)).toEqual(undefined); }); @@ -162,7 +162,7 @@ describe('simulateEmojiReact', () => { ].map((react) => emojiReactionSchema.parse(react))); expect(simulateEmojiReact(emojiReacts, '❤')).toEqual(fromJS([ { 'count': 2, 'me': false, 'name': '👍', 'url': undefined }, - { 'count': 3, 'me': true, 'name': '❤', 'url': undefined }, + { 'count': 3, 'me': true, 'name': '❤', 'url': undefined }, ])); }); @@ -174,7 +174,7 @@ describe('simulateEmojiReact', () => { expect(simulateEmojiReact(emojiReacts, '😯')).toEqual(fromJS([ { 'count': 2, 'me': false, 'name': '👍', 'url': undefined }, { 'count': 2, 'me': false, 'name': '❤', 'url': undefined }, - { 'count': 1, 'me': true, 'name': '😯', 'url': undefined }, + { 'count': 1, 'me': true, 'name': '😯', 'url': undefined }, ])); }); @@ -185,8 +185,8 @@ describe('simulateEmojiReact', () => { ].map((react) => emojiReactionSchema.parse(react))); expect(simulateEmojiReact(emojiReacts, 'soapbox', 'https://gleasonator.com/emoji/Gleasonator/soapbox.png')).toEqual(fromJS([ { 'count': 2, 'me': false, 'name': '👍', 'url': undefined }, - { 'count': 2, 'me': false, 'name': '❤', 'url': undefined }, - { 'count': 1, 'me': true, 'name': 'soapbox', 'url': 'https://gleasonator.com/emoji/Gleasonator/soapbox.png' }, + { 'count': 2, 'me': false, 'name': '❤', 'url': undefined }, + { 'count': 1, 'me': true, 'name': 'soapbox', 'url': 'https://gleasonator.com/emoji/Gleasonator/soapbox.png' }, ])); }); }); @@ -199,7 +199,7 @@ describe('simulateUnEmojiReact', () => { ].map((react) => emojiReactionSchema.parse(react))); expect(simulateUnEmojiReact(emojiReacts, '❤')).toEqual(fromJS([ { 'count': 2, 'me': false, 'name': '👍' }, - { 'count': 2, 'me': false, 'name': '❤' }, + { 'count': 2, 'me': false, 'name': '❤' }, ])); }); @@ -207,7 +207,7 @@ describe('simulateUnEmojiReact', () => { const emojiReacts = ImmutableList([ { 'count': 2, 'me': false, 'name': '👍' }, { 'count': 2, 'me': false, 'name': '❤' }, - { 'count': 1, 'me': true, 'name': '😯' }, + { 'count': 1, 'me': true, 'name': '😯' }, ].map((react) => emojiReactionSchema.parse(react))); expect(simulateUnEmojiReact(emojiReacts, '😯')).toEqual(fromJS([ { 'count': 2, 'me': false, 'name': '👍' }, @@ -219,7 +219,7 @@ describe('simulateUnEmojiReact', () => { const emojiReacts = ImmutableList([ { 'count': 2, 'me': false, 'name': '👍' }, { 'count': 2, 'me': false, 'name': '❤' }, - { 'count': 1, 'me': true, 'name': 'soapbox', 'url': 'https://gleasonator.com/emoji/Gleasonator/soapbox.png' }, + { 'count': 1, 'me': true, 'name': 'soapbox', 'url': 'https://gleasonator.com/emoji/Gleasonator/soapbox.png' }, ].map((react) => emojiReactionSchema.parse(react))); expect(simulateUnEmojiReact(emojiReacts, 'soapbox')).toEqual(fromJS([ { 'count': 2, 'me': false, 'name': '👍' }, diff --git a/src/utils/favicon-service.ts b/src/utils/favicon-service.ts index a8ba1de38..7a1ad6538 100644 --- a/src/utils/favicon-service.ts +++ b/src/utils/favicon-service.ts @@ -96,4 +96,4 @@ const createFaviconService = () => { const FaviconService = createFaviconService(); -export { FaviconService as default }; +export { checkCanvasExtractPermission, FaviconService as default }; diff --git a/src/utils/notification.ts b/src/utils/notification.ts index ed05e908b..cf7a74cf3 100644 --- a/src/utils/notification.ts +++ b/src/utils/notification.ts @@ -1,4 +1,4 @@ -import { type Notification } from 'pl-api'; +import type { Notification } from 'pl-api'; /** Notification types known to Soapbox. */ const NOTIFICATION_TYPES = [ diff --git a/src/utils/resize-image.ts b/src/utils/resize-image.ts index f23486ea5..a13a8f51c 100644 --- a/src/utils/resize-image.ts +++ b/src/utils/resize-image.ts @@ -1,3 +1,5 @@ +import { checkCanvasExtractPermission } from './favicon-service'; + /* eslint-disable no-case-declarations */ const DEFAULT_MAX_PIXELS = 1920 * 1080; @@ -45,47 +47,6 @@ const dropOrientationIfNeeded = (orientation: number) => new Promise(res } }); -// /** -// *Some browsers don't allow reading from a canvas and instead return all-white -// * or randomized data. Use a pre-defined image to check if reading the canvas -// * works. -// */ -// const checkCanvasReliability = () => new Promise((resolve, reject) => { -// switch(_browser_quirks['canvas-read-unreliable']) { -// case true: -// reject('Canvas reading unreliable'); -// break; -// case false: -// resolve(); -// break; -// default: -// // 2×2 GIF with white, red, green and blue pixels -// const testImageURL = -// 'data:image/gif;base64,R0lGODdhAgACAKEDAAAA//8AAAD/AP///ywAAAAAAgACAAACA1wEBQA7'; -// const refData = -// [255, 255, 255, 255, 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255]; -// const img = new Image(); -// img.onload = () => { -// const canvas = document.createElement('canvas'); -// const context = canvas.getContext('2d'); -// context?.drawImage(img, 0, 0, 2, 2); -// const imageData = context?.getImageData(0, 0, 2, 2); -// if (imageData?.data.every((x, i) => refData[i] === x)) { -// _browser_quirks['canvas-read-unreliable'] = false; -// resolve(); -// } else { -// _browser_quirks['canvas-read-unreliable'] = true; -// reject('Canvas reading unreliable'); -// } -// }; -// img.onerror = () => { -// _browser_quirks['canvas-read-unreliable'] = true; -// reject('Failed to load test image'); -// }; -// img.src = testImageURL; -// } -// }); - /** Convert the file into a local blob URL. */ const getImageUrl = (inputFile: File) => new Promise((resolve, reject) => { // @ts-ignore: This is a browser capabilities check. @@ -203,9 +164,8 @@ const resizeImage = ( const newWidth = Math.round(Math.sqrt(maxPixels * (width / height))); const newHeight = Math.round(Math.sqrt(maxPixels * (height / width))); - // Skip canvas reliability check for now (it's unreliable) - // checkCanvasReliability() - // .then(getOrientation(img, type)) + if (!checkCanvasExtractPermission()) return reject(); + getOrientation(img, type) .then(orientation => processImage(img, { width: newWidth, diff --git a/tailwind.config.ts b/tailwind.config.ts index b435cef0d..3362e49d8 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,11 +1,12 @@ import aspectRatioPlugin from '@tailwindcss/aspect-ratio'; import formsPlugin from '@tailwindcss/forms'; import typographyPlugin from '@tailwindcss/typography'; -import { type Config } from 'tailwindcss'; import plugin from 'tailwindcss/plugin'; import { parseColorMatrix } from './tailwind/colors'; +import type { Config } from 'tailwindcss'; + const blackVariantPlugin = plugin(({ addVariant }) => addVariant('black', '.black &')); const reducedMotionPlugin = plugin(({ addVariant }) => addVariant('no-reduce-motion', '.no-reduce-motion &')); diff --git a/tailwind/colors.ts b/tailwind/colors.ts index 5709a5dbc..fd88d9a6c 100644 --- a/tailwind/colors.ts +++ b/tailwind/colors.ts @@ -1,4 +1,4 @@ -import { type RecursiveKeyValuePair } from 'tailwindcss/types/config'; +import type{ RecursiveKeyValuePair } from 'tailwindcss/types/config'; /** https://tailwindcss.com/docs/customizing-colors#using-css-variables */ const withOpacityValue = (variable: string): string => `rgb(var(${variable}) / )`;