diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index af05d2949..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,164 +0,0 @@ -# Changelog - -## Unreleased - -Changes made since the project forked from Soapbox in April 2024. - -### Major changes - -- Switched to a separate library [`pl-api`](https://codeberg.org/nicolium/nicolium/src/branch/develop/packages/pl-api) for Mastodon API integration. It is mostly written from scratch, inheriting minor code parts from Soapbox/Mastodon. This also comes with improved compatibility with various Mastodon API extensions and abstracts out the implementation details. - -### Added - -- Cat ears - -**Behavior:** - -- Notifications of the same type and reposts of the same post are grouped client-side. -- Date is displayed for notifications that are not about new posts. -- Replies to your posts are displayed differently to other mentions in notification list. -- Hashtags from the last line of a post are displayed in a separate component. Adapted [from Mastodon](https://github.com/mastodon/mastodon/pull/26499). -- Native grouped notifications are used on Mastodon. -- Likes, reposts and reactions lists are displayed on long press of respective buttons. -- User local time is displayed on profile and in account hover card, if specified in profile fields. -- Poll results can be displayed before voting. - -**Settings:** - -- You can add image description to your avatar/backend, if supported by backend. -- GoToSocial users can manage post interaction policies. -- Users can set interface theme color. -- Users can adjust interface size. -- Users can use system font for emoji rendering. - -**Composing posts:** - -- WYSIWYG text formatting, available if Markdown is supported. -- When writing posts, links to statuses are added as quotes, when supported by backend. -- You can select post language manually, when composing. -- You can write posts with multiple language versions, when supported by backend. -- Language detection is done client-side for composed posts, utilizing `fasttext.wasm.js`. -- Draft posts. They are stored locally only and work with any backend. -- New visibility scopes are supported – local-only and list-only for Pleroma. Local-only is a separate switch on GoToSocial. -- On backends that support explicit mentioning, you can choose to include mentions in your replies body. -- GoToSocial users can set per-post interaction policies. -- When adding a URL with tracking parameters, a suggestion to remove them from the URL is displayed. -- On supported backends, you can see post preview before posting. -- When entering a long, all-lowercase hashtag, a suggestion about hashtag accessibility is displayed. - -**Dashboard:** - -- Dashboard main page displays metrics included in Mastodon admin dashboard, if supported by backend. - -**Features:** - -- The most recent scrobble is displayed on user profile/card. -- Users can generate _interaction circles_ for their profiles. -- You can bite users, if supported by backend. -- You can browse Bubble timeline, if supported by backend. -- Mastodon displays trending articles on Search page. -- Posts can be addressed to lists of users, on Pleroma. -- Support for events with external registration. -- Added a dedicated wrench reaction button. -- Interaction requests are supported. You can review pending requests and you get informed if your backend doesn't let you reply to a post. Supported on GoToSocial. -- Events with external sign up are supported. -- Application name used to post a status is displayed. -- Outgoing follow requests are displayed, if supported by backend. -- It is possible to remove tracking parameters from URLs in displayed posts. -- Displayed media now have a button for alternative text preview. -- Links in displayed posts can be configured to always display target domain, even when it's not a part of their content. -- Users can configure redirects from popular websites to proxy services like Nitter and Piped in displayed posts. -- It is possible to boost a post with specific visibility, if supported by backend. -- Pleroma shoutbox is displayed on chats page. -- Displaying user-provided media can be disabled, media descriptions will be displayed instead. -- MFM can be displayed on compatible backends. -- Lists can be set as exclusive and replies policy can be set up, if supported by backend. -- Threads can be displayed in a linear view, similarly to traditional Pleroma-FE, as an alternative to tree view. You can expand spoilers with one click. - -### Changed - -**Behavior:** - -- Separated favourites from reaction emojis. Limit for one reaction per post is removed. Facebook-like emoji reaction bar is removed. -- Simplified sensitive text/media logic. -- Reposting user is mentioned, when replying to a reposted status. -- Notification types filtering options are reasonably merged. -- Search results are never cleared by just leaving the page. -- Status spoilers are displayed with a collapse/expand button, not in an overlay. -- Mentions and hashtags in bio no longer link to external pages. -- Quotes are counted with reblogs for non-detailed statuses. -- Reactions/favourites/reblogs list modal is displayed on long press. -- Various accessibility changes, focused on screen reader compatibility. - -**Settings:** - -- Moved missing description confirmation option back to Settings page. -- Profile fields can be reordered on the Edit profile page. -- Explicit addressing can be disabled on supported backends. -- Developers options are no longer hidden behind a challenge. - -**Composing posts:** - -- Custom emojis are now split into categories. -- GoToSocial users can post with date in the past. -- Post scopes were renamed to match wording used by Mastodon. - -**UI changes:** - -- Removed header. Search bar and profile dropdown are moved to the sidebar. Mobile sidebar button is moved to the thumb navigation. -- Floating action button for creating new posts is moved to the thumb navigation. -- Mobile sidebar UI is changed to look like a popover. -- Added some animations, improved consistency of the existing ones. -- Max width of the layout is increased. -- Updated Lists UI, to match the overall style. -- RSS button is displayed in account header for local users, when unauthenticated. -- Conversations page is always displayed, even when Chats are supported. -- Made it woke. -- Emojis are zoomed on hover. -- Event create/edit form is now a page, instead of a modal. -- A star is used for favorite icon, instead of a heart. -- Account avatars are squared. -- Background gradients can be disabled. Some other visual behavior depends on this setting. -- Tabler Icons were replaced with Phosphor Icons. -- The entire loading process uses the same animation now. -- Changed status info and notification title design. -- Redesigned audio/video player controls. - -**Internal:** - -- Migrated some local stores from Redux to Zustand. Other stores have been migrated away from `immutable`, before moving them either to Zustand or TanStack Query. -- Posts are now emojified during render, instead of when inserting posts to the state. -- Barrel exports are no longer used. -- Search page uses URL params now. -- Themes use `adoptedStyleSheets` to work with stricter CSP. -- Settings store uses a different key in development environment. -- Styles are being migrated from TailwindCSS to just scss. -- Default max image size is increased to match Mastodon limits. - -**Dependencies:** - -- Replaced `react-popper` and `react-overlays` with `@floating-ui/react`. -- `uuid` package is replaced by the `randomUUID()` method. - -### Removed - -- Removed Truth Social-specific features. -- Removed Nostr-specific stuff. -- Removed Rumble-specific embed handling. -- Removed option that pretends to disable name editing for verified users. -- Removed Call to Action banner. -- Removed links to block explorers for crypto addresses. -- Removed support for custom apps provided during build. -- Removed so called 'GDPR banner'. -- Removed embed page which loads too much for the use case. - -### Fixed - -- When initializing FaviconService, canvas export permission is checked. -- Improved regex for mentions in post composer. -- Post tombstones don't interrupt status navigation with hotkeys. -- Emojis are supported in poll options. -- Unsupported content types are not listed as available, when composing a post. -- Admin dashboard now works on non-Pleroma backends. -- Removed excessive calls to `fetchOwnAccounts`. -- Media modal displays the whole thread correctly. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 120000 index 000000000..83b694704 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +CHANGELOG.md \ No newline at end of file diff --git a/packages/nicolium/CHANGELOG.md b/packages/nicolium/CHANGELOG.md index af05d2949..a000c5ed1 100644 --- a/packages/nicolium/CHANGELOG.md +++ b/packages/nicolium/CHANGELOG.md @@ -1,164 +1,179 @@ # Changelog -## Unreleased +## v0.1.0 -Changes made since the project forked from Soapbox in April 2024. +> This list includes changes made since the project forked from Soapbox in April 2024. It does not cover every UI change, consistency improvement, optimization, accessibility improvement, or backend compatibility update — maintaining such a list manually would be impractical. ### Major changes - Switched to a separate library [`pl-api`](https://codeberg.org/nicolium/nicolium/src/branch/develop/packages/pl-api) for Mastodon API integration. It is mostly written from scratch, inheriting minor code parts from Soapbox/Mastodon. This also comes with improved compatibility with various Mastodon API extensions and abstracts out the implementation details. +- Migrated client data stores from Redux to Zustand and remote data stores to TanStack Query. Stores have been migrated away from `immutable`. +- Migrated router from React Router to TanStack Router. +- Styles are being migrated from TailwindCSS to SCSS. +- Cat ears. ### Added -- Cat ears - -**Behavior:** +#### Behavior - Notifications of the same type and reposts of the same post are grouped client-side. -- Date is displayed for notifications that are not about new posts. -- Replies to your posts are displayed differently to other mentions in notification list. -- Hashtags from the last line of a post are displayed in a separate component. Adapted [from Mastodon](https://github.com/mastodon/mastodon/pull/26499). - Native grouped notifications are used on Mastodon. -- Likes, reposts and reactions lists are displayed on long press of respective buttons. +- Date is displayed for notifications that are not about new posts. +- Replies to your posts are displayed differently from other mentions in the notification list. +- Hashtags from the last line of a post are displayed in a separate component. Adapted [from Mastodon](https://github.com/mastodon/mastodon/pull/26499). +- Likes, reposts, and reactions lists are displayed on long press of respective buttons. - User local time is displayed on profile and in account hover card, if specified in profile fields. - Poll results can be displayed before voting. +- Home timeline remembers scroll position by default and restores it when you return to the app. +- A "Skip pinned posts" button was added to user profiles. +- Posts in timelines are displayed grouped together if they belong to the same context. -**Settings:** - -- You can add image description to your avatar/backend, if supported by backend. -- GoToSocial users can manage post interaction policies. -- Users can set interface theme color. -- Users can adjust interface size. -- Users can use system font for emoji rendering. - -**Composing posts:** +#### Composing posts - WYSIWYG text formatting, available if Markdown is supported. -- When writing posts, links to statuses are added as quotes, when supported by backend. -- You can select post language manually, when composing. -- You can write posts with multiple language versions, when supported by backend. -- Language detection is done client-side for composed posts, utilizing `fasttext.wasm.js`. -- Draft posts. They are stored locally only and work with any backend. -- New visibility scopes are supported – local-only and list-only for Pleroma. Local-only is a separate switch on GoToSocial. -- On backends that support explicit mentioning, you can choose to include mentions in your replies body. -- GoToSocial users can set per-post interaction policies. -- When adding a URL with tracking parameters, a suggestion to remove them from the URL is displayed. -- On supported backends, you can see post preview before posting. -- When entering a long, all-lowercase hashtag, a suggestion about hashtag accessibility is displayed. +- Links to statuses are added as quotes when supported by backend. +- Manual post language selection. +- Posts with multiple language versions, when supported by backend. +- Client-side language detection for composed posts via `fasttext.wasm.js`. +- Draft posts, stored locally and compatible with any backend. +- New visibility scopes: local-only and list-only for Pleroma; local-only as a separate switch on GoToSocial. +- Optional inclusion of mentions in reply body on backends that support explicit mentioning. +- Per-post interaction policies for GoToSocial users. +- Suggestion to remove tracking parameters when adding a URL. +- Post preview before posting, on supported backends. +- Suggestion about hashtag accessibility when entering long, all-lowercase hashtags. -**Dashboard:** +#### Features -- Dashboard main page displays metrics included in Mastodon admin dashboard, if supported by backend. - -**Features:** - -- The most recent scrobble is displayed on user profile/card. +- Most recent scrobble is displayed on user profile/card. - Users can generate _interaction circles_ for their profiles. -- You can bite users, if supported by backend. -- You can browse Bubble timeline, if supported by backend. -- Mastodon displays trending articles on Search page. -- Posts can be addressed to lists of users, on Pleroma. -- Support for events with external registration. -- Added a dedicated wrench reaction button. -- Interaction requests are supported. You can review pending requests and you get informed if your backend doesn't let you reply to a post. Supported on GoToSocial. -- Events with external sign up are supported. +- Bite users, if supported by backend. +- Bubble timeline, if supported by backend. +- Mastodon displays trending articles on the Search page. +- Posts can be addressed to lists of users on Pleroma. +- Events with external registration. +- Dedicated wrench reaction button. +- Interaction requests: review pending requests and get informed if your backend doesn't let you reply to a post (GoToSocial). - Application name used to post a status is displayed. - Outgoing follow requests are displayed, if supported by backend. -- It is possible to remove tracking parameters from URLs in displayed posts. -- Displayed media now have a button for alternative text preview. -- Links in displayed posts can be configured to always display target domain, even when it's not a part of their content. -- Users can configure redirects from popular websites to proxy services like Nitter and Piped in displayed posts. -- It is possible to boost a post with specific visibility, if supported by backend. -- Pleroma shoutbox is displayed on chats page. -- Displaying user-provided media can be disabled, media descriptions will be displayed instead. -- MFM can be displayed on compatible backends. -- Lists can be set as exclusive and replies policy can be set up, if supported by backend. -- Threads can be displayed in a linear view, similarly to traditional Pleroma-FE, as an alternative to tree view. You can expand spoilers with one click. +- Remove tracking parameters from URLs in displayed posts. +- Button for alternative text preview on displayed media. +- Option to always display target domain for links, even when not part of content. +- Configurable redirects from popular websites to proxy services like Nitter and Piped in displayed posts. +- Boost a post with specific visibility, if supported by backend. +- Pleroma shoutbox on the chats page. +- Option to disable user-provided media, showing descriptions instead. +- MFM rendering on compatible backends. +- Exclusive lists and replies policy, if supported by backend. +- Linear thread view (similar to traditional Pleroma-FE) as an alternative to tree view, with one-click spoiler expansion. +- Iceshrimp.NET drive support for uploading, managing, and attaching files to posts. +- Local post translation (including private posts, also for unauthenticated users) on browsers supporting the Translator API. +- Auto-expiring blocks on Pleroma. +- Antennas and circles on Mastodon forks that implement those. + +#### Settings + +- Image descriptions for avatar/header, if supported by backend. +- GoToSocial users can manage post interaction policies. +- Interface theme color selection. +- Adjustable interface size. +- Option to use system font for emoji rendering. + +#### Dashboard + +- Dashboard main page displays metrics from the Mastodon admin dashboard, if supported by backend. ### Changed -**Behavior:** +#### Behavior -- Separated favourites from reaction emojis. Limit for one reaction per post is removed. Facebook-like emoji reaction bar is removed. +- Separated favourites from reaction emojis; removed per-post reaction limit and Facebook-like emoji reaction bar. - Simplified sensitive text/media logic. -- Reposting user is mentioned, when replying to a reposted status. -- Notification types filtering options are reasonably merged. +- Reposting user is mentioned when replying to a reposted status. +- Notification type filtering options are reasonably merged. - Search results are never cleared by just leaving the page. -- Status spoilers are displayed with a collapse/expand button, not in an overlay. +- Status spoilers use a collapse/expand button instead of an overlay. - Mentions and hashtags in bio no longer link to external pages. - Quotes are counted with reblogs for non-detailed statuses. - Reactions/favourites/reblogs list modal is displayed on long press. -- Various accessibility changes, focused on screen reader compatibility. +- Various accessibility improvements, focused on screen reader compatibility. +- Users get asked to update account note when blocking/muting accounts. +- Bookmark folder selection modal supports filtering by search. +- Improved meta information for posts to improve Reader Mode on Firefox. -**Settings:** +#### Composing posts -- Moved missing description confirmation option back to Settings page. +- Custom emojis are split into categories. +- GoToSocial users can post with a date in the past. +- Post scopes renamed to match Mastodon wording. + +#### Settings + +- Missing description confirmation option moved back to the Settings page. - Profile fields can be reordered on the Edit profile page. - Explicit addressing can be disabled on supported backends. -- Developers options are no longer hidden behind a challenge. +- Developer options are no longer hidden behind a challenge. -**Composing posts:** +#### UI -- Custom emojis are now split into categories. -- GoToSocial users can post with date in the past. -- Post scopes were renamed to match wording used by Mastodon. - -**UI changes:** - -- Removed header. Search bar and profile dropdown are moved to the sidebar. Mobile sidebar button is moved to the thumb navigation. -- Floating action button for creating new posts is moved to the thumb navigation. -- Mobile sidebar UI is changed to look like a popover. -- Added some animations, improved consistency of the existing ones. -- Max width of the layout is increased. -- Updated Lists UI, to match the overall style. -- RSS button is displayed in account header for local users, when unauthenticated. +- Removed header; search bar and profile dropdown moved to the sidebar; mobile sidebar button moved to thumb navigation. +- Floating action button for new posts moved to thumb navigation. +- Mobile sidebar styled as a popover. +- Added animations and improved consistency of existing ones. +- Increased max width of the layout. +- Updated Lists UI to match overall style. +- RSS button displayed in account header for local users when unauthenticated. - Conversations page is always displayed, even when Chats are supported. - Made it woke. -- Emojis are zoomed on hover. -- Event create/edit form is now a page, instead of a modal. -- A star is used for favorite icon, instead of a heart. +- Emojis zoom on hover. +- Event create/edit form is now a page instead of a modal. +- Star used for favourite icon instead of a heart. - Account avatars are squared. -- Background gradients can be disabled. Some other visual behavior depends on this setting. -- Tabler Icons were replaced with Phosphor Icons. -- The entire loading process uses the same animation now. -- Changed status info and notification title design. +- Background gradients can be disabled; some visual behavior depends on this setting. +- Tabler Icons replaced with Phosphor Icons. +- Unified loading animation across the entire loading process. +- Redesigned status info and notification title. - Redesigned audio/video player controls. +- Animations and accessibility improvements for UI elements like sliders. -**Internal:** +#### Internal -- Migrated some local stores from Redux to Zustand. Other stores have been migrated away from `immutable`, before moving them either to Zustand or TanStack Query. -- Posts are now emojified during render, instead of when inserting posts to the state. +- Posts are emojified during render instead of when inserting into state. - Barrel exports are no longer used. -- Search page uses URL params now. +- Search page uses URL params. - Themes use `adoptedStyleSheets` to work with stricter CSP. - Settings store uses a different key in development environment. -- Styles are being migrated from TailwindCSS to just scss. -- Default max image size is increased to match Mastodon limits. +- Default max image size increased to match Mastodon limits. -**Dependencies:** +#### Dependencies - Replaced `react-popper` and `react-overlays` with `@floating-ui/react`. -- `uuid` package is replaced by the `randomUUID()` method. +- Replaced `uuid` package with the `randomUUID()` method. +- Inlined some libraries, like the PullToRefresh component, directly into the app. +- Removed `react-motion`; adopted migration to `@react-spring/web` from Mastodon. +- Replaced a fork of `react-hotkeys` with Mastodon's hotkey handling system. +- Replaced FlexSearch with `fuzzysort` for emoji search. +- Replaced ESLint with `oxfmt` and `oxlint`. ### Removed -- Removed Truth Social-specific features. -- Removed Nostr-specific stuff. -- Removed Rumble-specific embed handling. -- Removed option that pretends to disable name editing for verified users. -- Removed Call to Action banner. -- Removed links to block explorers for crypto addresses. -- Removed support for custom apps provided during build. -- Removed so called 'GDPR banner'. -- Removed embed page which loads too much for the use case. +- Truth Social-specific features. +- Nostr-specific features. +- Rumble-specific embed handling. +- Option that pretends to disable name editing for verified users. +- Call to Action banner. +- Links to block explorers for crypto addresses. +- Support for custom apps provided during build. +- So called 'GDPR banner'. +- Embed page (loaded too much for the use case). ### Fixed -- When initializing FaviconService, canvas export permission is checked. +- Canvas export permission is checked when initializing FaviconService. - Improved regex for mentions in post composer. -- Post tombstones don't interrupt status navigation with hotkeys. +- Post tombstones no longer interrupt status navigation with hotkeys. - Emojis are supported in poll options. -- Unsupported content types are not listed as available, when composing a post. +- Unsupported content types are not listed as available when composing a post. - Admin dashboard now works on non-Pleroma backends. - Removed excessive calls to `fetchOwnAccounts`. - Media modal displays the whole thread correctly. diff --git a/packages/nicolium/index.html b/packages/nicolium/index.html index bcb53dbb2..a2ddb8c49 100644 --- a/packages/nicolium/index.html +++ b/packages/nicolium/index.html @@ -4,7 +4,7 @@ @@ -25,6 +25,14 @@ - + diff --git a/packages/nicolium/package.json b/packages/nicolium/package.json index ba423988a..cb509a6ab 100644 --- a/packages/nicolium/package.json +++ b/packages/nicolium/package.json @@ -165,7 +165,7 @@ "vite-plugin-html": "^3.2.2", "vite-plugin-pwa": "^1.2.0", "vite-plugin-require": "^1.2.14", - "vite-plugin-static-copy": "^3.2.0", + "vite-plugin-static-copy": "^3.3.0", "vitest": "^4.1.0" }, "lint-staged": { diff --git a/packages/nicolium/src/actions/consumer-auth.ts b/packages/nicolium/src/actions/consumer-auth.ts index dc9044e61..b814d5876 100644 --- a/packages/nicolium/src/actions/consumer-auth.ts +++ b/packages/nicolium/src/actions/consumer-auth.ts @@ -1,5 +1,4 @@ import * as BuildConfig from '@/build-config'; -import { isURL } from '@/utils/auth'; import sourceCode from '@/utils/code'; import { getScopes } from '@/utils/scopes'; @@ -19,7 +18,7 @@ const createProviderApp = () => { }; const prepareRequest = async (provider: string) => { - const baseURL = isURL(BuildConfig.BACKEND_URL) ? BuildConfig.BACKEND_URL : ''; + const baseURL = URL.canParse(BuildConfig.BACKEND_URL) ? BuildConfig.BACKEND_URL : ''; const scopes = getScopes(undefined, true); const app = await createProviderApp(); diff --git a/packages/nicolium/src/actions/statuses.ts b/packages/nicolium/src/actions/statuses.ts index e03a21246..fb298425e 100644 --- a/packages/nicolium/src/actions/statuses.ts +++ b/packages/nicolium/src/actions/statuses.ts @@ -12,7 +12,7 @@ import { shouldHaveCard } from '@/utils/status'; import { importEntities } from '../queries/utils/import-entities'; -import type { NormalizedStatus as Status } from '@/normalizers/status'; +import type { NormalizedStatus as Status } from '@/queries/statuses/normalize'; import type { useQueryClient } from '@tanstack/react-query'; import type { CreateStatusParams, PlApiClient, Status as BaseStatus } from 'pl-api'; import type { IntlShape } from 'react-intl'; diff --git a/packages/nicolium/src/columns/notifications.tsx b/packages/nicolium/src/columns/notifications.tsx index f303b970a..42a2cfa40 100644 --- a/packages/nicolium/src/columns/notifications.tsx +++ b/packages/nicolium/src/columns/notifications.tsx @@ -1,20 +1,25 @@ +import iconAt from '@phosphor-icons/core/regular/at.svg'; +import iconBellSimpleRinging from '@phosphor-icons/core/regular/bell-simple-ringing.svg'; +import iconCalendarDots from '@phosphor-icons/core/regular/calendar-dots.svg'; +import iconChartBar from '@phosphor-icons/core/regular/chart-bar.svg'; +import iconRepeat from '@phosphor-icons/core/regular/repeat.svg'; +import iconStar from '@phosphor-icons/core/regular/star.svg'; +import iconUserPlus from '@phosphor-icons/core/regular/user-plus.svg'; import { useQueryClient } from '@tanstack/react-query'; import clsx from 'clsx'; import debounce from 'lodash/debounce'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; - -import '@/styles/new/notifications.scss'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { saveSettings } from '@/actions/settings'; +import Notification from '@/components/notification'; +import PlaceholderNotification from '@/components/placeholders/placeholder-notification'; import PullToRefresh from '@/components/pull-to-refresh'; import ScrollTopButton from '@/components/scroll-top-button'; import ScrollableList from '@/components/scrollable-list'; import Icon from '@/components/ui/icon'; import Portal from '@/components/ui/portal'; import Tabs from '@/components/ui/tabs'; -import Notification from '@/features/notifications/components/notification'; -import PlaceholderNotification from '@/features/placeholder/components/placeholder-notification'; import { useFeatures } from '@/hooks/use-features'; import { queryClient } from '@/queries/client'; import { queryKeys } from '@/queries/keys'; @@ -29,6 +34,8 @@ import { selectChild } from '@/utils/scroll-utils'; import type { Item } from '@/components/ui/tabs'; import type { VirtuosoHandle } from 'react-virtuoso'; +import '@/styles/new/notifications.scss'; + const messages = defineMessages({ title: { id: 'column.notifications', defaultMessage: 'Notifications' }, queue: { @@ -89,84 +96,46 @@ const FilterBar = () => { }); } else { items.push({ - text: ( - - ), + text: , title: intl.formatMessage(messages.mentions), action: onClick('mention'), name: 'mention', }); if (features.accountNotifies) items.push({ - text: ( - - ), + text: , title: intl.formatMessage(messages.statuses), action: onClick('status'), name: 'status', }); items.push({ - text: ( - - ), + text: , title: intl.formatMessage(messages.favourites), action: onClick('favourite'), name: 'favourite', }); items.push({ - text: ( - - ), + text: , title: intl.formatMessage(messages.boosts), action: onClick('reblog'), name: 'reblog', }); if (features.polls) items.push({ - text: ( - - ), + text: , title: intl.formatMessage(messages.polls), action: onClick('poll'), name: 'poll', }); if (features.events) items.push({ - text: ( - - ), + text: , title: intl.formatMessage(messages.events), action: onClick('events'), name: 'events', }); items.push({ - text: ( - - ), + text: , title: intl.formatMessage(messages.follows), action: onClick('follow'), name: 'follow', diff --git a/packages/nicolium/src/columns/search.tsx b/packages/nicolium/src/columns/search.tsx index c068246c5..f6a96b5b8 100644 --- a/packages/nicolium/src/columns/search.tsx +++ b/packages/nicolium/src/columns/search.tsx @@ -4,11 +4,11 @@ import { FormattedMessage } from 'react-intl'; import AccountContainer from '@/components/accounts/account-container'; import Hashtag from '@/components/hashtag'; +import PlaceholderAccount from '@/components/placeholders/placeholder-account'; +import PlaceholderHashtag from '@/components/placeholders/placeholder-hashtag'; +import PlaceholderStatus from '@/components/placeholders/placeholder-status'; import ScrollableList from '@/components/scrollable-list'; import StatusContainer from '@/components/statuses/status-container'; -import PlaceholderAccount from '@/features/placeholder/components/placeholder-account'; -import PlaceholderHashtag from '@/features/placeholder/components/placeholder-hashtag'; -import PlaceholderStatus from '@/features/placeholder/components/placeholder-status'; import { useSearchAccounts, useSearchHashtags, diff --git a/packages/nicolium/src/columns/timeline.tsx b/packages/nicolium/src/columns/timeline.tsx index 152b3a70e..e99a84c40 100644 --- a/packages/nicolium/src/columns/timeline.tsx +++ b/packages/nicolium/src/columns/timeline.tsx @@ -1,12 +1,18 @@ +import iconArrowLineDown from '@phosphor-icons/core/regular/arrow-line-down.svg'; +import iconCaretDoubleDown from '@phosphor-icons/core/regular/caret-double-down.svg'; +import iconCaretDoubleUp from '@phosphor-icons/core/regular/caret-double-up.svg'; +import iconRepeat from '@phosphor-icons/core/regular/repeat.svg'; import { Link } from '@tanstack/react-router'; import clsx from 'clsx'; import React, { useMemo, useRef, useState } from 'react'; import { defineMessages, FormattedList, FormattedMessage, useIntl } from 'react-intl'; import HoverAccountWrapper from '@/components/accounts/hover-account-wrapper'; +import PlaceholderStatus from '@/components/placeholders/placeholder-status'; import PullToRefresh from '@/components/pull-to-refresh'; import ScrollTopButton from '@/components/scroll-top-button'; import ScrollableList, { type IScrollableList } from '@/components/scrollable-list'; +import PendingStatus from '@/components/statuses/pending-status'; import Status, { StatusFollowedTagInfo } from '@/components/statuses/status'; import StatusInfo from '@/components/statuses/status-info'; import Tombstone from '@/components/statuses/tombstone'; @@ -14,8 +20,6 @@ import Icon from '@/components/ui/icon'; import Portal from '@/components/ui/portal'; import { useCurrentAccount } from '@/contexts/current-account-context'; import Emojify from '@/features/emoji/emojify'; -import PlaceholderStatus from '@/features/placeholder/components/placeholder-status'; -import PendingStatus from '@/features/ui/components/pending-status'; import { useFeatures } from '@/hooks/use-features'; import { useAccounts } from '@/queries/accounts/use-accounts'; import { type SelectedStatus, useStatus } from '@/queries/statuses/use-status'; @@ -64,7 +68,7 @@ const messages = defineMessages({ const SkipPinned: React.FC> = ({ onClick }) => { return ( @@ -250,13 +254,7 @@ const TimelineStatusInfo: React.FC = ({ status, rebloggedBy - } + icon={} text={ // status.visibility === 'private' ? ( // = ({ instance, ...props }) => { + const timelineFilters = useSettings().timelines.public; const timelineQuery = usePublicTimeline({ local, remote, instance }); - return ; + return ( + + ); }; interface IHashtagTimelineColumn extends IBaseTimeline { @@ -628,9 +629,12 @@ interface IHashtagTimelineColumn extends IBaseTimeline { } const HashtagTimelineColumn: React.FC = ({ hashtag, ...props }) => { + const timelineFilters = useSettings().timelines.hashtag; const timelineQuery = useHashtagTimeline(hashtag); - return ; + return ( + + ); }; interface ILinkTimelineColumn extends IBaseTimeline { @@ -648,9 +652,10 @@ interface IListTimelineColumn extends IBaseTimeline { } const ListTimelineColumn: React.FC = ({ listId, ...props }) => { + const timelineFilters = useSettings().timelines.list; const timelineQuery = useListTimeline(listId); - return ; + return ; }; interface IGroupTimelineColumn extends IBaseTimeline { @@ -658,15 +663,21 @@ interface IGroupTimelineColumn extends IBaseTimeline { } const GroupTimelineColumn: React.FC = ({ groupId, ...props }) => { + const timelineFilters = useSettings().timelines.group; const timelineQuery = useGroupTimeline(groupId); - return ; + return ( + + ); }; const BubbleTimelineColumn: React.FC = (props) => { + const timelineFilters = useSettings().timelines.bubble; const timelineQuery = useBubbleTimeline(); - return ; + return ( + + ); }; interface IAntennaTimelineColumn extends IBaseTimeline { @@ -674,9 +685,12 @@ interface IAntennaTimelineColumn extends IBaseTimeline { } const AntennaTimelineColumn: React.FC = ({ antennaId, ...props }) => { + const timelineFilters = useSettings().timelines.antenna; const timelineQuery = useAntennaTimeline(antennaId); - return ; + return ( + + ); }; interface ICircleTimelineColumn extends IBaseTimeline { @@ -684,15 +698,21 @@ interface ICircleTimelineColumn extends IBaseTimeline { } const CircleTimelineColumn: React.FC = ({ circleId, ...props }) => { + const timelineFilters = useSettings().timelines.bubble; const timelineQuery = useCircleTimeline(circleId); - return ; + return ( + + ); }; const WrenchedTimelineColumn: React.FC = (props) => { + const timelineFilters = useSettings().timelines.wrenched; const timelineQuery = useWrenchedTimeline(); - return ; + return ( + + ); }; interface IAccountTimelineColumn extends IBaseTimeline { diff --git a/packages/nicolium/src/columns/trends.tsx b/packages/nicolium/src/columns/trends.tsx index fef62d1e8..0af540a44 100644 --- a/packages/nicolium/src/columns/trends.tsx +++ b/packages/nicolium/src/columns/trends.tsx @@ -5,13 +5,13 @@ import { FormattedMessage } from 'react-intl'; import AccountContainer from '@/components/accounts/account-container'; import { EmptyMessage } from '@/components/empty-message'; import Hashtag from '@/components/hashtag'; +import PlaceholderAccount from '@/components/placeholders/placeholder-account'; +import PlaceholderHashtag from '@/components/placeholders/placeholder-hashtag'; +import PlaceholderStatus from '@/components/placeholders/placeholder-status'; import ScrollableList from '@/components/scrollable-list'; import StatusContainer from '@/components/statuses/status-container'; import TrendingLink from '@/components/trending-link'; import Button from '@/components/ui/button'; -import PlaceholderAccount from '@/features/placeholder/components/placeholder-account'; -import PlaceholderHashtag from '@/features/placeholder/components/placeholder-hashtag'; -import PlaceholderStatus from '@/features/placeholder/components/placeholder-status'; import { useFeatures } from '@/hooks/use-features'; import { useSuggestedAccounts } from '@/queries/trends/use-suggested-accounts'; import { useTrendingLinks } from '@/queries/trends/use-trending-links'; @@ -63,7 +63,6 @@ const TrendsColumn: React.FC = ({ type, multiColumn }) => { isLoading = isLoadingAccounts; placeholderComponent = PlaceholderAccount; - console.log(accounts, isFetching, isLoading); if (!isFetching && !isLoading && accounts?.length === 0) { children = [ = ({ from, to }) => (
- +

= ({ from, to }) => (

); -interface IHeader { +interface IAccountHeader { account?: AccountEntity; } -const Header: React.FC = ({ account }) => { +const AccountHeader: React.FC = ({ account }) => { const intl = useIntl(); const navigate = useNavigate(); @@ -253,7 +257,7 @@ const Header: React.FC = ({ account }) => { if (account.accepts_chat_messages) { return ( { createAndNavigateToChat.mutate(account.id); }} @@ -277,7 +281,7 @@ const Header: React.FC = ({ account }) => { return ( = ({ account }) => { return ( = ({ account }) => { ); }; -export { Header as default }; +export { AccountHeader as default }; diff --git a/packages/nicolium/src/components/accounts/account-hover-card.tsx b/packages/nicolium/src/components/accounts/account-hover-card.tsx index b9d541994..a1838ebb3 100644 --- a/packages/nicolium/src/components/accounts/account-hover-card.tsx +++ b/packages/nicolium/src/components/accounts/account-hover-card.tsx @@ -1,16 +1,18 @@ import { autoUpdate, flip, shift, useFloating, useTransitionStyles } from '@floating-ui/react'; +import iconCalendarDots from '@phosphor-icons/core/regular/calendar-dots.svg'; +import iconTag from '@phosphor-icons/core/regular/tag.svg'; import { useRouter } from '@tanstack/react-router'; import clsx from 'clsx'; import React, { useEffect } from 'react'; import { useIntl, FormattedMessage } from 'react-intl'; +import ActionButton from '@/components/accounts/action-button'; +import { isTimezoneLabel } from '@/components/accounts/profile-field'; import Badge from '@/components/badge'; import Card, { CardBody } from '@/components/ui/card'; import Icon from '@/components/ui/icon'; import Text from '@/components/ui/text'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import ActionButton from '@/features/ui/components/action-button'; -import { isTimezoneLabel } from '@/features/ui/components/profile-field'; import { UserPanel } from '@/features/ui/util/async-components'; import { useFrontendConfig } from '@/hooks/use-frontend-config'; import { useAccountScrobbleQuery } from '@/queries/accounts/account-scrobble'; @@ -181,10 +183,7 @@ const AccountHoverCard: React.FC = ({ visible = true }) => { {account.local ? (
- + = ({ visible = true }) => { {account.pronouns.length > 0 && (
- + = ({ accountId, field }) => className='mt-1 flex items-center gap-0.5' title={intl.formatMessage(messages.timezone, { timezone: field.value })} > - + {localTime} {me !== accountId && isTimezoneEqual && ( diff --git a/packages/nicolium/src/features/account/components/account-menu.tsx b/packages/nicolium/src/components/accounts/account-menu.tsx similarity index 86% rename from packages/nicolium/src/features/account/components/account-menu.tsx rename to packages/nicolium/src/components/accounts/account-menu.tsx index e96203d21..b65853b58 100644 --- a/packages/nicolium/src/features/account/components/account-menu.tsx +++ b/packages/nicolium/src/components/accounts/account-menu.tsx @@ -1,3 +1,25 @@ +import iconArrowSquareOut from '@phosphor-icons/core/regular/arrow-square-out.svg'; +import iconArrowsClockwise from '@phosphor-icons/core/regular/arrows-clockwise.svg'; +import iconAt from '@phosphor-icons/core/regular/at.svg'; +import iconDotsThree from '@phosphor-icons/core/regular/dots-three.svg'; +import iconEnvelopeSimple from '@phosphor-icons/core/regular/envelope-simple.svg'; +import iconExport from '@phosphor-icons/core/regular/export.svg'; +import iconFlag from '@phosphor-icons/core/regular/flag.svg'; +import iconGavel from '@phosphor-icons/core/regular/gavel.svg'; +import iconLinkSimpleHorizontal from '@phosphor-icons/core/regular/link-simple-horizontal.svg'; +import iconListBullets from '@phosphor-icons/core/regular/list-bullets.svg'; +import iconMagnifyingGlass from '@phosphor-icons/core/regular/magnifying-glass.svg'; +import iconNotePencil from '@phosphor-icons/core/regular/note-pencil.svg'; +import iconProhibit from '@phosphor-icons/core/regular/prohibit.svg'; +import iconRepeat from '@phosphor-icons/core/regular/repeat.svg'; +import iconRss from '@phosphor-icons/core/regular/rss.svg'; +import iconSlidersHorizontal from '@phosphor-icons/core/regular/sliders-horizontal.svg'; +import iconSpeakerSimpleX from '@phosphor-icons/core/regular/speaker-simple-x.svg'; +import iconTag from '@phosphor-icons/core/regular/tag.svg'; +import iconTooth from '@phosphor-icons/core/regular/tooth.svg'; +import iconUserCheck from '@phosphor-icons/core/regular/user-check.svg'; +import iconUserMinus from '@phosphor-icons/core/regular/user-minus.svg'; +import iconUser from '@phosphor-icons/core/regular/user.svg'; import { GOTOSOCIAL, MASTODON } from 'pl-api'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -319,7 +341,7 @@ const AccountMenu: React.FC = ({ account }) => { if (features.rssFeeds && account.local && (software !== GOTOSOCIAL || account.enable_rss)) { menu.push({ text: intl.formatMessage(messages.subscribeFeed), - icon: require('@phosphor-icons/core/regular/rss.svg'), + icon: iconRss, href: software === MASTODON ? `${account.url}.rss` : `${account.url}/feed.rss`, target: '_blank', }); @@ -329,7 +351,7 @@ const AccountMenu: React.FC = ({ account }) => { menu.push({ text: intl.formatMessage(messages.share, { name: account.username }), action: handleShare, - icon: require('@phosphor-icons/core/regular/export.svg'), + icon: iconExport, }); } @@ -339,14 +361,14 @@ const AccountMenu: React.FC = ({ account }) => { menu.push({ text: intl.formatMessage(messages.profileExternal, { domain }), href: account.url, - icon: require('@phosphor-icons/core/regular/arrow-square-out.svg'), + icon: iconArrowSquareOut, }); } menu.push({ text: intl.formatMessage(messages.copy), action: handleCopy, - icon: require('@phosphor-icons/core/regular/link-simple-horizontal.svg'), + icon: iconLinkSimpleHorizontal, }); if (!ownAccount) return menu; @@ -359,7 +381,7 @@ const AccountMenu: React.FC = ({ account }) => { ), to: '/search', search: { type: 'statuses', accountId: account.id }, - icon: require('@phosphor-icons/core/regular/magnifying-glass.svg'), + icon: iconMagnifyingGlass, }); } @@ -371,36 +393,36 @@ const AccountMenu: React.FC = ({ account }) => { menu.push({ text: intl.formatMessage(messages.editProfile), to: '/settings/profile', - icon: require('@phosphor-icons/core/regular/user.svg'), + icon: iconUser, }); menu.push({ text: intl.formatMessage(messages.preferences), to: '/settings', - icon: require('@phosphor-icons/core/regular/sliders-horizontal.svg'), + icon: iconSlidersHorizontal, }); menu.push(null); menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes', - icon: require('@phosphor-icons/core/regular/speaker-simple-x.svg'), + icon: iconSpeakerSimpleX, }); menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks', - icon: require('@phosphor-icons/core/regular/prohibit.svg'), + icon: iconProhibit, }); } else { menu.push({ text: intl.formatMessage(messages.mention, { name: account.username }), action: onMention, - icon: require('@phosphor-icons/core/regular/at.svg'), + icon: iconAt, }); if (features.privacyScopes) { menu.push({ text: intl.formatMessage(messages.direct, { name: account.username }), action: onDirect, - icon: require('@phosphor-icons/core/regular/envelope-simple.svg'), + icon: iconEnvelopeSimple, }); } @@ -409,13 +431,13 @@ const AccountMenu: React.FC = ({ account }) => { menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.username }), action: onReblogToggle, - icon: require('@phosphor-icons/core/regular/repeat.svg'), + icon: iconRepeat, }); } else { menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.username }), action: onReblogToggle, - icon: require('@phosphor-icons/core/regular/repeat.svg'), + icon: iconRepeat, }); } @@ -423,7 +445,7 @@ const AccountMenu: React.FC = ({ account }) => { menu.push({ text: intl.formatMessage(messages.addOrRemoveFromList), action: onAddToList, - icon: require('@phosphor-icons/core/regular/list-bullets.svg'), + icon: iconListBullets, }); } @@ -433,16 +455,14 @@ const AccountMenu: React.FC = ({ account }) => { account.relationship?.endorsed ? messages.unendorse : messages.endorse, ), action: onEndorseToggle, - icon: account.relationship?.endorsed - ? require('@phosphor-icons/core/regular/user-minus.svg') - : require('@phosphor-icons/core/regular/user-check.svg'), + icon: account.relationship?.endorsed ? iconUserMinus : iconUserCheck, }); } } else if (features.lists && features.unrestrictedLists) { menu.push({ text: intl.formatMessage(messages.addOrRemoveFromList), action: onAddToList, - icon: require('@phosphor-icons/core/regular/list-bullets.svg'), + icon: iconListBullets, }); } @@ -450,7 +470,7 @@ const AccountMenu: React.FC = ({ account }) => { menu.push({ text: intl.formatMessage(messages.bite, { name: account.username }), action: onBite, - icon: require('@phosphor-icons/core/regular/tooth.svg'), + icon: iconTooth, }); } @@ -458,7 +478,7 @@ const AccountMenu: React.FC = ({ account }) => { menu.push({ text: intl.formatMessage(messages.loadActivities), action: onLoadActivities, - icon: require('@phosphor-icons/core/regular/arrows-clockwise.svg'), + icon: iconArrowsClockwise, }); } @@ -466,14 +486,14 @@ const AccountMenu: React.FC = ({ account }) => { menu.push({ text: intl.formatMessage(messages.note, { name: account.acct }), action: onEditNote, - icon: require('@phosphor-icons/core/regular/note-pencil.svg'), + icon: iconNotePencil, }); } menu.push({ text: intl.formatMessage(messages.nickname, { name: account.acct }), action: onEditNickname, - icon: require('@phosphor-icons/core/regular/tag.svg'), + icon: iconTag, }); menu.push(null); @@ -482,7 +502,7 @@ const AccountMenu: React.FC = ({ account }) => { menu.push({ text: intl.formatMessage(messages.removeFromFollowers), action: onRemoveFromFollowers, - icon: require('@phosphor-icons/core/regular/user-minus.svg'), + icon: iconUserMinus, }); } @@ -490,13 +510,13 @@ const AccountMenu: React.FC = ({ account }) => { menu.push({ text: intl.formatMessage(messages.unmute, { name: account.username }), action: onMute, - icon: require('@phosphor-icons/core/regular/speaker-simple-x.svg'), + icon: iconSpeakerSimpleX, }); } else { menu.push({ text: intl.formatMessage(messages.mute, { name: account.username }), action: onMute, - icon: require('@phosphor-icons/core/regular/speaker-simple-x.svg'), + icon: iconSpeakerSimpleX, }); } @@ -504,20 +524,20 @@ const AccountMenu: React.FC = ({ account }) => { menu.push({ text: intl.formatMessage(messages.unblock, { name: account.username }), action: onBlock, - icon: require('@phosphor-icons/core/regular/prohibit.svg'), + icon: iconProhibit, }); } else { menu.push({ text: intl.formatMessage(messages.block, { name: account.username }), action: onBlock, - icon: require('@phosphor-icons/core/regular/prohibit.svg'), + icon: iconProhibit, }); } menu.push({ text: intl.formatMessage(messages.report, { name: account.username }), action: onReport, - icon: require('@phosphor-icons/core/regular/flag.svg'), + icon: iconFlag, }); } @@ -529,14 +549,18 @@ const AccountMenu: React.FC = ({ account }) => { if (account.relationship?.domain_blocking) { menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), - action: () => onUnblockDomain(domain), - icon: require('@phosphor-icons/core/regular/prohibit.svg'), + action: () => { + onUnblockDomain(domain); + }, + icon: iconProhibit, }); } else { menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), - action: () => onBlockDomain(domain), - icon: require('@phosphor-icons/core/regular/prohibit.svg'), + action: () => { + onBlockDomain(domain); + }, + icon: iconProhibit, }); } } @@ -548,7 +572,7 @@ const AccountMenu: React.FC = ({ account }) => { text: intl.formatMessage(messages.adminAccount, { name: account.username }), to: '/nicolium/admin/accounts/$accountId', params: { accountId: account.id }, - icon: require('@phosphor-icons/core/regular/gavel.svg'), + icon: iconGavel, }); } @@ -561,12 +585,7 @@ const AccountMenu: React.FC = ({ account }) => { return ( - + ); }; diff --git a/packages/nicolium/src/components/accounts/account.tsx b/packages/nicolium/src/components/accounts/account.tsx index 1cda4174e..5f2bd3cd7 100644 --- a/packages/nicolium/src/components/accounts/account.tsx +++ b/packages/nicolium/src/components/accounts/account.tsx @@ -1,8 +1,10 @@ +import iconLock from '@phosphor-icons/core/regular/lock.svg'; import { Link, linkOptions, useNavigate, useRouter } from '@tanstack/react-router'; import clsx from 'clsx'; import React, { useLayoutEffect, useRef, useState } from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; +import ActionButton from '@/components/accounts/action-button'; import HoverAccountWrapper from '@/components/accounts/hover-account-wrapper'; import VerificationBadge from '@/components/accounts/verification-badge'; import PatronIndicator from '@/components/patron-indicator'; @@ -13,8 +15,8 @@ import IconButton from '@/components/ui/icon-button'; import Text from '@/components/ui/text'; import { useCurrentAccount } from '@/contexts/current-account-context'; import Emojify from '@/features/emoji/emojify'; -import ActionButton from '@/features/ui/components/action-button'; import { useAcct } from '@/hooks/use-acct'; +import { useFeatures } from '@/hooks/use-features'; import { useFrontendConfig } from '@/hooks/use-frontend-config'; import { usePatronUser } from '@/queries/patron/use-patron-user'; import { useSettings } from '@/stores/settings'; @@ -23,7 +25,7 @@ import Badge from '../badge'; import RelativeTimestamp from '../relative-timestamp'; import { ParsedContent } from '../statuses/parsed-content'; -import type { StatusApprovalStatus } from '@/normalizers/status'; +import type { StatusApprovalStatus } from '@/queries/statuses/normalize'; import type { Account as AccountSchema } from 'pl-api'; interface IInstanceFavicon { @@ -168,6 +170,8 @@ const Account = ({ const [style, setStyle] = useState({}); + const intl = useIntl(); + const features = useFeatures(); const me = useCurrentAccount(); const username = useAcct(account); const frontendConfig = useFrontendConfig(); @@ -210,19 +214,19 @@ const Account = ({ return null; }; - const intl = useIntl(); - useLayoutEffect(() => { const onResize = () => { + console.log('resizing'); const style: React.CSSProperties = {}; const actionWidth = actionRef.current?.clientWidth || 0; if (overflowRef.current) { + const maxWidth = overflowRef.current.classList.contains('w-fit') + ? overflowRef.current.parentElement!.clientWidth + : overflowRef.current.clientWidth; style.maxWidth = Math.max( 0, - overflowRef.current.clientWidth - - (withAvatar ? avatarSize + 12 : 0) - - (actionWidth ? actionWidth + 12 : 0), + maxWidth - (withAvatar ? avatarSize + 12 : 0) - (actionWidth ? actionWidth + 12 : 0), ); } @@ -322,7 +326,7 @@ const Account = ({ {withLocked && !timestamp && account.locked && ( <> @@ -353,9 +357,8 @@ const Account = ({ className={clsx('⁂-account-card', { '⁂-account-card--action-top': actionAlignment === 'top', })} - ref={overflowRef} > -
+
@@ -447,7 +450,10 @@ const Account = ({ )} {account.favicon && !disableUserProvidedMedia && ( - + )} {timestamp ? ( diff --git a/packages/nicolium/src/features/ui/components/action-button.tsx b/packages/nicolium/src/components/accounts/action-button.tsx similarity index 96% rename from packages/nicolium/src/features/ui/components/action-button.tsx rename to packages/nicolium/src/components/accounts/action-button.tsx index 32d313ed5..a12889088 100644 --- a/packages/nicolium/src/features/ui/components/action-button.tsx +++ b/packages/nicolium/src/components/accounts/action-button.tsx @@ -1,3 +1,6 @@ +import iconPlus from '@phosphor-icons/core/regular/plus.svg'; +import iconProhibit from '@phosphor-icons/core/regular/prohibit.svg'; +import iconTooth from '@phosphor-icons/core/regular/tooth.svg'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -212,7 +215,7 @@ const ActionButton: React.FC = ({ account, actionType, small = tr /> } onClick={handleBite} - icon={require('@phosphor-icons/core/regular/tooth.svg')} + icon={iconTooth} /> ); }; @@ -243,7 +246,7 @@ const ActionButton: React.FC = ({ account, actionType, small = tr return ( )} diff --git a/packages/nicolium/src/components/authorize-reject-buttons.tsx b/packages/nicolium/src/components/authorize-reject-buttons.tsx index f39a5beec..c4809f42b 100644 --- a/packages/nicolium/src/components/authorize-reject-buttons.tsx +++ b/packages/nicolium/src/components/authorize-reject-buttons.tsx @@ -1,3 +1,6 @@ +import iconStopFill from '@phosphor-icons/core/fill/stop-fill.svg'; +import iconCheck from '@phosphor-icons/core/regular/check.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import clsx from 'clsx'; import React, { useEffect, useRef, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -129,7 +132,7 @@ const AuthorizeRejectButtons: React.FC = ({
= ({ /> = ({ })} > = ({ value, onChange, required }) = ({ value, onChange, required }) = ({ value, onChange, required }) = ({ value, onChange, required }) diff --git a/packages/nicolium/src/components/dropdown-menu/dropdown-menu.tsx b/packages/nicolium/src/components/dropdown-menu/dropdown-menu.tsx index 79be82a55..7e38f901d 100644 --- a/packages/nicolium/src/components/dropdown-menu/dropdown-menu.tsx +++ b/packages/nicolium/src/components/dropdown-menu/dropdown-menu.tsx @@ -8,6 +8,8 @@ import { size, useFloating, } from '@floating-ui/react'; +import iconArrowLeft from '@phosphor-icons/core/regular/arrow-left.svg'; +import iconDotsThree from '@phosphor-icons/core/regular/dots-three.svg'; import clsx from 'clsx'; import { supportsPassiveEvents } from 'detect-passive-events'; import React, { useEffect, useMemo, useRef, useState } from 'react'; @@ -201,7 +203,7 @@ const DropdownMenuContent: React.FC = ({
= (props) => { onOpen, onShiftClick, placement: initialPlacement = 'top', - src = require('@phosphor-icons/core/regular/dots-three.svg'), + src = iconDotsThree, title = 'Menu', width, className, @@ -387,6 +389,7 @@ const DropdownMenu: React.FC = (props) => { onClick: handleClick, onKeyPress: handleKeyPress, ref: refs.setReference, + 'aria-haspopup': true, 'aria-expanded': isOpen, }); }, [children, !!items?.length, component]); diff --git a/packages/nicolium/src/components/empty-message.tsx b/packages/nicolium/src/components/empty-message.tsx index 0eec37e0b..53e30a21f 100644 --- a/packages/nicolium/src/components/empty-message.tsx +++ b/packages/nicolium/src/components/empty-message.tsx @@ -1,3 +1,4 @@ +import iconEmpty from '@phosphor-icons/core/regular/empty.svg'; import React from 'react'; import Icon from './ui/icon'; @@ -8,11 +9,7 @@ interface IEmptyMessage { icon?: string | false; } -const EmptyMessage: React.FC = ({ - heading, - text, - icon = require('@phosphor-icons/core/regular/empty.svg'), -}) => ( +const EmptyMessage: React.FC = ({ heading, text, icon = iconEmpty }) => (
{icon !== false && } diff --git a/packages/nicolium/src/components/fork-awesome-icon.tsx b/packages/nicolium/src/components/fork-awesome-icon.tsx index 8847fdf6b..e6782de37 100644 --- a/packages/nicolium/src/components/fork-awesome-icon.tsx +++ b/packages/nicolium/src/components/fork-awesome-icon.tsx @@ -8,6 +8,8 @@ import clsx from 'clsx'; import React from 'react'; +import 'line-awesome/dist/font-awesome-line-awesome/css/all.css'; + interface IForkAwesomeIcon extends React.HTMLAttributes { id: string; className?: string; diff --git a/packages/nicolium/src/features/group/components/group-action-button.tsx b/packages/nicolium/src/components/groups/group-action-button.tsx similarity index 100% rename from packages/nicolium/src/features/group/components/group-action-button.tsx rename to packages/nicolium/src/components/groups/group-action-button.tsx diff --git a/packages/nicolium/src/components/group-card.tsx b/packages/nicolium/src/components/groups/group-card.tsx similarity index 82% rename from packages/nicolium/src/components/group-card.tsx rename to packages/nicolium/src/components/groups/group-card.tsx index 09d4047cd..2401dc8f1 100644 --- a/packages/nicolium/src/components/group-card.tsx +++ b/packages/nicolium/src/components/groups/group-card.tsx @@ -2,13 +2,13 @@ import React from 'react'; import Text from '@/components/ui/text'; import Emojify from '@/features/emoji/emojify'; -import GroupHeaderImage from '@/features/group/components/group-header-image'; -import GroupMemberCount from '@/features/group/components/group-member-count'; -import GroupPrivacy from '@/features/group/components/group-privacy'; -import GroupRelationship from '@/features/group/components/group-relationship'; import { useGroupQuery } from '@/queries/groups/use-group'; -import GroupAvatar from './groups/group-avatar'; +import GroupAvatar from './group-avatar'; +import GroupHeaderImage from './group-header-image'; +import GroupMemberCount from './group-member-count'; +import GroupPrivacy from './group-privacy'; +import GroupRelationship from './group-relationship'; interface IGroupCard { groupId: string; diff --git a/packages/nicolium/src/features/group/components/group-header-image.tsx b/packages/nicolium/src/components/groups/group-header-image.tsx similarity index 86% rename from packages/nicolium/src/features/group/components/group-header-image.tsx rename to packages/nicolium/src/components/groups/group-header-image.tsx index 416d55d72..afa7a0256 100644 --- a/packages/nicolium/src/features/group/components/group-header-image.tsx +++ b/packages/nicolium/src/components/groups/group-header-image.tsx @@ -1,3 +1,4 @@ +import iconImageSquare from '@phosphor-icons/core/regular/image-square.svg'; import clsx from 'clsx'; import React, { useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -32,10 +33,7 @@ const GroupHeaderImage: React.FC = ({ className, group }) => 'flex items-center justify-center bg-gray-200 dark:bg-gray-800/30', )} > - +
); } diff --git a/packages/nicolium/src/features/group/components/group-header.tsx b/packages/nicolium/src/components/groups/group-header.tsx similarity index 96% rename from packages/nicolium/src/features/group/components/group-header.tsx rename to packages/nicolium/src/components/groups/group-header.tsx index 5f0f0288b..e41304f43 100644 --- a/packages/nicolium/src/features/group/components/group-header.tsx +++ b/packages/nicolium/src/components/groups/group-header.tsx @@ -1,3 +1,4 @@ +import iconImageSquare from '@phosphor-icons/core/regular/image-square.svg'; import { mediaAttachmentSchema } from 'pl-api'; import React, { useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -118,10 +119,7 @@ const GroupHeader: React.FC = ({ group }) => { className='flex h-32 w-full items-center justify-center bg-gray-200 dark:bg-gray-800/30 md:rounded-t-xl lg:h-52' > {isHeaderMissing ? ( - + ) : ( header )} diff --git a/packages/nicolium/src/features/groups/components/discover/group-list-item.tsx b/packages/nicolium/src/components/groups/group-list-item.tsx similarity index 87% rename from packages/nicolium/src/features/groups/components/discover/group-list-item.tsx rename to packages/nicolium/src/components/groups/group-list-item.tsx index 9a65463ed..f8c4c3e5f 100644 --- a/packages/nicolium/src/features/groups/components/discover/group-list-item.tsx +++ b/packages/nicolium/src/components/groups/group-list-item.tsx @@ -1,3 +1,5 @@ +import iconGlobe from '@phosphor-icons/core/regular/globe.svg'; +import iconLock from '@phosphor-icons/core/regular/lock.svg'; import { Link } from '@tanstack/react-router'; import React from 'react'; import { FormattedMessage } from 'react-intl'; @@ -6,10 +8,11 @@ import GroupAvatar from '@/components/groups/group-avatar'; import Icon from '@/components/ui/icon'; import Text from '@/components/ui/text'; import Emojify from '@/features/emoji/emojify'; -import GroupActionButton from '@/features/group/components/group-action-button'; import { useGroupQuery } from '@/queries/groups/use-group'; import { shortNumberFormat } from '@/utils/numbers'; +import GroupActionButton from './group-action-button'; + interface IGroupListItem { groupId: string; withJoinAction?: boolean; @@ -37,14 +40,7 @@ const GroupListItem: React.FC = ({ groupId, withJoinAction = tru
- + {group.locked ? ( diff --git a/packages/nicolium/src/features/group/components/group-member-count.tsx b/packages/nicolium/src/components/groups/group-member-count.tsx similarity index 100% rename from packages/nicolium/src/features/group/components/group-member-count.tsx rename to packages/nicolium/src/components/groups/group-member-count.tsx diff --git a/packages/nicolium/src/features/group/components/group-member-list-item.tsx b/packages/nicolium/src/components/groups/group-member-list-item.tsx similarity index 95% rename from packages/nicolium/src/features/group/components/group-member-list-item.tsx rename to packages/nicolium/src/components/groups/group-member-list-item.tsx index 73f637b48..5f407eea0 100644 --- a/packages/nicolium/src/features/group/components/group-member-list-item.tsx +++ b/packages/nicolium/src/components/groups/group-member-list-item.tsx @@ -1,3 +1,6 @@ +import iconProhibit from '@phosphor-icons/core/regular/prohibit.svg'; +import iconSuitcase from '@phosphor-icons/core/regular/suitcase.svg'; +import iconUserMinus from '@phosphor-icons/core/regular/user-minus.svg'; import clsx from 'clsx'; import { GroupRoles } from 'pl-api'; import React, { useMemo } from 'react'; @@ -5,7 +8,7 @@ import { defineMessages, useIntl } from 'react-intl'; import Account from '@/components/accounts/account'; import DropdownMenu from '@/components/dropdown-menu/dropdown-menu'; -import PlaceholderAccount from '@/features/placeholder/components/placeholder-account'; +import PlaceholderAccount from '@/components/placeholders/placeholder-account'; import { useAccount } from '@/queries/accounts/use-account'; import { useBlockGroupUserMutation } from '@/queries/groups/use-group-blocks'; import { @@ -159,7 +162,7 @@ const GroupMemberListItem = ({ member, group }: IGroupMemberListItem) => { if (isMemberUser) { items.push({ text: intl.formatMessage(messages.groupModPromoteMod, { role: GroupRoles.ADMIN }), - icon: require('@phosphor-icons/core/regular/suitcase.svg'), + icon: iconSuitcase, action: handleAdminAssignment, }); } else if (isMemberAdmin) { @@ -168,7 +171,7 @@ const GroupMemberListItem = ({ member, group }: IGroupMemberListItem) => { role: GroupRoles.ADMIN, name: account.username, }), - icon: require('@phosphor-icons/core/regular/suitcase.svg'), + icon: iconSuitcase, action: handleUserAssignment, destructive: true, }); @@ -182,13 +185,13 @@ const GroupMemberListItem = ({ member, group }: IGroupMemberListItem) => { ) { items.push({ text: intl.formatMessage(messages.groupModKick, { name: account.username }), - icon: require('@phosphor-icons/core/regular/user-minus.svg'), + icon: iconUserMinus, action: handleKickFromGroup, }); items.push({ text: intl.formatMessage(messages.groupModBlock, { name: account.username }), - icon: require('@phosphor-icons/core/regular/prohibit.svg'), + icon: iconProhibit, action: handleBlockFromGroup, destructive: true, }); diff --git a/packages/nicolium/src/features/group/components/group-options-button.tsx b/packages/nicolium/src/components/groups/group-options-button.tsx similarity index 91% rename from packages/nicolium/src/features/group/components/group-options-button.tsx rename to packages/nicolium/src/components/groups/group-options-button.tsx index 9dc3dfdb7..b2df06703 100644 --- a/packages/nicolium/src/features/group/components/group-options-button.tsx +++ b/packages/nicolium/src/components/groups/group-options-button.tsx @@ -1,3 +1,6 @@ +import iconDotsThree from '@phosphor-icons/core/regular/dots-three.svg'; +import iconExport from '@phosphor-icons/core/regular/export.svg'; +import iconSignOut from '@phosphor-icons/core/regular/sign-out.svg'; import { GroupRoles, type Group } from 'pl-api'; import React, { useMemo } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -67,7 +70,7 @@ const GroupOptionsButton = ({ group }: IGroupActionButton) => { if (canShare) { items.push({ text: intl.formatMessage(messages.share), - icon: require('@phosphor-icons/core/regular/export.svg'), + icon: iconExport, action: handleShare, }); } @@ -76,7 +79,7 @@ const GroupOptionsButton = ({ group }: IGroupActionButton) => { items.push(null); items.push({ text: intl.formatMessage(messages.leave), - icon: require('@phosphor-icons/core/regular/sign-out.svg'), + icon: iconSignOut, action: handleLeave, }); } @@ -91,7 +94,7 @@ const GroupOptionsButton = ({ group }: IGroupActionButton) => { return ( (
@@ -54,14 +52,7 @@ const GroupPrivacy = ({ group }: IGroupPolicy) => ( } >
- + {group.locked ? ( diff --git a/packages/nicolium/src/features/group/components/group-relationship.tsx b/packages/nicolium/src/components/groups/group-relationship.tsx similarity index 82% rename from packages/nicolium/src/features/group/components/group-relationship.tsx rename to packages/nicolium/src/components/groups/group-relationship.tsx index 3b113cc71..6c881a3fb 100644 --- a/packages/nicolium/src/features/group/components/group-relationship.tsx +++ b/packages/nicolium/src/components/groups/group-relationship.tsx @@ -1,3 +1,5 @@ +import iconGavel from '@phosphor-icons/core/regular/gavel.svg'; +import iconUsers from '@phosphor-icons/core/regular/users.svg'; import { GroupRoles, type Group } from 'pl-api'; import React from 'react'; import { FormattedMessage } from 'react-intl'; @@ -22,14 +24,7 @@ const GroupRelationship = ({ group }: IGroupRelationship) => { data-testid='group-relationship' className='flex items-center gap-1 text-primary-600 dark:text-primary-400' > - + {isOwner ? ( diff --git a/packages/nicolium/src/components/groups/popover/group-popover.tsx b/packages/nicolium/src/components/groups/popover/group-popover.tsx index c0c810931..bc6599c1d 100644 --- a/packages/nicolium/src/components/groups/popover/group-popover.tsx +++ b/packages/nicolium/src/components/groups/popover/group-popover.tsx @@ -7,11 +7,11 @@ import Divider from '@/components/ui/divider'; import Popover from '@/components/ui/popover'; import Text from '@/components/ui/text'; import Emojify from '@/features/emoji/emojify'; -import GroupMemberCount from '@/features/group/components/group-member-count'; -import GroupPrivacy from '@/features/group/components/group-privacy'; import { groupTimelineRoute } from '@/features/ui/router'; import GroupAvatar from '../group-avatar'; +import GroupMemberCount from '../group-member-count'; +import GroupPrivacy from '../group-privacy'; import type { Group } from 'pl-api'; diff --git a/packages/nicolium/src/components/list.tsx b/packages/nicolium/src/components/list.tsx index e03bf8cd0..d5f3b3bbe 100644 --- a/packages/nicolium/src/components/list.tsx +++ b/packages/nicolium/src/components/list.tsx @@ -1,10 +1,11 @@ +import iconCaretRight from '@phosphor-icons/core/regular/caret-right.svg'; import { Link, type LinkOptions } from '@tanstack/react-router'; import clsx from 'clsx'; import React, { useState } from 'react'; import Icon from '@/components/ui/icon'; import Select from '@/components/ui/select'; -import { SelectDropdown } from '@/features/forms'; +import { SelectDropdown } from '@/components/ui/select-dropdown'; interface IList { children: React.ReactNode; @@ -105,7 +106,7 @@ const ListItem: React.FC = ({
{children} - +
) : null} diff --git a/packages/nicolium/src/components/load-gap.tsx b/packages/nicolium/src/components/load-gap.tsx index b604eba85..82b3af2db 100644 --- a/packages/nicolium/src/components/load-gap.tsx +++ b/packages/nicolium/src/components/load-gap.tsx @@ -1,3 +1,4 @@ +import iconDotsThree from '@phosphor-icons/core/regular/dots-three.svg'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -27,7 +28,7 @@ const LoadGap: React.FC = ({ disabled, maxId, onClick }) => { onClick={handleClick} aria-label={intl.formatMessage(messages.loadMore)} > - + ); }; diff --git a/packages/nicolium/src/components/location-search.tsx b/packages/nicolium/src/components/location-search.tsx index d7b70dcb5..62ae944a0 100644 --- a/packages/nicolium/src/components/location-search.tsx +++ b/packages/nicolium/src/components/location-search.tsx @@ -1,3 +1,5 @@ +import iconBackspace from '@phosphor-icons/core/regular/backspace.svg'; +import iconMagnifyingGlass from '@phosphor-icons/core/regular/magnifying-glass.svg'; import { useDebounce } from '@uidotdev/usehooks'; import clsx from 'clsx'; import React, { useState } from 'react'; @@ -81,13 +83,10 @@ const LocationSearch: React.FC = ({ id, onSelected }) => { title={intl.formatMessage(messages.clear)} > - +
); diff --git a/packages/nicolium/src/components/media/alt-indicator.tsx b/packages/nicolium/src/components/media/alt-indicator.tsx index abaa289e3..504ddfd46 100644 --- a/packages/nicolium/src/components/media/alt-indicator.tsx +++ b/packages/nicolium/src/components/media/alt-indicator.tsx @@ -1,3 +1,4 @@ +import iconWarning from '@phosphor-icons/core/regular/warning.svg'; import clsx from 'clsx'; import React from 'react'; import { FormattedMessage } from 'react-intl'; @@ -12,7 +13,7 @@ interface IAltIndicator extends Pick, 'tit const AltIndicator: React.FC = React.forwardRef( ({ className, warning, message, ...props }, ref) => ( - {warning && } + {warning && } {message ?? ( )} diff --git a/packages/nicolium/src/components/media/attachment-thumbs.tsx b/packages/nicolium/src/components/media/attachment-thumbs.tsx index f69d97da4..54f376437 100644 --- a/packages/nicolium/src/components/media/attachment-thumbs.tsx +++ b/packages/nicolium/src/components/media/attachment-thumbs.tsx @@ -6,7 +6,7 @@ import { useSettings } from '@/stores/settings'; import { useMediaVisible } from '../statuses/sensitive-content-overlay'; -import type { NormalizedStatus as Status } from '@/normalizers/status'; +import type { NormalizedStatus as Status } from '@/queries/statuses/normalize'; import type { MediaAttachment } from 'pl-api'; interface IAttachmentThumbs { diff --git a/packages/nicolium/src/features/audio/index.tsx b/packages/nicolium/src/components/media/audio.tsx similarity index 96% rename from packages/nicolium/src/features/audio/index.tsx rename to packages/nicolium/src/components/media/audio.tsx index 0b712aa82..15705828b 100644 --- a/packages/nicolium/src/features/audio/index.tsx +++ b/packages/nicolium/src/components/media/audio.tsx @@ -1,3 +1,8 @@ +import iconDownloadSimple from '@phosphor-icons/core/regular/download-simple.svg'; +import iconPause from '@phosphor-icons/core/regular/pause.svg'; +import iconPlay from '@phosphor-icons/core/regular/play.svg'; +import iconSpeakerHigh from '@phosphor-icons/core/regular/speaker-high.svg'; +import iconSpeakerX from '@phosphor-icons/core/regular/speaker-x.svg'; import clsx from 'clsx'; import debounce from 'lodash/debounce'; import throttle from 'lodash/throttle'; @@ -5,7 +10,7 @@ import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from import { defineMessages, useIntl } from 'react-intl'; import Icon from '@/components/icon'; -import { formatTime, getPointerPosition } from '@/features/video'; +import { formatTime, getPointerPosition } from '@/components/media/video'; import { useSettings } from '@/stores/settings'; import Visualizer from './visualizer'; @@ -554,13 +559,7 @@ const Audio: React.FC = (props) => { className={clsx('player-button', fullscreen && 'py-2.5')} onClick={togglePlay} > - +
= (props) => { download target='_blank' > - +
diff --git a/packages/nicolium/src/components/media/media-gallery.tsx b/packages/nicolium/src/components/media/media-gallery.tsx index e3bc444ea..c01abf48c 100644 --- a/packages/nicolium/src/components/media/media-gallery.tsx +++ b/packages/nicolium/src/components/media/media-gallery.tsx @@ -1,3 +1,5 @@ +import iconPaperclip from '@phosphor-icons/core/regular/paperclip.svg'; +import iconSpeakerHigh from '@phosphor-icons/core/regular/speaker-high.svg'; import clsx from 'clsx'; import React, { useState, useRef, useLayoutEffect, type CSSProperties } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -115,19 +117,18 @@ const Item: React.FC = ({ // FIXME: wtf? const handleClick: React.MouseEventHandler = (e: any) => { + e.preventDefault(); + e.stopPropagation(); + if (isIOS() && !e.target.autoPlay) { e.target.autoPlay = true; - e.preventDefault(); } else if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { if (hoverToPlay()) { e.target.pause(); e.target.currentTime = 0; } - e.preventDefault(); onClick(index); } - - e.stopPropagation(); }; const handleVideoHover: React.MouseEventHandler = ({ @@ -172,10 +173,7 @@ const Item: React.FC = ({ const attachmentIcon = ( ); @@ -278,7 +276,7 @@ const Item: React.FC = ({ title={attachment.description} > - + {ext} @@ -386,7 +384,7 @@ const MediaGallery: React.FC = (props) => { src={ MIMETYPE_ICONS[ (attachment.type === 'unknown' && attachment.mime_type) || attachment.type - ] ?? require('@phosphor-icons/core/regular/paperclip.svg') + ] ?? iconPaperclip } /> @@ -413,7 +411,6 @@ const MediaGallery: React.FC = (props) => { }[attachment.type]} - // ))}
); diff --git a/packages/nicolium/src/features/video/index.tsx b/packages/nicolium/src/components/media/video.tsx similarity index 95% rename from packages/nicolium/src/features/video/index.tsx rename to packages/nicolium/src/components/media/video.tsx index c8987efc9..e8c82527b 100644 --- a/packages/nicolium/src/features/video/index.tsx +++ b/packages/nicolium/src/components/media/video.tsx @@ -1,3 +1,10 @@ +import iconArrowsInSimple from '@phosphor-icons/core/regular/arrows-in-simple.svg'; +import iconArrowsOutSimple from '@phosphor-icons/core/regular/arrows-out-simple.svg'; +import iconDownloadSimple from '@phosphor-icons/core/regular/download-simple.svg'; +import iconPause from '@phosphor-icons/core/regular/pause.svg'; +import iconPlay from '@phosphor-icons/core/regular/play.svg'; +import iconSpeakerHigh from '@phosphor-icons/core/regular/speaker-high.svg'; +import iconSpeakerX from '@phosphor-icons/core/regular/speaker-x.svg'; import clsx from 'clsx'; import debounce from 'lodash/debounce'; import throttle from 'lodash/throttle'; @@ -7,6 +14,7 @@ import { defineMessages, useIntl } from 'react-intl'; import Icon from '@/components/icon'; import Blurhash from '@/components/media/blurhash'; import { useSettings } from '@/stores/settings'; +import { isFullscreen, requestFullscreen, exitFullscreen } from '@/utils/fullscreen'; import { isPanoramic, isPortrait, @@ -14,8 +22,6 @@ import { maximumAspectRatio, } from '@/utils/media-aspect-ratio'; -import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen'; - const DEFAULT_HEIGHT = 300; type Position = { x: number; y: number }; @@ -567,13 +573,7 @@ const Video: React.FC = ({ onClick={togglePlay} autoFocus={autoFocus} > - +
= ({ download target='_blank' > - +
diff --git a/packages/nicolium/src/features/audio/visualizer.ts b/packages/nicolium/src/components/media/visualizer.ts similarity index 100% rename from packages/nicolium/src/features/audio/visualizer.ts rename to packages/nicolium/src/components/media/visualizer.ts diff --git a/packages/nicolium/src/features/ui/components/zoomable-image.tsx b/packages/nicolium/src/components/media/zoomable-image.tsx similarity index 100% rename from packages/nicolium/src/features/ui/components/zoomable-image.tsx rename to packages/nicolium/src/components/media/zoomable-image.tsx diff --git a/packages/nicolium/src/features/ui/components/compose-button.tsx b/packages/nicolium/src/components/navigation/compose-button.tsx similarity index 92% rename from packages/nicolium/src/features/ui/components/compose-button.tsx rename to packages/nicolium/src/components/navigation/compose-button.tsx index 14c4854cd..b2613a6f0 100644 --- a/packages/nicolium/src/features/ui/components/compose-button.tsx +++ b/packages/nicolium/src/components/navigation/compose-button.tsx @@ -1,3 +1,4 @@ +import iconPlus from '@phosphor-icons/core/regular/plus.svg'; import { useMatch } from '@tanstack/react-router'; import React from 'react'; import { FormattedMessage } from 'react-intl'; @@ -8,7 +9,7 @@ import { useGroupQuery } from '@/queries/groups/use-group'; import { useComposeActions } from '@/stores/compose'; import { useModalsActions } from '@/stores/modals'; -import { layouts } from '../router'; +import { layouts } from '../../features/ui/router'; interface IComposeButton { /** Whether the button should shrink to fit in a smaller space. */ @@ -36,7 +37,7 @@ const HomeComposeButton: React.FC = ({ shrink }) => { return ( {switcher && } @@ -519,7 +560,7 @@ const DropdownNavigation: React.FC = React.memo((): React.JSX.Element | null => <> @@ -533,7 +574,7 @@ const DropdownNavigation: React.FC = React.memo((): React.JSX.Element | null => {features.bubbleTimeline && timelineAccess.live_feeds.bubble === 'public' && ( } onClick={closeSidebar} /> @@ -542,7 +583,7 @@ const DropdownNavigation: React.FC = React.memo((): React.JSX.Element | null => {features.federating && timelineAccess.live_feeds.remote === 'public' && ( } onClick={closeSidebar} /> @@ -551,7 +592,7 @@ const DropdownNavigation: React.FC = React.memo((): React.JSX.Element | null => {features.wrenchedTimeline && timelineAccess.live_feeds.wrenched === 'public' && ( } onClick={closeSidebar} /> @@ -563,7 +604,7 @@ const DropdownNavigation: React.FC = React.memo((): React.JSX.Element | null => } onClick={closeSidebar} /> @@ -571,7 +612,7 @@ const DropdownNavigation: React.FC = React.memo((): React.JSX.Element | null => {isOpen && ( } onClick={closeSidebar} /> @@ -581,7 +622,7 @@ const DropdownNavigation: React.FC = React.memo((): React.JSX.Element | null => } onClick={closeSidebar} /> diff --git a/packages/nicolium/src/features/ui/components/link-footer.tsx b/packages/nicolium/src/components/navigation/link-footer.tsx similarity index 100% rename from packages/nicolium/src/features/ui/components/link-footer.tsx rename to packages/nicolium/src/components/navigation/link-footer.tsx diff --git a/packages/nicolium/src/components/navigation/sidebar-navigation.tsx b/packages/nicolium/src/components/navigation/sidebar-navigation.tsx index 21df5a1d6..bbc661d2f 100644 --- a/packages/nicolium/src/components/navigation/sidebar-navigation.tsx +++ b/packages/nicolium/src/components/navigation/sidebar-navigation.tsx @@ -1,11 +1,60 @@ +import iconBellSimpleFill from '@phosphor-icons/core/fill/bell-simple-fill.svg'; +import iconChatsTeardropFill from '@phosphor-icons/core/fill/chats-teardrop-fill.svg'; +import iconCloudFill from '@phosphor-icons/core/fill/cloud-fill.svg'; +import iconEnvelopeSimpleFill from '@phosphor-icons/core/fill/envelope-simple-fill.svg'; +import iconFediverseLogoFill from '@phosphor-icons/core/fill/fediverse-logo-fill.svg'; +import iconGaugeFill from '@phosphor-icons/core/fill/gauge-fill.svg'; +import iconGraphFill from '@phosphor-icons/core/fill/graph-fill.svg'; +import iconHouseFill from '@phosphor-icons/core/fill/house-fill.svg'; +import iconMagnifyingGlassFill from '@phosphor-icons/core/fill/magnifying-glass-fill.svg'; +import iconPlanetFill from '@phosphor-icons/core/fill/planet-fill.svg'; +import iconPlantFill from '@phosphor-icons/core/fill/plant-fill.svg'; +import iconSignInFill from '@phosphor-icons/core/fill/sign-in-fill.svg'; +import iconSlidersHorizontalFill from '@phosphor-icons/core/fill/sliders-horizontal-fill.svg'; +import iconUserFill from '@phosphor-icons/core/fill/user-fill.svg'; +import iconUserPlusFill from '@phosphor-icons/core/fill/user-plus-fill.svg'; +import iconUsersThreeFill from '@phosphor-icons/core/fill/users-three-fill.svg'; +import iconAddressBook from '@phosphor-icons/core/regular/address-book.svg'; +import iconBellSimple from '@phosphor-icons/core/regular/bell-simple.svg'; +import iconBookOpen from '@phosphor-icons/core/regular/book-open.svg'; +import iconBookmarks from '@phosphor-icons/core/regular/bookmarks.svg'; +import iconBroadcast from '@phosphor-icons/core/regular/broadcast.svg'; +import iconCalendarDots from '@phosphor-icons/core/regular/calendar-dots.svg'; +import iconCaretDown from '@phosphor-icons/core/regular/caret-down.svg'; +import iconChatsTeardrop from '@phosphor-icons/core/regular/chats-teardrop.svg'; +import iconCirclesThree from '@phosphor-icons/core/regular/circles-three.svg'; +import iconCloud from '@phosphor-icons/core/regular/cloud.svg'; +import iconCode from '@phosphor-icons/core/regular/code.svg'; +import iconDotsThreeCircle from '@phosphor-icons/core/regular/dots-three-circle.svg'; +import iconEnvelopeSimple from '@phosphor-icons/core/regular/envelope-simple.svg'; +import iconFediverseLogo from '@phosphor-icons/core/regular/fediverse-logo.svg'; +import iconGauge from '@phosphor-icons/core/regular/gauge.svg'; +import iconGraph from '@phosphor-icons/core/regular/graph.svg'; +import iconHash from '@phosphor-icons/core/regular/hash.svg'; +import iconHeartHalf from '@phosphor-icons/core/regular/heart-half.svg'; +import iconHourglass from '@phosphor-icons/core/regular/hourglass.svg'; +import iconHouse from '@phosphor-icons/core/regular/house.svg'; +import iconKeyboard from '@phosphor-icons/core/regular/keyboard.svg'; +import iconListDashes from '@phosphor-icons/core/regular/list-dashes.svg'; +import iconMagnifyingGlass from '@phosphor-icons/core/regular/magnifying-glass.svg'; +import iconPencilSimple from '@phosphor-icons/core/regular/pencil-simple.svg'; +import iconPlanet from '@phosphor-icons/core/regular/planet.svg'; +import iconPlant from '@phosphor-icons/core/regular/plant.svg'; +import iconQuestion from '@phosphor-icons/core/regular/question.svg'; +import iconRss from '@phosphor-icons/core/regular/rss.svg'; +import iconSignIn from '@phosphor-icons/core/regular/sign-in.svg'; +import iconSlidersHorizontal from '@phosphor-icons/core/regular/sliders-horizontal.svg'; +import iconUserPlus from '@phosphor-icons/core/regular/user-plus.svg'; +import iconUser from '@phosphor-icons/core/regular/user.svg'; +import iconUsersThree from '@phosphor-icons/core/regular/users-three.svg'; import { useInfiniteQuery } from '@tanstack/react-query'; import clsx from 'clsx'; import React, { useMemo } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; +import ComposeButton from '@/components/navigation/compose-button'; import Icon from '@/components/ui/icon'; import { useStatContext } from '@/contexts/stat-context'; -import ComposeButton from '@/features/ui/components/compose-button'; import ProfileDropdown from '@/features/ui/components/profile-dropdown'; import { useClient } from '@/hooks/use-client'; import { useFeatures } from '@/hooks/use-features'; @@ -101,7 +150,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push({ to: '/conversations', text: intl.formatMessage(messages.conversations), - icon: require('@phosphor-icons/core/regular/envelope-simple.svg'), + icon: iconEnvelopeSimple, }); } @@ -109,7 +158,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push({ to: '/follow_requests', text: intl.formatMessage(messages.followRequests), - icon: require('@phosphor-icons/core/regular/user-plus.svg'), + icon: iconUserPlus, count: followRequestsCount, }); } @@ -118,7 +167,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push({ to: '/interaction_requests', text: intl.formatMessage(messages.interactionRequests), - icon: require('@phosphor-icons/core/regular/heart-half.svg'), + icon: iconHeartHalf, count: interactionRequestsCount, }); } @@ -127,7 +176,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push({ to: '/bookmarks', text: intl.formatMessage(messages.bookmarks), - icon: require('@phosphor-icons/core/regular/bookmarks.svg'), + icon: iconBookmarks, }); } @@ -135,7 +184,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push({ to: '/lists', text: intl.formatMessage(messages.lists), - icon: require('@phosphor-icons/core/regular/list-dashes.svg'), + icon: iconListDashes, }); } @@ -143,7 +192,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push({ to: '/circles', text: intl.formatMessage(messages.circles), - icon: require('@phosphor-icons/core/regular/circles-three.svg'), + icon: iconCirclesThree, }); } @@ -151,7 +200,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push({ to: '/antennas', text: intl.formatMessage({ id: 'column.antennas', defaultMessage: 'Antennas' }), - icon: require('@phosphor-icons/core/regular/broadcast.svg'), + icon: iconBroadcast, }); } @@ -159,7 +208,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push({ to: '/events', text: intl.formatMessage(messages.events), - icon: require('@phosphor-icons/core/regular/calendar-dots.svg'), + icon: iconCalendarDots, }); } @@ -167,7 +216,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push({ to: '/directory', text: intl.formatMessage(messages.profileDirectory), - icon: require('@phosphor-icons/core/regular/address-book.svg'), + icon: iconAddressBook, }); } @@ -175,7 +224,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push({ to: '/followed_tags', text: intl.formatMessage(messages.followedTags), - icon: require('@phosphor-icons/core/regular/hash.svg'), + icon: iconHash, }); } @@ -183,14 +232,14 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push({ to: '/rss_feed_subscriptions', text: intl.formatMessage(messages.rssFeedSubscriptions), - icon: require('@phosphor-icons/core/regular/rss.svg'), + icon: iconRss, }); } if (scheduledStatusCount > 0) { menu.push({ to: '/scheduled_statuses', - icon: require('@phosphor-icons/core/regular/hourglass.svg'), + icon: iconHourglass, text: intl.formatMessage(messages.scheduledStatuses), count: scheduledStatusCount, }); @@ -199,7 +248,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) if (draftCount > 0) { menu.push({ to: '/draft_statuses', - icon: require('@phosphor-icons/core/regular/pencil-simple.svg'), + icon: iconPencilSimple, text: intl.formatMessage(messages.drafts), count: draftCount, }); @@ -208,26 +257,26 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) menu.push(null); menu.push({ - icon: require('@phosphor-icons/core/regular/question.svg'), + icon: iconQuestion, text: intl.formatMessage(messages.help), items: [ { action: () => { openModal('HOTKEYS'); }, - icon: require('@phosphor-icons/core/regular/keyboard.svg'), + icon: iconKeyboard, text: intl.formatMessage(messages.keyboardShortcuts), }, { href: 'https://nicolium.app/docs/', target: '_blank', - icon: require('@phosphor-icons/core/regular/book-open.svg'), + icon: iconBookOpen, text: intl.formatMessage(messages.docs), }, { href: sourceCode.url, target: '_blank', - icon: require('@phosphor-icons/core/regular/code.svg'), + icon: iconCode, text: intl.formatMessage(messages.sourceCode), }, ], @@ -265,7 +314,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) account={account} action={ } @@ -282,15 +331,15 @@ const SidebarNavigation: React.FC = React.memo(({ shrink })
    } /> } /> @@ -298,8 +347,8 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) <> } /> @@ -307,8 +356,8 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) {features.chats && ( } @@ -318,8 +367,8 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) {!features.chats && features.conversations && ( } /> )} @@ -327,8 +376,8 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) {features.groups && ( } /> )} @@ -336,32 +385,32 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) } /> {features.drive && ( } /> )} } /> {(account.is_admin ?? account.is_moderator) && ( } /> @@ -377,8 +426,8 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) : timelineAccess.live_feeds.wrenched === 'public') && ( } /> )} @@ -388,8 +437,8 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) : timelineAccess.live_feeds.local === 'public') && ( @@ -406,8 +455,8 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) : timelineAccess.live_feeds.bubble === 'public') && ( } /> )} @@ -418,8 +467,8 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) : timelineAccess.live_feeds.remote === 'public') && ( } /> )} @@ -429,7 +478,7 @@ const SidebarNavigation: React.FC = React.memo(({ shrink }) {menu.length > 0 && ( } /> @@ -439,16 +488,16 @@ const SidebarNavigation: React.FC = React.memo(({ shrink })
    } /> {isOpen && ( } /> )} diff --git a/packages/nicolium/src/components/navigation/thumb-navigation.tsx b/packages/nicolium/src/components/navigation/thumb-navigation.tsx index 80c81fec5..3a84832a1 100644 --- a/packages/nicolium/src/components/navigation/thumb-navigation.tsx +++ b/packages/nicolium/src/components/navigation/thumb-navigation.tsx @@ -1,3 +1,13 @@ +import iconBellSimpleFill from '@phosphor-icons/core/fill/bell-simple-fill.svg'; +import iconChatsTeardropFill from '@phosphor-icons/core/fill/chats-teardrop-fill.svg'; +import iconHouseFill from '@phosphor-icons/core/fill/house-fill.svg'; +import iconMagnifyingGlassFill from '@phosphor-icons/core/fill/magnifying-glass-fill.svg'; +import iconBellSimple from '@phosphor-icons/core/regular/bell-simple.svg'; +import iconChatsTeardrop from '@phosphor-icons/core/regular/chats-teardrop.svg'; +import iconHouse from '@phosphor-icons/core/regular/house.svg'; +import iconList from '@phosphor-icons/core/regular/list.svg'; +import iconMagnifyingGlass from '@phosphor-icons/core/regular/magnifying-glass.svg'; +import iconPlusSquare from '@phosphor-icons/core/regular/plus-square.svg'; import { useQueryClient } from '@tanstack/react-query'; import { useMatch } from '@tanstack/react-router'; import React from 'react'; @@ -58,7 +68,7 @@ const ThumbNavigation: React.FC = React.memo((): React.JSX.Element => { onClick={handleOpenComposeModal} title={intl.formatMessage(messages.compose)} > - + ); @@ -69,12 +79,12 @@ const ThumbNavigation: React.FC = React.memo((): React.JSX.Element => { onClick={isSidebarOpen ? closeSidebar : openSidebar} title={intl.formatMessage(isSidebarOpen ? messages.closeSidebar : messages.openSidebar)} > - + { {/* {features.groups && ( } to='/groups' exact @@ -94,8 +104,8 @@ const ThumbNavigation: React.FC = React.memo((): React.JSX.Element => { {(!standalone || account) && ( { {account && ( { {account && features.chats && ( <> > = { - follow: require('@phosphor-icons/core/regular/user-plus.svg'), - follow_request: require('@phosphor-icons/core/regular/user-plus.svg'), - follow_request_accepted: require('@phosphor-icons/core/regular/user-plus.svg'), - mention: require('@phosphor-icons/core/regular/at.svg'), - favourite: require('@phosphor-icons/core/regular/star.svg'), - reblog: require('@phosphor-icons/core/regular/repeat.svg'), - status: require('@phosphor-icons/core/regular/bell-simple-ringing.svg'), - poll: require('@phosphor-icons/core/regular/chart-bar.svg'), - move: require('@phosphor-icons/core/regular/suitcase.svg'), - chat_mention: require('@phosphor-icons/core/regular/chats-teardrop.svg'), - emoji_reaction: require('@phosphor-icons/core/regular/smiley.svg'), - update: require('@phosphor-icons/core/regular/pencil-simple-line.svg'), - event_reminder: require('@phosphor-icons/core/regular/calendar-star.svg'), - participation_request: require('@phosphor-icons/core/regular/calendar-dot.svg'), - participation_accepted: require('@phosphor-icons/core/regular/calendar-dot.svg'), - bite: require('@phosphor-icons/core/regular/tooth.svg'), - reply: require('@phosphor-icons/core/regular/arrow-bend-up-left.svg'), - quote: require('@phosphor-icons/core/regular/quotes.svg'), - quoted_update: require('@phosphor-icons/core/regular/pencil-simple-line.svg'), + follow: iconUserPlus, + follow_request: iconUserPlus, + follow_request_accepted: iconUserPlus, + mention: iconAt, + favourite: iconStar, + reblog: iconRepeat, + status: iconBellSimpleRinging, + poll: iconChartBar, + move: iconSuitcase, + chat_mention: iconChatsTeardrop, + emoji_reaction: iconSmiley, + update: iconPencilSimpleLine, + event_reminder: iconCalendarStar, + participation_request: iconCalendarDot, + participation_accepted: iconCalendarDot, + bite: iconTooth, + reply: iconArrowBendUpLeft, + quote: iconQuotes, + quoted_update: iconPencilSimpleLine, }; // For use by the service worker diff --git a/packages/nicolium/src/features/ui/components/panels/account-note-panel.tsx b/packages/nicolium/src/components/panels/account-note-panel.tsx similarity index 100% rename from packages/nicolium/src/features/ui/components/panels/account-note-panel.tsx rename to packages/nicolium/src/components/panels/account-note-panel.tsx diff --git a/packages/nicolium/src/features/ui/components/panels/birthday-panel.tsx b/packages/nicolium/src/components/panels/birthday-panel.tsx similarity index 100% rename from packages/nicolium/src/features/ui/components/panels/birthday-panel.tsx rename to packages/nicolium/src/components/panels/birthday-panel.tsx diff --git a/packages/nicolium/src/features/ui/components/panels/group-media-panel.tsx b/packages/nicolium/src/components/panels/group-media-panel.tsx similarity index 100% rename from packages/nicolium/src/features/ui/components/panels/group-media-panel.tsx rename to packages/nicolium/src/components/panels/group-media-panel.tsx diff --git a/packages/nicolium/src/features/ui/components/panels/instance-moderation-panel.tsx b/packages/nicolium/src/components/panels/instance-moderation-panel.tsx similarity index 84% rename from packages/nicolium/src/features/ui/components/panels/instance-moderation-panel.tsx rename to packages/nicolium/src/components/panels/instance-moderation-panel.tsx index 5ec95f9d9..95e0e5e04 100644 --- a/packages/nicolium/src/features/ui/components/panels/instance-moderation-panel.tsx +++ b/packages/nicolium/src/components/panels/instance-moderation-panel.tsx @@ -1,3 +1,5 @@ +import iconDotsThreeVertical from '@phosphor-icons/core/regular/dots-three-vertical.svg'; +import iconPencilSimple from '@phosphor-icons/core/regular/pencil-simple.svg'; import React from 'react'; import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; @@ -33,7 +35,7 @@ const InstanceModerationPanel: React.FC = ({ host }) = { text: intl.formatMessage(messages.editFederation), action: handleEditFederation, - icon: require('@phosphor-icons/core/regular/pencil-simple.svg'), + icon: iconPencilSimple, }, ]; @@ -48,12 +50,7 @@ const InstanceModerationPanel: React.FC = ({ host }) = /> } action={ - account?.is_admin ? ( - - ) : undefined + account?.is_admin ? : undefined } > diff --git a/packages/nicolium/src/features/ui/components/panels/my-groups-panel.tsx b/packages/nicolium/src/components/panels/my-groups-panel.tsx similarity index 83% rename from packages/nicolium/src/features/ui/components/panels/my-groups-panel.tsx rename to packages/nicolium/src/components/panels/my-groups-panel.tsx index ead19bba7..aa9c07faf 100644 --- a/packages/nicolium/src/features/ui/components/panels/my-groups-panel.tsx +++ b/packages/nicolium/src/components/panels/my-groups-panel.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; +import GroupListItem from '@/components/groups/group-list-item'; +import PlaceholderGroupSearch from '@/components/placeholders/placeholder-group-search'; import Widget from '@/components/ui/widget'; -import GroupListItem from '@/features/groups/components/discover/group-list-item'; -import PlaceholderGroupSearch from '@/features/placeholder/components/placeholder-group-search'; import { useGroupsQuery } from '@/queries/groups/use-groups'; const MyGroupsPanel = () => { diff --git a/packages/nicolium/src/features/ui/components/panels/new-event-panel.tsx b/packages/nicolium/src/components/panels/new-event-panel.tsx similarity index 83% rename from packages/nicolium/src/features/ui/components/panels/new-event-panel.tsx rename to packages/nicolium/src/components/panels/new-event-panel.tsx index 023dee8a8..836ba455e 100644 --- a/packages/nicolium/src/features/ui/components/panels/new-event-panel.tsx +++ b/packages/nicolium/src/components/panels/new-event-panel.tsx @@ -1,3 +1,4 @@ +import iconCalendarDot from '@phosphor-icons/core/regular/calendar-dot.svg'; import React from 'react'; import { FormattedMessage } from 'react-intl'; @@ -19,12 +20,7 @@ const NewEventPanel = () => (
    -
diff --git a/packages/nicolium/src/features/ui/components/panels/new-group-panel.tsx b/packages/nicolium/src/components/panels/new-group-panel.tsx similarity index 100% rename from packages/nicolium/src/features/ui/components/panels/new-group-panel.tsx rename to packages/nicolium/src/components/panels/new-group-panel.tsx diff --git a/packages/nicolium/src/features/ui/components/panels/notifications-panel.tsx b/packages/nicolium/src/components/panels/notifications-panel.tsx similarity index 100% rename from packages/nicolium/src/features/ui/components/panels/notifications-panel.tsx rename to packages/nicolium/src/components/panels/notifications-panel.tsx diff --git a/packages/nicolium/src/features/ui/components/panels/pinned-accounts-panel.tsx b/packages/nicolium/src/components/panels/pinned-accounts-panel.tsx similarity index 100% rename from packages/nicolium/src/features/ui/components/panels/pinned-accounts-panel.tsx rename to packages/nicolium/src/components/panels/pinned-accounts-panel.tsx diff --git a/packages/nicolium/src/features/ui/components/panels/profile-fields-panel.tsx b/packages/nicolium/src/components/panels/profile-fields-panel.tsx similarity index 89% rename from packages/nicolium/src/features/ui/components/panels/profile-fields-panel.tsx rename to packages/nicolium/src/components/panels/profile-fields-panel.tsx index aa6857423..b77979871 100644 --- a/packages/nicolium/src/features/ui/components/panels/profile-fields-panel.tsx +++ b/packages/nicolium/src/components/panels/profile-fields-panel.tsx @@ -1,8 +1,7 @@ import React from 'react'; import Widget from '@/components/ui/widget'; - -import { ProfileField } from '../../util/async-components'; +import { ProfileField } from '@/features/ui/util/async-components'; import type { Account } from 'pl-api'; diff --git a/packages/nicolium/src/features/ui/components/panels/profile-info-panel.tsx b/packages/nicolium/src/components/panels/profile-info-panel.tsx similarity index 89% rename from packages/nicolium/src/features/ui/components/panels/profile-info-panel.tsx rename to packages/nicolium/src/components/panels/profile-info-panel.tsx index 2b9ede380..13d4abcff 100644 --- a/packages/nicolium/src/features/ui/components/panels/profile-info-panel.tsx +++ b/packages/nicolium/src/components/panels/profile-info-panel.tsx @@ -1,6 +1,14 @@ +import iconBalloon from '@phosphor-icons/core/regular/balloon.svg'; +import iconCake from '@phosphor-icons/core/regular/cake.svg'; +import iconCalendarDots from '@phosphor-icons/core/regular/calendar-dots.svg'; +import iconLock from '@phosphor-icons/core/regular/lock.svg'; +import iconMapPin from '@phosphor-icons/core/regular/map-pin.svg'; +import iconTag from '@phosphor-icons/core/regular/tag.svg'; import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; +import ProfileFamiliarFollowers from '@/components/accounts/profile-familiar-followers'; +import ProfileStats from '@/components/accounts/profile-stats'; import Scrobble from '@/components/accounts/scrobble'; import Badge from '@/components/badge'; import Markup from '@/components/markup'; @@ -10,6 +18,7 @@ import Icon from '@/components/ui/icon'; import Text from '@/components/ui/text'; import { useCurrentAccount } from '@/contexts/current-account-context'; import Emojify from '@/features/emoji/emojify'; +import { ProfileField } from '@/features/ui/util/async-components'; import { useAcct } from '@/hooks/use-acct'; import { useFrontendConfig } from '@/hooks/use-frontend-config'; import { useAccountScrobbleQuery } from '@/queries/accounts/account-scrobble'; @@ -17,10 +26,6 @@ import { usePatronUser } from '@/queries/patron/use-patron-user'; import { useSettings } from '@/stores/settings'; import { capitalize } from '@/utils/strings'; -import { ProfileField } from '../../util/async-components'; -import ProfileFamiliarFollowers from '../profile-familiar-followers'; -import ProfileStats from '../profile-stats'; - import type { Account } from 'pl-api'; const messages = defineMessages({ @@ -148,11 +153,7 @@ const ProfileInfoPanel: React.FC = ({ account, username }) => return (
@@ -195,7 +196,7 @@ const ProfileInfoPanel: React.FC = ({ account, username }) => {account.deactivated ? ( ) : ( - + )} @@ -224,7 +225,7 @@ const ProfileInfoPanel: React.FC = ({ account, username }) => {account.locked && ( @@ -248,10 +249,7 @@ const ProfileInfoPanel: React.FC = ({ account, username }) =>
{account.local ? (
- + = ({ account, username }) => {account.location ? (
- + {account.location}
@@ -285,10 +280,7 @@ const ProfileInfoPanel: React.FC = ({ account, username }) => pronouns: account.pronouns.join('/'), })} > - + {account.pronouns.join('/')}
diff --git a/packages/nicolium/src/features/ui/components/panels/profile-media-panel.tsx b/packages/nicolium/src/components/panels/profile-media-panel.tsx similarity index 100% rename from packages/nicolium/src/features/ui/components/panels/profile-media-panel.tsx rename to packages/nicolium/src/components/panels/profile-media-panel.tsx diff --git a/packages/nicolium/src/features/ui/components/panels/promo-panel.tsx b/packages/nicolium/src/components/panels/promo-panel.tsx similarity index 100% rename from packages/nicolium/src/features/ui/components/panels/promo-panel.tsx rename to packages/nicolium/src/components/panels/promo-panel.tsx diff --git a/packages/nicolium/src/features/ui/components/panels/sign-up-panel.tsx b/packages/nicolium/src/components/panels/sign-up-panel.tsx similarity index 94% rename from packages/nicolium/src/features/ui/components/panels/sign-up-panel.tsx rename to packages/nicolium/src/components/panels/sign-up-panel.tsx index 3f9e8af6c..4de4b494f 100644 --- a/packages/nicolium/src/features/ui/components/panels/sign-up-panel.tsx +++ b/packages/nicolium/src/components/panels/sign-up-panel.tsx @@ -6,10 +6,10 @@ import { fetchInstance } from '@/actions/instance'; import Button from '@/components/ui/button'; import Text from '@/components/ui/text'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import LoginForm from '@/features/auth-login/components/login-form'; -import OtpAuthForm from '@/features/auth-login/components/otp-auth-form'; -import ExternalLoginForm from '@/features/external-login/components/external-login-form'; import { useRegistrationStatus } from '@/hooks/use-registration-status'; +import ExternalLoginForm from '@/pages/auth/components/external-login-form'; +import LoginForm from '@/pages/auth/components/login-form'; +import OtpAuthForm from '@/pages/auth/components/otp-auth-form'; import { useAuthActions } from '@/stores/auth'; import { useInstance } from '@/stores/instance'; import { getRedirectUrl } from '@/utils/redirect'; diff --git a/packages/nicolium/src/features/ui/components/panels/trends-panel.tsx b/packages/nicolium/src/components/panels/trends-panel.tsx similarity index 92% rename from packages/nicolium/src/features/ui/components/panels/trends-panel.tsx rename to packages/nicolium/src/components/panels/trends-panel.tsx index 28c8287aa..df4be30e3 100644 --- a/packages/nicolium/src/features/ui/components/panels/trends-panel.tsx +++ b/packages/nicolium/src/components/panels/trends-panel.tsx @@ -3,9 +3,9 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import Hashtag from '@/components/hashtag'; +import PlaceholderSidebarTrends from '@/components/placeholders/placeholder-sidebar-trends'; import Text from '@/components/ui/text'; import Widget from '@/components/ui/widget'; -import PlaceholderSidebarTrends from '@/features/placeholder/components/placeholder-sidebar-trends'; import useTrendingTags from '@/queries/trends/use-trending-tags'; interface ITrendsPanel { diff --git a/packages/nicolium/src/features/ui/components/panels/user-panel.tsx b/packages/nicolium/src/components/panels/user-panel.tsx similarity index 98% rename from packages/nicolium/src/features/ui/components/panels/user-panel.tsx rename to packages/nicolium/src/components/panels/user-panel.tsx index fe0838709..8f5ed5c7f 100644 --- a/packages/nicolium/src/features/ui/components/panels/user-panel.tsx +++ b/packages/nicolium/src/components/panels/user-panel.tsx @@ -1,3 +1,4 @@ +import iconLock from '@phosphor-icons/core/regular/lock.svg'; import { Link } from '@tanstack/react-router'; import clsx from 'clsx'; import React from 'react'; @@ -121,7 +122,7 @@ const UserPanel: React.FC = ({ accountId, action, badges, domain, is {account.locked && ( diff --git a/packages/nicolium/src/features/ui/components/panels/who-to-follow-panel.tsx b/packages/nicolium/src/components/panels/who-to-follow-panel.tsx similarity index 91% rename from packages/nicolium/src/features/ui/components/panels/who-to-follow-panel.tsx rename to packages/nicolium/src/components/panels/who-to-follow-panel.tsx index 978f3b94d..d112ac464 100644 --- a/packages/nicolium/src/features/ui/components/panels/who-to-follow-panel.tsx +++ b/packages/nicolium/src/components/panels/who-to-follow-panel.tsx @@ -1,11 +1,12 @@ +import iconX from '@phosphor-icons/core/regular/x.svg'; import { Link } from '@tanstack/react-router'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import AccountContainer from '@/components/accounts/account-container'; +import PlaceholderSidebarSuggestions from '@/components/placeholders/placeholder-sidebar-suggestions'; import Text from '@/components/ui/text'; import Widget from '@/components/ui/widget'; -import PlaceholderSidebarSuggestions from '@/features/placeholder/components/placeholder-sidebar-suggestions'; import { useFeatures } from '@/hooks/use-features'; import { useDismissSuggestion, @@ -57,7 +58,7 @@ const WhoToFollowPanel = ({ limit }: IWhoToFollowPanel) => { diff --git a/packages/nicolium/src/components/pending-items-row.tsx b/packages/nicolium/src/components/pending-items-row.tsx index 8b042a140..a578497ea 100644 --- a/packages/nicolium/src/components/pending-items-row.tsx +++ b/packages/nicolium/src/components/pending-items-row.tsx @@ -1,3 +1,5 @@ +import iconCaretRight from '@phosphor-icons/core/regular/caret-right.svg'; +import iconWarningCircle from '@phosphor-icons/core/regular/warning-circle.svg'; import { Link, type LinkOptions } from '@tanstack/react-router'; import clsx from 'clsx'; import React from 'react'; @@ -27,7 +29,7 @@ const PendingItemsRow: React.FC = ({ count, size = 'md', ...pr )} > = ({ count, size = 'md', ...pr
diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-account.tsx b/packages/nicolium/src/components/placeholders/placeholder-account.tsx similarity index 100% rename from packages/nicolium/src/features/placeholder/components/placeholder-account.tsx rename to packages/nicolium/src/components/placeholders/placeholder-account.tsx diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-avatar.tsx b/packages/nicolium/src/components/placeholders/placeholder-avatar.tsx similarity index 100% rename from packages/nicolium/src/features/placeholder/components/placeholder-avatar.tsx rename to packages/nicolium/src/components/placeholders/placeholder-avatar.tsx diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-card.tsx b/packages/nicolium/src/components/placeholders/placeholder-card.tsx similarity index 90% rename from packages/nicolium/src/features/placeholder/components/placeholder-card.tsx rename to packages/nicolium/src/components/placeholders/placeholder-card.tsx index c8f2af9c5..72ce9fc98 100644 --- a/packages/nicolium/src/features/placeholder/components/placeholder-card.tsx +++ b/packages/nicolium/src/components/placeholders/placeholder-card.tsx @@ -1,7 +1,7 @@ import clsx from 'clsx'; import React from 'react'; -import { randomIntFromInterval, generateText } from '../utils'; +import { randomIntFromInterval, generateText } from '@/utils/placeholders'; /** Fake link preview to display while data is loading. */ const PlaceholderCard: React.FC = React.memo(() => ( diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-chat-message.tsx b/packages/nicolium/src/components/placeholders/placeholder-chat-message.tsx similarity index 96% rename from packages/nicolium/src/features/placeholder/components/placeholder-chat-message.tsx rename to packages/nicolium/src/components/placeholders/placeholder-chat-message.tsx index c5c97f5f1..29aef934f 100644 --- a/packages/nicolium/src/features/placeholder/components/placeholder-chat-message.tsx +++ b/packages/nicolium/src/components/placeholders/placeholder-chat-message.tsx @@ -2,8 +2,7 @@ import clsx from 'clsx'; import React from 'react'; import Text from '@/components/ui/text'; - -import { randomIntFromInterval } from '../utils'; +import { randomIntFromInterval } from '@/utils/placeholders'; import PlaceholderAvatar from './placeholder-avatar'; diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-chat.tsx b/packages/nicolium/src/components/placeholders/placeholder-chat.tsx similarity index 100% rename from packages/nicolium/src/features/placeholder/components/placeholder-chat.tsx rename to packages/nicolium/src/components/placeholders/placeholder-chat.tsx diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-display-name.tsx b/packages/nicolium/src/components/placeholders/placeholder-display-name.tsx similarity index 91% rename from packages/nicolium/src/features/placeholder/components/placeholder-display-name.tsx rename to packages/nicolium/src/components/placeholders/placeholder-display-name.tsx index a071d5270..edecfc26b 100644 --- a/packages/nicolium/src/features/placeholder/components/placeholder-display-name.tsx +++ b/packages/nicolium/src/components/placeholders/placeholder-display-name.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { randomIntFromInterval, generateText } from '../utils'; +import { randomIntFromInterval, generateText } from '@/utils/placeholders'; interface IPlaceholderDisplayName { maxLength: number; diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-event-header.tsx b/packages/nicolium/src/components/placeholders/placeholder-event-header.tsx similarity index 90% rename from packages/nicolium/src/features/placeholder/components/placeholder-event-header.tsx rename to packages/nicolium/src/components/placeholders/placeholder-event-header.tsx index c09c971e8..3725efc4a 100644 --- a/packages/nicolium/src/features/placeholder/components/placeholder-event-header.tsx +++ b/packages/nicolium/src/components/placeholders/placeholder-event-header.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { generateText, randomIntFromInterval } from '../utils'; +import { generateText, randomIntFromInterval } from '@/utils/placeholders'; const PlaceholderEventHeader = () => { const eventNameLength = randomIntFromInterval(5, 25); diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-event-preview.tsx b/packages/nicolium/src/components/placeholders/placeholder-event-preview.tsx similarity index 92% rename from packages/nicolium/src/features/placeholder/components/placeholder-event-preview.tsx rename to packages/nicolium/src/components/placeholders/placeholder-event-preview.tsx index b5f8f2669..290c7059d 100644 --- a/packages/nicolium/src/features/placeholder/components/placeholder-event-preview.tsx +++ b/packages/nicolium/src/components/placeholders/placeholder-event-preview.tsx @@ -1,8 +1,7 @@ import React from 'react'; import Text from '@/components/ui/text'; - -import { generateText, randomIntFromInterval } from '../utils'; +import { generateText, randomIntFromInterval } from '@/utils/placeholders'; const PlaceholderEventPreview = () => { const eventNameLength = randomIntFromInterval(5, 25); diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-group-card.tsx b/packages/nicolium/src/components/placeholders/placeholder-group-card.tsx similarity index 94% rename from packages/nicolium/src/features/placeholder/components/placeholder-group-card.tsx rename to packages/nicolium/src/components/placeholders/placeholder-group-card.tsx index f0406d806..cf6eba642 100644 --- a/packages/nicolium/src/features/placeholder/components/placeholder-group-card.tsx +++ b/packages/nicolium/src/components/placeholders/placeholder-group-card.tsx @@ -1,8 +1,7 @@ import React from 'react'; import Text from '@/components/ui/text'; - -import { generateText, randomIntFromInterval } from '../utils'; +import { generateText, randomIntFromInterval } from '@/utils/placeholders'; const PlaceholderGroupCard = () => { const groupNameLength = randomIntFromInterval(12, 20); diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-group-search.tsx b/packages/nicolium/src/components/placeholders/placeholder-group-search.tsx similarity index 94% rename from packages/nicolium/src/features/placeholder/components/placeholder-group-search.tsx rename to packages/nicolium/src/components/placeholders/placeholder-group-search.tsx index 51395fac5..111992f9b 100644 --- a/packages/nicolium/src/features/placeholder/components/placeholder-group-search.tsx +++ b/packages/nicolium/src/components/placeholders/placeholder-group-search.tsx @@ -1,8 +1,7 @@ import React from 'react'; import Text from '@/components/ui/text'; - -import { generateText, randomIntFromInterval } from '../utils'; +import { generateText, randomIntFromInterval } from '@/utils/placeholders'; const PlaceholderGroupSearch = ({ withJoinAction = true }: { withJoinAction?: boolean }) => { const groupNameLength = randomIntFromInterval(12, 20); diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-hashtag.tsx b/packages/nicolium/src/components/placeholders/placeholder-hashtag.tsx similarity index 82% rename from packages/nicolium/src/features/placeholder/components/placeholder-hashtag.tsx rename to packages/nicolium/src/components/placeholders/placeholder-hashtag.tsx index 9b2a02b63..dc91e71fa 100644 --- a/packages/nicolium/src/features/placeholder/components/placeholder-hashtag.tsx +++ b/packages/nicolium/src/components/placeholders/placeholder-hashtag.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { generateText, randomIntFromInterval } from '../utils'; +import { generateText, randomIntFromInterval } from '@/utils/placeholders'; /** Fake hashtag to display while data is loading. */ const PlaceholderHashtag: React.FC = () => { diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-media-gallery.tsx b/packages/nicolium/src/components/placeholders/placeholder-media-gallery.tsx similarity index 100% rename from packages/nicolium/src/features/placeholder/components/placeholder-media-gallery.tsx rename to packages/nicolium/src/components/placeholders/placeholder-media-gallery.tsx diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-notification.tsx b/packages/nicolium/src/components/placeholders/placeholder-notification.tsx similarity index 100% rename from packages/nicolium/src/features/placeholder/components/placeholder-notification.tsx rename to packages/nicolium/src/components/placeholders/placeholder-notification.tsx diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-sidebar-suggestions.tsx b/packages/nicolium/src/components/placeholders/placeholder-sidebar-suggestions.tsx similarity index 68% rename from packages/nicolium/src/features/placeholder/components/placeholder-sidebar-suggestions.tsx rename to packages/nicolium/src/components/placeholders/placeholder-sidebar-suggestions.tsx index a6073c290..a529009ee 100644 --- a/packages/nicolium/src/features/placeholder/components/placeholder-sidebar-suggestions.tsx +++ b/packages/nicolium/src/components/placeholders/placeholder-sidebar-suggestions.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { randomIntFromInterval, generateText } from '../utils'; +import { randomIntFromInterval, generateText } from '@/utils/placeholders'; const PlaceholderSidebarSuggestions = ({ limit }: { limit: number }) => { const length = randomIntFromInterval(15, 3); @@ -9,8 +9,8 @@ const PlaceholderSidebarSuggestions = ({ limit }: { limit: number }) => { return ( <> {new Array(limit).fill(undefined).map((_, idx) => ( -
-
+
+

{generateText(length)}

diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-sidebar-trends.tsx b/packages/nicolium/src/components/placeholders/placeholder-sidebar-trends.tsx similarity index 88% rename from packages/nicolium/src/features/placeholder/components/placeholder-sidebar-trends.tsx rename to packages/nicolium/src/components/placeholders/placeholder-sidebar-trends.tsx index d3fa2d197..f23b115d8 100644 --- a/packages/nicolium/src/features/placeholder/components/placeholder-sidebar-trends.tsx +++ b/packages/nicolium/src/components/placeholders/placeholder-sidebar-trends.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { randomIntFromInterval, generateText } from '../utils'; +import { randomIntFromInterval, generateText } from '@/utils/placeholders'; const PlaceholderSidebarTrends = ({ limit }: { limit: number }) => { const trend = randomIntFromInterval(6, 3); diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-status-content.tsx b/packages/nicolium/src/components/placeholders/placeholder-status-content.tsx similarity index 87% rename from packages/nicolium/src/features/placeholder/components/placeholder-status-content.tsx rename to packages/nicolium/src/components/placeholders/placeholder-status-content.tsx index 15951295b..69cd86b88 100644 --- a/packages/nicolium/src/features/placeholder/components/placeholder-status-content.tsx +++ b/packages/nicolium/src/components/placeholders/placeholder-status-content.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { randomIntFromInterval, generateText } from '../utils'; +import { randomIntFromInterval, generateText } from '@/utils/placeholders'; interface IPlaceholderStatusContent { maxLength: number; diff --git a/packages/nicolium/src/features/placeholder/components/placeholder-status.tsx b/packages/nicolium/src/components/placeholders/placeholder-status.tsx similarity index 100% rename from packages/nicolium/src/features/placeholder/components/placeholder-status.tsx rename to packages/nicolium/src/components/placeholders/placeholder-status.tsx diff --git a/packages/nicolium/src/components/polls/poll-option.tsx b/packages/nicolium/src/components/polls/poll-option.tsx index 69fc4179c..94e9d0a1e 100644 --- a/packages/nicolium/src/components/polls/poll-option.tsx +++ b/packages/nicolium/src/components/polls/poll-option.tsx @@ -1,3 +1,5 @@ +import iconCheckCircle from '@phosphor-icons/core/regular/check-circle.svg'; +import iconCheck from '@phosphor-icons/core/regular/check.svg'; import { animated, config, useSpring } from '@react-spring/web'; import clsx from 'clsx'; import React from 'react'; @@ -104,12 +106,7 @@ const PollOptionText: React.FC = ({ aria-checked={active} aria-label={option.title} > - {active && ( - - )} + {active && }
@@ -165,7 +162,7 @@ const PollOption: React.FC = (props): React.JSX.Element | null => {
{voted ? ( diff --git a/packages/nicolium/src/features/ui/components/poll-preview.tsx b/packages/nicolium/src/components/polls/poll-preview.tsx similarity index 100% rename from packages/nicolium/src/features/ui/components/poll-preview.tsx rename to packages/nicolium/src/components/polls/poll-preview.tsx diff --git a/packages/nicolium/src/components/polls/poll.tsx b/packages/nicolium/src/components/polls/poll.tsx index 33b4eace9..1bd056469 100644 --- a/packages/nicolium/src/components/polls/poll.tsx +++ b/packages/nicolium/src/components/polls/poll.tsx @@ -10,7 +10,7 @@ import { useStatusMeta } from '@/stores/status-meta'; import PollFooter from './poll-footer'; import PollOption from './poll-option'; -import type { NormalizedStatus as Status } from '@/normalizers/status'; +import type { NormalizedStatus as Status } from '@/queries/statuses/normalize'; type Selected = Record; diff --git a/packages/nicolium/src/components/preview-card.tsx b/packages/nicolium/src/components/preview-card.tsx index 51366538c..77a5c7a6c 100644 --- a/packages/nicolium/src/components/preview-card.tsx +++ b/packages/nicolium/src/components/preview-card.tsx @@ -1,3 +1,7 @@ +import iconArrowSquareOut from '@phosphor-icons/core/regular/arrow-square-out.svg'; +import iconLinkSimple from '@phosphor-icons/core/regular/link-simple.svg'; +import iconMagnifyingGlassPlus from '@phosphor-icons/core/regular/magnifying-glass-plus.svg'; +import iconPlay from '@phosphor-icons/core/regular/play.svg'; import { Link } from '@tanstack/react-router'; import clsx from 'clsx'; import DOMPurify from 'dompurify'; @@ -14,6 +18,7 @@ import Blurhash from '@/components/media/blurhash'; import Icon from '@/components/ui/icon'; import Text from '@/components/ui/text'; import Emojify from '@/features/emoji/emojify'; +import { useSettings } from '@/stores/settings'; import { getTextDirection } from '@/utils/rtl'; import HoverAccountWrapper from './accounts/hover-account-wrapper'; @@ -109,6 +114,10 @@ const PreviewCard: React.FC = ({ onOpenMedia, }): React.JSX.Element => { const intl = useIntl(); + const { + // urlPrivacy: { clearLinksInContent, redirectLinksMode }, + disableUserProvidedMedia, + } = useSettings(); const [width, setWidth] = useState(defaultWidth); const [embedded, setEmbedded] = useState(false); @@ -240,7 +249,7 @@ const PreviewCard: React.FC = ({ {trimmedDescription && {trimmedDescription}}
- + {card.provider_name} @@ -266,14 +275,14 @@ const PreviewCard: React.FC = ({ /> ); - if (interactive) { + if (interactive && !disableUserProvidedMedia) { if (embedded) { embed = renderVideo(); } else { - let iconVariant = require('@phosphor-icons/core/regular/play.svg'); + let iconVariant = iconPlay; if (card.type === 'photo') { - iconVariant = require('@phosphor-icons/core/regular/magnifying-glass-plus.svg'); + iconVariant = iconMagnifyingGlassPlus; } embed = ( @@ -303,10 +312,7 @@ const PreviewCard: React.FC = ({ className='text-gray-700 hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100' title={intl.formatMessage(messages.externalLink)} > - + )}
@@ -322,7 +328,7 @@ const PreviewCard: React.FC = ({ {description}
); - } else if (card.image) { + } else if (card.image && !disableUserProvidedMedia) { embed = (
= ({ accountIds, }) => { const intl = useIntl(); - const { autoloadTimelines } = useSettings(); + const { autoloadTimelines, disableUserProvidedMedia } = useSettings(); // Whether we are scrolled past the `threshold`. const [scrolled, setScrolled] = useState(false); @@ -105,10 +106,10 @@ const ScrollTopButton: React.FC = ({ aria-hidden={!visible} > )}
@@ -230,7 +229,7 @@ const StatusContent: React.FC = React.memo( {expandable && ( )} diff --git a/packages/nicolium/src/components/statuses/status-reply-mentions.tsx b/packages/nicolium/src/components/statuses/status-reply-mentions.tsx index 1dc5ce74a..615abc4eb 100644 --- a/packages/nicolium/src/components/statuses/status-reply-mentions.tsx +++ b/packages/nicolium/src/components/statuses/status-reply-mentions.tsx @@ -6,7 +6,7 @@ import HoverAccountWrapper from '@/components/accounts/hover-account-wrapper'; import HoverStatusWrapper from '@/components/statuses/hover-status-wrapper'; import { useModalsActions } from '@/stores/modals'; -import type { NormalizedStatus as Status } from '@/normalizers/status'; +import type { NormalizedStatus as Status } from '@/queries/statuses/normalize'; interface IStatusReplyMentions { status: Pick; diff --git a/packages/nicolium/src/components/statuses/status.tsx b/packages/nicolium/src/components/statuses/status.tsx index a0ffe11df..b31f13199 100644 --- a/packages/nicolium/src/components/statuses/status.tsx +++ b/packages/nicolium/src/components/statuses/status.tsx @@ -1,3 +1,8 @@ +import iconHash from '@phosphor-icons/core/regular/hash.svg'; +import iconPencilSimple from '@phosphor-icons/core/regular/pencil-simple.svg'; +import iconPushPin from '@phosphor-icons/core/regular/push-pin.svg'; +import iconRepeat from '@phosphor-icons/core/regular/repeat.svg'; +import iconUsersThree from '@phosphor-icons/core/regular/users-three.svg'; import { Link, linkOptions, useNavigate, useRouter } from '@tanstack/react-router'; import clsx from 'clsx'; import React, { useEffect, useMemo, useRef } from 'react'; @@ -29,7 +34,7 @@ import { textForScreenReader } from '@/utils/status'; import HashtagLink from '../hashtag-link'; import RelativeTimestamp from '../relative-timestamp'; -import EventPreview from './event-preview'; +import EventPreview from './events/event-preview'; import RssFeedInfo from './rss-feed-info'; import StatusActionBar from './status-action-bar'; import StatusContent from './status-content'; @@ -50,35 +55,47 @@ interface IAccountInfo { status: SelectedStatus; } -const AccountInfo: React.FC = React.memo(({ status }) => ( -
- event.stopPropagation()} - > - - - - - {!!status.edited_at && ( - <> - +const AccountInfo: React.FC = React.memo(({ status }) => { + const intl = useIntl(); - + event.stopPropagation()} + > + - - )} -
-)); + + + + {!!status.edited_at && ( + <> + + + + )} +
+ ); +}); AccountInfo.displayName = 'AccountInfo'; @@ -126,12 +143,7 @@ const StatusFollowedTagInfo: React.FC = ({ - } + icon={} text={ = React.memo((props) => { - } + icon={} text={ = React.memo((props) => { - } + icon={} text={ status.visibility === 'private' ? ( = React.memo((props) => { - } + icon={} text={} /> ); @@ -457,10 +454,7 @@ const Status: React.FC = React.memo((props) => { className='-mb-1' avatarSize={avatarSize} icon={ - + } text={ = ({ status }) => { const button = ( )} - +
diff --git a/packages/nicolium/src/components/ui/avatar.tsx b/packages/nicolium/src/components/ui/avatar.tsx index 615836080..53a72f8c9 100644 --- a/packages/nicolium/src/components/ui/avatar.tsx +++ b/packages/nicolium/src/components/ui/avatar.tsx @@ -1,8 +1,10 @@ +import iconImageSquare from '@phosphor-icons/core/regular/image-square.svg'; import clsx from 'clsx'; import { FastAverageColor } from 'fast-average-color'; import React, { useEffect, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; +import missingAvatar from '@/assets/images/avatar-missing.png'; import StillImage, { type IStillImage } from '@/components/still-image'; import { useSettings } from '@/stores/settings'; @@ -122,10 +124,7 @@ const Avatar: React.FC = (props) => { )} >
- +
); @@ -142,7 +141,7 @@ const Avatar: React.FC = (props) => { diff --git a/packages/nicolium/src/components/ui/card.tsx b/packages/nicolium/src/components/ui/card.tsx index e271356fa..dbb3e78c0 100644 --- a/packages/nicolium/src/components/ui/card.tsx +++ b/packages/nicolium/src/components/ui/card.tsx @@ -1,3 +1,4 @@ +import iconArrowLeft from '@phosphor-icons/core/regular/arrow-left.svg'; import { Link } from '@tanstack/react-router'; import clsx from 'clsx'; import React from 'react'; @@ -81,7 +82,7 @@ const CardHeader: React.FC = ({ aria-label={intl.formatMessage(messages.back)} title={intl.formatMessage(messages.back)} > - + ); }; diff --git a/packages/nicolium/src/components/ui/column.tsx b/packages/nicolium/src/components/ui/column.tsx index 69d66291f..60cf9bdf2 100644 --- a/packages/nicolium/src/components/ui/column.tsx +++ b/packages/nicolium/src/components/ui/column.tsx @@ -8,17 +8,28 @@ import { useFrontendConfig } from '@/hooks/use-frontend-config'; import { Card, CardBody, CardHeader, CardTitle, type CardSizes } from './card'; -interface IColumnHeader extends Pick { - label?: React.ReactNode; -} +type IColumnHeader = Pick< + IColumn, + | 'label' + | 'title' + | 'withBack' + | 'backHref' + | 'backParams' + | 'className' + | 'action' + | 'truncateTitle' +>; /** Contains the column title with optional back button. */ const ColumnHeader: React.FC = ({ label, + title, + withBack, backHref, backParams, className, action, + truncateTitle, }) => { const navigate = useNavigate(); const { history } = useRouter(); @@ -37,8 +48,8 @@ const ColumnHeader: React.FC = ({ }; return ( - - + + {action &&
{action}
}
@@ -47,11 +58,13 @@ const ColumnHeader: React.FC = ({ interface IColumn { /** Route the back button goes to. */ + withBack?: boolean; backHref?: LinkOptions['to']; backParams?: LinkOptions['params']; backSearch?: LinkOptions['search']; /** Column title text. */ label?: string; + title?: React.ReactNode; /** Whether this column should have a transparent background. */ transparent?: boolean; /** Whether this column should have a title and back button. */ @@ -66,20 +79,24 @@ interface IColumn { action?: React.ReactNode; /** Column size, inherited from Card. */ size?: CardSizes; + truncateTitle?: boolean; } /** A backdrop for the main section of the UI. */ const Column: React.FC = (props): React.JSX.Element => { const { + withBack = true, backHref, children, label, + title, transparent = false, withHeader = true, className, bodyClassName, action, size, + truncateTitle, } = props; const frontendConfig = useFrontendConfig(); const [isScrolled, setIsScrolled] = useState(false); @@ -120,11 +137,14 @@ const Column: React.FC = (props): React.JSX.Element => { {withHeader && ( )} diff --git a/packages/nicolium/src/components/ui/emoji.tsx b/packages/nicolium/src/components/ui/emoji.tsx index 3287d1780..269434a12 100644 --- a/packages/nicolium/src/components/ui/emoji.tsx +++ b/packages/nicolium/src/components/ui/emoji.tsx @@ -53,6 +53,7 @@ const Emoji: React.FC = (props) => { {...rest} draggable='false' alt={alt ?? emoji} + title={alt ?? emoji} src={joinPublicPath(`packs/emoji/${filename}.svg`)} decoding='async' className={clsx( diff --git a/packages/nicolium/src/components/ui/input.tsx b/packages/nicolium/src/components/ui/input.tsx index 71a4a85c6..060958df8 100644 --- a/packages/nicolium/src/components/ui/input.tsx +++ b/packages/nicolium/src/components/ui/input.tsx @@ -1,3 +1,5 @@ +import iconEyeSlash from '@phosphor-icons/core/regular/eye-slash.svg'; +import iconEye from '@phosphor-icons/core/regular/eye.svg'; import clsx from 'clsx'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -134,14 +136,7 @@ const Input = React.forwardRef((props, ref) => { >
diff --git a/packages/nicolium/src/components/ui/modal.tsx b/packages/nicolium/src/components/ui/modal.tsx index d10c06c7c..9ea19a584 100644 --- a/packages/nicolium/src/components/ui/modal.tsx +++ b/packages/nicolium/src/components/ui/modal.tsx @@ -1,3 +1,6 @@ +import iconArrowLeft from '@phosphor-icons/core/regular/arrow-left.svg'; +import iconTwitterLogo from '@phosphor-icons/core/regular/twitter-logo.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import clsx from 'clsx'; import React from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; @@ -21,10 +24,10 @@ const useDefaultCloseIcon = (): string => { account?.url === 'https://donotsta.re/users/pmysl' || account?.url === 'https://to.juz.sie.federu.je/@pmysl' ) { - return require('@phosphor-icons/core/regular/twitter-logo.svg'); + return iconTwitterLogo; } - return require('@phosphor-icons/core/regular/x.svg'); + return iconX; }; interface IModal { @@ -120,7 +123,7 @@ const Modal = React.forwardRef(
{onBack && ( diff --git a/packages/nicolium/src/components/ui/multiselect.tsx b/packages/nicolium/src/components/ui/multiselect.tsx index 1ad963c91..91f4182e0 100644 --- a/packages/nicolium/src/components/ui/multiselect.tsx +++ b/packages/nicolium/src/components/ui/multiselect.tsx @@ -23,6 +23,7 @@ THE SOFTWARE. */ // Adapted from [multiselect-react-dropdown](https://github.com/srigar/multiselect-react-dropdown) +import iconXCircle from '@phosphor-icons/core/regular/x-circle.svg'; import clsx from 'clsx'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -234,10 +235,7 @@ const Multiselect: React.FC = ({ title={intl.formatMessage(messages.removeItem)} aria-label={intl.formatMessage(messages.removeItem)} > - + ))} diff --git a/packages/nicolium/src/features/forms/index.tsx b/packages/nicolium/src/components/ui/select-dropdown.tsx similarity index 100% rename from packages/nicolium/src/features/forms/index.tsx rename to packages/nicolium/src/components/ui/select-dropdown.tsx diff --git a/packages/nicolium/src/components/ui/slider.tsx b/packages/nicolium/src/components/ui/slider.tsx index 8b76e74d2..539343df4 100644 --- a/packages/nicolium/src/components/ui/slider.tsx +++ b/packages/nicolium/src/components/ui/slider.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import throttle from 'lodash/throttle'; import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { getPointerPosition } from '@/features/video'; +import { getPointerPosition } from '@/components/media/video'; interface ISlider { id?: string; diff --git a/packages/nicolium/src/components/ui/spinner.css b/packages/nicolium/src/components/ui/spinner.scss similarity index 88% rename from packages/nicolium/src/components/ui/spinner.css rename to packages/nicolium/src/components/ui/spinner.scss index 70ce8ac90..58e844e2e 100644 --- a/packages/nicolium/src/components/ui/spinner.css +++ b/packages/nicolium/src/components/ui/spinner.scss @@ -14,12 +14,23 @@ } .spinner > div::after { - @apply block absolute rounded-full bg-gray-700 dark:bg-gray-400; + display: block; + position: absolute; + border-radius: 9999px; content: ' '; top: 3.75%; left: 46.25%; width: 7.5%; height: 22.5%; + background: rgb(var(--color-gray-700)); + + .dark & { + background: rgb(var(--color-gray-400)); + } + + .dark button & { + background: white; + } } .spinner > div:nth-child(1) { diff --git a/packages/nicolium/src/components/ui/spinner.tsx b/packages/nicolium/src/components/ui/spinner.tsx index 2deceb8f9..627862028 100644 --- a/packages/nicolium/src/components/ui/spinner.tsx +++ b/packages/nicolium/src/components/ui/spinner.tsx @@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl'; import Text from './text'; -import './spinner.css'; +import './spinner.scss'; interface ISpinner { /** Width and height of the spinner in pixels. */ diff --git a/packages/nicolium/src/components/ui/step-slider.tsx b/packages/nicolium/src/components/ui/step-slider.tsx index cd7a78db1..1bc2bc9d1 100644 --- a/packages/nicolium/src/components/ui/step-slider.tsx +++ b/packages/nicolium/src/components/ui/step-slider.tsx @@ -1,7 +1,7 @@ import throttle from 'lodash/throttle'; import React, { useCallback, useRef } from 'react'; -import { getPointerPosition } from '@/features/video'; +import { getPointerPosition } from '@/components/media/video'; interface IStepSlider { id?: string; diff --git a/packages/nicolium/src/components/ui/streamfield.tsx b/packages/nicolium/src/components/ui/streamfield.tsx index 10a74e6f9..5f4ee8708 100644 --- a/packages/nicolium/src/components/ui/streamfield.tsx +++ b/packages/nicolium/src/components/ui/streamfield.tsx @@ -1,3 +1,4 @@ +import iconX from '@phosphor-icons/core/regular/x.svg'; import React, { useRef } from 'react'; import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; @@ -118,7 +119,7 @@ const Streamfield = ({ { onRemoveItem(i); }} diff --git a/packages/nicolium/src/components/ui/tag.tsx b/packages/nicolium/src/components/ui/tag.tsx index 38d38c0b0..765570beb 100644 --- a/packages/nicolium/src/components/ui/tag.tsx +++ b/packages/nicolium/src/components/ui/tag.tsx @@ -1,3 +1,4 @@ +import iconX from '@phosphor-icons/core/regular/x.svg'; import React from 'react'; import IconButton from './icon-button'; @@ -17,7 +18,7 @@ const Tag: React.FC = ({ tag, onDelete }) => ( { onDelete(tag); }} diff --git a/packages/nicolium/src/components/ui/toast.tsx b/packages/nicolium/src/components/ui/toast.tsx index 385bb139a..c4138115a 100644 --- a/packages/nicolium/src/components/ui/toast.tsx +++ b/packages/nicolium/src/components/ui/toast.tsx @@ -1,3 +1,7 @@ +import iconCheckCircle from '@phosphor-icons/core/regular/check-circle.svg'; +import iconInfo from '@phosphor-icons/core/regular/info.svg'; +import iconWarningCircle from '@phosphor-icons/core/regular/warning-circle.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import { Link, type LinkOptions } from '@tanstack/react-router'; import clsx from 'clsx'; import React from 'react'; @@ -50,13 +54,11 @@ const Toast: React.FC = (props) => { const renderIcon = () => { switch (type) { case 'success': - return ; + return ; case 'info': - return ; + return ; case 'error': - return ( - - ); + return ; } }; @@ -122,7 +124,7 @@ const Toast: React.FC = (props) => { title={intl.formatMessage(messages.close)} aria-label={intl.formatMessage(messages.close)} > - +
diff --git a/packages/nicolium/src/components/ui/widget.tsx b/packages/nicolium/src/components/ui/widget.tsx index ac60ac39c..079fa6f9e 100644 --- a/packages/nicolium/src/components/ui/widget.tsx +++ b/packages/nicolium/src/components/ui/widget.tsx @@ -1,3 +1,4 @@ +import iconArrowRight from '@phosphor-icons/core/regular/arrow-right.svg'; import clsx from 'clsx'; import React, { useMemo } from 'react'; @@ -22,7 +23,7 @@ const Widget: React.FC = ({ title, children, onActionClick, - actionIcon = require('@phosphor-icons/core/regular/arrow-right.svg'), + actionIcon = iconArrowRight, actionTitle, action, className, diff --git a/packages/nicolium/src/components/upload-progress.tsx b/packages/nicolium/src/components/upload-progress.tsx index 23630638e..afde8ff3c 100644 --- a/packages/nicolium/src/components/upload-progress.tsx +++ b/packages/nicolium/src/components/upload-progress.tsx @@ -1,3 +1,4 @@ +import iconUploadSimple from '@phosphor-icons/core/regular/upload-simple.svg'; import React from 'react'; import { FormattedMessage } from 'react-intl'; @@ -13,10 +14,7 @@ interface IUploadProgress { /** Displays a progress bar for uploading files. */ const UploadProgress: React.FC = ({ progress }) => (
- +
diff --git a/packages/nicolium/src/features/birthdays/account.tsx b/packages/nicolium/src/features/birthdays/account.tsx index ebfab5281..f70782692 100644 --- a/packages/nicolium/src/features/birthdays/account.tsx +++ b/packages/nicolium/src/features/birthdays/account.tsx @@ -1,3 +1,4 @@ +import iconCake from '@phosphor-icons/core/regular/cake.svg'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -39,7 +40,7 @@ const Account: React.FC = ({ accountId }) => { date: formattedBirthday, })} > - + {formattedBirthday}
diff --git a/packages/nicolium/src/features/chats/components/chat-composer.tsx b/packages/nicolium/src/features/chats/components/chat-composer.tsx index b5e106143..bf26f02ed 100644 --- a/packages/nicolium/src/features/chats/components/chat-composer.tsx +++ b/packages/nicolium/src/features/chats/components/chat-composer.tsx @@ -1,3 +1,4 @@ +import iconPaperPlaneRight from '@phosphor-icons/core/regular/paper-plane-right.svg'; import React, { useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -252,7 +253,7 @@ const ChatComposer = React.forwardRef ) : null} = React.memo( }, }); }, - icon: require('@phosphor-icons/core/regular/sign-out.svg'), + icon: iconSignOut, }, ], [], @@ -169,10 +171,7 @@ const ChatListItem: React.FC = React.memo( {features.chatsDelete && (
- +
)} diff --git a/packages/nicolium/src/features/chats/components/chat-list.tsx b/packages/nicolium/src/features/chats/components/chat-list.tsx index 3bd7ace31..8298c9f6d 100644 --- a/packages/nicolium/src/features/chats/components/chat-list.tsx +++ b/packages/nicolium/src/features/chats/components/chat-list.tsx @@ -2,9 +2,9 @@ import clsx from 'clsx'; import React, { useCallback, useRef, useState } from 'react'; import { Virtuoso, type VirtuosoHandle } from 'react-virtuoso'; +import PlaceholderChat from '@/components/placeholders/placeholder-chat'; import PullToRefresh from '@/components/pull-to-refresh'; import Spinner from '@/components/ui/spinner'; -import PlaceholderChat from '@/features/placeholder/components/placeholder-chat'; import { useChats } from '@/queries/chats'; import { useShoutboxIsLoading } from '@/stores/shoutbox'; import { selectChild } from '@/utils/scroll-utils'; diff --git a/packages/nicolium/src/features/chats/components/chat-message-list.tsx b/packages/nicolium/src/features/chats/components/chat-message-list.tsx index e7f065bfa..14ec8550c 100644 --- a/packages/nicolium/src/features/chats/components/chat-message-list.tsx +++ b/packages/nicolium/src/features/chats/components/chat-message-list.tsx @@ -2,12 +2,12 @@ import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react' import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; import { type Components, Virtuoso, type VirtuosoHandle } from 'react-virtuoso'; +import PlaceholderChatMessage from '@/components/placeholders/placeholder-chat-message'; import Avatar from '@/components/ui/avatar'; import Button from '@/components/ui/button'; import Divider from '@/components/ui/divider'; import Spinner from '@/components/ui/spinner'; import Text from '@/components/ui/text'; -import PlaceholderChatMessage from '@/features/placeholder/components/placeholder-chat-message'; import { useRelationshipQuery } from '@/queries/accounts/use-relationship'; import { useChatMessages, diff --git a/packages/nicolium/src/features/chats/components/chat-message.tsx b/packages/nicolium/src/features/chats/components/chat-message.tsx index 1acb75f93..a663f3326 100644 --- a/packages/nicolium/src/features/chats/components/chat-message.tsx +++ b/packages/nicolium/src/features/chats/components/chat-message.tsx @@ -1,3 +1,6 @@ +import iconClipboard from '@phosphor-icons/core/regular/clipboard.svg'; +import iconDotsThree from '@phosphor-icons/core/regular/dots-three.svg'; +import iconTrash from '@phosphor-icons/core/regular/trash.svg'; import clsx from 'clsx'; import escape from 'lodash/escape'; import React, { useMemo, useState } from 'react'; @@ -119,7 +122,7 @@ const ChatMessage: React.FC = React.memo((props) => { action: () => { handleCopyText(chatMessage); }, - icon: require('@phosphor-icons/core/regular/clipboard.svg'), + icon: iconClipboard, }); } @@ -129,7 +132,7 @@ const ChatMessage: React.FC = React.memo((props) => { action: () => { deleteChatMessage.mutate(chatMessage.id); }, - icon: require('@phosphor-icons/core/regular/trash.svg'), + icon: iconTrash, destructive: true, }); } else { @@ -138,7 +141,7 @@ const ChatMessage: React.FC = React.memo((props) => { action: () => { deleteChatMessage.mutate(chatMessage.id); }, - icon: require('@phosphor-icons/core/regular/trash.svg'), + icon: iconTrash, destructive: true, }); } @@ -180,10 +183,7 @@ const ChatMessage: React.FC = React.memo((props) => { })} data-testid='chat-message-menu' > - + )} diff --git a/packages/nicolium/src/features/chats/components/chat-pane/chat-pane.tsx b/packages/nicolium/src/features/chats/components/chat-pane/chat-pane.tsx index f1eb415b0..4558c4bff 100644 --- a/packages/nicolium/src/features/chats/components/chat-pane/chat-pane.tsx +++ b/packages/nicolium/src/features/chats/components/chat-pane/chat-pane.tsx @@ -1,3 +1,4 @@ +import iconNotePencil from '@phosphor-icons/core/regular/note-pencil.svg'; import React, { useCallback } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -103,7 +104,7 @@ const ChatPane = () => { toggleChatPane(); } }} - secondaryActionIcon={require('@phosphor-icons/core/regular/note-pencil.svg')} + secondaryActionIcon={iconNotePencil} secondaryActionTitle={intl.formatMessage(messages.newChat)} /> diff --git a/packages/nicolium/src/features/chats/components/chat-search/chat-search.tsx b/packages/nicolium/src/features/chats/components/chat-search/chat-search.tsx index 3e0ea5e2c..3bf1a7413 100644 --- a/packages/nicolium/src/features/chats/components/chat-search/chat-search.tsx +++ b/packages/nicolium/src/features/chats/components/chat-search/chat-search.tsx @@ -1,3 +1,5 @@ +import iconMagnifyingGlass from '@phosphor-icons/core/regular/magnifying-glass.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import { useMutation } from '@tanstack/react-query'; import { useNavigate } from '@tanstack/react-router'; import React, { useRef, useState } from 'react'; @@ -110,11 +112,7 @@ const ChatSearch: React.FC = ({ isMainPage = false }) => { )} >
navigate({ to: '/chats' })} title={intl.formatMessage(messages.back)} @@ -156,7 +160,7 @@ const ChatsPageChat = () => {
(
diff --git a/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-new.tsx b/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-new.tsx index 12720c7c8..8ef178c7b 100644 --- a/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-new.tsx +++ b/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-new.tsx @@ -1,3 +1,4 @@ +import iconArrowLeft from '@phosphor-icons/core/regular/arrow-left.svg'; import { useNavigate } from '@tanstack/react-router'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -21,7 +22,7 @@ const ChatsPageNew: React.FC = () => {
navigate({ to: '/chats' })} title={intl.formatMessage(messages.back)} diff --git a/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-settings.tsx b/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-settings.tsx index 94a0833b5..e2247ee15 100644 --- a/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-settings.tsx +++ b/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-settings.tsx @@ -1,3 +1,4 @@ +import iconArrowLeft from '@phosphor-icons/core/regular/arrow-left.svg'; import { useNavigate } from '@tanstack/react-router'; import React, { useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -9,8 +10,8 @@ import { CardBody, CardTitle } from '@/components/ui/card'; import Form from '@/components/ui/form'; import IconButton from '@/components/ui/icon-button'; import Toggle from '@/components/ui/toggle'; -import SettingToggle from '@/features/settings/components/setting-toggle'; import { useOwnAccount } from '@/hooks/use-own-account'; +import SettingToggle from '@/pages/settings/components/setting-toggle'; import { useUpdateCredentials } from '@/queries/accounts/use-account-credentials'; import { useSettings } from '@/stores/settings'; import toast from '@/toast'; @@ -72,7 +73,7 @@ const ChatsPageSettings = () => {
navigate({ to: '/chats' })} title={intl.formatMessage(messages.back)} diff --git a/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-shoutbox.tsx b/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-shoutbox.tsx index 21528cbf7..5afd5b334 100644 --- a/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-shoutbox.tsx +++ b/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-shoutbox.tsx @@ -1,3 +1,4 @@ +import iconArrowLeft from '@phosphor-icons/core/regular/arrow-left.svg'; import { useNavigate } from '@tanstack/react-router'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -25,7 +26,7 @@ const ChatsPageShoutbox = () => {
navigate({ to: '/chats' })} title={intl.formatMessage(messages.back)} diff --git a/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-sidebar.tsx b/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-sidebar.tsx index acabbb533..955c066a2 100644 --- a/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-sidebar.tsx +++ b/packages/nicolium/src/features/chats/components/chats-page/components/chats-page-sidebar.tsx @@ -1,3 +1,5 @@ +import iconPencilSimple from '@phosphor-icons/core/regular/pencil-simple.svg'; +import iconSlidersHorizontal from '@phosphor-icons/core/regular/sliders-horizontal.svg'; import { useNavigate } from '@tanstack/react-router'; import React, { useCallback } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -45,14 +47,14 @@ const ChatsPageSidebar = () => {
= ({ disabled, onSelectFile }) => { return (
@@ -248,14 +250,14 @@ const getLanguageDropdown = title={intl.formatMessage(messages.deleteLanguage)} onClick={handleDeleteLanguageClick} > - + ) : ( ))} @@ -298,9 +300,9 @@ const LanguageDropdownButton: React.FC = ({ composeId, return ( ); diff --git a/packages/nicolium/src/features/compose/components/location-button.tsx b/packages/nicolium/src/features/compose/components/location-button.tsx index 15740ca8f..cee9ed331 100644 --- a/packages/nicolium/src/features/compose/components/location-button.tsx +++ b/packages/nicolium/src/features/compose/components/location-button.tsx @@ -1,3 +1,4 @@ +import iconMapPin from '@phosphor-icons/core/regular/map-pin.svg'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -44,7 +45,7 @@ const LocationButton: React.FC = ({ composeId }) => { return ( = ({ composeId }) => {
{location ? (
- +
{location.description} @@ -53,7 +51,7 @@ const LocationForm: React.FC = ({ composeId }) => {
{ onChangeLocation(null); }} diff --git a/packages/nicolium/src/features/compose/components/poll-button.tsx b/packages/nicolium/src/features/compose/components/poll-button.tsx index 8f700518c..7905cfa95 100644 --- a/packages/nicolium/src/features/compose/components/poll-button.tsx +++ b/packages/nicolium/src/features/compose/components/poll-button.tsx @@ -1,3 +1,4 @@ +import iconChartBar from '@phosphor-icons/core/regular/chart-bar.svg'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -36,7 +37,7 @@ const PollButton: React.FC = ({ composeId, disabled }) => { return ( [ { - icon: require('@phosphor-icons/core/regular/globe.svg'), + icon: iconGlobe, value: 'public', text: intl.formatMessage(messages.publicShort), meta: intl.formatMessage(messages.publicLong), }, { - icon: require('@phosphor-icons/core/regular/moon.svg'), + icon: iconMoon, value: 'unlisted', text: intl.formatMessage(messages.unlistedShort), meta: intl.formatMessage(messages.unlistedLong), }, { - icon: require('@phosphor-icons/core/regular/lock.svg'), + icon: iconLock, value: 'private', text: intl.formatMessage(messages.privateShort), meta: intl.formatMessage(messages.privateLong), }, isReply && features.createStatusConversationScope ? { - icon: require('@phosphor-icons/core/regular/chats-circle.svg'), + icon: iconChatsCircle, value: 'conversation', text: intl.formatMessage(messages.conversationShort), meta: intl.formatMessage(messages.conversationLong), @@ -90,7 +101,7 @@ const getItems = ( : undefined, features.createStatusMutualsOnlyScope ? { - icon: require('@phosphor-icons/core/regular/users-three.svg'), + icon: iconUsersThree, value: 'mutuals_only', text: intl.formatMessage(messages.mutualsOnlyShort), meta: intl.formatMessage(messages.mutualsOnlyLong), @@ -98,21 +109,21 @@ const getItems = ( : undefined, features.createStatusSubscribersScope ? { - icon: require('@phosphor-icons/core/regular/coins.svg'), + icon: iconCoins, value: 'subscribers', text: intl.formatMessage(messages.subscribersShort), meta: intl.formatMessage(messages.subscribersLong), } : undefined, { - icon: require('@phosphor-icons/core/regular/at.svg'), + icon: iconAt, value: 'direct', text: intl.formatMessage(messages.directShort), meta: intl.formatMessage(messages.directLong), }, features.createStatusLocalScope ? { - icon: require('@phosphor-icons/core/regular/planet.svg'), + icon: iconPlanet, value: 'local', text: intl.formatMessage(messages.localShort), meta: intl.formatMessage(messages.localLong), @@ -120,10 +131,10 @@ const getItems = ( : undefined, features.createStatusListScope && Object.keys(lists).length ? ({ - icon: require('@phosphor-icons/core/regular/list-dashes.svg'), + icon: iconListDashes, value: '', items: Object.values(lists).map((list) => ({ - icon: require('@phosphor-icons/core/regular/list-dashes.svg'), + icon: iconListDashes, value: `list:${list.id}`, text: list.title, })), @@ -133,10 +144,10 @@ const getItems = ( : undefined, features.circles && Object.keys(circles).length ? ({ - icon: require('@phosphor-icons/core/regular/circles-three.svg'), + icon: iconCirclesThree, value: '', items: Object.values(circles).map((circle) => ({ - icon: require('@phosphor-icons/core/regular/list-dashes.svg'), + icon: iconListDashes, value: `circle:${circle.id}`, text: circle.title, })), @@ -188,7 +199,7 @@ const PrivacyDropdown: React.FC = ({ composeId, compact }) => if (features.localOnlyStatuses) items.push({ - icon: require('@phosphor-icons/core/regular/planet.svg'), + icon: iconPlanet, text: intl.formatMessage(messages.localShort), meta: intl.formatMessage(messages.localLong), type: 'toggle', @@ -222,7 +233,7 @@ const PrivacyDropdown: React.FC = ({ composeId, compact }) => ); diff --git a/packages/nicolium/src/features/compose/components/reply-indicator.tsx b/packages/nicolium/src/features/compose/components/reply-indicator.tsx index 7ebc96c68..c4cb75f46 100644 --- a/packages/nicolium/src/features/compose/components/reply-indicator.tsx +++ b/packages/nicolium/src/features/compose/components/reply-indicator.tsx @@ -1,3 +1,4 @@ +import iconX from '@phosphor-icons/core/regular/x.svg'; import clsx from 'clsx'; import React from 'react'; @@ -8,7 +9,7 @@ import { ParsedContent } from '@/components/statuses/parsed-content'; import QuotedStatusIndicator from '@/components/statuses/quoted-status-indicator'; import { getTextDirection } from '@/utils/rtl'; -import type { NormalizedStatus as Status } from '@/normalizers/status'; +import type { NormalizedStatus as Status } from '@/queries/statuses/normalize'; interface IReplyIndicator { className?: string; @@ -49,7 +50,7 @@ const ReplyIndicator: React.FC = ({ if (!hideActions && onCancel) { actions = { onActionClick: handleClick, - actionIcon: require('@phosphor-icons/core/regular/x.svg'), + actionIcon: iconX, actionAlignment: 'top', actionTitle: 'Dismiss', }; diff --git a/packages/nicolium/src/features/compose/components/schedule-button.tsx b/packages/nicolium/src/features/compose/components/schedule-button.tsx index 978fb538e..7bbe809fd 100644 --- a/packages/nicolium/src/features/compose/components/schedule-button.tsx +++ b/packages/nicolium/src/features/compose/components/schedule-button.tsx @@ -1,3 +1,4 @@ +import iconCalendarPlus from '@phosphor-icons/core/regular/calendar-plus.svg'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -36,7 +37,7 @@ const ScheduleButton: React.FC = ({ composeId, disabled }) => { return ( = ({ composeId }) => { /> diff --git a/packages/nicolium/src/features/compose/components/sensitive-media-button.tsx b/packages/nicolium/src/features/compose/components/sensitive-media-button.tsx index e68158188..c1328e9f5 100644 --- a/packages/nicolium/src/features/compose/components/sensitive-media-button.tsx +++ b/packages/nicolium/src/features/compose/components/sensitive-media-button.tsx @@ -1,3 +1,4 @@ +import iconWarning from '@phosphor-icons/core/regular/warning.svg'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -30,7 +31,7 @@ const SensitiveMediaButton: React.FC = ({ composeId }) => return ( = ({ return null; } - const src = - icon ?? - (onlyImages(attachmentTypes) - ? require('@phosphor-icons/core/regular/image.svg') - : require('@phosphor-icons/core/regular/paperclip.svg')); + const src = icon ?? (onlyImages(attachmentTypes) ? iconImage : iconPaperclip); return (
diff --git a/packages/nicolium/src/features/compose/containers/preview-compose-container.tsx b/packages/nicolium/src/features/compose/containers/preview-compose-container.tsx index 807a2b2dc..9f8dcd798 100644 --- a/packages/nicolium/src/features/compose/containers/preview-compose-container.tsx +++ b/packages/nicolium/src/features/compose/containers/preview-compose-container.tsx @@ -1,9 +1,11 @@ +import iconEye from '@phosphor-icons/core/regular/eye.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import AccountContainer from '@/components/accounts/account-container'; import OutlineBox from '@/components/outline-box'; -import EventPreview from '@/components/statuses/event-preview'; +import EventPreview from '@/components/statuses/events/event-preview'; import QuotedStatusIndicator from '@/components/statuses/quoted-status-indicator'; import SensitiveContentOverlay from '@/components/statuses/sensitive-content-overlay'; import StatusContent from '@/components/statuses/status-content'; @@ -15,7 +17,7 @@ import Text from '@/components/ui/text'; import Toggle from '@/components/ui/toggle'; import { useCompose, useComposeActions } from '@/stores/compose'; -import type { NormalizedStatus as Status } from '@/normalizers/status'; +import type { NormalizedStatus as Status } from '@/queries/statuses/normalize'; const messages = defineMessages({ close: { @@ -59,10 +61,7 @@ const PreviewComposeContainer: React.FC = ({ composeId }
- + @@ -72,7 +71,7 @@ const PreviewComposeContainer: React.FC = ({ composeId } - + )} diff --git a/packages/nicolium/src/features/compose/editor/plugins/floating-block-type-toolbar-plugin.tsx b/packages/nicolium/src/features/compose/editor/plugins/floating-block-type-toolbar-plugin.tsx index af393a5c5..e2517e02a 100644 --- a/packages/nicolium/src/features/compose/editor/plugins/floating-block-type-toolbar-plugin.tsx +++ b/packages/nicolium/src/features/compose/editor/plugins/floating-block-type-toolbar-plugin.tsx @@ -7,6 +7,8 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { $createHorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode'; import { $wrapNodeInElement, mergeRegister } from '@lexical/utils'; +import iconImage from '@phosphor-icons/core/regular/image.svg'; +import iconMinus from '@phosphor-icons/core/regular/minus.svg'; import { $createParagraphNode, $getSelection, @@ -76,7 +78,7 @@ const UploadButton: React.FC = ({ onSelectFile }) => { fileElement.current?.click(); }; - const src = require('@phosphor-icons/core/regular/image.svg'); + const src = iconImage; return (
) : ( @@ -242,11 +240,7 @@ const FloatingLinkEditor = ({ } }} > - +
)} diff --git a/packages/nicolium/src/features/compose/editor/plugins/floating-text-format-toolbar-plugin.tsx b/packages/nicolium/src/features/compose/editor/plugins/floating-text-format-toolbar-plugin.tsx index e009e6c61..56b9f840a 100644 --- a/packages/nicolium/src/features/compose/editor/plugins/floating-text-format-toolbar-plugin.tsx +++ b/packages/nicolium/src/features/compose/editor/plugins/floating-text-format-toolbar-plugin.tsx @@ -22,6 +22,24 @@ import { } from '@lexical/rich-text'; import { $setBlocksType } from '@lexical/selection'; import { $findMatchingParent, $getNearestNodeOfType, mergeRegister } from '@lexical/utils'; +import iconCaretDown from '@phosphor-icons/core/regular/caret-down.svg'; +import iconCode from '@phosphor-icons/core/regular/code.svg'; +import iconLinkSimpleHorizontal from '@phosphor-icons/core/regular/link-simple-horizontal.svg'; +import iconListBullets from '@phosphor-icons/core/regular/list-bullets.svg'; +import iconListChecks from '@phosphor-icons/core/regular/list-checks.svg'; +import iconListNumbers from '@phosphor-icons/core/regular/list-numbers.svg'; +import iconQuotes from '@phosphor-icons/core/regular/quotes.svg'; +import iconTextAlignLeft from '@phosphor-icons/core/regular/text-align-left.svg'; +import iconTextB from '@phosphor-icons/core/regular/text-b.svg'; +import iconTextHFive from '@phosphor-icons/core/regular/text-h-five.svg'; +import iconTextHFour from '@phosphor-icons/core/regular/text-h-four.svg'; +import iconTextHOne from '@phosphor-icons/core/regular/text-h-one.svg'; +import iconTextHSix from '@phosphor-icons/core/regular/text-h-six.svg'; +import iconTextHThree from '@phosphor-icons/core/regular/text-h-three.svg'; +import iconTextHTwo from '@phosphor-icons/core/regular/text-h-two.svg'; +import iconTextItalic from '@phosphor-icons/core/regular/text-italic.svg'; +import iconTextStrikethrough from '@phosphor-icons/core/regular/text-strikethrough.svg'; +import iconTextUnderline from '@phosphor-icons/core/regular/text-underline.svg'; import clsx from 'clsx'; import { $createParagraphNode, @@ -74,18 +92,18 @@ const messages = defineMessages({ }); const blockTypeToIcon = { - bullet: require('@phosphor-icons/core/regular/list-bullets.svg'), - check: require('@phosphor-icons/core/regular/list-checks.svg'), - code: require('@phosphor-icons/core/regular/code.svg'), - h1: require('@phosphor-icons/core/regular/text-h-one.svg'), - h2: require('@phosphor-icons/core/regular/text-h-two.svg'), - h3: require('@phosphor-icons/core/regular/text-h-three.svg'), - h4: require('@phosphor-icons/core/regular/text-h-four.svg'), - h5: require('@phosphor-icons/core/regular/text-h-five.svg'), - h6: require('@phosphor-icons/core/regular/text-h-six.svg'), - number: require('@phosphor-icons/core/regular/list-numbers.svg'), - paragraph: require('@phosphor-icons/core/regular/text-align-left.svg'), - quote: require('@phosphor-icons/core/regular/quotes.svg'), + bullet: iconListBullets, + check: iconListChecks, + code: iconCode, + h1: iconTextHOne, + h2: iconTextHTwo, + h3: iconTextHThree, + h4: iconTextHFour, + h5: iconTextHFive, + h6: iconTextHSix, + number: iconListNumbers, + paragraph: iconTextAlignLeft, + quote: iconQuotes, }; const blockTypeToBlockName = { @@ -212,10 +230,7 @@ const BlockTypeDropdown = ({ type='button' > - + {showDropDown && (
{ @@ -411,7 +426,7 @@ const TextFormatFloatingToolbar = ({ active={isItalic} aria-label={intl.formatMessage(messages.formatItalic)} title={intl.formatMessage(messages.formatItalic)} - icon={require('@phosphor-icons/core/regular/text-italic.svg')} + icon={iconTextItalic} /> { @@ -420,7 +435,7 @@ const TextFormatFloatingToolbar = ({ active={isUnderline} aria-label={intl.formatMessage(messages.formatUnderline)} title={intl.formatMessage(messages.formatUnderline)} - icon={require('@phosphor-icons/core/regular/text-underline.svg')} + icon={iconTextUnderline} /> { @@ -429,7 +444,7 @@ const TextFormatFloatingToolbar = ({ active={isStrikethrough} aria-label={intl.formatMessage(messages.formatStrikethrough)} title={intl.formatMessage(messages.formatStrikethrough)} - icon={require('@phosphor-icons/core/regular/text-strikethrough.svg')} + icon={iconTextStrikethrough} /> { @@ -438,14 +453,14 @@ const TextFormatFloatingToolbar = ({ active={isCode} aria-label={intl.formatMessage(messages.insertCodeBlock)} title={intl.formatMessage(messages.insertCodeBlock)} - icon={require('@phosphor-icons/core/regular/code.svg')} + icon={iconCode} /> )} diff --git a/packages/nicolium/src/features/crypto-donate/components/crypto-address.tsx b/packages/nicolium/src/features/crypto-donate/components/crypto-address.tsx index 7e2d22db0..6f7425036 100644 --- a/packages/nicolium/src/features/crypto-donate/components/crypto-address.tsx +++ b/packages/nicolium/src/features/crypto-donate/components/crypto-address.tsx @@ -1,3 +1,4 @@ +import iconQrCode from '@phosphor-icons/core/regular/qr-code.svg'; import React from 'react'; import CopyableInput from '@/components/copyable-input'; @@ -40,7 +41,7 @@ const CryptoAddress: React.FC = (props): React.JSX.Element => {
diff --git a/packages/nicolium/src/features/draft-statuses/builder.tsx b/packages/nicolium/src/features/draft-statuses/builder.tsx index 8ad1db390..49bed3957 100644 --- a/packages/nicolium/src/features/draft-statuses/builder.tsx +++ b/packages/nicolium/src/features/draft-statuses/builder.tsx @@ -1,7 +1,7 @@ import { pollSchema, statusSchema, type Account } from 'pl-api'; import * as v from 'valibot'; -import { normalizeStatus } from '@/normalizers/status'; +import { normalizeStatus } from '@/queries/statuses/normalize'; import type { DraftStatus } from '@/queries/statuses/use-draft-statuses'; diff --git a/packages/nicolium/src/features/draft-statuses/components/draft-status-action-bar.tsx b/packages/nicolium/src/features/draft-statuses/components/draft-status-action-bar.tsx index 3e13eea91..26f7c7312 100644 --- a/packages/nicolium/src/features/draft-statuses/components/draft-status-action-bar.tsx +++ b/packages/nicolium/src/features/draft-statuses/components/draft-status-action-bar.tsx @@ -11,7 +11,7 @@ import { useComposeActions } from '@/stores/compose'; import { useModalsActions } from '@/stores/modals'; import { useSettings } from '@/stores/settings'; -import type { NormalizedStatus as StatusEntity } from '@/normalizers/status'; +import type { NormalizedStatus as StatusEntity } from '@/queries/statuses/normalize'; import type { DraftStatus } from '@/queries/statuses/use-draft-statuses'; const messages = defineMessages({ diff --git a/packages/nicolium/src/features/draft-statuses/components/draft-status.tsx b/packages/nicolium/src/features/draft-statuses/components/draft-status.tsx index 635c6f726..7430c70a9 100644 --- a/packages/nicolium/src/features/draft-statuses/components/draft-status.tsx +++ b/packages/nicolium/src/features/draft-statuses/components/draft-status.tsx @@ -5,10 +5,10 @@ import { FormattedMessage } from 'react-intl'; import Account from '@/components/accounts/account'; import AttachmentThumbs from '@/components/media/attachment-thumbs'; import OutlineBox from '@/components/outline-box'; +import PollPreview from '@/components/polls/poll-preview'; import StatusContent from '@/components/statuses/status-content'; import StatusReplyMentions from '@/components/statuses/status-reply-mentions'; import QuotedStatus from '@/features/status/containers/quoted-status-container'; -import PollPreview from '@/features/ui/components/poll-preview'; import { useOwnAccount } from '@/hooks/use-own-account'; import { buildPoll, buildStatus } from '../builder'; diff --git a/packages/nicolium/src/features/emoji/components/emoji-picker.tsx b/packages/nicolium/src/features/emoji/components/emoji-picker.tsx index 5c08796b5..39c8a45a7 100644 --- a/packages/nicolium/src/features/emoji/components/emoji-picker.tsx +++ b/packages/nicolium/src/features/emoji/components/emoji-picker.tsx @@ -1,3 +1,4 @@ +import spritesheetURL from 'emoji-datasource/img/twitter/sheets/32.png'; import { Picker as EmojiPicker } from 'emoji-mart'; import React, { useRef, useEffect } from 'react'; @@ -5,7 +6,7 @@ import { joinPublicPath } from '@/utils/static'; import data from '../data'; -const getSpritesheetURL = () => require('emoji-datasource/img/twitter/sheets/32.png'); +const getSpritesheetURL = () => spritesheetURL; const getImageURL = (set: string, name: string) => joinPublicPath(`/packs/emoji/${name}.svg`); diff --git a/packages/nicolium/src/features/emoji/containers/emoji-picker-dropdown-container.tsx b/packages/nicolium/src/features/emoji/containers/emoji-picker-dropdown-container.tsx index 67c8410e9..2b8976019 100644 --- a/packages/nicolium/src/features/emoji/containers/emoji-picker-dropdown-container.tsx +++ b/packages/nicolium/src/features/emoji/containers/emoji-picker-dropdown-container.tsx @@ -1,4 +1,5 @@ import { useFloating, shift, flip, autoUpdate, useTransitionStyles } from '@floating-ui/react'; +import iconSmiley from '@phosphor-icons/core/regular/smiley.svg'; import clsx from 'clsx'; import React, { useMemo, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -92,7 +93,7 @@ const EmojiPickerDropdownContainer: React.FC = ({ theme === 'inverse', })} ref={refs.setReference} - src={require('@phosphor-icons/core/regular/smiley.svg')} + src={iconSmiley} title={title} aria-label={title} aria-expanded={isOpen} diff --git a/packages/nicolium/src/features/emoji/emojify.tsx b/packages/nicolium/src/features/emoji/emojify.tsx index 0aa29b1c1..f7cba858a 100644 --- a/packages/nicolium/src/features/emoji/emojify.tsx +++ b/packages/nicolium/src/features/emoji/emojify.tsx @@ -2,7 +2,6 @@ import clsx from 'clsx'; import React from 'react'; import Emoji from '@/components/ui/emoji'; -import Tooltip from '@/components/ui/tooltip'; import { useSettings } from '@/stores/settings'; import { makeEmojiMap } from '@/utils/normalizers'; import nyaize from '@/utils/nyaize'; @@ -21,28 +20,36 @@ interface IMaybeEmoji { emojis: Record; large?: boolean; nyaize: boolean; + truncated: boolean; } -const MaybeEmoji: React.FC = ({ text, emojis, large, nyaize: shouldNyaize }) => { +const MaybeEmoji: React.FC = ({ + text, + emojis, + large, + nyaize: shouldNyaize, + truncated, +}) => { if (text.length < 3) return text; if (text in emojis) { const emoji = emojis[text]; const filename = emoji.static_url; if (filename?.length > 0) { - return ( - - - - - + const emojiEl = ( + ); + if (truncated) { + return {emojiEl}; + } else { + return emojiEl; + } } } @@ -54,10 +61,11 @@ interface IEmojify { emojis?: Array | Record; large?: boolean; nyaize?: boolean; + truncated?: boolean; } const Emojify: React.FC = React.memo( - ({ text, emojis = {}, large, nyaize: shouldNyaize = false }) => { + ({ text, emojis = {}, large, nyaize: shouldNyaize = false, truncated = false }) => { const { disableUserProvidedMedia, systemEmojiFont } = useSettings(); if (Array.isArray(emojis)) emojis = makeEmojiMap(emojis); @@ -89,7 +97,7 @@ const Emojify: React.FC = React.memo( const { unified, shortcode } = unicodeMapping[c]; - nodes.push( + const emoji = ( = React.memo( alt={c} title={`:${shortcode}:`} src={joinPublicPath(`packs/emoji/${unified}.svg`)} - />, + /> ); + + if (truncated) { + nodes.push({emoji}); + } else { + nodes.push(emoji); + } } else if (!systemEmojiFont && unqualified in unicodeMapping) { clearStack(); const { unified, shortcode } = unicodeMapping[unqualified]; - nodes.push( + const emoji = ( = React.memo( alt={unqualified} title={`:${shortcode}:`} src={joinPublicPath(`packs/emoji/${unified}.svg`)} - />, + /> ); + + if (truncated) { + nodes.push({emoji}); + } else { + nodes.push(emoji); + } } else if (!disableUserProvidedMedia && c === ':') { if (!open) { clearStack(); @@ -130,6 +150,7 @@ const Emojify: React.FC = React.memo( emojis={emojis} large={large} nyaize={shouldNyaize} + truncated={truncated} />, ); stack = ''; diff --git a/packages/nicolium/src/features/emoji/search.ts b/packages/nicolium/src/features/emoji/search.ts index 8dfda1b5e..6b8ceba40 100644 --- a/packages/nicolium/src/features/emoji/search.ts +++ b/packages/nicolium/src/features/emoji/search.ts @@ -6,8 +6,15 @@ import type { CustomEmoji } from 'pl-api'; let emojis: EmojiData['emojis'] = {}; -const nativeData: Array<{ key: string; id: string }> = []; -let customData: Array<{ key: string; id: string }> = []; +interface SearchableEmoji { + emojiId: string; + emojiName: string; + emojiKeywords: string; + id: string; +} + +const nativeData: Array = []; +let customData: Array = []; import('./data') .then((data) => { @@ -16,8 +23,10 @@ import('./data') const sortedEmojis = Object.entries(emojis).toSorted((a, b) => a[0].localeCompare(b[0])); for (const [key, emoji] of sortedEmojis) { nativeData.push({ - key: `${emoji.id} ${emoji.name} ${emoji.keywords.join(' ')}`.replaceAll('-', '_'), - id: 'n' + key, + emojiId: emoji.id.replaceAll('-', '_'), + emojiName: emoji.name.replaceAll('-', '_'), + emojiKeywords: `${emoji.id} ${emoji.keywords.join(' ')}`.replaceAll('-', '_'), + id: 'e' + key, }); } }) @@ -25,14 +34,19 @@ import('./data') const addCustomToPool = (customEmojis: CustomEmoji[]) => { customData = customEmojis.map((emoji, i) => ({ - key: emoji.shortcode.replaceAll('-', '_'), + emojiId: '', + emojiName: emoji.shortcode.replaceAll('-', '_'), + emojiKeywords: emoji.shortcode.replaceAll('-', '_'), id: 'c' + i, })); }; const search = (query: string, customEmojis: Array = [], limit = 5): Emoji[] => { return fuzzysort - .go(query.replaceAll('-', '_'), [...nativeData, ...customData], { key: 'key', limit }) + .go(query.replaceAll('-', '_'), [...nativeData, ...customData], { + keys: ['emojiId', 'emojiName', 'emojiKeywords'], + limit, + }) .map((result) => { const { id } = result.obj; diff --git a/packages/nicolium/src/features/federation-restrictions/components/instance-restrictions.tsx b/packages/nicolium/src/features/federation-restrictions/components/instance-restrictions.tsx index 88040cea4..2dbf86d89 100644 --- a/packages/nicolium/src/features/federation-restrictions/components/instance-restrictions.tsx +++ b/packages/nicolium/src/features/federation-restrictions/components/instance-restrictions.tsx @@ -1,3 +1,9 @@ +import iconCheck from '@phosphor-icons/core/regular/check.svg'; +import iconEyeSlash from '@phosphor-icons/core/regular/eye-slash.svg'; +import iconImageBroken from '@phosphor-icons/core/regular/image-broken.svg'; +import iconLockOpen from '@phosphor-icons/core/regular/lock-open.svg'; +import iconLock from '@phosphor-icons/core/regular/lock.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import React from 'react'; import { FormattedMessage } from 'react-intl'; @@ -49,7 +55,7 @@ const InstanceRestrictions: React.FC = ({ remoteInstance if (followers_only) { items.push( - + = ({ remoteInstance ); } else if (federated_timeline_removal) { items.push( - + = ({ remoteInstance if (fullMediaRemoval) { items.push( - + = ({ remoteInstance ); } else if (partialMediaRemoval) { items.push( - + = ({ remoteInstance if (!fullMediaRemoval && media_nsfw) { items.push( - + = ({ remoteInstance if (remoteInstance.federation.reject) { return ( - + = ({ remoteInstance } else if (hasRestrictions(remoteInstance)) { return ( <> - + = ({ remoteInstance ); } else { return ( - + = ({ host }) => { return (
- +
{remoteInstance.host}
diff --git a/packages/nicolium/src/features/remote-timeline/components/pinned-hosts-picker.tsx b/packages/nicolium/src/features/remote-timeline/components/pinned-hosts-picker.tsx deleted file mode 100644 index 6342eb417..000000000 --- a/packages/nicolium/src/features/remote-timeline/components/pinned-hosts-picker.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; - -import Button from '@/components/ui/button'; -import { useSettings } from '@/stores/settings'; - -interface IPinnedHostsPicker { - /** The active host among pinned hosts. */ - host?: string; -} - -const PinnedHostsPicker: React.FC = ({ host: activeHost }) => { - const settings = useSettings(); - const pinnedHosts = settings.remote_timeline.pinnedHosts; - - if (!pinnedHosts.length) return null; - - return ( -
- {pinnedHosts.map((host) => ( - - ))} -
- ); -}; - -export { PinnedHostsPicker as default }; diff --git a/packages/nicolium/src/features/reply-mentions/account.tsx b/packages/nicolium/src/features/reply-mentions/account.tsx deleted file mode 100644 index 379ecddeb..000000000 --- a/packages/nicolium/src/features/reply-mentions/account.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; -import { defineMessages, useIntl } from 'react-intl'; - -import AccountComponent from '@/components/accounts/account'; -import IconButton from '@/components/ui/icon-button'; -import { useAccount } from '@/queries/accounts/use-account'; -import { useCompose, useComposeActions } from '@/stores/compose'; - -const messages = defineMessages({ - remove: { id: 'reply_mentions.account.remove', defaultMessage: 'Remove from mentions' }, - add: { id: 'reply_mentions.account.add', defaultMessage: 'Add to mentions' }, -}); - -interface IAccount { - composeId: string; - accountId: string; - author: boolean; -} - -const Account: React.FC = ({ composeId, accountId, author }) => { - const intl = useIntl(); - const { updateCompose } = useComposeActions(); - - const compose = useCompose(composeId); - const { data: account } = useAccount(accountId); - const added = !!account && compose.to?.includes(account.acct); - - const onRemove = () => - updateCompose(composeId, (draft) => { - if (account) { - draft.to = draft.to?.filter((acct) => acct !== account.acct) || []; - } - }); - const onAdd = () => - updateCompose(composeId, (draft) => { - if (account) { - if (draft.to?.includes(account.acct)) return; - draft.to = [...(draft.to || []), account.acct]; - } - }); - - if (!account) return null; - - let button; - - if (added) { - button = ( - - ); - } else { - button = ( - - ); - } - - return ( -
- -
- ); -}; - -export { Account as default }; diff --git a/packages/nicolium/src/features/scheduled-statuses/builder.tsx b/packages/nicolium/src/features/scheduled-statuses/builder.tsx index 33b4ce049..4dfd61717 100644 --- a/packages/nicolium/src/features/scheduled-statuses/builder.tsx +++ b/packages/nicolium/src/features/scheduled-statuses/builder.tsx @@ -1,7 +1,7 @@ import { statusSchema, type Account, type ScheduledStatus } from 'pl-api'; import * as v from 'valibot'; -import { normalizeStatus } from '@/normalizers/status'; +import { normalizeStatus } from '@/queries/statuses/normalize'; const buildStatus = (account: Account, scheduledStatus: ScheduledStatus) => { const poll = scheduledStatus.params.poll diff --git a/packages/nicolium/src/features/scheduled-statuses/components/scheduled-status-action-bar.tsx b/packages/nicolium/src/features/scheduled-statuses/components/scheduled-status-action-bar.tsx index 02f7c771e..9ac4e5f0d 100644 --- a/packages/nicolium/src/features/scheduled-statuses/components/scheduled-status-action-bar.tsx +++ b/packages/nicolium/src/features/scheduled-statuses/components/scheduled-status-action-bar.tsx @@ -6,7 +6,7 @@ import { useCancelScheduledStatusMutation } from '@/queries/statuses/scheduled-s import { useModalsActions } from '@/stores/modals'; import { useSettings } from '@/stores/settings'; -import type { NormalizedStatus as StatusEntity } from '@/normalizers/status'; +import type { NormalizedStatus as StatusEntity } from '@/queries/statuses/normalize'; const messages = defineMessages({ cancel: { id: 'scheduled_status.cancel', defaultMessage: 'Cancel' }, diff --git a/packages/nicolium/src/features/scheduled-statuses/components/scheduled-status.tsx b/packages/nicolium/src/features/scheduled-statuses/components/scheduled-status.tsx index 59c349adc..2e8848304 100644 --- a/packages/nicolium/src/features/scheduled-statuses/components/scheduled-status.tsx +++ b/packages/nicolium/src/features/scheduled-statuses/components/scheduled-status.tsx @@ -3,10 +3,10 @@ import React from 'react'; import Account from '@/components/accounts/account'; import AttachmentThumbs from '@/components/media/attachment-thumbs'; +import PollPreview from '@/components/polls/poll-preview'; import StatusContent from '@/components/statuses/status-content'; import StatusReplyMentions from '@/components/statuses/status-reply-mentions'; import { buildPoll } from '@/features/draft-statuses/builder'; -import PollPreview from '@/features/ui/components/poll-preview'; import { useOwnAccount } from '@/hooks/use-own-account'; import { buildStatus } from '../builder'; diff --git a/packages/nicolium/src/features/status/components/detailed-status.tsx b/packages/nicolium/src/features/status/components/detailed-status.tsx index 52e23df4a..de7d7edc2 100644 --- a/packages/nicolium/src/features/status/components/detailed-status.tsx +++ b/packages/nicolium/src/features/status/components/detailed-status.tsx @@ -1,3 +1,4 @@ +import iconUsersThree from '@phosphor-icons/core/regular/users-three.svg'; import { Link } from '@tanstack/react-router'; import React, { useRef } from 'react'; import { defineMessages, FormattedDate, FormattedMessage, useIntl } from 'react-intl'; @@ -55,7 +56,7 @@ const DetailedStatus: React.FC = ({ avatarSize={42} icon={ diff --git a/packages/nicolium/src/features/status/components/status-interaction-bar.tsx b/packages/nicolium/src/features/status/components/status-interaction-bar.tsx index 89013f9b3..87971cafb 100644 --- a/packages/nicolium/src/features/status/components/status-interaction-bar.tsx +++ b/packages/nicolium/src/features/status/components/status-interaction-bar.tsx @@ -9,7 +9,7 @@ import { useFeatures } from '@/hooks/use-features'; import { useAccount } from '@/queries/accounts/use-account'; import { useModalsActions } from '@/stores/modals'; -import type { NormalizedStatus as Status } from '@/normalizers/status'; +import type { NormalizedStatus as Status } from '@/queries/statuses/normalize'; interface IStatusInteractionBar { status: Pick< diff --git a/packages/nicolium/src/features/status/components/status-type-icon.tsx b/packages/nicolium/src/features/status/components/status-type-icon.tsx index 8aba30190..131d1bee9 100644 --- a/packages/nicolium/src/features/status/components/status-type-icon.tsx +++ b/packages/nicolium/src/features/status/components/status-type-icon.tsx @@ -1,9 +1,15 @@ +import iconAt from '@phosphor-icons/core/regular/at.svg'; +import iconCoins from '@phosphor-icons/core/regular/coins.svg'; +import iconListBullets from '@phosphor-icons/core/regular/list-bullets.svg'; +import iconLock from '@phosphor-icons/core/regular/lock.svg'; +import iconPlanet from '@phosphor-icons/core/regular/planet.svg'; +import iconUsersThree from '@phosphor-icons/core/regular/users-three.svg'; import React from 'react'; import { defineMessages, type MessageDescriptor, useIntl } from 'react-intl'; import Icon from '@/components/ui/icon'; -import type { NormalizedStatus as Status } from '@/normalizers/status'; +import type { NormalizedStatus as Status } from '@/queries/statuses/normalize'; interface IStatusTypeIcon { visibility: Status['visibility']; @@ -41,12 +47,12 @@ const messages: Record = defineMessages({ }); const STATUS_TYPE_ICONS: Record = { - direct: require('@phosphor-icons/core/regular/at.svg'), - private: require('@phosphor-icons/core/regular/lock.svg'), - mutuals_only: require('@phosphor-icons/core/regular/users-three.svg'), - local: require('@phosphor-icons/core/regular/planet.svg'), - list: require('@phosphor-icons/core/regular/list-bullets.svg'), - subscribers: require('@phosphor-icons/core/regular/coins.svg'), + direct: iconAt, + private: iconLock, + mutuals_only: iconUsersThree, + local: iconPlanet, + list: iconListBullets, + subscribers: iconCoins, }; const StatusTypeIcon: React.FC = React.memo(({ visibility }) => { diff --git a/packages/nicolium/src/features/status/components/thread-status.tsx b/packages/nicolium/src/features/status/components/thread-status.tsx index 56b0a8ee1..3d6ac6fec 100644 --- a/packages/nicolium/src/features/status/components/thread-status.tsx +++ b/packages/nicolium/src/features/status/components/thread-status.tsx @@ -1,9 +1,9 @@ import clsx from 'clsx'; import React from 'react'; +import PlaceholderStatus from '@/components/placeholders/placeholder-status'; import StatusContainer from '@/components/statuses/status-container'; import Tombstone from '@/components/statuses/tombstone'; -import PlaceholderStatus from '@/features/placeholder/components/placeholder-status'; import { useMinimalStatus } from '@/queries/statuses/use-status'; import { useReplyCount, useReplyToId } from '@/stores/contexts'; import { useStatusMeta } from '@/stores/status-meta'; diff --git a/packages/nicolium/src/features/status/components/thread.tsx b/packages/nicolium/src/features/status/components/thread.tsx index 100b97772..0c223e91d 100644 --- a/packages/nicolium/src/features/status/components/thread.tsx +++ b/packages/nicolium/src/features/status/components/thread.tsx @@ -3,12 +3,12 @@ import clsx from 'clsx'; import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { useIntl } from 'react-intl'; +import PlaceholderStatus from '@/components/placeholders/placeholder-status'; import ScrollableList from '@/components/scrollable-list'; +import PendingStatus from '@/components/statuses/pending-status'; import StatusActionBar from '@/components/statuses/status-action-bar'; import Tombstone from '@/components/statuses/tombstone'; -import PlaceholderStatus from '@/features/placeholder/components/placeholder-status'; import { Hotkeys } from '@/features/ui/components/hotkeys'; -import PendingStatus from '@/features/ui/components/pending-status'; import { useFavouriteStatus, useReblogStatus, @@ -26,7 +26,7 @@ import { textForScreenReader } from '@/utils/status'; import DetailedStatus from './detailed-status'; import ThreadStatus from './thread-status'; -import type { NormalizedStatus as Status } from '@/normalizers/status'; +import type { NormalizedStatus as Status } from '@/queries/statuses/normalize'; import type { SelectedStatus } from '@/queries/statuses/use-status'; import type { Account } from 'pl-api'; import type { VirtuosoHandle } from 'react-virtuoso'; diff --git a/packages/nicolium/src/features/ui/components/panels/instance-info-panel.tsx b/packages/nicolium/src/features/ui/components/panels/instance-info-panel.tsx deleted file mode 100644 index 415084eb7..000000000 --- a/packages/nicolium/src/features/ui/components/panels/instance-info-panel.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import { useIntl, defineMessages } from 'react-intl'; - -import { changeSetting } from '@/actions/settings'; -import Widget from '@/components/ui/widget'; -import { useRemoteInstance } from '@/queries/instance/use-remote-instance'; -import { useSettings } from '@/stores/settings'; - -const messages = defineMessages({ - pinHost: { id: 'remote_instance.pin_host', defaultMessage: 'Pin {host}' }, - unpinHost: { id: 'remote_instance.unpin_host', defaultMessage: 'Unpin {host}' }, -}); - -interface IInstanceInfoPanel { - /** Hostname (domain) of the remote instance, eg "gleasonator.com" */ - host: string; -} - -/** Widget that displays information about a remote instance to users. */ -const InstanceInfoPanel: React.FC = ({ host }) => { - const intl = useIntl(); - - const settings = useSettings(); - const remoteInstance = useRemoteInstance(host); - const pinnedHosts = settings.remote_timeline.pinnedHosts; - const isPinned = pinnedHosts.includes(host); - - const pinHost = (host: string) => { - changeSetting(['remote_timeline', 'pinnedHosts'], [...pinnedHosts, host]); - }; - - const unpinHost = (host: string) => { - changeSetting( - ['remote_timeline', 'pinnedHosts'], - pinnedHosts.filter((value) => value !== host), - ); - }; - - const handlePinHost = () => { - if (!isPinned) { - pinHost(host); - } else { - unpinHost(host); - } - }; - - if (!remoteInstance) return null; - - return ( - - ); -}; - -export { InstanceInfoPanel as default }; diff --git a/packages/nicolium/src/features/ui/components/profile-dropdown.tsx b/packages/nicolium/src/features/ui/components/profile-dropdown.tsx index abb1add59..fff07b946 100644 --- a/packages/nicolium/src/features/ui/components/profile-dropdown.tsx +++ b/packages/nicolium/src/features/ui/components/profile-dropdown.tsx @@ -1,3 +1,5 @@ +import iconPlus from '@phosphor-icons/core/regular/plus.svg'; +import iconSignOut from '@phosphor-icons/core/regular/sign-out.svg'; import { Link, type LinkOptions } from '@tanstack/react-router'; import clsx from 'clsx'; import React, { useMemo } from 'react'; @@ -5,7 +7,7 @@ import { defineMessages, useIntl } from 'react-intl'; import Account from '@/components/accounts/account'; import DropdownMenu from '@/components/dropdown-menu'; -import PlaceholderAccount from '@/features/placeholder/components/placeholder-account'; +import PlaceholderAccount from '@/components/placeholders/placeholder-account'; import { useFeatures } from '@/hooks/use-features'; import { useLoggedInAccount, @@ -92,14 +94,14 @@ const ProfileDropdown: React.FC = ({ account, children }) => { menu.push({ text: intl.formatMessage(messages.add), linkOptions: { to: '/login/add' }, - icon: require('@phosphor-icons/core/regular/plus.svg'), + icon: iconPlus, }); menu.push({ text: intl.formatMessage(messages.logout, { acct: account.acct }), linkOptions: { to: '/logout' }, action: handleLogOut, - icon: require('@phosphor-icons/core/regular/sign-out.svg'), + icon: iconSignOut, }); return () => ( diff --git a/packages/nicolium/src/features/ui/components/theme-selector.tsx b/packages/nicolium/src/features/ui/components/theme-selector.tsx index 20bd6d432..52bb5a97e 100644 --- a/packages/nicolium/src/features/ui/components/theme-selector.tsx +++ b/packages/nicolium/src/features/ui/components/theme-selector.tsx @@ -1,3 +1,8 @@ +import iconCaretDown from '@phosphor-icons/core/regular/caret-down.svg'; +import iconDesktop from '@phosphor-icons/core/regular/desktop.svg'; +import iconMoonStars from '@phosphor-icons/core/regular/moon-stars.svg'; +import iconMoon from '@phosphor-icons/core/regular/moon.svg'; +import iconSunDim from '@phosphor-icons/core/regular/sun-dim.svg'; import React, { useMemo } from 'react'; import { FormattedMessage } from 'react-intl'; @@ -15,13 +20,13 @@ const ThemeSelector: React.FC = ({ id, value, onChange }) => { const themeIconSrc = useMemo(() => { switch (value) { case 'system': - return require('@phosphor-icons/core/regular/desktop.svg'); + return iconDesktop; case 'light': - return require('@phosphor-icons/core/regular/sun-dim.svg'); + return iconSunDim; case 'dark': - return require('@phosphor-icons/core/regular/moon.svg'); + return iconMoon; case 'black': - return require('@phosphor-icons/core/regular/moon-stars.svg'); + return iconMoonStars; default: return null; } @@ -34,7 +39,15 @@ const ThemeSelector: React.FC = ({ id, value, onChange }) => { return (
- + {themeIconSrc ? ( + + ) : ( +
+ )}
- +
); diff --git a/packages/nicolium/src/features/ui/index.tsx b/packages/nicolium/src/features/ui/index.tsx index c08736821..3880a3255 100644 --- a/packages/nicolium/src/features/ui/index.tsx +++ b/packages/nicolium/src/features/ui/index.tsx @@ -27,6 +27,7 @@ import { useInstance, useInstanceStore } from '@/stores/instance'; import { useModalsActions } from '@/stores/modals'; import { useShoutboxSubscription } from '@/stores/shoutbox'; import { useIsDropdownMenuOpen } from '@/stores/ui'; +import GlobalHotkeys from '@/utils/global-hotkeys'; import { useIsStandalone } from '@/utils/state'; import { @@ -36,7 +37,6 @@ import { DropdownNavigation, StatusHoverCard, } from './util/async-components'; -import GlobalHotkeys from './util/global-hotkeys'; // Dummy import, to make sure that ends up in the application bundle. // Without this it ends up in ~8 very commonly used bundles. import '@/components/statuses/status'; diff --git a/packages/nicolium/src/features/ui/router/index.tsx b/packages/nicolium/src/features/ui/router/index.tsx index 488779b04..61e2817b8 100644 --- a/packages/nicolium/src/features/ui/router/index.tsx +++ b/packages/nicolium/src/features/ui/router/index.tsx @@ -759,7 +759,7 @@ export const groupMembershipRequestsRoute = createRoute({ export const newStatusRoute = createRoute({ getParentRoute: () => layouts.default, path: '/statuses/new', - component: lazy(() => import('@/pages/utils/new-status')), + component: lazy(() => import('@/pages/compose/new-status')), }); export const scheduledStatusesRoute = createRoute({ @@ -1045,19 +1045,19 @@ export const adminRulesRoute = createRoute({ export const serverInfoRoute = createRoute({ getParentRoute: () => layouts.empty, path: '/info', - component: lazy(() => import('@/pages/utils/server-info')), + component: lazy(() => import('@/pages/instance/server-info')), }); export const aboutRoute = createRoute({ getParentRoute: () => layouts.default, path: '/about/{-$slug}', - component: lazy(() => import('@/pages/utils/about')), + component: lazy(() => import('@/pages/instance/about')), }); export const shareRoute = createRoute({ getParentRoute: () => layouts.default, path: '/share', - component: lazy(() => import('@/pages/utils/share')), + component: lazy(() => import('@/pages/compose/share')), validateSearch: v.object({ title: v.optional(v.string(), ''), text: v.optional(v.string(), ''), @@ -1098,7 +1098,7 @@ export const developersSwRoute = createRoute({ export const errorRoute = createRoute({ getParentRoute: () => layouts.empty, path: '/error', - component: lazy(() => import('@/pages/utils/intentional-error')), + component: lazy(() => import('@/pages/errors/intentional-error')), }); export const networkErrorRoute = createRoute({ @@ -1116,7 +1116,7 @@ export const networkErrorRoute = createRoute({ export const cryptoDonateRoute = createRoute({ getParentRoute: () => layouts.default, path: '/donate/crypto', - component: lazy(() => import('@/pages/utils/crypto-donate')), + component: lazy(() => import('@/pages/instance/crypto-donate')), beforeLoad: ({ context: { hasCrypto } }) => { if (!hasCrypto) throw notFound(); }, @@ -1126,7 +1126,7 @@ export const cryptoDonateRoute = createRoute({ export const federationRestrictionsRoute = createRoute({ getParentRoute: () => layouts.default, path: '/federation_restrictions', - component: lazy(() => import('@/pages/utils/federation-restrictions')), + component: lazy(() => import('@/pages/instance/federation-restrictions')), beforeLoad: ({ context: { features } }) => { if (!features.federating) throw notFound(); }, @@ -1465,7 +1465,7 @@ const router = createRouter({ isAdmin: false, hasCrypto: false, }, - defaultNotFoundComponent: lazy(() => import('@/pages/utils/generic-not-found')), + defaultNotFoundComponent: lazy(() => import('@/pages/errors/generic-not-found')), defaultPendingComponent: PendingComponent, defaultErrorComponent: SiteError, defaultPreload: 'intent', diff --git a/packages/nicolium/src/features/ui/router/util.tsx b/packages/nicolium/src/features/ui/router/util.tsx index be0db2637..690f77633 100644 --- a/packages/nicolium/src/features/ui/router/util.tsx +++ b/packages/nicolium/src/features/ui/router/util.tsx @@ -7,7 +7,7 @@ import { useLoggedIn } from '@/hooks/use-logged-in'; import { useIsStandalone } from '@/utils/state'; const HomeTimeline = lazyRouteComponent(() => import('@/pages/timelines/home-timeline')); -const LandingPage = lazyRouteComponent(() => import('@/pages/utils/landing')); +const LandingPage = lazyRouteComponent(() => import('@/pages/instance/landing')); const LandingTimeline = lazyRouteComponent(() => import('@/pages/timelines/landing-timeline')); const HomeRoute = () => { diff --git a/packages/nicolium/src/features/ui/util/async-components.ts b/packages/nicolium/src/features/ui/util/async-components.ts index 377fa33d3..8fb25ed81 100644 --- a/packages/nicolium/src/features/ui/util/async-components.ts +++ b/packages/nicolium/src/features/ui/util/async-components.ts @@ -1,66 +1,47 @@ import { lazy } from 'react'; // Panels -export const AccountNotePanel = lazy( - () => import('@/features/ui/components/panels/account-note-panel'), -); +export const AccountNotePanel = lazy(() => import('@/components/panels/account-note-panel')); export const AnnouncementsPanel = lazy( () => import('@/components/announcements/announcements-panel'), ); -export const BirthdayPanel = lazy(() => import('@/features/ui/components/panels/birthday-panel')); +export const BirthdayPanel = lazy(() => import('@/components/panels/birthday-panel')); export const CryptoDonatePanel = lazy( () => import('@/features/crypto-donate/components/crypto-donate-panel'), ); export const FundingPanel = lazy(() => import('@/features/patron/components/funding-panel')); -export const GroupMediaPanel = lazy( - () => import('@/features/ui/components/panels/group-media-panel'), -); -export const InstanceInfoPanel = lazy( - () => import('@/features/ui/components/panels/instance-info-panel'), -); +export const GroupMediaPanel = lazy(() => import('@/components/panels/group-media-panel')); export const InstanceModerationPanel = lazy( - () => import('@/features/ui/components/panels/instance-moderation-panel'), + () => import('@/components/panels/instance-moderation-panel'), ); export const LatestAccountsPanel = lazy( - () => import('@/features/admin/components/latest-accounts-panel'), -); -export const MyGroupsPanel = lazy(() => import('@/features/ui/components/panels/my-groups-panel')); -export const NewEventPanel = lazy(() => import('@/features/ui/components/panels/new-event-panel')); -export const NewGroupPanel = lazy(() => import('@/features/ui/components/panels/new-group-panel')); -export const NotificationsPanel = lazy( - () => import('@/features/ui/components/panels/notifications-panel'), -); -export const PinnedAccountsPanel = lazy( - () => import('@/features/ui/components/panels/pinned-accounts-panel'), -); -export const ProfileFieldsPanel = lazy( - () => import('@/features/ui/components/panels/profile-fields-panel'), -); -export const ProfileInfoPanel = lazy( - () => import('@/features/ui/components/panels/profile-info-panel'), -); -export const ProfileMediaPanel = lazy( - () => import('@/features/ui/components/panels/profile-media-panel'), -); -export const PromoPanel = lazy(() => import('@/features/ui/components/panels/promo-panel')); -export const SignUpPanel = lazy(() => import('@/features/ui/components/panels/sign-up-panel')); -export const TrendsPanel = lazy(() => import('@/features/ui/components/panels/trends-panel')); -export const UserPanel = lazy(() => import('@/features/ui/components/panels/user-panel')); -export const WhoToFollowPanel = lazy( - () => import('@/features/ui/components/panels/who-to-follow-panel'), + () => import('@/pages/dashboard/components/latest-accounts-panel'), ); +export const MyGroupsPanel = lazy(() => import('@/components/panels/my-groups-panel')); +export const NewEventPanel = lazy(() => import('@/components/panels/new-event-panel')); +export const NewGroupPanel = lazy(() => import('@/components/panels/new-group-panel')); +export const NotificationsPanel = lazy(() => import('@/components/panels/notifications-panel')); +export const PinnedAccountsPanel = lazy(() => import('@/components/panels/pinned-accounts-panel')); +export const ProfileFieldsPanel = lazy(() => import('@/components/panels/profile-fields-panel')); +export const ProfileInfoPanel = lazy(() => import('@/components/panels/profile-info-panel')); +export const ProfileMediaPanel = lazy(() => import('@/components/panels/profile-media-panel')); +export const PromoPanel = lazy(() => import('@/components/panels/promo-panel')); +export const SignUpPanel = lazy(() => import('@/components/panels/sign-up-panel')); +export const TrendsPanel = lazy(() => import('@/components/panels/trends-panel')); +export const UserPanel = lazy(() => import('@/components/panels/user-panel')); +export const WhoToFollowPanel = lazy(() => import('@/components/panels/who-to-follow-panel')); -export const Audio = lazy(() => import('@/features/audio')); +export const Audio = lazy(() => import('@/components/media/audio')); export const ChatWidget = lazy(() => import('@/features/chats/components/chat-widget/chat-widget')); export const ComposeEditor = lazy(() => import('@/features/compose/editor')); export const ComposeForm = lazy(() => import('@/features/compose/components/compose-form')); export const DatePicker = lazy(() => import('@/features/birthdays/date-picker')); export const DropdownNavigation = lazy(() => import('@/components/navigation/dropdown-navigation')); export const EmojiPicker = lazy(() => import('@/features/emoji/components/emoji-picker')); -export const EventHeader = lazy(() => import('@/features/event/components/event-header')); +export const EventHeader = lazy(() => import('@/components/statuses/events/event-header')); export const MediaGallery = lazy(() => import('@/components/media/media-gallery')); export const ModalRoot = lazy(() => import('@/features/ui/components/modal-root')); -export const ProfileField = lazy(() => import('@/features/ui/components/profile-field')); +export const ProfileField = lazy(() => import('@/components/accounts/profile-field')); export const AccountHoverCard = lazy(() => import('@/components/accounts/account-hover-card')); export const StatusHoverCard = lazy(() => import('@/components/statuses/status-hover-card')); -export const Video = lazy(() => import('@/features/video')); +export const Video = lazy(() => import('@/components/media/video')); diff --git a/packages/nicolium/src/hooks/use-account-gallery.ts b/packages/nicolium/src/hooks/use-account-gallery.ts index 79b8a2463..022a58cef 100644 --- a/packages/nicolium/src/hooks/use-account-gallery.ts +++ b/packages/nicolium/src/hooks/use-account-gallery.ts @@ -6,7 +6,7 @@ import { useGroupMediaTimeline, } from '@/queries/timelines/use-account-media-timeline'; -import type { NormalizedStatus } from '@/normalizers/status'; +import type { NormalizedStatus } from '@/queries/statuses/normalize'; import type { MediaAttachment } from 'pl-api'; type AccountGalleryAttachment = MediaAttachment & { diff --git a/packages/nicolium/src/hooks/use-can-interact.ts b/packages/nicolium/src/hooks/use-can-interact.ts index 88f331132..4f0d59987 100644 --- a/packages/nicolium/src/hooks/use-can-interact.ts +++ b/packages/nicolium/src/hooks/use-can-interact.ts @@ -2,7 +2,7 @@ import { useMemo } from 'react'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import type { NormalizedStatus } from '@/normalizers/status'; +import type { NormalizedStatus } from '@/queries/statuses/normalize'; import type { InteractionPolicy, InteractionPolicyEntry } from 'pl-api'; const useCanInteract = ( diff --git a/packages/nicolium/src/hooks/use-timeline-filters-options.ts b/packages/nicolium/src/hooks/use-timeline-filters-options.ts new file mode 100644 index 000000000..be569957c --- /dev/null +++ b/packages/nicolium/src/hooks/use-timeline-filters-options.ts @@ -0,0 +1,123 @@ +import { useMemo } from 'react'; +import { defineMessages, useIntl } from 'react-intl'; + +import { changeSetting } from '@/actions/settings'; +import { useSettings } from '@/stores/settings'; + +import { useFeatures } from './use-features'; + +import type { Menu } from '@/components/dropdown-menu'; + +const messages = defineMessages({ + showReblogs: { id: 'timeline_filters.show_reblogs', defaultMessage: 'Show reposts' }, + showSelfReblogs: { + id: 'timeline_filters.show_self_reblogs', + defaultMessage: 'Show self-reposts', + }, + showReplies: { id: 'timeline_filters.show_replies', defaultMessage: 'Show replies' }, + showQuotes: { id: 'timeline_filters.show_quotes', defaultMessage: 'Show quotes' }, + showDirect: { + id: 'timeline_filters.show_direct', + defaultMessage: 'Show direct messages', + }, + hideNonMedia: { + id: 'timeline_filters.show_media_only', + defaultMessage: 'Only show posts with media', + }, + setAsDefault: { + id: 'timeline_filters.set_as_default', + defaultMessage: 'Set as default timeline', + }, +}); + +const defaultSettings = { + showReblogs: true, + showSelfReblogs: true, + showReplies: true, + showQuotes: true, + showDirect: true, + showNonMedia: true, +}; + +const useTimelineFiltersOptions = ( + timeline: + | 'home' + | 'antenna' + | 'bubble' + | 'circle' + | 'local' + | 'group' + | 'hashtag' + | 'list' + | 'public' + | 'wrenched', +) => { + const intl = useIntl(); + const features = useFeatures(); + const timelineSettings = useSettings().timelines[timeline] || defaultSettings; + + return useMemo(() => { + const items: Menu = []; + + if (timeline === 'home') { + items.push({ + text: intl.formatMessage(messages.showReblogs), + type: 'toggle', + checked: timelineSettings?.showReblogs, + onChange: (checked) => changeSetting(['timelines', timeline, 'showReblogs'], checked), + }); + items.push({ + text: intl.formatMessage(messages.showSelfReblogs), + type: 'toggle', + checked: timelineSettings?.showSelfReblogs, + disabled: !timelineSettings?.showReblogs, + onChange: (checked) => changeSetting(['timelines', timeline, 'showSelfReblogs'], checked), + }); + } + + items.push({ + text: intl.formatMessage(messages.showReplies), + type: 'toggle', + checked: timelineSettings?.showReplies, + onChange: (checked) => changeSetting(['timelines', timeline, 'showReplies'], checked), + }); + + if (features.quotePosts) { + items.push({ + text: intl.formatMessage(messages.showQuotes), + type: 'toggle', + checked: timelineSettings?.showQuotes, + onChange: (checked) => changeSetting(['timelines', timeline, 'showQuotes'], checked), + }); + } + + if (timeline === 'home') { + items.push({ + text: intl.formatMessage(messages.showDirect), + type: 'toggle', + checked: timelineSettings?.showDirect, + onChange: (checked) => changeSetting(['timelines', timeline, 'showDirect'], checked), + }); + } + + items.push({ + text: intl.formatMessage(messages.hideNonMedia), + type: 'toggle', + checked: !timelineSettings?.showNonMedia, + onChange: (checked) => changeSetting(['timelines', timeline, 'showNonMedia'], !checked), + }); + + // { + // items.push(null); + + // items.push({ + // text: intl.formatMessage(messages.setAsDefault), + // icon: iconHouse, + // }); + // } + + return items; + }, [timeline, features, timelineSettings]); +}; + +export { useTimelineFiltersOptions }; diff --git a/packages/nicolium/src/layouts/admin-layout.tsx b/packages/nicolium/src/layouts/admin-layout.tsx index f40b57290..6cd4fda7b 100644 --- a/packages/nicolium/src/layouts/admin-layout.tsx +++ b/packages/nicolium/src/layouts/admin-layout.tsx @@ -4,7 +4,7 @@ import React from 'react'; import Layout from '@/components/ui/layout'; import { LatestAccountsPanel } from '@/features/ui/util/async-components'; -import LinkFooter from '../features/ui/components/link-footer'; +import LinkFooter from '../components/navigation/link-footer'; const AdminLayout = () => ( <> diff --git a/packages/nicolium/src/layouts/default-layout.tsx b/packages/nicolium/src/layouts/default-layout.tsx index 8cbf88b40..e63b08735 100644 --- a/packages/nicolium/src/layouts/default-layout.tsx +++ b/packages/nicolium/src/layouts/default-layout.tsx @@ -1,9 +1,9 @@ import { Outlet } from '@tanstack/react-router'; import React from 'react'; +import LinkFooter from '@/components/navigation/link-footer'; import Layout from '@/components/ui/layout'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import LinkFooter from '@/features/ui/components/link-footer'; import { WhoToFollowPanel, TrendsPanel, SignUpPanel } from '@/features/ui/util/async-components'; import { useFeatures } from '@/hooks/use-features'; const DefaultLayout: React.FC = () => { diff --git a/packages/nicolium/src/layouts/event-layout.tsx b/packages/nicolium/src/layouts/event-layout.tsx index e78e0f457..f3d29dac1 100644 --- a/packages/nicolium/src/layouts/event-layout.tsx +++ b/packages/nicolium/src/layouts/event-layout.tsx @@ -2,12 +2,12 @@ import { Outlet, useLocation, useNavigate } from '@tanstack/react-router'; import React, { useMemo } from 'react'; import { FormattedMessage } from 'react-intl'; +import LinkFooter from '@/components/navigation/link-footer'; +import PlaceholderStatus from '@/components/placeholders/placeholder-status'; import Column from '@/components/ui/column'; import Layout from '@/components/ui/layout'; import Tabs, { type Item } from '@/components/ui/tabs'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import PlaceholderStatus from '@/features/placeholder/components/placeholder-status'; -import LinkFooter from '@/features/ui/components/link-footer'; import { layouts } from '@/features/ui/router'; import { EventHeader, diff --git a/packages/nicolium/src/layouts/events-layout.tsx b/packages/nicolium/src/layouts/events-layout.tsx index 8d132ff89..32fa83cdc 100644 --- a/packages/nicolium/src/layouts/events-layout.tsx +++ b/packages/nicolium/src/layouts/events-layout.tsx @@ -1,8 +1,8 @@ import { Outlet } from '@tanstack/react-router'; import React from 'react'; +import LinkFooter from '@/components/navigation/link-footer'; import Layout from '@/components/ui/layout'; -import LinkFooter from '@/features/ui/components/link-footer'; import { WhoToFollowPanel, TrendsPanel, NewEventPanel } from '@/features/ui/util/async-components'; import { useFeatures } from '@/hooks/use-features'; diff --git a/packages/nicolium/src/layouts/external-login-layout.tsx b/packages/nicolium/src/layouts/external-login-layout.tsx index 88ebcfacb..5d798700b 100644 --- a/packages/nicolium/src/layouts/external-login-layout.tsx +++ b/packages/nicolium/src/layouts/external-login-layout.tsx @@ -1,9 +1,9 @@ import { Outlet } from '@tanstack/react-router'; import React from 'react'; +import LinkFooter from '@/components/navigation/link-footer'; import Layout from '@/components/ui/layout'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import LinkFooter from '@/features/ui/components/link-footer'; import { WhoToFollowPanel, TrendsPanel, SignUpPanel } from '@/features/ui/util/async-components'; import { useFeatures } from '@/hooks/use-features'; import { useIsStandalone } from '@/utils/state'; diff --git a/packages/nicolium/src/layouts/group-layout.tsx b/packages/nicolium/src/layouts/group-layout.tsx index f8ded8827..d91d7f811 100644 --- a/packages/nicolium/src/layouts/group-layout.tsx +++ b/packages/nicolium/src/layouts/group-layout.tsx @@ -1,14 +1,15 @@ +import iconEyeSlash from '@phosphor-icons/core/regular/eye-slash.svg'; import { Outlet, useLocation } from '@tanstack/react-router'; import React, { useMemo } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; +import GroupHeader from '@/components/groups/group-header'; +import LinkFooter from '@/components/navigation/link-footer'; import Column from '@/components/ui/column'; import Icon from '@/components/ui/icon'; import Layout from '@/components/ui/layout'; import Tabs, { type Item } from '@/components/ui/tabs'; import Text from '@/components/ui/text'; -import GroupHeader from '@/features/group/components/group-header'; -import LinkFooter from '@/features/ui/components/link-footer'; import { layouts } from '@/features/ui/router'; import { GroupMediaPanel, SignUpPanel } from '@/features/ui/util/async-components'; import { useOwnAccount } from '@/hooks/use-own-account'; @@ -24,10 +25,7 @@ const messages = defineMessages({ const PrivacyBlankslate = () => (
- +
diff --git a/packages/nicolium/src/layouts/groups-layout.tsx b/packages/nicolium/src/layouts/groups-layout.tsx index ac1b95139..677601593 100644 --- a/packages/nicolium/src/layouts/groups-layout.tsx +++ b/packages/nicolium/src/layouts/groups-layout.tsx @@ -1,9 +1,9 @@ import { Outlet } from '@tanstack/react-router'; import React from 'react'; +import LinkFooter from '@/components/navigation/link-footer'; import Column from '@/components/ui/column'; import Layout from '@/components/ui/layout'; -import LinkFooter from '@/features/ui/components/link-footer'; import { MyGroupsPanel, NewGroupPanel } from '@/features/ui/util/async-components'; /** Layout to display groups. */ diff --git a/packages/nicolium/src/layouts/home-layout.tsx b/packages/nicolium/src/layouts/home-layout.tsx index ea6f85085..ca7a2b688 100644 --- a/packages/nicolium/src/layouts/home-layout.tsx +++ b/packages/nicolium/src/layouts/home-layout.tsx @@ -3,12 +3,12 @@ import clsx from 'clsx'; import React, { useRef } from 'react'; import { BANNER_HTML } from '@/build-config'; +import LinkFooter from '@/components/navigation/link-footer'; import Avatar from '@/components/ui/avatar'; import Layout from '@/components/ui/layout'; import Text from '@/components/ui/text'; import { useCurrentAccount } from '@/contexts/current-account-context'; import Warning from '@/features/compose/components/warning'; -import LinkFooter from '@/features/ui/components/link-footer'; import { WhoToFollowPanel, TrendsPanel, diff --git a/packages/nicolium/src/layouts/manage-groups-layout.tsx b/packages/nicolium/src/layouts/manage-groups-layout.tsx index 5434dbdcd..6d787c603 100644 --- a/packages/nicolium/src/layouts/manage-groups-layout.tsx +++ b/packages/nicolium/src/layouts/manage-groups-layout.tsx @@ -1,8 +1,8 @@ import { Outlet } from '@tanstack/react-router'; import React from 'react'; +import LinkFooter from '@/components/navigation/link-footer'; import Layout from '@/components/ui/layout'; -import LinkFooter from '@/features/ui/components/link-footer'; import { MyGroupsPanel, NewGroupPanel } from '@/features/ui/util/async-components'; /** Layout to display groups. */ diff --git a/packages/nicolium/src/layouts/profile-layout.tsx b/packages/nicolium/src/layouts/profile-layout.tsx index 414911146..2664d9d87 100644 --- a/packages/nicolium/src/layouts/profile-layout.tsx +++ b/packages/nicolium/src/layouts/profile-layout.tsx @@ -2,12 +2,12 @@ import { Navigate, Outlet, useLocation } from '@tanstack/react-router'; import React from 'react'; import { FormattedMessage } from 'react-intl'; +import AccountHeader from '@/components/accounts/account-header'; +import LinkFooter from '@/components/navigation/link-footer'; import Column from '@/components/ui/column'; import Layout from '@/components/ui/layout'; import Tabs, { type Item } from '@/components/ui/tabs'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import Header from '@/features/account/components/header'; -import LinkFooter from '@/features/ui/components/link-footer'; import { layouts } from '@/features/ui/router'; import { WhoToFollowPanel, @@ -103,7 +103,7 @@ const ProfileLayout: React.FC = () => { label={account ? `@${acct}` : ''} withHeader={false} > -
+ {account && showTabs && ( diff --git a/packages/nicolium/src/layouts/remote-instance-layout.tsx b/packages/nicolium/src/layouts/remote-instance-layout.tsx index e62323593..8104ad68a 100644 --- a/packages/nicolium/src/layouts/remote-instance-layout.tsx +++ b/packages/nicolium/src/layouts/remote-instance-layout.tsx @@ -1,14 +1,10 @@ import { Outlet } from '@tanstack/react-router'; import React from 'react'; +import LinkFooter from '@/components/navigation/link-footer'; import Layout from '@/components/ui/layout'; -import LinkFooter from '@/features/ui/components/link-footer'; import { layouts } from '@/features/ui/router'; -import { - PromoPanel, - InstanceInfoPanel, - InstanceModerationPanel, -} from '@/features/ui/util/async-components'; +import { PromoPanel, InstanceModerationPanel } from '@/features/ui/util/async-components'; import { useOwnAccount } from '@/hooks/use-own-account'; import { useFederationRestrictionsDisclosed } from '@/utils/state'; @@ -27,7 +23,6 @@ const RemoteInstanceLayout = () => { - {(disclosed || account?.is_admin) && } diff --git a/packages/nicolium/src/layouts/search-layout.tsx b/packages/nicolium/src/layouts/search-layout.tsx index e8c6fe5dd..a6324d013 100644 --- a/packages/nicolium/src/layouts/search-layout.tsx +++ b/packages/nicolium/src/layouts/search-layout.tsx @@ -1,9 +1,9 @@ import { Outlet } from '@tanstack/react-router'; import React from 'react'; +import LinkFooter from '@/components/navigation/link-footer'; import Layout from '@/components/ui/layout'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import LinkFooter from '@/features/ui/components/link-footer'; import { WhoToFollowPanel, TrendsPanel, SignUpPanel } from '@/features/ui/util/async-components'; import { useFeatures } from '@/hooks/use-features'; const SearchLayout = () => { diff --git a/packages/nicolium/src/layouts/status-layout.tsx b/packages/nicolium/src/layouts/status-layout.tsx index 0d4ce575c..f03acbdde 100644 --- a/packages/nicolium/src/layouts/status-layout.tsx +++ b/packages/nicolium/src/layouts/status-layout.tsx @@ -1,9 +1,9 @@ import { Outlet } from '@tanstack/react-router'; import React from 'react'; +import LinkFooter from '@/components/navigation/link-footer'; import Layout from '@/components/ui/layout'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import LinkFooter from '@/features/ui/components/link-footer'; import { WhoToFollowPanel, TrendsPanel, SignUpPanel } from '@/features/ui/util/async-components'; import { useFeatures } from '@/hooks/use-features'; const StatusLayout = () => { diff --git a/packages/nicolium/src/locales/en.json b/packages/nicolium/src/locales/en.json index fec784df6..673cec352 100644 --- a/packages/nicolium/src/locales/en.json +++ b/packages/nicolium/src/locales/en.json @@ -729,6 +729,10 @@ "confirmations.block_from_group.confirm": "Ban user", "confirmations.block_from_group.heading": "Ban from group", "confirmations.block_from_group.message": "Are you sure you want to ban @{name} from the group?", + "confirmations.boost_missing_description.confirm": "Repost anyway", + "confirmations.boost_missing_description.filename_warning": "One or more attachments likely has a filename (e.g. 'image.jpg') as its description instead of meaningful alt text. Do you want to repost it anyway?", + "confirmations.boost_missing_description.heading": "Reposting a post with missing description", + "confirmations.boost_missing_description.message": "The post does not have a description for all attachments. Do you want to repost it anyway?", "confirmations.cancel.confirm": "Discard", "confirmations.cancel.heading": "Discard post", "confirmations.cancel.message": "Are you sure you want to discard the currently composed post?", @@ -799,6 +803,9 @@ "confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.heading": "Unfollow {name}", "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "confirmations.wrench.confirm": "Wrench", + "confirmations.wrench.heading": "Wrench the post", + "confirmations.wrench.message": "Are you sure you want to wrench this post? This can have disastrous consequences.", "copy.success": "Copied to clipboard!", "crypto.lightning": "Lightning", "crypto_donate.explanation_box.message": "{siteTitle} accepts cryptocurrency donations. You may send a donation to any of the addresses below. Thank you for your support!", @@ -1604,6 +1611,7 @@ "preferences.fields.language_label": "Display language", "preferences.fields.media_display_label": "Sensitive content", "preferences.fields.mention_policy_label": "Accept mentions from", + "preferences.fields.missing_description_boost_modal_label": "Show confirmation dialog before reposting a post without media descriptions", "preferences.fields.missing_description_modal_label": "Show confirmation dialog before sending a post without media descriptions", "preferences.fields.preserve_spoilers_label": "Preserve content warning when replying", "preferences.fields.privacy_label": "Default post privacy", @@ -1631,6 +1639,8 @@ "preferences.fields.web_layout_label": "Layout of the web view of your profile", "preferences.fields.web_visibility_label": "Visibility level of posts displayed on your profile", "preferences.fields.eggplant_label": "Display eggplant reaction button", + "preferences.fields.eggplant_modal_hint": "Prevents accidentally using the eggplant button.", + "preferences.fields.eggplant_modal_label": "Show confirmation dialog before adding eggplant reaction", "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", "preferences.hints.mention_policy": "Applies to direct messages and public posts", "preferences.hints.web_include_boosts": "Show reposts created by the account on the web view of your profile", @@ -2028,6 +2038,14 @@ "timeline.gap.load_newer": "Load newer posts", "timeline.gap.load_older": "Load older posts", "timeline.gap.load_recent": "Load recent posts", + "timeline_filters.set_as_default": "Set as default timeline", + "timeline_filters.show_direct": "Show direct messages", + "timeline_filters.show_media_only": "Only show posts with media", + "timeline_filters.show_quotes": "Show quotes", + "timeline_filters.show_reblogs": "Show reposts", + "timeline_filters.show_replies": "Show replies", + "timeline_filters.show_self_reblogs": "Show self-reposts", + "timeline_picker.pinned_instances": "Pinned instances", "toast.view": "View", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", "trends.no_accounts": "Try entering a search query or browsing the profile directory to find accounts to follow.", diff --git a/packages/nicolium/src/main.tsx b/packages/nicolium/src/main.tsx index 62b426bcb..aab16167a 100644 --- a/packages/nicolium/src/main.tsx +++ b/packages/nicolium/src/main.tsx @@ -23,7 +23,6 @@ import '@fontsource/inter/600.css'; import '@fontsource/inter/700.css'; import '@fontsource/inter/900.css'; import '@fontsource/roboto-mono/400.css'; -import 'line-awesome/dist/font-awesome-line-awesome/css/all.css'; import './styles/i18n.css'; import './styles/application.scss'; import './styles/tailwind.css'; diff --git a/packages/nicolium/src/modals/alt-text-modal.tsx b/packages/nicolium/src/modals/alt-text-modal.tsx index 2f2198c64..97a481929 100644 --- a/packages/nicolium/src/modals/alt-text-modal.tsx +++ b/packages/nicolium/src/modals/alt-text-modal.tsx @@ -4,12 +4,12 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import Blurhash from '@/components/media/blurhash'; +import { getPointerPosition } from '@/components/media/video'; import FormGroup from '@/components/ui/form-group'; import Icon from '@/components/ui/icon'; import Modal from '@/components/ui/modal'; import Textarea from '@/components/ui/textarea'; import { MIMETYPE_ICONS } from '@/components/upload'; -import { getPointerPosition } from '@/features/video'; import { useCompose } from '@/hooks/use-compose'; import { useFeatures } from '@/hooks/use-features'; import toast from '@/toast'; diff --git a/packages/nicolium/src/modals/antenna-editor-modal.tsx b/packages/nicolium/src/modals/antenna-editor-modal.tsx index 2f481d9ae..c4d929659 100644 --- a/packages/nicolium/src/modals/antenna-editor-modal.tsx +++ b/packages/nicolium/src/modals/antenna-editor-modal.tsx @@ -1,3 +1,4 @@ +import iconX from '@phosphor-icons/core/regular/x.svg'; import React, { useMemo, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -10,10 +11,10 @@ import FormGroup from '@/components/ui/form-group'; import IconButton from '@/components/ui/icon-button'; import Input from '@/components/ui/input'; import Modal from '@/components/ui/modal'; +import { SelectDropdown } from '@/components/ui/select-dropdown'; import Spinner from '@/components/ui/spinner'; import Text from '@/components/ui/text'; import Toggle from '@/components/ui/toggle'; -import { SelectDropdown } from '@/features/forms'; import { useAddAccountsToAntenna, useAddDomainsToAntenna, @@ -257,7 +258,7 @@ const AntennaValuesForm: React.FC = ({
{item} = ({
{item} = ({ antennaId, onTabChange }) const { mutate: updateAntenna, isPending: updateDisabled } = useUpdateAntenna(antennaId!); const { mutate: createAntenna, isPending: createDisabled } = useCreateAntenna(); - const { data: lists } = useLists((lists) => lists); + const { data: lists } = useLists(); const [title, setTitle] = useState(antenna ? antenna.title : ''); const [ltl, setLtl] = useState(antenna ? antenna.ltl : false); diff --git a/packages/nicolium/src/modals/birthdays-modal.tsx b/packages/nicolium/src/modals/birthdays-modal.tsx index 1dab66382..8a062415c 100644 --- a/packages/nicolium/src/modals/birthdays-modal.tsx +++ b/packages/nicolium/src/modals/birthdays-modal.tsx @@ -1,11 +1,11 @@ import React, { useState } from 'react'; import { FormattedMessage } from 'react-intl'; +import { getCurrentDate } from '@/components/panels/birthday-panel'; import ScrollableList from '@/components/scrollable-list'; import Modal from '@/components/ui/modal'; import Spinner from '@/components/ui/spinner'; import Account from '@/features/birthdays/account'; -import { getCurrentDate } from '@/features/ui/components/panels/birthday-panel'; import { useBirthdayReminders } from '@/queries/accounts/use-birthday-reminders'; import type { BaseModalProps } from '@/features/ui/components/modal-root'; diff --git a/packages/nicolium/src/modals/boost-modal.tsx b/packages/nicolium/src/modals/boost-modal.tsx index d5b3cd5a5..a3fb38424 100644 --- a/packages/nicolium/src/modals/boost-modal.tsx +++ b/packages/nicolium/src/modals/boost-modal.tsx @@ -1,3 +1,4 @@ +import iconRepeat from '@phosphor-icons/core/regular/repeat.svg'; import React from 'react'; import { FormattedMessage } from 'react-intl'; @@ -58,11 +59,7 @@ const BoostModal: React.FC = ({ values={{ combo: ( - Shift +{' '} - + Shift + ), }} diff --git a/packages/nicolium/src/modals/compose-interaction-policy-modal.tsx b/packages/nicolium/src/modals/compose-interaction-policy-modal.tsx index 393764bbe..874961d49 100644 --- a/packages/nicolium/src/modals/compose-interaction-policy-modal.tsx +++ b/packages/nicolium/src/modals/compose-interaction-policy-modal.tsx @@ -1,3 +1,4 @@ +import iconArrowLeft from '@phosphor-icons/core/regular/arrow-left.svg'; import { Link } from '@tanstack/react-router'; import { create } from 'mutative'; import React, { useEffect, useState } from 'react'; @@ -92,11 +93,7 @@ const ComposeInteractionPolicyModal: React.FC< /> } onClose={onClickClose} - closeIcon={ - composeId === 'compose-modal' - ? require('@phosphor-icons/core/regular/arrow-left.svg') - : undefined - } + closeIcon={composeId === 'compose-modal' ? iconArrowLeft : undefined} closePosition={composeId === 'compose-modal' ? 'left' : undefined} >
diff --git a/packages/nicolium/src/modals/edit-bookmark-folder-modal.tsx b/packages/nicolium/src/modals/edit-bookmark-folder-modal.tsx index 4325d85dc..05743808a 100644 --- a/packages/nicolium/src/modals/edit-bookmark-folder-modal.tsx +++ b/packages/nicolium/src/modals/edit-bookmark-folder-modal.tsx @@ -1,4 +1,5 @@ import { useFloating, shift, autoUpdate, flip } from '@floating-ui/react'; +import iconSmiley from '@phosphor-icons/core/regular/smiley.svg'; import React, { useState } from 'react'; import { createPortal } from 'react-dom'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; @@ -73,7 +74,7 @@ const EmojiPicker: React.FC = ({ emoji, emojiUrl, ...props }) => { ) : ( )} diff --git a/packages/nicolium/src/modals/event-map-modal.tsx b/packages/nicolium/src/modals/event-map-modal.tsx index 8e9a82db9..9554d3792 100644 --- a/packages/nicolium/src/modals/event-map-modal.tsx +++ b/packages/nicolium/src/modals/event-map-modal.tsx @@ -1,19 +1,23 @@ +import iconCompass from '@phosphor-icons/core/regular/compass.svg'; import L from 'leaflet'; +import iconRetinaUrl from 'leaflet/dist/images/marker-icon-2x.png'; +import iconUrl from 'leaflet/dist/images/marker-icon.png'; +import shadowUrl from 'leaflet/dist/images/marker-shadow.png'; import React, { useEffect, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; +import 'leaflet/dist/leaflet.css'; import Button from '@/components/ui/button'; import Modal from '@/components/ui/modal'; import { useFrontendConfig } from '@/hooks/use-frontend-config'; import { useMinimalStatus } from '@/queries/statuses/use-status'; -import 'leaflet/dist/leaflet.css'; import type { BaseModalProps } from '@/features/ui/components/modal-root'; L.Icon.Default.mergeOptions({ - iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), - iconUrl: require('leaflet/dist/images/marker-icon.png'), - shadowUrl: require('leaflet/dist/images/marker-shadow.png'), + iconRetinaUrl, + iconUrl, + shadowUrl, }); interface EventMapModalProps { @@ -65,10 +69,7 @@ const EventMapModal: React.FC = ({ onClose, >
-
diff --git a/packages/nicolium/src/modals/list-adder-modal/components/list.tsx b/packages/nicolium/src/modals/list-adder-modal/components/list.tsx index 26c5656d6..11f748bb1 100644 --- a/packages/nicolium/src/modals/list-adder-modal/components/list.tsx +++ b/packages/nicolium/src/modals/list-adder-modal/components/list.tsx @@ -1,3 +1,6 @@ +import iconListBullets from '@phosphor-icons/core/regular/list-bullets.svg'; +import iconPlus from '@phosphor-icons/core/regular/plus.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -44,7 +47,7 @@ const List: React.FC = ({ listId, accountId, added }) => { @@ -54,7 +57,7 @@ const List: React.FC = ({ listId, accountId, added }) => { @@ -63,7 +66,7 @@ const List: React.FC = ({ listId, accountId, added }) => { return (
- + {list.title} {button}
diff --git a/packages/nicolium/src/modals/list-editor-modal/components/account.tsx b/packages/nicolium/src/modals/list-editor-modal/components/account.tsx index fa8a45fe5..222ddf9dd 100644 --- a/packages/nicolium/src/modals/list-editor-modal/components/account.tsx +++ b/packages/nicolium/src/modals/list-editor-modal/components/account.tsx @@ -1,3 +1,5 @@ +import iconPlus from '@phosphor-icons/core/regular/plus.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -24,7 +26,7 @@ const Account: React.FC = ({ accountId, added, onAdd, onRemove }) => { if (added) { button = ( = ({ accountId, added, onAdd, onRemove }) => { } else { button = ( = ({ value, onSubmit }) => { }} > diff --git a/packages/nicolium/src/modals/manage-group-modal/steps/confirmation-step.tsx b/packages/nicolium/src/modals/manage-group-modal/steps/confirmation-step.tsx index 83ff8a5f4..5a4e6a221 100644 --- a/packages/nicolium/src/modals/manage-group-modal/steps/confirmation-step.tsx +++ b/packages/nicolium/src/modals/manage-group-modal/steps/confirmation-step.tsx @@ -1,3 +1,5 @@ +import iconExport from '@phosphor-icons/core/regular/export.svg'; +import iconLinkSimple from '@phosphor-icons/core/regular/link-simple.svg'; import React from 'react'; import { FormattedMessage, defineMessages } from 'react-intl'; @@ -115,7 +117,7 @@ const ConfirmationStep: React.FC = ({ group }) => {
= (props) => { onClick={handleNextClick} aria-label={intl.formatMessage(messages.next)} > - +
diff --git a/packages/nicolium/src/modals/reply-mentions-modal.tsx b/packages/nicolium/src/modals/reply-mentions-modal.tsx index d1ec2c253..470457974 100644 --- a/packages/nicolium/src/modals/reply-mentions-modal.tsx +++ b/packages/nicolium/src/modals/reply-mentions-modal.tsx @@ -1,15 +1,91 @@ +import iconArrowLeft from '@phosphor-icons/core/regular/arrow-left.svg'; +import iconPlus from '@phosphor-icons/core/regular/plus.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import React from 'react'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; +import AccountComponent from '@/components/accounts/account'; +import IconButton from '@/components/ui/icon-button'; import Modal from '@/components/ui/modal'; -import Account from '@/features/reply-mentions/account'; -import { useCompose } from '@/hooks/use-compose'; import { useOwnAccount } from '@/hooks/use-own-account'; +import { useAccount } from '@/queries/accounts/use-account'; import { useMinimalStatus } from '@/queries/statuses/use-status'; +import { useCompose, useComposeActions } from '@/stores/compose'; import { statusToMentionsAccountIdsArray } from '@/stores/compose'; import type { BaseModalProps } from '@/features/ui/components/modal-root'; +const messages = defineMessages({ + remove: { id: 'reply_mentions.account.remove', defaultMessage: 'Remove from mentions' }, + add: { id: 'reply_mentions.account.add', defaultMessage: 'Add to mentions' }, +}); + +interface IReplyMentionAccount { + composeId: string; + accountId: string; + author: boolean; +} + +const ReplyMentionAccount: React.FC = ({ composeId, accountId, author }) => { + const intl = useIntl(); + const { updateCompose } = useComposeActions(); + + const compose = useCompose(composeId); + const { data: account } = useAccount(accountId); + const added = !!account && compose.to?.includes(account.acct); + + const onRemove = () => + updateCompose(composeId, (draft) => { + if (account) { + draft.to = draft.to?.filter((acct) => acct !== account.acct) || []; + } + }); + const onAdd = () => + updateCompose(composeId, (draft) => { + if (account) { + if (draft.to?.includes(account.acct)) return; + draft.to = [...(draft.to || []), account.acct]; + } + }); + + if (!account) return null; + + let button; + + if (added) { + button = ( + + ); + } else { + button = ( + + ); + } + + return ( +
+ +
+ ); +}; + interface ReplyMentionsModalProps { composeId: string; } @@ -34,12 +110,12 @@ const ReplyMentionsModal: React.FC = ( } onClose={onClickClose} - closeIcon={require('@phosphor-icons/core/regular/arrow-left.svg')} + closeIcon={iconArrowLeft} closePosition='left' >
{mentions.map((accountId) => ( -
) : (
@@ -126,7 +128,7 @@ const SelectBookmarkFolderModal: React.FC - + ) : ( - + )} {folder.name}
diff --git a/packages/nicolium/src/modals/select-drive-file-modal.tsx b/packages/nicolium/src/modals/select-drive-file-modal.tsx index c506eab07..62458c82b 100644 --- a/packages/nicolium/src/modals/select-drive-file-modal.tsx +++ b/packages/nicolium/src/modals/select-drive-file-modal.tsx @@ -1,3 +1,4 @@ +import iconFolder from '@phosphor-icons/core/regular/folder.svg'; import defaultIcon from '@phosphor-icons/core/regular/paperclip.svg'; import clsx from 'clsx'; import React, { useMemo } from 'react'; @@ -48,10 +49,7 @@ const Folder: React.FC = ({ folder, active, disabled, onSelect, onDoubl onClick={disabled ? undefined : () => onSelect?.(folder)} disabled={disabled} > - + {folder.name} diff --git a/packages/nicolium/src/pages/account-lists/antennas.tsx b/packages/nicolium/src/pages/account-lists/antennas.tsx index 687720487..17313dd72 100644 --- a/packages/nicolium/src/pages/account-lists/antennas.tsx +++ b/packages/nicolium/src/pages/account-lists/antennas.tsx @@ -1,3 +1,6 @@ +import iconDotsThreeVertical from '@phosphor-icons/core/regular/dots-three-vertical.svg'; +import iconListBullets from '@phosphor-icons/core/regular/list-bullets.svg'; +import iconPlus from '@phosphor-icons/core/regular/plus.svg'; import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; @@ -39,7 +42,7 @@ const AntennasPage: React.FC = () => { action: () => { openModal('ANTENNA_EDITOR', {}); }, - icon: require('@phosphor-icons/core/regular/plus.svg'), + icon: iconPlus, }, ]; @@ -53,12 +56,7 @@ const AntennasPage: React.FC = () => { return ( - } + action={} >
{/* */} @@ -76,10 +74,7 @@ const AntennasPage: React.FC = () => { params={{ antennaId: antenna.id }} label={
- + {antenna.title}
} diff --git a/packages/nicolium/src/pages/account-lists/circles.tsx b/packages/nicolium/src/pages/account-lists/circles.tsx index 361c3f8fd..8226f9b86 100644 --- a/packages/nicolium/src/pages/account-lists/circles.tsx +++ b/packages/nicolium/src/pages/account-lists/circles.tsx @@ -1,3 +1,4 @@ +import iconListBullets from '@phosphor-icons/core/regular/list-bullets.svg'; import React, { useState } from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; @@ -100,10 +101,7 @@ const CirclesPage: React.FC = () => { params={{ circleId: circle.id }} label={
- + {circle.title}
} diff --git a/packages/nicolium/src/pages/account-lists/directory.tsx b/packages/nicolium/src/pages/account-lists/directory.tsx index 29214827d..e96ea36f9 100644 --- a/packages/nicolium/src/pages/account-lists/directory.tsx +++ b/packages/nicolium/src/pages/account-lists/directory.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import Account from '@/components/accounts/account'; +import ActionButton from '@/components/accounts/action-button'; import HoverAccountWrapper from '@/components/accounts/hover-account-wrapper'; import Badge from '@/components/badge'; import LoadMore from '@/components/load-more'; @@ -15,7 +16,6 @@ import { CardTitle } from '@/components/ui/card'; import Column from '@/components/ui/column'; import { RadioGroup, RadioItem } from '@/components/ui/radio'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import ActionButton from '@/features/ui/components/action-button'; import { directoryRoute } from '@/features/ui/router'; import { useFeatures } from '@/hooks/use-features'; import { useAccount } from '@/queries/accounts/use-account'; diff --git a/packages/nicolium/src/pages/account-lists/lists.tsx b/packages/nicolium/src/pages/account-lists/lists.tsx index 2ef0b89e3..1b41bb303 100644 --- a/packages/nicolium/src/pages/account-lists/lists.tsx +++ b/packages/nicolium/src/pages/account-lists/lists.tsx @@ -1,3 +1,4 @@ +import iconListBullets from '@phosphor-icons/core/regular/list-bullets.svg'; import React, { useState } from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; @@ -111,10 +112,7 @@ const ListsPage: React.FC = () => { params={{ listId: list.id }} label={
- + {list.title}
} diff --git a/packages/nicolium/src/pages/accounts/account-gallery.tsx b/packages/nicolium/src/pages/accounts/account-gallery.tsx index 6995cf0d1..b37e036f8 100644 --- a/packages/nicolium/src/pages/accounts/account-gallery.tsx +++ b/packages/nicolium/src/pages/accounts/account-gallery.tsx @@ -1,3 +1,5 @@ +import iconEyeSlash from '@phosphor-icons/core/regular/eye-slash.svg'; +import iconSpeakerHigh from '@phosphor-icons/core/regular/speaker-high.svg'; import { Link } from '@tanstack/react-router'; import clsx from 'clsx'; import React, { useState } from 'react'; @@ -117,7 +119,7 @@ const MediaItem: React.FC = ({ attachment, onOpenMedia, isLast }) => thumbnail = (
- + {fileExtension}
@@ -127,7 +129,7 @@ const MediaItem: React.FC = ({ attachment, onOpenMedia, isLast }) => if (!visible) { icon = ( - + ); } diff --git a/packages/nicolium/src/features/auth-login/components/captcha.tsx b/packages/nicolium/src/pages/auth/components/captcha.tsx similarity index 100% rename from packages/nicolium/src/features/auth-login/components/captcha.tsx rename to packages/nicolium/src/pages/auth/components/captcha.tsx diff --git a/packages/nicolium/src/features/auth-login/components/consumer-button.tsx b/packages/nicolium/src/pages/auth/components/consumer-button.tsx similarity index 62% rename from packages/nicolium/src/features/auth-login/components/consumer-button.tsx rename to packages/nicolium/src/pages/auth/components/consumer-button.tsx index 9718ce0df..887e5a8a8 100644 --- a/packages/nicolium/src/features/auth-login/components/consumer-button.tsx +++ b/packages/nicolium/src/pages/auth/components/consumer-button.tsx @@ -1,3 +1,10 @@ +import iconFacebookLogo from '@phosphor-icons/core/regular/facebook-logo.svg'; +import iconGithubLogo from '@phosphor-icons/core/regular/github-logo.svg'; +import iconGoogleLogo from '@phosphor-icons/core/regular/google-logo.svg'; +import iconKey from '@phosphor-icons/core/regular/key.svg'; +import iconSlackLogo from '@phosphor-icons/core/regular/slack-logo.svg'; +import iconSquaresFour from '@phosphor-icons/core/regular/squares-four.svg'; +import iconTwitterLogo from '@phosphor-icons/core/regular/twitter-logo.svg'; import React from 'react'; import { useIntl, defineMessages } from 'react-intl'; @@ -12,12 +19,12 @@ const messages = defineMessages({ /** Map between OAuth providers and brand icons. */ const BRAND_ICONS: Record = { - twitter: require('@phosphor-icons/core/regular/twitter-logo.svg'), - facebook: require('@phosphor-icons/core/regular/facebook-logo.svg'), - google: require('@phosphor-icons/core/regular/google-logo.svg'), - microsoft: require('@phosphor-icons/core/regular/squares-four.svg'), - slack: require('@phosphor-icons/core/regular/slack-logo.svg'), - github: require('@phosphor-icons/core/regular/github-logo.svg'), + twitter: iconTwitterLogo, + facebook: iconFacebookLogo, + google: iconGoogleLogo, + microsoft: iconSquaresFour, + slack: iconSlackLogo, + github: iconGithubLogo, }; interface IConsumerButton { @@ -28,7 +35,7 @@ interface IConsumerButton { const ConsumerButton: React.FC = ({ provider }) => { const intl = useIntl(); - const icon = BRAND_ICONS[provider] || require('@phosphor-icons/core/regular/key.svg'); + const icon = BRAND_ICONS[provider] || iconKey; const handleClick = () => { prepareRequest(provider); diff --git a/packages/nicolium/src/features/auth-login/components/consumers-list.tsx b/packages/nicolium/src/pages/auth/components/consumers-list.tsx similarity index 100% rename from packages/nicolium/src/features/auth-login/components/consumers-list.tsx rename to packages/nicolium/src/pages/auth/components/consumers-list.tsx diff --git a/packages/nicolium/src/features/external-login/components/external-login-form.tsx b/packages/nicolium/src/pages/auth/components/external-login-form.tsx similarity index 100% rename from packages/nicolium/src/features/external-login/components/external-login-form.tsx rename to packages/nicolium/src/pages/auth/components/external-login-form.tsx diff --git a/packages/nicolium/src/features/auth-login/components/login-form.tsx b/packages/nicolium/src/pages/auth/components/login-form.tsx similarity index 100% rename from packages/nicolium/src/features/auth-login/components/login-form.tsx rename to packages/nicolium/src/pages/auth/components/login-form.tsx diff --git a/packages/nicolium/src/features/auth-login/components/otp-auth-form.tsx b/packages/nicolium/src/pages/auth/components/otp-auth-form.tsx similarity index 100% rename from packages/nicolium/src/features/auth-login/components/otp-auth-form.tsx rename to packages/nicolium/src/pages/auth/components/otp-auth-form.tsx diff --git a/packages/nicolium/src/features/auth-login/components/registration-form.tsx b/packages/nicolium/src/pages/auth/components/registration-form.tsx similarity index 98% rename from packages/nicolium/src/features/auth-login/components/registration-form.tsx rename to packages/nicolium/src/pages/auth/components/registration-form.tsx index 263e3a6d5..088f2562a 100644 --- a/packages/nicolium/src/features/auth-login/components/registration-form.tsx +++ b/packages/nicolium/src/pages/auth/components/registration-form.tsx @@ -1,3 +1,4 @@ +import iconAt from '@phosphor-icons/core/regular/at.svg'; import { Link, useNavigate } from '@tanstack/react-router'; import debounce from 'lodash/debounce'; import React, { useState, useRef, useCallback } from 'react'; @@ -12,7 +13,6 @@ import FormGroup from '@/components/ui/form-group'; import Input from '@/components/ui/input'; import Select from '@/components/ui/select'; import Textarea from '@/components/ui/textarea'; -import CaptchaField from '@/features/auth-login/components/captcha'; import { useClient } from '@/hooks/use-client'; import { useFeatures } from '@/hooks/use-features'; import { useAuthActions } from '@/stores/auth'; @@ -20,6 +20,8 @@ import { useInstance } from '@/stores/instance'; import { useModalsActions } from '@/stores/modals'; import { useSettings } from '@/stores/settings'; +import CaptchaField from './captcha'; + import type { CreateAccountParams } from 'pl-api'; const messages = defineMessages({ @@ -297,7 +299,7 @@ const RegistrationForm: React.FC = ({ inviteToken }) => { autoCorrect='off' autoCapitalize='off' pattern='^[a-zA-Z\d_-]+' - icon={require('@phosphor-icons/core/regular/at.svg')} + icon={iconAt} onChange={onUsernameChange} value={params.username} required diff --git a/packages/nicolium/src/pages/auth/external-login.tsx b/packages/nicolium/src/pages/auth/external-login.tsx index 73291c75f..0f168a400 100644 --- a/packages/nicolium/src/pages/auth/external-login.tsx +++ b/packages/nicolium/src/pages/auth/external-login.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import { BigCard } from '@/components/ui/big-card'; -import ExternalLoginForm from '@/features/external-login/components/external-login-form'; + +import ExternalLoginForm from './components/external-login-form'; /** Page for logging into a remote instance */ const ExternalLoginPage: React.FC = () => ( diff --git a/packages/nicolium/src/pages/auth/login.tsx b/packages/nicolium/src/pages/auth/login.tsx index 1ed5cfcc1..6228beca7 100644 --- a/packages/nicolium/src/pages/auth/login.tsx +++ b/packages/nicolium/src/pages/auth/login.tsx @@ -7,14 +7,15 @@ import { BigCard } from '@/components/ui/big-card'; import Button from '@/components/ui/button'; import Text from '@/components/ui/text'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import ConsumersList from '@/features/auth-login/components/consumers-list'; -import LoginForm from '@/features/auth-login/components/login-form'; -import OtpAuthForm from '@/features/auth-login/components/otp-auth-form'; import { useAuthActions } from '@/stores/auth'; import { useModalsActions } from '@/stores/modals'; import { getRedirectUrl } from '@/utils/redirect'; import { useIsStandalone } from '@/utils/state'; +import ConsumersList from './components/consumers-list'; +import LoginForm from './components/login-form'; +import OtpAuthForm from './components/otp-auth-form'; + import type { NicoliumResponse } from '@/api'; const LoginPage = () => { diff --git a/packages/nicolium/src/pages/auth/register-with-invite.tsx b/packages/nicolium/src/pages/auth/register-with-invite.tsx index 29ef33367..44869fb48 100644 --- a/packages/nicolium/src/pages/auth/register-with-invite.tsx +++ b/packages/nicolium/src/pages/auth/register-with-invite.tsx @@ -2,10 +2,11 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import { BigCard } from '@/components/ui/big-card'; -import RegistrationForm from '@/features/auth-login/components/registration-form'; import { inviteRoute } from '@/features/ui/router'; import { useInstance } from '@/stores/instance'; +import RegistrationForm from './components/registration-form'; + /** Page to register with an invitation. */ const RegisterWithInvitePage: React.FC = () => { const { token } = inviteRoute.useParams(); diff --git a/packages/nicolium/src/pages/auth/registration.tsx b/packages/nicolium/src/pages/auth/registration.tsx index 6eb4111d1..24c23c773 100644 --- a/packages/nicolium/src/pages/auth/registration.tsx +++ b/packages/nicolium/src/pages/auth/registration.tsx @@ -3,10 +3,11 @@ import { FormattedMessage } from 'react-intl'; import { BigCard } from '@/components/ui/big-card'; import Text from '@/components/ui/text'; -import RegistrationForm from '@/features/auth-login/components/registration-form'; import { useRegistrationStatus } from '@/hooks/use-registration-status'; import { useInstance } from '@/stores/instance'; +import RegistrationForm from './components/registration-form'; + const RegistrationPage: React.FC = () => { const instance = useInstance(); const { isOpen } = useRegistrationStatus(); diff --git a/packages/nicolium/src/pages/utils/new-status.tsx b/packages/nicolium/src/pages/compose/new-status.tsx similarity index 100% rename from packages/nicolium/src/pages/utils/new-status.tsx rename to packages/nicolium/src/pages/compose/new-status.tsx diff --git a/packages/nicolium/src/pages/utils/share.tsx b/packages/nicolium/src/pages/compose/share.tsx similarity index 100% rename from packages/nicolium/src/pages/utils/share.tsx rename to packages/nicolium/src/pages/compose/share.tsx diff --git a/packages/nicolium/src/pages/dashboard/account.tsx b/packages/nicolium/src/pages/dashboard/account.tsx index 70fe65a29..5d1df10c2 100644 --- a/packages/nicolium/src/pages/dashboard/account.tsx +++ b/packages/nicolium/src/pages/dashboard/account.tsx @@ -1,3 +1,4 @@ +import iconArrowSquareOut from '@phosphor-icons/core/regular/arrow-square-out.svg'; import { PLEROMA } from 'pl-api'; import React, { type ChangeEventHandler, useMemo, useState } from 'react'; import { defineMessages, FormattedMessage, type MessageDescriptor, useIntl } from 'react-intl'; @@ -8,10 +9,10 @@ import MissingIndicator from '@/components/missing-indicator'; import OutlineBox from '@/components/outline-box'; import Button from '@/components/ui/button'; import Column from '@/components/ui/column'; +import { SelectDropdown } from '@/components/ui/select-dropdown'; import TagInput from '@/components/ui/tag-input'; import Text from '@/components/ui/text'; import Toggle from '@/components/ui/toggle'; -import { SelectDropdown } from '@/features/forms'; import ColumnLoading from '@/features/ui/components/column-loading'; import { adminAccountRoute } from '@/features/ui/router'; import { useDeactivateUserModal, useDeleteUserModal } from '@/hooks/use-admin-modals'; @@ -344,12 +345,7 @@ const AdminAccountPage: React.FC = () => { {features.version.software === PLEROMA && (
-
)} @@ -87,10 +88,7 @@ const ReportStatuses: React.FC = ({ statusIds }) => { }} className='flex size-8 items-center justify-center rounded-full bg-white/50 backdrop-blur dark:bg-gray-900/50' > - +
)} @@ -268,7 +266,7 @@ const ReportPage: React.FC = () => { @@ -277,7 +275,7 @@ const ReportPage: React.FC = () => { diff --git a/packages/nicolium/src/pages/dashboard/reports.tsx b/packages/nicolium/src/pages/dashboard/reports.tsx index 9ad4db841..2a482ad4f 100644 --- a/packages/nicolium/src/pages/dashboard/reports.tsx +++ b/packages/nicolium/src/pages/dashboard/reports.tsx @@ -1,3 +1,4 @@ +import iconX from '@phosphor-icons/core/regular/x.svg'; import { useNavigate } from '@tanstack/react-router'; import React from 'react'; import { defineMessages, FormattedList, FormattedMessage, useIntl } from 'react-intl'; @@ -6,8 +7,8 @@ import ScrollableList from '@/components/scrollable-list'; import Column from '@/components/ui/column'; import IconButton from '@/components/ui/icon-button'; import Text from '@/components/ui/text'; -import Report from '@/features/admin/components/report'; import { adminReportsRoute } from '@/features/ui/router'; +import Report from '@/pages/dashboard/components/report'; import { useAccount } from '@/queries/accounts/use-account'; import { useReports } from '@/queries/admin/use-reports'; @@ -48,7 +49,7 @@ const Reports: React.FC = () => {
diff --git a/packages/nicolium/src/pages/dashboard/rules.tsx b/packages/nicolium/src/pages/dashboard/rules.tsx index ccc16fe90..6382b6a09 100644 --- a/packages/nicolium/src/pages/dashboard/rules.tsx +++ b/packages/nicolium/src/pages/dashboard/rules.tsx @@ -1,3 +1,4 @@ +import iconPlus from '@phosphor-icons/core/regular/plus.svg'; import React from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; @@ -100,7 +101,7 @@ const RulesPage: React.FC = () => {
); @@ -170,7 +185,7 @@ const Breadcrumbs: React.FC = ({ folderId, depth = 0, onClick }) = aria-label={intl.formatMessage(messages.home)} title={intl.formatMessage(messages.home)} > - + {label} ); @@ -181,7 +196,7 @@ const Breadcrumbs: React.FC = ({ folderId, depth = 0, onClick }) = const spacer = (
- +
); @@ -214,7 +229,7 @@ const Breadcrumbs: React.FC = ({ folderId, depth = 0, onClick }) = {spacer}
- +
{spacer} {button} @@ -451,44 +466,44 @@ const File: React.FC = ({ file, index, onMove }) => { isMedia ? { text: intl.formatMessage(messages.fileView), - icon: require('@phosphor-icons/core/regular/eye.svg'), + icon: iconEye, action: handleView, } : { text: intl.formatMessage(messages.fileDownload), - icon: require('@phosphor-icons/core/regular/download.svg'), + icon: iconDownload, href: file.url, }, { text: intl.formatMessage(messages.fileRename), - icon: require('@phosphor-icons/core/regular/cursor-text.svg'), + icon: iconCursorText, action: handleRename, }, { text: intl.formatMessage(messages.updateDescription), - icon: require('@phosphor-icons/core/regular/file-text.svg'), + icon: iconFileText, action: handleUpdateDescription, }, file.sensitive ? { text: intl.formatMessage(messages.unmarkSensitive), - icon: require('@phosphor-icons/core/regular/eye.svg'), + icon: iconEye, action: handleToggleSensitive, } : { text: intl.formatMessage(messages.markSensitive), - icon: require('@phosphor-icons/core/regular/eye-slash.svg'), + icon: iconEyeSlash, action: handleToggleSensitive, }, null, { text: intl.formatMessage(messages.fileMove), - icon: require('@phosphor-icons/core/regular/folders.svg'), + icon: iconFolders, action: handleMove, }, { text: intl.formatMessage(messages.fileDelete), - icon: require('@phosphor-icons/core/regular/trash.svg'), + icon: iconTrash, destructive: true, action: handleDelete, }, @@ -517,7 +532,7 @@ const File: React.FC = ({ file, index, onMove }) => { }} > @@ -670,23 +685,23 @@ const Folder: React.FC = ({ folder, index, onMove }) => { return [ { text: intl.formatMessage(messages.folderView), - icon: require('@phosphor-icons/core/regular/folder-open.svg'), + icon: iconFolderOpen, to: '/drive/{-$folderId}', params: { folderId: folder.id ?? undefined }, }, { text: intl.formatMessage(messages.folderRename), - icon: require('@phosphor-icons/core/regular/cursor-text.svg'), + icon: iconCursorText, action: handleRename, }, { text: intl.formatMessage(messages.folderMove), - icon: require('@phosphor-icons/core/regular/folders.svg'), + icon: iconFolders, action: handleMove, }, { text: intl.formatMessage(messages.folderDelete), - icon: require('@phosphor-icons/core/regular/trash.svg'), + icon: iconTrash, destructive: true, action: handleDelete, }, @@ -715,17 +730,14 @@ const Folder: React.FC = ({ folder, index, onMove }) => { }} >
- + {folder.name}
@@ -747,7 +759,7 @@ const DrivePage: React.FC = () => { const items: Menu = [ { text: intl.formatMessage(messages.fileUpload), - icon: require('@phosphor-icons/core/regular/upload.svg'), + icon: iconUpload, onSelectFile: (files: FileList) => { uploadFile(files[0], { onSuccess: () => { @@ -761,7 +773,7 @@ const DrivePage: React.FC = () => { }, { text: intl.formatMessage(messages.newFolder), - icon: require('@phosphor-icons/core/regular/folder-plus.svg'), + icon: iconFolderPlus, action: () => { openModal('TEXT_FIELD', { heading: , @@ -812,12 +824,7 @@ const DrivePage: React.FC = () => { label={data?.name ?? intl.formatMessage(messages.heading)} backHref={'/drive/{-$folderId}'} backParams={{ folderId: data?.parent_id ?? undefined }} - action={ - - } + action={} >
@@ -830,7 +837,7 @@ const DrivePage: React.FC = () => { defaultMessage='There are no files or folders in this folder.' /> } - icon={require('@phosphor-icons/core/regular/folder-open.svg')} + icon={iconFolderOpen} /> ) : (
diff --git a/packages/nicolium/src/pages/utils/generic-not-found.tsx b/packages/nicolium/src/pages/errors/generic-not-found.tsx similarity index 100% rename from packages/nicolium/src/pages/utils/generic-not-found.tsx rename to packages/nicolium/src/pages/errors/generic-not-found.tsx diff --git a/packages/nicolium/src/pages/utils/intentional-error.tsx b/packages/nicolium/src/pages/errors/intentional-error.tsx similarity index 100% rename from packages/nicolium/src/pages/utils/intentional-error.tsx rename to packages/nicolium/src/pages/errors/intentional-error.tsx diff --git a/packages/nicolium/src/pages/fun/circle.tsx b/packages/nicolium/src/pages/fun/circle.tsx index 421f4b4bb..fd882c158 100644 --- a/packages/nicolium/src/pages/fun/circle.tsx +++ b/packages/nicolium/src/pages/fun/circle.tsx @@ -21,6 +21,9 @@ import toast from '@/toast'; const toRad = (x: number) => x * (Math.PI / 180); +import iconDownloadSimple from '@phosphor-icons/core/regular/download-simple.svg'; +import iconNotePencil from '@phosphor-icons/core/regular/note-pencil.svg'; + import avatarMissing from '@/assets/images/avatar-missing.png'; const HEIGHT = 1000; @@ -256,16 +259,10 @@ const CirclePage: React.FC = () => {
- -
diff --git a/packages/nicolium/src/pages/groups/edit-group.tsx b/packages/nicolium/src/pages/groups/edit-group.tsx index d5163c341..aebf958e2 100644 --- a/packages/nicolium/src/pages/groups/edit-group.tsx +++ b/packages/nicolium/src/pages/groups/edit-group.tsx @@ -1,3 +1,4 @@ +import iconLock from '@phosphor-icons/core/regular/lock.svg'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -10,11 +11,11 @@ import Icon from '@/components/ui/icon'; import Input from '@/components/ui/input'; import Spinner from '@/components/ui/spinner'; import Textarea from '@/components/ui/textarea'; -import AvatarPicker from '@/features/edit-profile/components/avatar-picker'; -import HeaderPicker from '@/features/edit-profile/components/header-picker'; import { editGroupRoute } from '@/features/ui/router'; import { useImageField } from '@/hooks/forms/use-image-field'; import { useTextField } from '@/hooks/forms/use-text-field'; +import AvatarPicker from '@/pages/settings/components/avatar-picker'; +import HeaderPicker from '@/pages/settings/components/header-picker'; import { useGroupQuery, useUpdateGroupMutation } from '@/queries/groups/use-group'; import { useInstance } from '@/stores/instance'; import toast from '@/toast'; @@ -117,12 +118,7 @@ const EditGroup: React.FC = () => { placeholder={intl.formatMessage(messages.groupNamePlaceholder)} maxLength={maxName} {...displayName} - append={ - - } + append={} disabled /> diff --git a/packages/nicolium/src/pages/groups/group-members.tsx b/packages/nicolium/src/pages/groups/group-members.tsx index dba81c8bc..702a7cdaa 100644 --- a/packages/nicolium/src/pages/groups/group-members.tsx +++ b/packages/nicolium/src/pages/groups/group-members.tsx @@ -2,10 +2,10 @@ import clsx from 'clsx'; import { GroupRoles } from 'pl-api'; import React, { useMemo } from 'react'; +import GroupMemberListItem from '@/components/groups/group-member-list-item'; import { PendingItemsRow } from '@/components/pending-items-row'; +import PlaceholderAccount from '@/components/placeholders/placeholder-account'; import ScrollableList from '@/components/scrollable-list'; -import GroupMemberListItem from '@/features/group/components/group-member-list-item'; -import PlaceholderAccount from '@/features/placeholder/components/placeholder-account'; import { groupMembersRoute } from '@/features/ui/router'; import { useGroupQuery } from '@/queries/groups/use-group'; import { diff --git a/packages/nicolium/src/pages/groups/groups.tsx b/packages/nicolium/src/pages/groups/groups.tsx index 56ddd407f..c77cffa8a 100644 --- a/packages/nicolium/src/pages/groups/groups.tsx +++ b/packages/nicolium/src/pages/groups/groups.tsx @@ -1,12 +1,13 @@ +import iconUsersThree from '@phosphor-icons/core/regular/users-three.svg'; import { Link } from '@tanstack/react-router'; import React from 'react'; import { FormattedMessage } from 'react-intl'; -import GroupCard from '@/components/group-card'; +import GroupCard from '@/components/groups/group-card'; +import PlaceholderGroupCard from '@/components/placeholders/placeholder-group-card'; import ScrollableList from '@/components/scrollable-list'; import Button from '@/components/ui/button'; import Text from '@/components/ui/text'; -import PlaceholderGroupCard from '@/features/placeholder/components/placeholder-group-card'; import { useGroupsQuery } from '@/queries/groups/use-groups'; import { useModalsActions } from '@/stores/modals'; @@ -45,7 +46,7 @@ const Groups: React.FC = () => { {!(!isFetching && groupIds.length === 0) && (
@@ -196,7 +192,7 @@ const SearchResults = () => { diff --git a/packages/nicolium/src/pages/settings/aliases.tsx b/packages/nicolium/src/pages/settings/aliases.tsx index 98b76cfc9..67beca1f1 100644 --- a/packages/nicolium/src/pages/settings/aliases.tsx +++ b/packages/nicolium/src/pages/settings/aliases.tsx @@ -1,3 +1,6 @@ +import iconBackspace from '@phosphor-icons/core/regular/backspace.svg'; +import iconPlus from '@phosphor-icons/core/regular/plus.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import clsx from 'clsx'; import React, { useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -56,7 +59,7 @@ const Account: React.FC = ({ accountId, aliases }) => { if (!added && accountId !== me) { button = ( = ({ onSubmit }) => { title={intl.formatMessage(messages.clear)} > @@ -210,7 +213,7 @@ const AliasesPage = () => { aria-label={intl.formatMessage(messages.delete)} > - + = ({ token, isCurrent }) => { {token.app_name} {token.app_website && ( - + )}

diff --git a/packages/nicolium/src/features/edit-profile/components/avatar-picker.tsx b/packages/nicolium/src/pages/settings/components/avatar-picker.tsx similarity index 96% rename from packages/nicolium/src/features/edit-profile/components/avatar-picker.tsx rename to packages/nicolium/src/pages/settings/components/avatar-picker.tsx index 6a7bae157..1a17b0aa9 100644 --- a/packages/nicolium/src/features/edit-profile/components/avatar-picker.tsx +++ b/packages/nicolium/src/pages/settings/components/avatar-picker.tsx @@ -1,3 +1,4 @@ +import iconCameraPlus from '@phosphor-icons/core/regular/camera-plus.svg'; import clsx from 'clsx'; import React, { useRef } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -80,10 +81,7 @@ const AvatarPicker = React.forwardRef( }, )} > - +
diff --git a/packages/nicolium/src/features/security/mfa/disable-otp-form.tsx b/packages/nicolium/src/pages/settings/components/disable-otp-form.tsx similarity index 100% rename from packages/nicolium/src/features/security/mfa/disable-otp-form.tsx rename to packages/nicolium/src/pages/settings/components/disable-otp-form.tsx diff --git a/packages/nicolium/src/features/security/mfa/enable-otp-form.tsx b/packages/nicolium/src/pages/settings/components/enable-otp-form.tsx similarity index 100% rename from packages/nicolium/src/features/security/mfa/enable-otp-form.tsx rename to packages/nicolium/src/pages/settings/components/enable-otp-form.tsx diff --git a/packages/nicolium/src/features/edit-profile/components/header-picker.tsx b/packages/nicolium/src/pages/settings/components/header-picker.tsx similarity index 95% rename from packages/nicolium/src/features/edit-profile/components/header-picker.tsx rename to packages/nicolium/src/pages/settings/components/header-picker.tsx index 78dee1cc7..8e18cee06 100644 --- a/packages/nicolium/src/features/edit-profile/components/header-picker.tsx +++ b/packages/nicolium/src/pages/settings/components/header-picker.tsx @@ -1,3 +1,5 @@ +import iconUpload from '@phosphor-icons/core/regular/upload.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import clsx from 'clsx'; import React, { useRef } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; @@ -96,7 +98,7 @@ const HeaderPicker = React.forwardRef( }, )} > - + @@ -117,7 +119,7 @@ const HeaderPicker = React.forwardRef( {onClear && src && ( { /> + + } + > + + + { onChange={onToggleChange} /> + + {settings.showWrenchButton && ( + + } + hint={ + + } + > + + + )} diff --git a/packages/nicolium/src/features/settings/components/setting-toggle.tsx b/packages/nicolium/src/pages/settings/components/setting-toggle.tsx similarity index 100% rename from packages/nicolium/src/features/settings/components/setting-toggle.tsx rename to packages/nicolium/src/pages/settings/components/setting-toggle.tsx diff --git a/packages/nicolium/src/pages/settings/domain-blocks.tsx b/packages/nicolium/src/pages/settings/domain-blocks.tsx index 2edd55de7..c2e1f510d 100644 --- a/packages/nicolium/src/pages/settings/domain-blocks.tsx +++ b/packages/nicolium/src/pages/settings/domain-blocks.tsx @@ -1,3 +1,4 @@ +import iconLockOpen from '@phosphor-icons/core/regular/lock-open.svg'; import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; @@ -32,7 +33,7 @@ const Domain: React.FC = ({ domain }) => { {domain} diff --git a/packages/nicolium/src/pages/settings/edit-filter.tsx b/packages/nicolium/src/pages/settings/edit-filter.tsx index 57896116a..b70a7cf29 100644 --- a/packages/nicolium/src/pages/settings/edit-filter.tsx +++ b/packages/nicolium/src/pages/settings/edit-filter.tsx @@ -11,10 +11,10 @@ import FormActions from '@/components/ui/form-actions'; import FormGroup from '@/components/ui/form-group'; import Input from '@/components/ui/input'; import Select from '@/components/ui/select'; +import { SelectDropdown } from '@/components/ui/select-dropdown'; import Streamfield from '@/components/ui/streamfield'; import Text from '@/components/ui/text'; import Toggle from '@/components/ui/toggle'; -import { SelectDropdown } from '@/features/forms'; import { editFilterRoute } from '@/features/ui/router'; import { useFeatures } from '@/hooks/use-features'; import { useCreateFilter, useFilter, useUpdateFilter } from '@/queries/settings/use-filters'; diff --git a/packages/nicolium/src/pages/settings/edit-profile.tsx b/packages/nicolium/src/pages/settings/edit-profile.tsx index 1099a9851..4fdbce424 100644 --- a/packages/nicolium/src/pages/settings/edit-profile.tsx +++ b/packages/nicolium/src/pages/settings/edit-profile.tsx @@ -12,16 +12,16 @@ import Form from '@/components/ui/form'; import FormActions from '@/components/ui/form-actions'; import FormGroup from '@/components/ui/form-group'; import Input from '@/components/ui/input'; +import { SelectDropdown } from '@/components/ui/select-dropdown'; import Streamfield from '@/components/ui/streamfield'; import Textarea from '@/components/ui/textarea'; import Toggle from '@/components/ui/toggle'; -import AvatarPicker from '@/features/edit-profile/components/avatar-picker'; -import HeaderPicker from '@/features/edit-profile/components/header-picker'; -import { SelectDropdown } from '@/features/forms'; import { useImageField } from '@/hooks/forms/use-image-field'; import { useClient } from '@/hooks/use-client'; import { useFeatures } from '@/hooks/use-features'; import { useOwnAccount } from '@/hooks/use-own-account'; +import AvatarPicker from '@/pages/settings/components/avatar-picker'; +import HeaderPicker from '@/pages/settings/components/header-picker'; import { useAuthActions } from '@/stores/auth'; import { useInstance } from '@/stores/instance'; import toast from '@/toast'; diff --git a/packages/nicolium/src/pages/settings/import-data.tsx b/packages/nicolium/src/pages/settings/import-data.tsx index 0e0294fdd..838e73ab4 100644 --- a/packages/nicolium/src/pages/settings/import-data.tsx +++ b/packages/nicolium/src/pages/settings/import-data.tsx @@ -137,7 +137,7 @@ const ImportDataPage = () => { .request('/api/pleroma/archive_import', { method: 'POST', body: form, - contentType: '', + formData: true, }) .then(() => { toast.success(messages.archiveSuccess); diff --git a/packages/nicolium/src/pages/settings/index.tsx b/packages/nicolium/src/pages/settings/index.tsx index bd089f3d8..65a629d5d 100644 --- a/packages/nicolium/src/pages/settings/index.tsx +++ b/packages/nicolium/src/pages/settings/index.tsx @@ -2,9 +2,9 @@ import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import Hashtag from '@/components/hashtag'; +import PlaceholderHashtag from '@/components/placeholders/placeholder-hashtag'; import ScrollableList from '@/components/scrollable-list'; import Column from '@/components/ui/column'; -import PlaceholderHashtag from '@/features/placeholder/components/placeholder-hashtag'; import { useFollowedTags } from '@/queries/hashtags/use-followed-tags'; const messages = defineMessages({ diff --git a/packages/nicolium/src/pages/settings/interaction-policies.tsx b/packages/nicolium/src/pages/settings/interaction-policies.tsx index 3e205e94d..471228cf8 100644 --- a/packages/nicolium/src/pages/settings/interaction-policies.tsx +++ b/packages/nicolium/src/pages/settings/interaction-policies.tsx @@ -8,10 +8,10 @@ import Column from '@/components/ui/column'; import Form from '@/components/ui/form'; import FormActions from '@/components/ui/form-actions'; import { InlineMultiselect } from '@/components/ui/inline-multiselect'; +import { SelectDropdown } from '@/components/ui/select-dropdown'; import Tabs from '@/components/ui/tabs'; import Text from '@/components/ui/text'; import Warning from '@/features/compose/components/warning'; -import { SelectDropdown } from '@/features/forms'; import { useClient } from '@/hooks/use-client'; import { useFeatures } from '@/hooks/use-features'; import { useInteractionPolicies } from '@/queries/settings/use-interaction-policies'; diff --git a/packages/nicolium/src/pages/settings/mfa-form.tsx b/packages/nicolium/src/pages/settings/mfa-form.tsx index e0b78db17..d5673da5f 100644 --- a/packages/nicolium/src/pages/settings/mfa-form.tsx +++ b/packages/nicolium/src/pages/settings/mfa-form.tsx @@ -5,9 +5,9 @@ import Column from '@/components/ui/column'; import { useFeatures } from '@/hooks/use-features'; import { useMfaConfig } from '@/queries/security/use-mfa'; -import DisableOtpForm from '../../features/security/mfa/disable-otp-form'; -import EnableOtpForm from '../../features/security/mfa/enable-otp-form'; -import OtpConfirmForm from '../../features/security/mfa/otp-confirm-form'; +import DisableOtpForm from './components/disable-otp-form'; +import EnableOtpForm from './components/enable-otp-form'; +import OtpConfirmForm from './components/otp-confirm-form'; const messages = defineMessages({ heading: { id: 'column.mfa', defaultMessage: 'Multi-factor authentication' }, diff --git a/packages/nicolium/src/pages/settings/privacy.tsx b/packages/nicolium/src/pages/settings/privacy.tsx index f262e6fec..21dadf585 100644 --- a/packages/nicolium/src/pages/settings/privacy.tsx +++ b/packages/nicolium/src/pages/settings/privacy.tsx @@ -12,9 +12,9 @@ import Form from '@/components/ui/form'; import FormActions from '@/components/ui/form-actions'; import FormGroup from '@/components/ui/form-group'; import Input from '@/components/ui/input'; +import { SelectDropdown } from '@/components/ui/select-dropdown'; import Toggle from '@/components/ui/toggle'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import { SelectDropdown } from '@/features/forms'; import KVStore from '@/storage/kv-store'; import { useSettings } from '@/stores/settings'; import { hasCanvasExtractPermission } from '@/utils/favicon-service'; diff --git a/packages/nicolium/src/pages/settings/rss-feed-subscriptions.tsx b/packages/nicolium/src/pages/settings/rss-feed-subscriptions.tsx index 2ab1865e8..106c0f63d 100644 --- a/packages/nicolium/src/pages/settings/rss-feed-subscriptions.tsx +++ b/packages/nicolium/src/pages/settings/rss-feed-subscriptions.tsx @@ -1,3 +1,5 @@ +import iconRss from '@phosphor-icons/core/regular/rss.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; @@ -123,7 +125,7 @@ const RssFeedSubscriptions = () => { {feed.image_url ? ( ) : ( - + )}
{feed.title} @@ -135,7 +137,7 @@ const RssFeedSubscriptions = () => { onClick={handleDelete(feed.url)} disabled={isPending} className='size-8 text-gray-700 dark:text-gray-600' - src={require('@phosphor-icons/core/regular/x.svg')} + src={iconX} title={intl.formatMessage(messages.deleteFeed)} />
diff --git a/packages/nicolium/src/pages/settings/settings.tsx b/packages/nicolium/src/pages/settings/settings.tsx index 86fa9ac4d..9fab57e1b 100644 --- a/packages/nicolium/src/pages/settings/settings.tsx +++ b/packages/nicolium/src/pages/settings/settings.tsx @@ -5,10 +5,10 @@ import List, { ListItem } from '@/components/list'; import Card, { CardBody, CardHeader, CardTitle } from '@/components/ui/card'; import Column from '@/components/ui/column'; import Text from '@/components/ui/text'; -import Preferences from '@/features/preferences'; -import MessagesSettings from '@/features/settings/components/messages-settings'; import { useFeatures } from '@/hooks/use-features'; import { useOwnAccount } from '@/hooks/use-own-account'; +import MessagesSettings from '@/pages/settings/components/messages-settings'; +import Preferences from '@/pages/settings/components/preferences'; import { useMfaConfig } from '@/queries/security/use-mfa'; const messages = defineMessages({ diff --git a/packages/nicolium/src/pages/status-lists/bookmark-folders.tsx b/packages/nicolium/src/pages/status-lists/bookmark-folders.tsx index ff84d4c94..c84148730 100644 --- a/packages/nicolium/src/pages/status-lists/bookmark-folders.tsx +++ b/packages/nicolium/src/pages/status-lists/bookmark-folders.tsx @@ -1,3 +1,5 @@ +import iconBookmarks from '@phosphor-icons/core/regular/bookmarks.svg'; +import iconFolderSimple from '@phosphor-icons/core/regular/folder-simple.svg'; import { Navigate } from '@tanstack/react-router'; import React from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; @@ -120,7 +122,7 @@ const BookmarkFoldersPage: React.FC = () => { params={{ folderId: 'all' }} label={
- + { className='size-5 flex-none' /> ) : ( - + )} {folder.name}
diff --git a/packages/nicolium/src/pages/status-lists/bookmarks.tsx b/packages/nicolium/src/pages/status-lists/bookmarks.tsx index 8f5057a8e..dc79c57d4 100644 --- a/packages/nicolium/src/pages/status-lists/bookmarks.tsx +++ b/packages/nicolium/src/pages/status-lists/bookmarks.tsx @@ -1,3 +1,6 @@ +import iconDotsThreeVertical from '@phosphor-icons/core/regular/dots-three-vertical.svg'; +import iconPencilSimple from '@phosphor-icons/core/regular/pencil-simple.svg'; +import iconTrash from '@phosphor-icons/core/regular/trash.svg'; import { useNavigate } from '@tanstack/react-router'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -101,12 +104,12 @@ const BookmarksPage: React.FC = () => { { text: intl.formatMessage(messages.editFolder), action: handleEditFolder, - icon: require('@phosphor-icons/core/regular/pencil-simple.svg'), + icon: iconPencilSimple, }, { text: intl.formatMessage(messages.deleteFolder), action: handleDeleteFolder, - icon: require('@phosphor-icons/core/regular/trash.svg'), + icon: iconTrash, }, ] : []; @@ -114,12 +117,7 @@ const BookmarksPage: React.FC = () => { return ( - } + action={} > = ({ statusIds, isLoading, emptyMe }} className='flex size-8 items-center justify-center rounded-full bg-white/50 backdrop-blur dark:bg-gray-900/50' > - +
)} @@ -89,10 +88,7 @@ const EventCarousel: React.FC = ({ statusIds, isLoading, emptyMe }} className='flex size-8 items-center justify-center rounded-full bg-white/50 backdrop-blur dark:bg-gray-900/50' > - +
)} diff --git a/packages/nicolium/src/pages/status-lists/interaction-requests.tsx b/packages/nicolium/src/pages/status-lists/interaction-requests.tsx index 2b4097a29..24e29d664 100644 --- a/packages/nicolium/src/pages/status-lists/interaction-requests.tsx +++ b/packages/nicolium/src/pages/status-lists/interaction-requests.tsx @@ -1,3 +1,6 @@ +import iconArrowBendUpLeft from '@phosphor-icons/core/regular/arrow-bend-up-left.svg'; +import iconRepeat from '@phosphor-icons/core/regular/repeat.svg'; +import iconStar from '@phosphor-icons/core/regular/star.svg'; import { Link } from '@tanstack/react-router'; import clsx from 'clsx'; import React from 'react'; @@ -6,6 +9,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import AccountContainer from '@/components/accounts/account-container'; import Icon from '@/components/icon'; import AttachmentThumbs from '@/components/media/attachment-thumbs'; +import { buildLink } from '@/components/notification'; import PullToRefresh from '@/components/pull-to-refresh'; import RelativeTimestamp from '@/components/relative-timestamp'; import ScrollableList from '@/components/scrollable-list'; @@ -13,7 +17,6 @@ import StatusContent from '@/components/statuses/status-content'; import Button from '@/components/ui/button'; import Column from '@/components/ui/column'; import Text from '@/components/ui/text'; -import { buildLink } from '@/features/notifications/components/notification'; import { Hotkeys } from '@/features/ui/components/hotkeys'; import { useOwnAccount } from '@/hooks/use-own-account'; import { useAccount } from '@/queries/accounts/use-account'; @@ -61,9 +64,9 @@ const messages = defineMessages({ }); const icons = { - favourite: require('@phosphor-icons/core/regular/star.svg'), - reblog: require('@phosphor-icons/core/regular/repeat.svg'), - reply: require('@phosphor-icons/core/regular/arrow-bend-up-left.svg'), + favourite: iconStar, + reblog: iconRepeat, + reply: iconArrowBendUpLeft, }; const avatarSize = 42; diff --git a/packages/nicolium/src/pages/statuses/event-discussion.tsx b/packages/nicolium/src/pages/statuses/event-discussion.tsx index 2fa703f5f..7f7800974 100644 --- a/packages/nicolium/src/pages/statuses/event-discussion.tsx +++ b/packages/nicolium/src/pages/statuses/event-discussion.tsx @@ -2,12 +2,12 @@ import React, { useEffect, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import MissingIndicator from '@/components/missing-indicator'; +import PlaceholderStatus from '@/components/placeholders/placeholder-status'; import ScrollableList from '@/components/scrollable-list'; +import PendingStatus from '@/components/statuses/pending-status'; import Tombstone from '@/components/statuses/tombstone'; import { useCurrentAccount } from '@/contexts/current-account-context'; -import PlaceholderStatus from '@/features/placeholder/components/placeholder-status'; import ThreadStatus from '@/features/status/components/thread-status'; -import PendingStatus from '@/features/ui/components/pending-status'; import { eventDiscussionRoute } from '@/features/ui/router'; import { ComposeForm } from '@/features/ui/util/async-components'; import { useStatus } from '@/queries/statuses/use-status'; diff --git a/packages/nicolium/src/pages/statuses/event-information.tsx b/packages/nicolium/src/pages/statuses/event-information.tsx index 29aabe2a1..f3795ff5a 100644 --- a/packages/nicolium/src/pages/statuses/event-information.tsx +++ b/packages/nicolium/src/pages/statuses/event-information.tsx @@ -1,3 +1,6 @@ +import iconCalendarDots from '@phosphor-icons/core/regular/calendar-dots.svg'; +import iconLinkSimple from '@phosphor-icons/core/regular/link-simple.svg'; +import iconMapPin from '@phosphor-icons/core/regular/map-pin.svg'; import React, { useCallback } from 'react'; import { FormattedDate, FormattedMessage } from 'react-intl'; @@ -76,7 +79,7 @@ const EventInformationPage: React.FC = () => {
- +

{text}

@@ -104,7 +107,7 @@ const EventInformationPage: React.FC = () => {
- +

{

} - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + emptyMessageIcon={iconChatCenteredText} /> ); diff --git a/packages/nicolium/src/pages/timelines/community-timeline.tsx b/packages/nicolium/src/pages/timelines/community-timeline.tsx index d80147d4f..df513a9a4 100644 --- a/packages/nicolium/src/pages/timelines/community-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/community-timeline.tsx @@ -1,18 +1,30 @@ +import iconChatCenteredText from '@phosphor-icons/core/regular/chat-centered-text.svg'; +import iconDotsThreeVertical from '@phosphor-icons/core/regular/dots-three-vertical.svg'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { PublicTimelineColumn } from '@/columns/timeline'; +import DropdownMenu from '@/components/dropdown-menu'; +import { TimelinePicker } from '@/components/timeline-picker'; import Column from '@/components/ui/column'; +import { useTimelineFiltersOptions } from '@/hooks/use-timeline-filters-options'; const messages = defineMessages({ title: { id: 'column.community', defaultMessage: 'Local timeline' }, }); const CommunityTimelinePage = () => { + const items = useTimelineFiltersOptions('public'); const intl = useIntl(); return ( - + } + truncateTitle={false} + action={} + > { defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' /> } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + emptyMessageIcon={iconChatCenteredText} /> ); diff --git a/packages/nicolium/src/pages/timelines/group-timeline.tsx b/packages/nicolium/src/pages/timelines/group-timeline.tsx index c9003b500..da56af871 100644 --- a/packages/nicolium/src/pages/timelines/group-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/group-timeline.tsx @@ -1,3 +1,4 @@ +import iconChatCenteredText from '@phosphor-icons/core/regular/chat-centered-text.svg'; import { Link } from '@tanstack/react-router'; import clsx from 'clsx'; import React, { useEffect, useRef } from 'react'; @@ -75,7 +76,7 @@ const GroupTimelinePage: React.FC = () => { defaultMessage='There are no posts in this group yet.' /> } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + emptyMessageIcon={iconChatCenteredText} // showGroup={falsse} />
diff --git a/packages/nicolium/src/pages/timelines/hashtag-timeline.tsx b/packages/nicolium/src/pages/timelines/hashtag-timeline.tsx index 45dd07fcb..5166f1d11 100644 --- a/packages/nicolium/src/pages/timelines/hashtag-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/hashtag-timeline.tsx @@ -1,13 +1,16 @@ +import iconDotsThreeVertical from '@phosphor-icons/core/regular/dots-three-vertical.svg'; import React from 'react'; import { FormattedMessage } from 'react-intl'; import { HashtagTimelineColumn } from '@/columns/timeline'; +import DropdownMenu from '@/components/dropdown-menu'; import List, { ListItem } from '@/components/list'; import Column from '@/components/ui/column'; import Toggle from '@/components/ui/toggle'; import { hashtagTimelineRoute } from '@/features/ui/router'; import { useFeatures } from '@/hooks/use-features'; import { useLoggedIn } from '@/hooks/use-logged-in'; +import { useTimelineFiltersOptions } from '@/hooks/use-timeline-filters-options'; import { useFollowHashtagMutation, useUnfollowHashtagMutation, @@ -20,6 +23,7 @@ const HashtagTimelinePage: React.FC = () => { const features = useFeatures(); const { data: tag } = useHashtag(hashtag); const { isLoggedIn } = useLoggedIn(); + const items = useTimelineFiltersOptions('hashtag'); const { mutate: followHashtag } = useFollowHashtagMutation(hashtag); const { mutate: unfollowHashtag } = useUnfollowHashtagMutation(hashtag); @@ -33,7 +37,10 @@ const HashtagTimelinePage: React.FC = () => { }; return ( - + } + > {features.followHashtags && isLoggedIn && ( { const features = useFeatures(); const instance = useInstance(); + const items = useTimelineFiltersOptions('home'); const { isSledzikRemoved } = useUiStore(); if (isSledzikRemoved) return null; return ( - + } + withBack={false} + truncateTitle={false} + action={} + > @@ -85,7 +98,7 @@ const HomeTimelinePage: React.FC = () => { )}
} - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + emptyMessageIcon={iconChatCenteredText} /> ); diff --git a/packages/nicolium/src/pages/timelines/landing-timeline.tsx b/packages/nicolium/src/pages/timelines/landing-timeline.tsx index c15dc8da6..2dc8d522a 100644 --- a/packages/nicolium/src/pages/timelines/landing-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/landing-timeline.tsx @@ -1,3 +1,4 @@ +import iconChatCenteredText from '@phosphor-icons/core/regular/chat-centered-text.svg'; import clsx from 'clsx'; import React from 'react'; import { FormattedMessage } from 'react-intl'; @@ -8,7 +9,7 @@ import { ParsedContent } from '@/components/statuses/parsed-content'; import Button from '@/components/ui/button'; import Column from '@/components/ui/column'; import { useRegistrationStatus } from '@/hooks/use-registration-status'; -import { About } from '@/pages/utils/about'; +import { About } from '@/pages/instance/about'; import { usePublicTimeline } from '@/queries/timelines/use-timelines'; import { useInstance } from '@/stores/instance'; import { getTextDirection } from '@/utils/rtl'; @@ -82,7 +83,7 @@ const LandingTimelinePage = () => { defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' /> } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + emptyMessageIcon={iconChatCenteredText} /> ) : ( diff --git a/packages/nicolium/src/pages/timelines/link-timeline.tsx b/packages/nicolium/src/pages/timelines/link-timeline.tsx index a94281a8a..fb28c3e5b 100644 --- a/packages/nicolium/src/pages/timelines/link-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/link-timeline.tsx @@ -1,3 +1,4 @@ +import iconChatCenteredText from '@phosphor-icons/core/regular/chat-centered-text.svg'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -27,7 +28,7 @@ const LinkTimelinePage: React.FC = () => { defaultMessage='There are no posts with this link yet.' /> } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + emptyMessageIcon={iconChatCenteredText} /> ); diff --git a/packages/nicolium/src/pages/timelines/list-timeline.tsx b/packages/nicolium/src/pages/timelines/list-timeline.tsx index bd67658b0..8a69f11cc 100644 --- a/packages/nicolium/src/pages/timelines/list-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/list-timeline.tsx @@ -1,14 +1,20 @@ +import iconDotsThreeVertical from '@phosphor-icons/core/regular/dots-three-vertical.svg'; +import iconListBullets from '@phosphor-icons/core/regular/list-bullets.svg'; +import iconPencilSimple from '@phosphor-icons/core/regular/pencil-simple.svg'; +import iconTrash from '@phosphor-icons/core/regular/trash.svg'; import { useNavigate } from '@tanstack/react-router'; -import React from 'react'; +import React, { useMemo } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { ListTimelineColumn } from '@/columns/timeline'; import DropdownMenu from '@/components/dropdown-menu'; import { EmptyMessage } from '@/components/empty-message'; +import { TimelinePicker } from '@/components/timeline-picker'; import Button from '@/components/ui/button'; import Column from '@/components/ui/column'; import Spinner from '@/components/ui/spinner'; import { listTimelineRoute } from '@/features/ui/router'; +import { useTimelineFiltersOptions } from '@/hooks/use-timeline-filters-options'; import { useDeleteList, useList } from '@/queries/accounts/use-lists'; import { useModalsActions } from '@/stores/modals'; @@ -30,6 +36,7 @@ const ListTimelinePage: React.FC = () => { const intl = useIntl(); const { openModal } = useModalsActions(); const navigate = useNavigate(); + const timelineFilterOptions = useTimelineFiltersOptions('list'); const { data: list, isFetching } = useList(listId); const { mutate: deleteList } = useDeleteList(); @@ -86,28 +93,30 @@ const ListTimelinePage: React.FC = () => { ); } - const items = [ - { - text: intl.formatMessage(messages.editList), - action: handleEditClick, - icon: require('@phosphor-icons/core/regular/pencil-simple.svg'), - }, - { - text: intl.formatMessage(messages.deleteList), - action: handleDeleteClick, - icon: require('@phosphor-icons/core/regular/trash.svg'), - }, - ]; + const items = useMemo( + () => [ + ...timelineFilterOptions, + null, + { + text: intl.formatMessage(messages.editList), + action: handleEditClick, + icon: iconPencilSimple, + }, + { + text: intl.formatMessage(messages.deleteList), + action: handleDeleteClick, + icon: iconTrash, + }, + ], + [timelineFilterOptions], + ); return ( - } + title={} + truncateTitle={false} + action={} > {
} - emptyMessageIcon={require('@phosphor-icons/core/regular/list-bullets.svg')} + emptyMessageIcon={iconListBullets} /> ); diff --git a/packages/nicolium/src/pages/timelines/public-timeline.tsx b/packages/nicolium/src/pages/timelines/public-timeline.tsx index c0ffbd33e..9d7271fcf 100644 --- a/packages/nicolium/src/pages/timelines/public-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/public-timeline.tsx @@ -1,12 +1,17 @@ +import iconChatCenteredText from '@phosphor-icons/core/regular/chat-centered-text.svg'; +import iconDotsThreeVertical from '@phosphor-icons/core/regular/dots-three-vertical.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import { Link } from '@tanstack/react-router'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { changeSetting } from '@/actions/settings'; import { PublicTimelineColumn } from '@/columns/timeline'; +import DropdownMenu from '@/components/dropdown-menu'; +import { TimelinePicker } from '@/components/timeline-picker'; import Accordion from '@/components/ui/accordion'; import Column from '@/components/ui/column'; -import PinnedHostsPicker from '@/features/remote-timeline/components/pinned-hosts-picker'; +import { useTimelineFiltersOptions } from '@/hooks/use-timeline-filters-options'; import { useInstance } from '@/stores/instance'; import { useSettings } from '@/stores/settings'; @@ -20,6 +25,7 @@ const PublicTimelinePage = () => { const instance = useInstance(); const settings = useSettings(); + const items = useTimelineFiltersOptions('public'); const explanationBoxExpanded = settings.explanationBox; const showExplanationBox = settings.showExplanationBox; @@ -33,9 +39,13 @@ const PublicTimelinePage = () => { }; return ( - - - + } + truncateTitle={false} + action={} + > {showExplanationBox && ( { /> } action={dismissExplanationBox} - actionIcon={require('@phosphor-icons/core/regular/x.svg')} + actionIcon={iconX} actionLabel={intl.formatMessage(messages.dismiss)} expanded={explanationBoxExpanded} onToggle={toggleExplanationBox} @@ -76,7 +86,7 @@ const PublicTimelinePage = () => { defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up' /> } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + emptyMessageIcon={iconChatCenteredText} /> ); diff --git a/packages/nicolium/src/pages/timelines/remote-timeline.tsx b/packages/nicolium/src/pages/timelines/remote-timeline.tsx index 69f78a761..e4c2f40fc 100644 --- a/packages/nicolium/src/pages/timelines/remote-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/remote-timeline.tsx @@ -1,44 +1,87 @@ +import iconChatCenteredText from '@phosphor-icons/core/regular/chat-centered-text.svg'; +import iconDotsThreeVertical from '@phosphor-icons/core/regular/dots-three-vertical.svg'; +import iconPushPinSlash from '@phosphor-icons/core/regular/push-pin-slash.svg'; +import iconPushPin from '@phosphor-icons/core/regular/push-pin.svg'; +import iconX from '@phosphor-icons/core/regular/x.svg'; import { useNavigate } from '@tanstack/react-router'; -import React from 'react'; +import React, { useMemo } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; +import { changeSetting } from '@/actions/settings'; import { PublicTimelineColumn } from '@/columns/timeline'; +import DropdownMenu from '@/components/dropdown-menu'; +import { TimelinePicker } from '@/components/timeline-picker'; import Column from '@/components/ui/column'; import IconButton from '@/components/ui/icon-button'; import Text from '@/components/ui/text'; -import PinnedHostsPicker from '@/features/remote-timeline/components/pinned-hosts-picker'; import { remoteTimelineRoute } from '@/features/ui/router'; +import { useTimelineFiltersOptions } from '@/hooks/use-timeline-filters-options'; import { useSettings } from '@/stores/settings'; const messages = defineMessages({ close: { id: 'remote_timeline.close', defaultMessage: 'Close remote timeline' }, + pinHost: { id: 'remote_instance.pin_host', defaultMessage: 'Pin {host}' }, + unpinHost: { id: 'remote_instance.unpin_host', defaultMessage: 'Unpin {host}' }, }); /** View statuses from a remote instance. */ const RemoteTimelinePage: React.FC = () => { const { instance } = remoteTimelineRoute.useParams(); + const timelineFiltersOptions = useTimelineFiltersOptions('public'); const intl = useIntl(); const navigate = useNavigate(); const settings = useSettings(); - const pinned = settings.remote_timeline.pinnedHosts.includes(instance); + const isPinned = settings.remote_timeline.pinnedHosts.includes(instance); const handleCloseClick: React.MouseEventHandler = () => { navigate({ to: '/timeline/fediverse' }); }; - return ( - - {instance && } + const handlePinHost = () => { + if (!isPinned) { + changeSetting( + ['remote_timeline', 'pinnedHosts'], + [...settings.remote_timeline.pinnedHosts, instance], + ); + } else { + changeSetting( + ['remote_timeline', 'pinnedHosts'], + settings.remote_timeline.pinnedHosts.filter((value) => value !== instance), + ); + } + }; - {!pinned && ( + const items = useMemo( + () => [ + ...timelineFiltersOptions, + null, + { + text: intl.formatMessage(isPinned ? messages.unpinHost : messages.pinHost, { + host: instance, + }), + action: handlePinHost, + icon: isPinned ? iconPushPinSlash : iconPushPin, + }, + ], + [timelineFiltersOptions, isPinned, instance], + ); + + return ( + } + truncateTitle={false} + action={} + > + {!isPinned && (
@@ -60,7 +103,7 @@ const RemoteTimelinePage: React.FC = () => { values={{ instance }} /> } - emptyMessageIcon={require('@phosphor-icons/core/regular/chat-centered-text.svg')} + emptyMessageIcon={iconChatCenteredText} instance={instance} /> diff --git a/packages/nicolium/src/pages/timelines/wrenched-timeline.tsx b/packages/nicolium/src/pages/timelines/wrenched-timeline.tsx index 7b581f30e..cd41a0557 100644 --- a/packages/nicolium/src/pages/timelines/wrenched-timeline.tsx +++ b/packages/nicolium/src/pages/timelines/wrenched-timeline.tsx @@ -1,8 +1,13 @@ +import iconDotsThreeVertical from '@phosphor-icons/core/regular/dots-three-vertical.svg'; +import iconPlant from '@phosphor-icons/core/regular/plant.svg'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { WrenchedTimelineColumn } from '@/columns/timeline'; +import DropdownMenu from '@/components/dropdown-menu'; +import { TimelinePicker } from '@/components/timeline-picker'; import Column from '@/components/ui/column'; +import { useTimelineFiltersOptions } from '@/hooks/use-timeline-filters-options'; const messages = defineMessages({ title: { id: 'column.eggplanted', defaultMessage: 'Recent eggplants timeline' }, @@ -10,9 +15,16 @@ const messages = defineMessages({ const WrenchedTimelinePage = () => { const intl = useIntl(); + const items = useTimelineFiltersOptions('wrenched'); return ( - + } + truncateTitle={false} + action={} + > { defaultMessage='There is nothing here! 🍆 a public post to fill it up' /> } - emptyMessageIcon={require('@phosphor-icons/core/regular/plant.svg')} + emptyMessageIcon={iconPlant} /> ); diff --git a/packages/nicolium/src/queries/accounts/use-antennas.ts b/packages/nicolium/src/queries/accounts/use-antennas.ts index df7966208..8dccb3af7 100644 --- a/packages/nicolium/src/queries/accounts/use-antennas.ts +++ b/packages/nicolium/src/queries/accounts/use-antennas.ts @@ -2,6 +2,7 @@ import { useMutation, useQuery, type UseQueryResult } from '@tanstack/react-quer import { useClient } from '@/hooks/use-client'; import { useFeatures } from '@/hooks/use-features'; +import { useLoggedIn } from '@/hooks/use-logged-in'; import { queryKeys } from '@/queries/keys'; import { queryClient } from '../client'; @@ -16,11 +17,12 @@ function useAntennas(): UseQueryResult, Error>; function useAntennas>(select?: (data: Array) => T) { const client = useClient(); const features = useFeatures(); + const { isLoggedIn } = useLoggedIn(); return useQuery({ queryKey: queryKeys.antennas.all, queryFn: () => client.antennas.fetchAntennas(), - enabled: features.antennas, + enabled: isLoggedIn && features.antennas, select, }); } diff --git a/packages/nicolium/src/queries/accounts/use-circles.ts b/packages/nicolium/src/queries/accounts/use-circles.ts index 35868d31e..fdd4b0e21 100644 --- a/packages/nicolium/src/queries/accounts/use-circles.ts +++ b/packages/nicolium/src/queries/accounts/use-circles.ts @@ -2,6 +2,7 @@ import { type UseQueryResult, useMutation, useQuery } from '@tanstack/react-quer import { useClient } from '@/hooks/use-client'; import { useFeatures } from '@/hooks/use-features'; +import { useLoggedIn } from '@/hooks/use-logged-in'; import { queryKeys } from '@/queries/keys'; import { queryClient } from '../client'; @@ -16,11 +17,12 @@ function useCircles(): UseQueryResult, Error>; function useCircles>(select?: (data: Array) => T) { const client = useClient(); const features = useFeatures(); + const { isLoggedIn } = useLoggedIn(); return useQuery({ queryKey: queryKeys.circles.all, queryFn: () => client.circles.fetchCircles(), - enabled: features.circles, + enabled: isLoggedIn && features.circles, select, }); } diff --git a/packages/nicolium/src/queries/accounts/use-lists.ts b/packages/nicolium/src/queries/accounts/use-lists.ts index 90764b31e..a4704c040 100644 --- a/packages/nicolium/src/queries/accounts/use-lists.ts +++ b/packages/nicolium/src/queries/accounts/use-lists.ts @@ -1,7 +1,8 @@ -import { useMutation, useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery, type UseQueryResult } from '@tanstack/react-query'; import { useClient } from '@/hooks/use-client'; import { useFeatures } from '@/hooks/use-features'; +import { useLoggedIn } from '@/hooks/use-logged-in'; import { queryKeys } from '@/queries/keys'; import { queryClient } from '../client'; @@ -11,17 +12,20 @@ import { minifyAccountList } from '../utils/minify-list'; import type { CreateListParams, List, UpdateListParams } from 'pl-api'; -const useLists = (select?: (data: Array) => T) => { +function useLists(select: (data: Array) => T): UseQueryResult; +function useLists(): UseQueryResult, Error>; +function useLists>(select?: (data: Array) => T) { const client = useClient(); const features = useFeatures(); + const { isLoggedIn } = useLoggedIn(); return useQuery({ queryKey: queryKeys.lists.all, queryFn: () => client.lists.getLists(), - enabled: features.lists, + enabled: isLoggedIn && features.lists, select, }); -}; +} const useList = (listId?: string) => useLists((data) => (listId ? data.find((list) => list.id === listId) : undefined)); diff --git a/packages/nicolium/src/queries/keys.ts b/packages/nicolium/src/queries/keys.ts index b557369b7..061804c4d 100644 --- a/packages/nicolium/src/queries/keys.ts +++ b/packages/nicolium/src/queries/keys.ts @@ -13,7 +13,7 @@ import type { MinifiedAdminReport, MinifiedConversation, } from './utils/minify-list'; -import type { NormalizedStatus } from '@/normalizers/status'; +import type { NormalizedStatus } from '@/queries/statuses/normalize'; import type { DataTag, InfiniteData } from '@tanstack/react-query'; import type { Account, diff --git a/packages/nicolium/src/queries/notifications/use-notifications.ts b/packages/nicolium/src/queries/notifications/use-notifications.ts index c6b90f340..f19f9545d 100644 --- a/packages/nicolium/src/queries/notifications/use-notifications.ts +++ b/packages/nicolium/src/queries/notifications/use-notifications.ts @@ -10,10 +10,7 @@ import { import { useCallback, useEffect, useMemo } from 'react'; import { useIntl } from 'react-intl'; -import { - getNotificationStatusId, - notificationMessages, -} from '@/features/notifications/components/notification'; +import { getNotificationStatusId, notificationMessages } from '@/components/notification'; import { useClient } from '@/hooks/use-client'; import { useLoggedIn } from '@/hooks/use-logged-in'; import { appendFollowRequest } from '@/queries/accounts/use-follow-requests'; diff --git a/packages/nicolium/src/normalizers/status.ts b/packages/nicolium/src/queries/statuses/normalize.ts similarity index 100% rename from packages/nicolium/src/normalizers/status.ts rename to packages/nicolium/src/queries/statuses/normalize.ts diff --git a/packages/nicolium/src/queries/statuses/use-status-interactions.ts b/packages/nicolium/src/queries/statuses/use-status-interactions.ts index 66d77740c..896540ad0 100644 --- a/packages/nicolium/src/queries/statuses/use-status-interactions.ts +++ b/packages/nicolium/src/queries/statuses/use-status-interactions.ts @@ -23,7 +23,7 @@ import { simulateEmojiReact, simulateUnEmojiReact } from '@/utils/emoji-reacts'; import { queryKeys } from '../keys'; import { filterById } from '../utils/filter-id'; -import type { NormalizedStatus } from '@/normalizers/status'; +import type { NormalizedStatus } from '@/queries/statuses/normalize'; import type { EmojiReaction, PaginatedResponse } from 'pl-api'; const messages = defineMessages({ diff --git a/packages/nicolium/src/queries/statuses/use-status.ts b/packages/nicolium/src/queries/statuses/use-status.ts index bace5ebde..5fec9c107 100644 --- a/packages/nicolium/src/queries/statuses/use-status.ts +++ b/packages/nicolium/src/queries/statuses/use-status.ts @@ -10,8 +10,8 @@ import { useMemo } from 'react'; import { decrementReplyCount, incrementReplyCount } from '@/actions/statuses'; import { useClient } from '@/hooks/use-client'; import { useFeatures } from '@/hooks/use-features'; -import { normalizeStatus, type NormalizedStatus } from '@/normalizers/status'; import { useFilters } from '@/queries/settings/use-filters'; +import { normalizeStatus, type NormalizedStatus } from '@/queries/statuses/normalize'; import { importEntities } from '@/queries/utils/import-entities'; import { useComposeActions } from '@/stores/compose'; import { useContextsActions } from '@/stores/contexts'; diff --git a/packages/nicolium/src/queries/utils/import-entities.ts b/packages/nicolium/src/queries/utils/import-entities.ts index 85d43fee4..e1bb5a9de 100644 --- a/packages/nicolium/src/queries/utils/import-entities.ts +++ b/packages/nicolium/src/queries/utils/import-entities.ts @@ -1,9 +1,9 @@ import { notifyManager } from '@tanstack/react-query'; -import { normalizeStatus } from '@/normalizers/status'; import { selectAccount } from '@/queries/accounts/selectors'; import { queryClient } from '@/queries/client'; import { queryKeys } from '@/queries/keys'; +import { normalizeStatus } from '@/queries/statuses/normalize'; import { useContextStore } from '@/stores/contexts'; import type { diff --git a/packages/nicolium/src/schemas/frontend-settings.ts b/packages/nicolium/src/schemas/frontend-settings.ts index 0e70ef20e..6cb4cdc78 100644 --- a/packages/nicolium/src/schemas/frontend-settings.ts +++ b/packages/nicolium/src/schemas/frontend-settings.ts @@ -21,6 +21,8 @@ const settingsSchema = v.object({ boostModal: v.fallback(v.boolean(), false), deleteModal: v.fallback(v.boolean(), true), missingDescriptionModal: v.fallback(v.boolean(), true), + wrenchModal: v.fallback(v.boolean(), false), + missingDescriptionBoostModal: v.fallback(v.boolean(), false), ignoreHashtagCasingSuggestions: v.fallback(v.boolean(), false), defaultPrivacy: v.fallback(v.picklist(['public', 'unlisted', 'private', 'direct']), 'public'), defaultContentType: v.fallback( @@ -81,14 +83,25 @@ const settingsSchema = v.object({ timelines: v.fallback( v.record( - v.string(), + v.picklist([ + 'home', + 'antenna', + 'bubble', + 'circle', + 'local', + 'group', + 'hashtag', + 'list', + 'public', + 'wrenched', + ]), coerceObject({ - showReblogs: v.optional(v.boolean(), true), - showSelfReblogs: v.optional(v.boolean(), true), - showReplies: v.optional(v.boolean(), true), - showQuotes: v.optional(v.boolean(), true), - showDirect: v.optional(v.boolean(), true), - showNonMedia: v.optional(v.boolean(), true), + showReblogs: v.fallback(v.boolean(), true), + showSelfReblogs: v.fallback(v.boolean(), true), + showReplies: v.fallback(v.boolean(), true), + showQuotes: v.fallback(v.boolean(), true), + showDirect: v.fallback(v.boolean(), true), + showNonMedia: v.fallback(v.boolean(), true), }), ), {}, @@ -129,6 +142,6 @@ const settingsSchema = v.object({ }); type Settings = v.InferOutput; -type TimelineFilters = Settings['timelines'][string]; +type TimelineFilters = Settings['timelines']['home']; export { settingsSchema, type Settings, type TimelineFilters }; diff --git a/packages/nicolium/src/schemas/utils.ts b/packages/nicolium/src/schemas/utils.ts index 66982220b..596b69281 100644 --- a/packages/nicolium/src/schemas/utils.ts +++ b/packages/nicolium/src/schemas/utils.ts @@ -31,7 +31,9 @@ const coerceObject = (shape: T) => v.optional( v.pipe( v.any(), - v.transform((input) => (typeof input === 'object' && input !== null ? input : {})), + v.transform((input) => + typeof input === 'object' && !Array.isArray(input) && input !== null ? input : {}, + ), v.object(shape), ), {}, diff --git a/packages/nicolium/src/service-worker/sw.ts b/packages/nicolium/src/service-worker/sw.ts index 25aa1402e..d03cfe8f9 100644 --- a/packages/nicolium/src/service-worker/sw.ts +++ b/packages/nicolium/src/service-worker/sw.ts @@ -3,6 +3,10 @@ import IntlMessageFormat from 'intl-messageformat'; import 'intl-pluralrules'; import unescape from 'lodash/unescape'; +import iconExpand from '../assets/images/web-push/web-push-icon_expand.png'; +import iconFavourite from '../assets/images/web-push/web-push-icon_favourite.png'; +import iconReblog from '../assets/images/web-push/web-push-icon_reblog.png'; + import locales from './web-push-locales'; import type { @@ -230,21 +234,21 @@ const handlePush = (event: PushEvent) => { /** Native action to open a status on the device. */ const actionExpand = (preferred_locale: string) => ({ action: 'expand', - icon: `/${require('../assets/images/web-push/web-push-icon_expand.png')}`, + icon: `/${iconExpand}`, title: formatMessage('status.show_more', preferred_locale), }); /** Native action to repost status. */ const actionReblog = (preferred_locale: string) => ({ action: 'reblog', - icon: `/${require('../assets/images/web-push/web-push-icon_reblog.png')}`, + icon: `/${iconReblog}`, title: formatMessage('status.reblog', preferred_locale), }); /** Native action to like status. */ const actionFavourite = (preferred_locale: string) => ({ action: 'favourite', - icon: `/${require('../assets/images/web-push/web-push-icon_favourite.png')}`, + icon: `/${iconFavourite}`, title: formatMessage('status.favourite', preferred_locale), }); diff --git a/packages/nicolium/src/stores/auth.ts b/packages/nicolium/src/stores/auth.ts index 3c173eadb..7869752c6 100644 --- a/packages/nicolium/src/stores/auth.ts +++ b/packages/nicolium/src/stores/auth.ts @@ -26,7 +26,7 @@ import { coerceObject } from '@/schemas/utils'; import { setSentryAccount, unsetSentryAccount } from '@/sentry'; import KVStore from '@/storage/kv-store'; import toast from '@/toast'; -import { validId, isURL, parseBaseURL } from '@/utils/auth'; +import { validId, parseBaseURL } from '@/utils/auth'; import sourceCode from '@/utils/code'; import { normalizeUsername } from '@/utils/input'; import { getScopes } from '@/utils/scopes'; @@ -68,7 +68,7 @@ const instance = (() => { } })(); -const backendUrl = isURL(BuildConfig.BACKEND_URL) ? BuildConfig.BACKEND_URL : ''; +const backendUrl = URL.canParse(BuildConfig.BACKEND_URL) ? BuildConfig.BACKEND_URL : ''; const mastodonPreloadSchema = coerceObject({ meta: coerceObject({ @@ -189,7 +189,7 @@ const setSessionUser = (state: AuthData) => { const isUpgradingUrlId = (state: AuthData) => { const me = state.me; const user = state.users[me!]; - return validId(me) && user && !isURL(me); + return validId(me) && user && !URL.canParse(me as string); }; const sanitizeState = (state: AuthData) => { @@ -263,7 +263,7 @@ const importTokenData = (state: AuthData, token: Token, app?: CredentialApplicat }; const upgradeNonUrlId = (state: AuthData, account: CredentialAccount) => { - if (isURL(state.me)) return; + if (state.me && URL.canParse(state.me)) return; state.me = state.me === account.id ? account.url : state.me; delete state.users[account.id]; }; @@ -312,7 +312,7 @@ const importMastodonPreloadData = (state: AuthData, data: Record) = const accountUrl = parsedData.accounts[accountId]?.url; const accessToken = parsedData.meta.access_token; - if (validId(accessToken) && validId(accountId) && isURL(accountUrl)) { + if (validId(accessToken) && validId(accountId) && URL.canParse(accountUrl)) { state.tokens[accessToken] = v.parse(tokenSchema, { access_token: accessToken, account: accountId, @@ -830,7 +830,9 @@ const isLoggedIn = () => validId(getCurrentAccountId()); const getAuthUserUrl = () => { const { me, users } = useAuthStore.getState(); - return [users[me!]?.url, me].filter((url) => url).find(isURL); + return [users[me!]?.url, me] + .filter((url): url is string => !!url) + .find((url) => URL.canParse(url)); }; const getMeUrl = () => getOwnAccount()?.url; diff --git a/packages/nicolium/src/stores/compose.ts b/packages/nicolium/src/stores/compose.ts index d88bf02a9..00f1a2d9a 100644 --- a/packages/nicolium/src/stores/compose.ts +++ b/packages/nicolium/src/stores/compose.ts @@ -23,8 +23,8 @@ import toast from '@/toast'; import { useUiStoreActions } from './ui'; import type { AutoSuggestion } from '@/components/autosuggest-input'; -import type { Language } from '@/features/preferences'; -import type { NormalizedStatus as Status } from '@/normalizers/status'; +import type { Language } from '@/pages/settings/components/preferences'; +import type { NormalizedStatus as Status } from '@/queries/statuses/normalize'; import type { LinkOptions } from '@tanstack/react-router'; import type { Account, diff --git a/packages/nicolium/src/stores/timelines.ts b/packages/nicolium/src/stores/timelines.ts index ee82db147..e9a67f27f 100644 --- a/packages/nicolium/src/stores/timelines.ts +++ b/packages/nicolium/src/stores/timelines.ts @@ -3,7 +3,7 @@ import { mutative } from 'zustand-mutative'; import { findStatuses } from '@/queries/statuses/use-status'; -import type { NormalizedStatus } from '@/normalizers/status'; +import type { NormalizedStatus } from '@/queries/statuses/normalize'; import type { CreateStatusParams, Status } from 'pl-api'; type TimelineEntry = diff --git a/packages/nicolium/src/styles/new/accounts.scss b/packages/nicolium/src/styles/new/accounts.scss index c8aae3f6e..31fc960ce 100644 --- a/packages/nicolium/src/styles/new/accounts.scss +++ b/packages/nicolium/src/styles/new/accounts.scss @@ -189,7 +189,7 @@ .⁂-profile-field { span[data-markup] { - overflow-wrap: break-word; + overflow-wrap: anywhere; } dt span[data-markup] { @@ -218,7 +218,7 @@ @include mixins.text; overflow: hidden; - overflow-wrap: break-word; + overflow-wrap: anywhere; } } diff --git a/packages/nicolium/src/styles/new/components.scss b/packages/nicolium/src/styles/new/components.scss index 4c219e366..694b1d47b 100644 --- a/packages/nicolium/src/styles/new/components.scss +++ b/packages/nicolium/src/styles/new/components.scss @@ -245,7 +245,7 @@ min-width: 1.25rem; height: 1.25rem; - overflow-wrap: break-word; + overflow-wrap: anywhere; white-space: nowrap; } } @@ -332,7 +332,7 @@ > :first-child { color: rgb(var(--color-gray-900)); - overflow-wrap: break-word; + overflow-wrap: anywhere; .dark & { color: rgb(var(--color-gray-100)); diff --git a/packages/nicolium/src/styles/new/layout.scss b/packages/nicolium/src/styles/new/layout.scss index 9ea6edc43..ad665175c 100644 --- a/packages/nicolium/src/styles/new/layout.scss +++ b/packages/nicolium/src/styles/new/layout.scss @@ -54,6 +54,19 @@ body { } } +.noscript { + position: absolute; + inset: 0; + padding: 1rem; + text-align: center; + + a { + @include mixins.text($size: inherit, $theme: subtle); + + text-decoration-line: underline; + } +} + #app { height: 100%; } diff --git a/packages/nicolium/src/styles/new/mixins.scss b/packages/nicolium/src/styles/new/mixins.scss index 5f7b66aab..6d096fb6f 100644 --- a/packages/nicolium/src/styles/new/mixins.scss +++ b/packages/nicolium/src/styles/new/mixins.scss @@ -296,6 +296,7 @@ } &:focus { + background-color: rgb(var(--color-danger-100)); outline-color: rgb(var(--color-danger-500)); } diff --git a/packages/nicolium/src/styles/new/statuses.scss b/packages/nicolium/src/styles/new/statuses.scss index 02ec1587e..30c0fb901 100644 --- a/packages/nicolium/src/styles/new/statuses.scss +++ b/packages/nicolium/src/styles/new/statuses.scss @@ -626,7 +626,7 @@ position: relative; color: rgb(var(--color-gray-900)); text-overflow: ellipsis; - overflow-wrap: break-word; + overflow-wrap: anywhere; &:focus { outline: 2px solid transparent; @@ -659,11 +659,11 @@ } .dark.black .⁂-quoted-status:hover & { - background-image: linear-gradient(to bottom, transparent, rgb(var(--color-gray-800))); + background-image: linear-gradient(to bottom, transparent, rgb(var(--color-primary-800))); } .dark .⁂-quoted-status:hover & { - background-image: linear-gradient(to bottom, transparent, rgb(var(--color-gray-800))); + background-image: linear-gradient(to bottom, transparent, rgb(var(--color-primary-800))); } } diff --git a/packages/nicolium/src/styles/new/timelines.scss b/packages/nicolium/src/styles/new/timelines.scss index 662031a2f..b547b37a0 100644 --- a/packages/nicolium/src/styles/new/timelines.scss +++ b/packages/nicolium/src/styles/new/timelines.scss @@ -158,3 +158,29 @@ .⁂-load-more { @include mixins.button($theme: primary, $block: true); } + +.⁂-timeline-picker { + cursor: pointer; + + display: flex; + gap: 0.5rem; + align-items: center; + + margin: -0.25rem -0.5rem; + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + + transition: background 150ms ease-in-out; + + &:hover, &:focus-within, &[aria-expanded="true"] { + background: rgb(var(--color-primary-100)); + + .dark & { + background: rgb(var(--color-primary-800)); + } + + .dark.black & { + background: rgb(var(--color-gray-900)); + } + } +} diff --git a/packages/nicolium/src/utils/auth.ts b/packages/nicolium/src/utils/auth.ts index 9a0008915..246311fe2 100644 --- a/packages/nicolium/src/utils/auth.ts +++ b/packages/nicolium/src/utils/auth.ts @@ -1,23 +1,9 @@ const validId = (id?: string | null | false) => typeof id === 'string' && id !== 'null' && id !== 'undefined'; -const isURL = (url?: string | null) => { - if (typeof url !== 'string') return false; - try { - new URL(url); - return true; - } catch { - return false; - } -}; - const parseBaseURL = (url?: string) => { - if (typeof url !== 'string') return ''; - try { - return new URL(url).origin; - } catch { - return ''; - } + if (!url || !URL.canParse(url)) return ''; + return new URL(url).origin; }; -export { validId, isURL, parseBaseURL }; +export { validId, parseBaseURL }; diff --git a/packages/nicolium/src/features/ui/util/fullscreen.ts b/packages/nicolium/src/utils/fullscreen.ts similarity index 100% rename from packages/nicolium/src/features/ui/util/fullscreen.ts rename to packages/nicolium/src/utils/fullscreen.ts diff --git a/packages/nicolium/src/features/ui/util/global-hotkeys.tsx b/packages/nicolium/src/utils/global-hotkeys.tsx similarity index 98% rename from packages/nicolium/src/features/ui/util/global-hotkeys.tsx rename to packages/nicolium/src/utils/global-hotkeys.tsx index c9c53b999..5226886a5 100644 --- a/packages/nicolium/src/features/ui/util/global-hotkeys.tsx +++ b/packages/nicolium/src/utils/global-hotkeys.tsx @@ -6,7 +6,7 @@ import { useOwnAccount } from '@/hooks/use-own-account'; import { useComposeActions } from '@/stores/compose'; import { useModalsActions } from '@/stores/modals'; -import { Hotkeys } from '../components/hotkeys'; +import { Hotkeys } from '../features/ui/components/hotkeys'; import type { LexicalEditor } from 'lexical'; diff --git a/packages/nicolium/src/features/ui/util/pending-status-builder.ts b/packages/nicolium/src/utils/pending-status-builder.ts similarity index 95% rename from packages/nicolium/src/features/ui/util/pending-status-builder.ts rename to packages/nicolium/src/utils/pending-status-builder.ts index 0ae956de0..4658d1c47 100644 --- a/packages/nicolium/src/features/ui/util/pending-status-builder.ts +++ b/packages/nicolium/src/utils/pending-status-builder.ts @@ -1,9 +1,9 @@ import { statusSchema, type Account } from 'pl-api'; import * as v from 'valibot'; -import { normalizeStatus } from '@/normalizers/status'; import { queryClient } from '@/queries/client'; import { queryKeys } from '@/queries/keys'; +import { normalizeStatus } from '@/queries/statuses/normalize'; import type { PendingStatus } from '@/stores/pending-statuses'; diff --git a/packages/nicolium/src/features/placeholder/utils.ts b/packages/nicolium/src/utils/placeholders.ts similarity index 100% rename from packages/nicolium/src/features/placeholder/utils.ts rename to packages/nicolium/src/utils/placeholders.ts diff --git a/packages/nicolium/src/utils/state.ts b/packages/nicolium/src/utils/state.ts index 89399bf52..d31b16b3e 100644 --- a/packages/nicolium/src/utils/state.ts +++ b/packages/nicolium/src/utils/state.ts @@ -6,7 +6,6 @@ import * as BuildConfig from '@/build-config'; import { isPrerendered } from '@/precheck'; import { useInstanceStore } from '@/stores/instance'; -import { isURL } from '@/utils/auth'; /** * Determine whether Nicolium is running in standalone mode. @@ -14,12 +13,12 @@ import { isURL } from '@/utils/auth'; */ const isStandalone = (): boolean => { const instanceFetchFailed = useInstanceStore.getState().instanceFetchFailed; - return isURL(BuildConfig.BACKEND_URL) ? false : !isPrerendered && instanceFetchFailed; + return URL.canParse(BuildConfig.BACKEND_URL) ? false : !isPrerendered && instanceFetchFailed; }; const useIsStandalone = () => { const instanceFetchFailed = useInstanceStore((state) => state.instanceFetchFailed); - return isURL(BuildConfig.BACKEND_URL) ? false : !isPrerendered && instanceFetchFailed; + return URL.canParse(BuildConfig.BACKEND_URL) ? false : !isPrerendered && instanceFetchFailed; }; const useFederationRestrictionsDisclosed = () => diff --git a/packages/nicolium/src/utils/status.ts b/packages/nicolium/src/utils/status.ts index b3f6ac16f..79669f26c 100644 --- a/packages/nicolium/src/utils/status.ts +++ b/packages/nicolium/src/utils/status.ts @@ -1,6 +1,6 @@ import { selectAccount } from '@/queries/accounts/selectors'; -import type { NormalizedStatus as Status } from '@/normalizers/status'; +import type { NormalizedStatus as Status } from '@/queries/statuses/normalize'; import type { IntlShape } from 'react-intl'; /** Grab the first external link from a status. */ diff --git a/packages/nicolium/vite.config.ts b/packages/nicolium/vite.config.ts index e5a6eaca1..634920b79 100644 --- a/packages/nicolium/vite.config.ts +++ b/packages/nicolium/vite.config.ts @@ -8,7 +8,6 @@ import { defineConfig } from 'vite'; import compileTime from 'vite-plugin-compile-time'; import { createHtmlPlugin } from 'vite-plugin-html'; import { VitePWA } from 'vite-plugin-pwa'; -import vitePluginRequire from 'vite-plugin-require'; import { viteStaticCopy } from 'vite-plugin-static-copy'; const config = defineConfig(() => ({ @@ -35,8 +34,6 @@ const config = defineConfig(() => ({ }, plugins: [ tsgoChecker(), - // @ts-expect-error https://github.com/wangzongming/vite-plugin-require/issues/23 - vitePluginRequire.default(), compileTime(), createHtmlPlugin({ template: 'index.html', diff --git a/packages/pl-api/lib/client-base.ts b/packages/pl-api/lib/client-base.ts index 9bcc579af..70ff502c3 100644 --- a/packages/pl-api/lib/client-base.ts +++ b/packages/pl-api/lib/client-base.ts @@ -3,7 +3,7 @@ import * as v from 'valibot'; import { instanceSchema } from '@/entities/instance'; import { filteredArray } from '@/entities/utils'; import { type Features, getFeatures } from '@/features'; -import request, { getNextLink, getPrevLink, type RequestBody } from '@/request'; +import request, { getLinks, type RequestBody } from '@/request'; import { PaginatedResponse } from '@/responses'; import type { Instance } from '@/entities/instance'; @@ -69,15 +69,18 @@ class PlApiBaseClient { ) => { const targetSchema = isArray ? filteredArray(schema) : schema; - const processResponse = (response: PlApiResponse) => - new PaginatedResponse( + const processResponse = (response: PlApiResponse) => { + const { prev: prevLink, next: nextLink } = getLinks(response); + + return new PaginatedResponse( v.parse(targetSchema, response.json) as IsArray extends true ? Array : T, { - previous: getMore(getPrevLink(response)), - next: getMore(getNextLink(response)), + previous: getMore(prevLink), + next: getMore(nextLink), partial: response.status === 206, }, ); + }; const getMore = (input: string | null) => input ? () => this.request(input).then(processResponse) : null; diff --git a/packages/pl-api/lib/client/accounts.ts b/packages/pl-api/lib/client/accounts.ts index 89759d923..2fc9b9ae0 100644 --- a/packages/pl-api/lib/client/accounts.ts +++ b/packages/pl-api/lib/client/accounts.ts @@ -327,7 +327,7 @@ const accounts = (client: PlApiBaseClient) => ({ */ getAccountEndorsements: (accountId: string, params?: GetAccountEndorsementsParams) => client.paginatedGet( - `/api/v1/${[PLEROMA].includes(client.features.version.software as string) ? 'pleroma/' : ''}accounts/${accountId}/endorsements`, + `/api/v1/${client.features.version.software === PLEROMA ? 'pleroma/' : ''}accounts/${accountId}/endorsements`, { params }, accountSchema, ), diff --git a/packages/pl-api/lib/client/admin.ts b/packages/pl-api/lib/client/admin.ts index f5bf82704..53654e765 100644 --- a/packages/pl-api/lib/client/admin.ts +++ b/packages/pl-api/lib/client/admin.ts @@ -1525,7 +1525,7 @@ const admin = (client: PlApiBaseClient) => { const response = await client.request('/api/v1/admin/custom_emojis', { method: 'POST', body: params, - contentType: '', + formData: true, }); return v.parse(adminCustomEmojiSchema, response.json); @@ -1535,7 +1535,7 @@ const admin = (client: PlApiBaseClient) => { const response = await client.request(`/api/v1/admin/custom_emojis/${emojiId}`, { method: 'PATCH', body: params, - contentType: '', + formData: true, }); return v.parse(adminCustomEmojiSchema, response.json); diff --git a/packages/pl-api/lib/client/drive.ts b/packages/pl-api/lib/client/drive.ts index a410b6f56..5ef824b32 100644 --- a/packages/pl-api/lib/client/drive.ts +++ b/packages/pl-api/lib/client/drive.ts @@ -81,7 +81,7 @@ const drive = (client: PlApiBaseClient) => ({ method: 'POST', body: { file }, params: { folderId }, - contentType: '', + formData: true, }); return v.parse(driveFileSchema, response.json); diff --git a/packages/pl-api/lib/client/events.ts b/packages/pl-api/lib/client/events.ts index ddb832416..f6be65dc0 100644 --- a/packages/pl-api/lib/client/events.ts +++ b/packages/pl-api/lib/client/events.ts @@ -131,7 +131,7 @@ const events = (client: PlApiBaseClient) => ({ */ getEventIcs: async (statusId: string) => { const response = await client.request(`/api/v1/pleroma/events/${statusId}/ics`, { - contentType: '', + formData: true, }); return response.data; diff --git a/packages/pl-api/lib/client/media.ts b/packages/pl-api/lib/client/media.ts index 9f57089ab..0fdc03518 100644 --- a/packages/pl-api/lib/client/media.ts +++ b/packages/pl-api/lib/client/media.ts @@ -16,7 +16,7 @@ const media = (client: PlApiBaseClient) => ({ uploadMedia: async (params: UploadMediaParams, meta?: RequestMeta) => { const response = await client.request( client.features.mediaV2 ? '/api/v2/media' : '/api/v1/media', - { ...meta, method: 'POST', body: params, contentType: '' }, + { ...meta, method: 'POST', body: params, formData: true }, ); return v.parse(mediaAttachmentSchema, response.json); diff --git a/packages/pl-api/lib/client/my-account.ts b/packages/pl-api/lib/client/my-account.ts index f11c4a5cd..4dc233def 100644 --- a/packages/pl-api/lib/client/my-account.ts +++ b/packages/pl-api/lib/client/my-account.ts @@ -11,7 +11,7 @@ import { } from '@/entities'; import { filteredArray } from '@/entities/utils'; import { GOTOSOCIAL, ICESHRIMP_NET, MITRA, PIXELFED, PLEROMA } from '@/features'; -import { getNextLink, getPrevLink } from '@/request'; +import { getLinks } from '@/request'; import { PaginatedResponse } from '@/responses'; import type { accounts } from './accounts'; @@ -40,8 +40,7 @@ const paginatedIceshrimpAccountsList = async ( const items = await client.accounts.getAccounts(ids); - const prevLink = getPrevLink(response); - const nextLink = getNextLink(response); + const { prev: prevLink, next: nextLink } = getLinks(response); return new PaginatedResponse(items, { previous: prevLink ? () => paginatedIceshrimpAccountsList(client, prevLink, fn) : null, diff --git a/packages/pl-api/lib/client/oauth.ts b/packages/pl-api/lib/client/oauth.ts index b17092c82..a2e051a90 100644 --- a/packages/pl-api/lib/client/oauth.ts +++ b/packages/pl-api/lib/client/oauth.ts @@ -19,7 +19,7 @@ const oauth = (client: PlApiBaseClient) => ({ * @see {@link https://docs.joinmastodon.org/methods/oauth/#authorize} */ authorize: async (params: OauthAuthorizeParams) => { - const response = await client.request('/oauth/authorize', { params, contentType: '' }); + const response = await client.request('/oauth/authorize', { params, formData: true }); return v.parse(v.string(), response.json); }, @@ -76,7 +76,7 @@ const oauth = (client: PlApiBaseClient) => ({ const response = await client.request('/oauth/token', { method: 'POST', body: params, - contentType: '', + formData: true, }); return v.parse(tokenSchema, { scope: params.scope || '', ...response.json }); @@ -91,7 +91,7 @@ const oauth = (client: PlApiBaseClient) => ({ const response = await client.request('/oauth/revoke', { method: 'POST', body: params, - contentType: '', + formData: true, }); client.socket?.close(); diff --git a/packages/pl-api/lib/client/settings.ts b/packages/pl-api/lib/client/settings.ts index d4472b3dc..9a6847d2e 100644 --- a/packages/pl-api/lib/client/settings.ts +++ b/packages/pl-api/lib/client/settings.ts @@ -559,7 +559,7 @@ const settings = (client: PlApiBaseClient) => ({ response = await client.request('/api/v1/import', { method: 'POST', body: { data: list, type: 'following', mode }, - contentType: '', + formData: true, }); break; case MITRA: @@ -572,7 +572,7 @@ const settings = (client: PlApiBaseClient) => ({ response = await client.request('/api/pleroma/follow_import', { method: 'POST', body: { list }, - contentType: '', + formData: true, }); } @@ -611,14 +611,14 @@ const settings = (client: PlApiBaseClient) => ({ response = await client.request('/api/v1/import', { method: 'POST', body: { data: list, type: 'blocks', mode }, - contentType: '', + formData: true, }); break; default: response = await client.request('/api/pleroma/blocks_import', { method: 'POST', body: { list }, - contentType: '', + formData: true, }); } @@ -640,14 +640,14 @@ const settings = (client: PlApiBaseClient) => ({ response = await client.request('/api/v1/import', { method: 'POST', body: { data: list, type: 'blocks', mode }, - contentType: '', + formData: true, }); break; default: response = await client.request('/api/pleroma/mutes_import', { method: 'POST', body: { list }, - contentType: '', + formData: true, }); } diff --git a/packages/pl-api/lib/client/stories.ts b/packages/pl-api/lib/client/stories.ts index 88e54dbfe..f39426d35 100644 --- a/packages/pl-api/lib/client/stories.ts +++ b/packages/pl-api/lib/client/stories.ts @@ -110,7 +110,7 @@ const stories = (client: PlApiBaseClient) => ({ const response = await client.request('/api/web/stories/v1/add', { method: 'POST', body: { file }, - contentType: '', + formData: true, }); return v.parse(storyMediaSchema, response.json); diff --git a/packages/pl-api/lib/entities/status.ts b/packages/pl-api/lib/entities/status.ts index be8aef867..7d9818b13 100644 --- a/packages/pl-api/lib/entities/status.ts +++ b/packages/pl-api/lib/entities/status.ts @@ -50,12 +50,8 @@ const baseStatusSchema = v.object({ account: v.pipe( v.unknown(), v.transform((account) => { - if ( - typeof window !== 'undefined' && - (window as any).__PL_API_FALLBACK_ACCOUNT && - JSON.stringify(account) === '{}' - ) - return (window as any).__PL_API_FALLBACK_ACCOUNT; + const fallbackAccount = (window as any)?.__PL_API_FALLBACK_ACCOUNT; + if (fallbackAccount && Object.keys(account as object).length === 0) return fallbackAccount; return account; }), accountSchema, diff --git a/packages/pl-api/lib/entities/utils.ts b/packages/pl-api/lib/entities/utils.ts index 06d82fcfb..8271dcaa5 100644 --- a/packages/pl-api/lib/entities/utils.ts +++ b/packages/pl-api/lib/entities/utils.ts @@ -40,7 +40,9 @@ const coerceObject = (shape: T) => v.optional( v.pipe( v.any(), - v.transform((input) => (typeof input === 'object' && input !== null ? input : {})), + v.transform((input) => + typeof input === 'object' && !Array.isArray(input) && input !== null ? input : {}, + ), v.object(shape), ), {}, diff --git a/packages/pl-api/lib/params/index.ts b/packages/pl-api/lib/params/index.ts index 7bb9e34bc..5814c9260 100644 --- a/packages/pl-api/lib/params/index.ts +++ b/packages/pl-api/lib/params/index.ts @@ -22,6 +22,7 @@ export * from './scheduled-statuses'; export * from './search'; export * from './settings'; export * from './statuses'; +export * from './stories'; export * from './streaming'; export * from './timelines'; export * from './trends'; diff --git a/packages/pl-api/lib/request.ts b/packages/pl-api/lib/request.ts index 7c99efa5d..962a7b188 100644 --- a/packages/pl-api/lib/request.ts +++ b/packages/pl-api/lib/request.ts @@ -22,14 +22,17 @@ type Response = { @param {object} response - Fetch API response object @returns {object} Link object */ -const getLinks = (response: Pick): LinkHeader => - new LinkHeader(response.headers?.get('link') || undefined); +const getLinks = ( + response: Pick, +): { next: string | null; prev: string | null } => { + const headers = response.headers?.get('link'); + const linkHeader = (headers && new LinkHeader(headers)) || null; -const getNextLink = (response: Pick): string | null => - getLinks(response).refs.find((link) => link.rel.toLocaleLowerCase() === 'next')?.uri || null; - -const getPrevLink = (response: Pick): string | null => - getLinks(response).refs.find((link) => link.rel.toLocaleLowerCase() === 'prev')?.uri || null; + return { + next: linkHeader?.refs.find((link) => link.rel.toLocaleLowerCase() === 'next')?.uri || null, + prev: linkHeader?.refs.find((link) => link.rel.toLocaleLowerCase() === 'prev')?.uri || null, + }; +}; interface AsyncRefreshHeader { id: string; @@ -78,6 +81,7 @@ interface RequestBody> { onUploadProgress?: (e: ProgressEvent) => void; signal?: AbortSignal; contentType?: string; + formData?: boolean; idempotencyKey?: string; } @@ -96,6 +100,7 @@ function request( onUploadProgress, signal, contentType = 'application/json', + formData, idempotencyKey, }: RequestBody = {}, ) { @@ -108,10 +113,10 @@ function request( else if (this.accessToken) headers.set('Authorization', `Bearer ${this.accessToken}`); else if (this.customAuthorizationToken) headers.set('Authorization', this.customAuthorizationToken); - if (contentType !== '' && body) headers.set('Content-Type', contentType); + if (!formData) headers.set('Content-Type', contentType); if (idempotencyKey) headers.set('Idempotency-Key', idempotencyKey); - body = body && contentType === '' ? serialize(body, { indices: true }) : JSON.stringify(body); + body = body && formData ? serialize(body, { indices: true }) : JSON.stringify(body); // Fetch API doesn't report upload progress, use XHR if (onUploadProgress) { @@ -196,8 +201,6 @@ export { type RequestMeta, type AsyncRefreshHeader, getLinks, - getNextLink, - getPrevLink, getAsyncRefreshHeader, request as default, }; diff --git a/packages/pl-api/package.json b/packages/pl-api/package.json index cd0b2a3b8..d4a99d1d3 100644 --- a/packages/pl-api/package.json +++ b/packages/pl-api/package.json @@ -56,7 +56,7 @@ "oxlint": "^1.55.0", "typedoc": "^0.28.17", "typedoc-material-theme": "^1.4.1", - "typedoc-plugin-valibot": "^1.0.0", + "typedoc-plugin-valibot": "^1.0.2", "typescript": "^5.9.3", "vite": "^8.0.0", "vite-plugin-dts": "^4.5.4", diff --git a/packages/pl-api/typedoc.config.mjs b/packages/pl-api/typedoc.config.mjs index ce91934d2..99a4443e2 100644 --- a/packages/pl-api/typedoc.config.mjs +++ b/packages/pl-api/typedoc.config.mjs @@ -7,6 +7,25 @@ const config = { navigation: { includeCategories: true, }, + categorizeByGroup: true, + sort: ['kind', 'alphabetical'], + kindSortOrder: ['Function', 'Class', 'Interface', 'TypeAlias', 'Variable', 'Enum'], + intentionallyNotExported: [ + 'CreateStatusOptionalParams', + 'CreateStatusWithContent', + 'CreateStatusWithMedia', + 'EditStatusOptionalParams', + 'GetTrends', + 'LanguageParam', + 'OnlyEventsParam', + 'OnlyMediaParam', + 'Params', + 'PlApiClientConstructorOpts', + 'WithMutedParam', + 'WithRelationshipsParam', + ], + groupReferencesByType: true, + suppressCommentWarningsInDeclarationFiles: true, }; export default config; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8317d56ca..653055602 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -301,7 +301,7 @@ importers: devDependencies: '@formatjs/cli': specifier: ^6.13.1 - version: 6.13.1(@vue/compiler-core@3.5.29) + version: 6.13.1(@vue/compiler-core@3.5.30) '@mkljczk/vite-tsgo-checker': specifier: ^0.0.2 version: 0.0.2 @@ -414,7 +414,7 @@ importers: specifier: ^1.2.14 version: 1.2.14(esbuild@0.24.2)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.24.2)(jiti@1.21.7)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.0)(yaml@2.8.2)) vite-plugin-static-copy: - specifier: ^3.2.0 + specifier: ^3.3.0 version: 3.3.0(vite@8.0.0(@types/node@25.5.0)(esbuild@0.24.2)(jiti@1.21.7)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.0)(yaml@2.8.2)) vitest: specifier: ^4.1.0 @@ -469,8 +469,8 @@ importers: specifier: ^1.4.1 version: 1.4.1(typedoc@0.28.17(typescript@5.9.3)) typedoc-plugin-valibot: - specifier: ^1.0.0 - version: 1.0.1(typedoc@0.28.17(typescript@5.9.3)) + specifier: ^1.0.2 + version: 1.0.2(typedoc@0.28.17(typescript@5.9.3)) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -3601,20 +3601,20 @@ packages: '@vue/compiler-core@3.5.18': resolution: {integrity: sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==} - '@vue/compiler-core@3.5.29': - resolution: {integrity: sha512-cuzPhD8fwRHk8IGfmYaR4eEe4cAyJEL66Ove/WZL7yWNL134nqLddSLwNRIsFlnnW1kK+p8Ck3viFnC0chXCXw==} + '@vue/compiler-core@3.5.30': + resolution: {integrity: sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==} '@vue/compiler-dom@3.5.18': resolution: {integrity: sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==} - '@vue/compiler-dom@3.5.29': - resolution: {integrity: sha512-n0G5o7R3uBVmVxjTIYcz7ovr8sy7QObFG8OQJ3xGCDNhbG60biP/P5KnyY8NLd81OuT1WJflG7N4KWYHaeeaIg==} + '@vue/compiler-dom@3.5.30': + resolution: {integrity: sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==} - '@vue/compiler-sfc@3.5.29': - resolution: {integrity: sha512-oJZhN5XJs35Gzr50E82jg2cYdZQ78wEwvRO6Y63TvLVTc+6xICzJHP1UIecdSPPYIbkautNBanDiWYa64QSFIA==} + '@vue/compiler-sfc@3.5.30': + resolution: {integrity: sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==} - '@vue/compiler-ssr@3.5.29': - resolution: {integrity: sha512-Y/ARJZE6fpjzL5GH/phJmsFwx3g6t2KmHKHx5q+MLl2kencADKIrhH5MLF6HHpRMmlRAYBRSvv347Mepf1zVNw==} + '@vue/compiler-ssr@3.5.30': + resolution: {integrity: sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==} '@vue/compiler-vue2@2.7.16': resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} @@ -3630,8 +3630,8 @@ packages: '@vue/shared@3.5.18': resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==} - '@vue/shared@3.5.29': - resolution: {integrity: sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==} + '@vue/shared@3.5.30': + resolution: {integrity: sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==} '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -4409,8 +4409,8 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - enhanced-resolve@5.20.0: - resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} + enhanced-resolve@5.20.1: + resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} entities@2.2.0: @@ -7276,8 +7276,8 @@ packages: resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} engines: {node: '>=10'} - terser-webpack-plugin@5.3.16: - resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} + terser-webpack-plugin@5.4.0: + resolution: {integrity: sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -7424,8 +7424,8 @@ packages: peerDependencies: typedoc: ^0.25.13 || ^0.26.x || ^0.27.x || ^0.28.x - typedoc-plugin-valibot@1.0.1: - resolution: {integrity: sha512-HlF4/zVqPhNZ2KaWuJeqejCwoJfhhDsM92Kp52GHH61bXyrfiVRr9JXHW2ZKpdh5jge8Xw7tmRLYCAPYY6I4ag==} + typedoc-plugin-valibot@1.0.2: + resolution: {integrity: sha512-PTzEEPq4MM9KBV6i/7v3nBstSUpqJGNKViFZCgIQyPc1T35essUCZbAWKa/020feDbtReJ1y5gEWv0LnNx53CQ==} peerDependencies: typedoc: 0.23.x || 0.24.x || 0.25.x || 0.26.x || 0.27.x || 0.28.x @@ -7858,8 +7858,8 @@ packages: resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} engines: {node: '>=10.13.0'} - webpack@5.105.3: - resolution: {integrity: sha512-LLBBA4oLmT7sZdHiYE/PeVuifOxYyE2uL/V+9VQP7YSYdJU7bSf7H8bZRRxW8kEPMkmVjnrXmoR3oejIdX0xbg==} + webpack@5.105.4: + resolution: {integrity: sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -9249,9 +9249,9 @@ snapshots: '@fontsource/tajawal@5.2.7': {} - '@formatjs/cli@6.13.1(@vue/compiler-core@3.5.29)': + '@formatjs/cli@6.13.1(@vue/compiler-core@3.5.30)': optionalDependencies: - '@vue/compiler-core': 3.5.29 + '@vue/compiler-core': 3.5.30 '@formatjs/ecma402-abstract@3.1.1': dependencies: @@ -10917,10 +10917,10 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-core@3.5.29': + '@vue/compiler-core@3.5.30': dependencies: '@babel/parser': 7.29.0 - '@vue/shared': 3.5.29 + '@vue/shared': 3.5.30 entities: 7.0.1 estree-walker: 2.0.2 source-map-js: 1.2.1 @@ -10930,27 +10930,27 @@ snapshots: '@vue/compiler-core': 3.5.18 '@vue/shared': 3.5.18 - '@vue/compiler-dom@3.5.29': + '@vue/compiler-dom@3.5.30': dependencies: - '@vue/compiler-core': 3.5.29 - '@vue/shared': 3.5.29 + '@vue/compiler-core': 3.5.30 + '@vue/shared': 3.5.30 - '@vue/compiler-sfc@3.5.29': + '@vue/compiler-sfc@3.5.30': dependencies: '@babel/parser': 7.29.0 - '@vue/compiler-core': 3.5.29 - '@vue/compiler-dom': 3.5.29 - '@vue/compiler-ssr': 3.5.29 - '@vue/shared': 3.5.29 + '@vue/compiler-core': 3.5.30 + '@vue/compiler-dom': 3.5.30 + '@vue/compiler-ssr': 3.5.30 + '@vue/shared': 3.5.30 estree-walker: 2.0.2 magic-string: 0.30.21 postcss: 8.5.8 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.29': + '@vue/compiler-ssr@3.5.30': dependencies: - '@vue/compiler-dom': 3.5.29 - '@vue/shared': 3.5.29 + '@vue/compiler-dom': 3.5.30 + '@vue/shared': 3.5.30 '@vue/compiler-vue2@2.7.16': dependencies: @@ -10972,7 +10972,7 @@ snapshots: '@vue/shared@3.5.18': {} - '@vue/shared@3.5.29': {} + '@vue/shared@3.5.30': {} '@webassemblyjs/ast@1.14.1': dependencies: @@ -11859,7 +11859,7 @@ snapshots: emoji-regex@8.0.0: {} - enhanced-resolve@5.20.0: + enhanced-resolve@5.20.1: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 @@ -15469,14 +15469,13 @@ snapshots: type-fest: 0.16.0 unique-string: 2.0.0 - terser-webpack-plugin@5.3.16(esbuild@0.24.2)(webpack@5.105.3(esbuild@0.24.2)): + terser-webpack-plugin@5.4.0(esbuild@0.24.2)(webpack@5.105.4(esbuild@0.24.2)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 - serialize-javascript: 6.0.2 terser: 5.46.0 - webpack: 5.105.3(esbuild@0.24.2) + webpack: 5.105.4(esbuild@0.24.2) optionalDependencies: esbuild: 0.24.2 @@ -15604,7 +15603,7 @@ snapshots: '@material/material-color-utilities': 0.3.0 typedoc: 0.28.17(typescript@5.9.3) - typedoc-plugin-valibot@1.0.1(typedoc@0.28.17(typescript@5.9.3)): + typedoc-plugin-valibot@1.0.2(typedoc@0.28.17(typescript@5.9.3)): dependencies: typedoc: 0.28.17(typescript@5.9.3) @@ -15848,10 +15847,10 @@ snapshots: '@babel/parser': 7.29.0 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 - '@vue/compiler-sfc': 3.5.29 + '@vue/compiler-sfc': 3.5.30 vite: 8.0.0(@types/node@25.5.0)(esbuild@0.24.2)(jiti@1.21.7)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.46.0)(yaml@2.8.2) - vue-loader: 17.4.2(@vue/compiler-sfc@3.5.29)(webpack@5.105.3(esbuild@0.24.2)) - webpack: 5.105.3(esbuild@0.24.2) + vue-loader: 17.4.2(@vue/compiler-sfc@3.5.30)(webpack@5.105.4(esbuild@0.24.2)) + webpack: 5.105.4(esbuild@0.24.2) transitivePeerDependencies: - '@swc/core' - esbuild @@ -15984,14 +15983,14 @@ snapshots: vscode-uri@3.1.0: {} - vue-loader@17.4.2(@vue/compiler-sfc@3.5.29)(webpack@5.105.3(esbuild@0.24.2)): + vue-loader@17.4.2(@vue/compiler-sfc@3.5.30)(webpack@5.105.4(esbuild@0.24.2)): dependencies: chalk: 4.1.2 hash-sum: 2.0.0 watchpack: 2.5.1 - webpack: 5.105.3(esbuild@0.24.2) + webpack: 5.105.4(esbuild@0.24.2) optionalDependencies: - '@vue/compiler-sfc': 3.5.29 + '@vue/compiler-sfc': 3.5.30 w3c-xmlserializer@5.0.0: dependencies: @@ -16014,7 +16013,7 @@ snapshots: webpack-sources@3.3.4: {} - webpack@5.105.3(esbuild@0.24.2): + webpack@5.105.4(esbuild@0.24.2): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -16026,7 +16025,7 @@ snapshots: acorn-import-phases: 1.0.4(acorn@8.16.0) browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.20.0 + enhanced-resolve: 5.20.1 es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -16038,7 +16037,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(esbuild@0.24.2)(webpack@5.105.3(esbuild@0.24.2)) + terser-webpack-plugin: 5.4.0(esbuild@0.24.2)(webpack@5.105.4(esbuild@0.24.2)) watchpack: 2.5.1 webpack-sources: 3.3.4 transitivePeerDependencies: