From 0504f671e170a28b0ff5610d72d5af94f84dc0e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Mon, 23 Mar 2026 18:01:00 +0100 Subject: [PATCH 01/11] nicolium: add missing rel=noopener noreferrer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/nicolium/src/components/media/audio.tsx | 1 + packages/nicolium/src/components/media/video.tsx | 1 + packages/nicolium/src/components/ui/icon-button.tsx | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nicolium/src/components/media/audio.tsx b/packages/nicolium/src/components/media/audio.tsx index 15705828b..87a69933a 100644 --- a/packages/nicolium/src/components/media/audio.tsx +++ b/packages/nicolium/src/components/media/audio.tsx @@ -613,6 +613,7 @@ const Audio: React.FC = (props) => { href={src} download target='_blank' + rel='noopener noreferrer' > diff --git a/packages/nicolium/src/components/media/video.tsx b/packages/nicolium/src/components/media/video.tsx index e8c82527b..94961b0fa 100644 --- a/packages/nicolium/src/components/media/video.tsx +++ b/packages/nicolium/src/components/media/video.tsx @@ -621,6 +621,7 @@ const Video: React.FC = ({ href={src} download target='_blank' + rel='noopener noreferrer' > diff --git a/packages/nicolium/src/components/ui/icon-button.tsx b/packages/nicolium/src/components/ui/icon-button.tsx index f2bb33db8..f4974860d 100644 --- a/packages/nicolium/src/components/ui/icon-button.tsx +++ b/packages/nicolium/src/components/ui/icon-button.tsx @@ -33,7 +33,7 @@ const IconButton = React.forwardRef( className={clsx('⁂-icon-button', `⁂-icon-button--${theme}`, className)} {...filteredProps} data-testid={filteredProps['data-testid'] ?? 'icon-button'} - {...(props.href ? { target: '_blank' } : {})} + {...(props.href ? { target: '_blank', rel: 'noopener noreferrer' } : {})} > From 52fcb72c1dd43cce09510ac3c142fee6bce13eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Wed, 25 Mar 2026 22:10:38 +0100 Subject: [PATCH 02/11] pl-api: fix regression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/pl-api/lib/request.ts | 5 +++-- packages/pl-api/typedoc.config.mjs | 9 +-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/pl-api/lib/request.ts b/packages/pl-api/lib/request.ts index 5017708cc..d86574d33 100644 --- a/packages/pl-api/lib/request.ts +++ b/packages/pl-api/lib/request.ts @@ -99,7 +99,7 @@ function request( params, onUploadProgress, signal, - contentType = 'application/json', + contentType, formData, idempotencyKey, }: RequestBody = {}, @@ -113,7 +113,8 @@ function request( else if (this.accessToken) headers.set('Authorization', `Bearer ${this.accessToken}`); else if (this.customAuthorizationToken) headers.set('Authorization', this.customAuthorizationToken); - if (!formData) headers.set('Content-Type', contentType); + if ((!formData && body) || contentType) + headers.set('Content-Type', contentType || 'application/json'); if (idempotencyKey) headers.set('Idempotency-Key', idempotencyKey); body = body && formData ? serialize(body, { indices: true }) : JSON.stringify(body); diff --git a/packages/pl-api/typedoc.config.mjs b/packages/pl-api/typedoc.config.mjs index f7e8f0a00..99a4443e2 100644 --- a/packages/pl-api/typedoc.config.mjs +++ b/packages/pl-api/typedoc.config.mjs @@ -9,14 +9,7 @@ const config = { }, categorizeByGroup: true, sort: ['kind', 'alphabetical'], - kindSortOrder: [ - 'Function', - 'Class', - 'Interface', - 'TypeAlias', - 'Variable', - 'Enum', - ], + kindSortOrder: ['Function', 'Class', 'Interface', 'TypeAlias', 'Variable', 'Enum'], intentionallyNotExported: [ 'CreateStatusOptionalParams', 'CreateStatusWithContent', From 11902eb7bb9e2d368584dbbc302e1dfd0074ab01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Thu, 26 Mar 2026 00:01:18 +0100 Subject: [PATCH 03/11] nicolium: fix hasDescendants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/nicolium/src/features/status/components/thread.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/nicolium/src/features/status/components/thread.tsx b/packages/nicolium/src/features/status/components/thread.tsx index 0c223e91d..366ffb0b7 100644 --- a/packages/nicolium/src/features/status/components/thread.tsx +++ b/packages/nicolium/src/features/status/components/thread.tsx @@ -243,7 +243,7 @@ const Thread = ({ const renderChildren = (list: Array) => list.map((id) => { - if (id === status.id) + if (id === status.id) { return (
{deleted ? ( @@ -289,6 +289,7 @@ const Thread = ({ )}
); + } if (id.endsWith('-tombstone')) { return renderTombstone(id); @@ -328,7 +329,7 @@ const Thread = ({ [status.id], ); - const hasDescendants = thread.length > statusIndex; + const hasDescendants = thread.length > statusIndex + 1; type HotkeyHandlers = { [key: string]: (keyEvent?: KeyboardEvent) => void }; From 7d787575dd0863eefbeb9e3b59a70bf006a78ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Thu, 26 Mar 2026 00:12:53 +0100 Subject: [PATCH 04/11] nicolium: add a CORS warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/nicolium/src/locales/en.json | 3 ++- .../src/pages/auth/components/external-login-form.tsx | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/nicolium/src/locales/en.json b/packages/nicolium/src/locales/en.json index 3a10ad624..9ba4f9e8e 100644 --- a/packages/nicolium/src/locales/en.json +++ b/packages/nicolium/src/locales/en.json @@ -1416,7 +1416,8 @@ "login.otp_log_in.fail": "Invalid code, please try again.", "login.reset_password_hint": "Trouble logging in?", "login.sign_in": "Sign in", - "login_external.errors.instance_fail": "The instance returned an error.", + "login_external.errors.cors_fail": "Connection failed, likely due to CORS configuration. Is the instance configured to allow logins from other origins?", + "login_external.errors.instance_fail": "The instance returned an error. Is the URL correct?", "login_external.errors.network_fail": "Connection failed. Is a browser extension blocking it?", "login_form.divider": "or", "login_form.external": "Sign in from remote instance", diff --git a/packages/nicolium/src/pages/auth/components/external-login-form.tsx b/packages/nicolium/src/pages/auth/components/external-login-form.tsx index a8402458e..3e93c2eb6 100644 --- a/packages/nicolium/src/pages/auth/components/external-login-form.tsx +++ b/packages/nicolium/src/pages/auth/components/external-login-form.tsx @@ -13,7 +13,12 @@ const messages = defineMessages({ instancePlaceholder: { id: 'login.fields.instance_placeholder', defaultMessage: 'example.com' }, instanceFailed: { id: 'login_external.errors.instance_fail', - defaultMessage: 'The instance returned an error.', + defaultMessage: 'The instance returned an error. Is the URL correct?', + }, + corsFailed: { + id: 'login_external.errors.cors_fail', + defaultMessage: + 'Connection failed, likely due to CORS configuration. Is the instance configured to allow logins from other origins?', }, networkFailed: { id: 'login_external.errors.network_fail', @@ -47,8 +52,10 @@ const ExternalLoginForm: React.FC = () => { console.error(error); const status = error.response?.status; - if (status) { + if (status || !error.message) { toast.error(intl.formatMessage(messages.instanceFailed)); + } else if (error.message === 'NetworkError when attempting to fetch resource.') { + toast.error(intl.formatMessage(messages.corsFailed)); } else if (!status && ['Network request failed', 'Timeout'].includes(error.message)) { toast.error(intl.formatMessage(messages.networkFailed)); } From 67dc2249948cdfbe8cdd66b3e6f9b00e1db6089f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Thu, 26 Mar 2026 00:18:42 +0100 Subject: [PATCH 05/11] Bump version, upgrade changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/nicolium/CHANGELOG.md | 16 ++++++++++++++++ packages/nicolium/package.json | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/nicolium/CHANGELOG.md b/packages/nicolium/CHANGELOG.md index 19699d1e8..b7be44c3b 100644 --- a/packages/nicolium/CHANGELOG.md +++ b/packages/nicolium/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## v0.1.1 + +### Added +- Missing ARIA attributes and other minor accessibility improvements. + +### Changed +- Continued work on migrating styles from TailwindCSS. +- Some files were moved to different locations. +- Added a warning related to CORS configuration when signing in to an external instance. + +### Fixed + +- Removed `console.log` statement accidentally left in the release code. +- Requests without body do not get `Content-Type: application/json` appended by default, which fixes a bug with GoToSocial. +- Last status in a thread view, if expanded, no longer gets bottom padding. + ## v0.1.0 > 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. diff --git a/packages/nicolium/package.json b/packages/nicolium/package.json index e7783df0f..4bdd8cd4d 100644 --- a/packages/nicolium/package.json +++ b/packages/nicolium/package.json @@ -1,7 +1,7 @@ { "name": "nicolium", "displayName": "Nicolium", - "version": "0.1.0", + "version": "0.1.1", "description": "Mastodon-compatible social media front-end", "keywords": [ "fediverse", From 006f5a317d67964dfbbb46a3e313b4433024b211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Thu, 26 Mar 2026 12:31:11 +0100 Subject: [PATCH 06/11] nicolium: show avatars of repost authors in queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/nicolium/CHANGELOG.md | 2 ++ packages/nicolium/src/stores/timelines.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nicolium/CHANGELOG.md b/packages/nicolium/CHANGELOG.md index b7be44c3b..648499f85 100644 --- a/packages/nicolium/CHANGELOG.md +++ b/packages/nicolium/CHANGELOG.md @@ -3,9 +3,11 @@ ## v0.1.1 ### Added + - Missing ARIA attributes and other minor accessibility improvements. ### Changed + - Continued work on migrating styles from TailwindCSS. - Some files were moved to different locations. - Added a warning related to CORS configuration when signing in to an external instance. diff --git a/packages/nicolium/src/stores/timelines.ts b/packages/nicolium/src/stores/timelines.ts index e9a67f27f..d5c97faaf 100644 --- a/packages/nicolium/src/stores/timelines.ts +++ b/packages/nicolium/src/stores/timelines.ts @@ -222,7 +222,7 @@ const useTimelinesStore = create()( timeline.queuedEntries.unshift(status); timeline.queuedCount += 1; - timeline.queuedAccountIds.unshift(status.account.id); + timeline.queuedAccountIds.unshift((status.reblog || status).account.id); }); }, deleteStatus: (statusId) => { From dcb24b5ebc49a8725cc45bbbe55d50d253cb93d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Fri, 27 Mar 2026 13:10:00 +0100 Subject: [PATCH 07/11] pl-api: update comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/pl-api/lib/utils/accounts.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pl-api/lib/utils/accounts.ts b/packages/pl-api/lib/utils/accounts.ts index 83cd29322..b5e13cb19 100644 --- a/packages/pl-api/lib/utils/accounts.ts +++ b/packages/pl-api/lib/utils/accounts.ts @@ -1,7 +1,7 @@ /** Default header filenames from various backends */ const DEFAULT_HEADERS: Array = [ '/assets/default_header.webp', // GoToSocial - '/headers/original/missing.png', // Mastodon + '/headers/original/missing.png', // Hollo, Mastodon '/api/v1/accounts/identicon', // Mitra /\/static\/img\/missing\.[a-z0-9]+\.png$/, // NeoDB '/storage/headers/missing.png', // Pixelfed @@ -19,7 +19,7 @@ const isDefaultHeader = (url: string = '') => /** Default avatar filenames from various backends */ const DEFAULT_AVATARS: Array = [ /\/assets\/default_avatars\/GoToSocial_icon[1-6]\.webp$/, // GoToSocial - '/avatars/original/missing.png', // Mastodon + '/avatars/original/missing.png', // Hollo, Mastodon '/api/v1/accounts/identicon', // Mitra '/s/img/avatar.svg', // NeoDB '/avatars/default.jpg', // Pixelfed From 9f4d5afaa652d6c8df6c9d9c642b9e2458089eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Fri, 27 Mar 2026 13:39:29 +0100 Subject: [PATCH 08/11] pl-api: update feature definitions for Hollo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/pl-api/lib/features.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/pl-api/lib/features.ts b/packages/pl-api/lib/features.ts index a9fd76be7..15de99ba4 100644 --- a/packages/pl-api/lib/features.ts +++ b/packages/pl-api/lib/features.ts @@ -906,6 +906,7 @@ const getFeatures = (instance: Instance) => { v.software === AKKOMA, v.software === FIREFISH, v.software === GOTOSOCIAL, + v.software === HOLLO, v.software === MASTODON, v.software === MITRA, v.software === PLEROMA, @@ -1390,6 +1391,7 @@ const getFeatures = (instance: Instance) => { polls: any([ v.software === FIREFISH, v.software === GOTOSOCIAL, + v.software === HOLLO, v.software === ICESHRIMP, v.software === ICESHRIMP_NET, v.software === MASTODON, @@ -1407,6 +1409,7 @@ const getFeatures = (instance: Instance) => { postLanguages: any([ v.software === AKKOMA, v.software === GOTOSOCIAL, + v.software === HOLLO, v.software === MASTODON, v.software === MITRA && gte(v.version, '3.23.0'), v.software === PLEROMA && gte(v.version, '2.9.0'), @@ -1473,6 +1476,7 @@ const getFeatures = (instance: Instance) => { v.software === FIREFISH, v.software === FRIENDICA, v.software === GOTOSOCIAL, + v.software === HOLLO, v.software === ICESHRIMP, v.software === ICESHRIMP_NET, v.software === MASTODON, From 28039ae7cc82cd6c6350be0e7a58dfa549dbb874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Fri, 27 Mar 2026 14:34:02 +0100 Subject: [PATCH 09/11] nicolium: fix polling behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/nicolium/src/queries/timelines/use-timeline.ts | 2 +- packages/nicolium/src/stores/timelines.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/nicolium/src/queries/timelines/use-timeline.ts b/packages/nicolium/src/queries/timelines/use-timeline.ts index a9d6b5587..5ee5aada7 100644 --- a/packages/nicolium/src/queries/timelines/use-timeline.ts +++ b/packages/nicolium/src/queries/timelines/use-timeline.ts @@ -60,7 +60,7 @@ const useTimeline = ( const poll = async () => { const sinceId = - useTimelinesStore.getState().timelines[timelineId]?.queuedEntries[0]?.id ?? + useTimelinesStore.getState().timelines[timelineId]?.newestStatusId ?? newestStatusId.current; if (!sinceId) return; diff --git a/packages/nicolium/src/stores/timelines.ts b/packages/nicolium/src/stores/timelines.ts index d5c97faaf..28c42e3e0 100644 --- a/packages/nicolium/src/stores/timelines.ts +++ b/packages/nicolium/src/stores/timelines.ts @@ -46,6 +46,7 @@ interface TimelineData { isError: boolean; hasNextPage: boolean; oldestStatusId?: string; + newestStatusId?: string; } interface State { @@ -202,6 +203,9 @@ const useTimelinesStore = create()( } timeline.isPending = false; timeline.isFetching = false; + if ((initialFetch || restoring) && statuses.length > 0) { + timeline.newestStatusId = statuses[0].id; + } if (typeof hasMore === 'boolean') { timeline.hasNextPage = hasMore; const oldestStatus = statuses.at(-1); @@ -220,6 +224,9 @@ const useTimelinesStore = create()( ) return; + if (!timeline.newestStatusId || timeline.newestStatusId.localeCompare(status.id) < 0) { + timeline.newestStatusId = status.id; + } timeline.queuedEntries.unshift(status); timeline.queuedCount += 1; timeline.queuedAccountIds.unshift((status.reblog || status).account.id); @@ -269,6 +276,7 @@ const useTimelinesStore = create()( const processedEntries = processPage(timeline.queuedEntries); + timeline.newestStatusId = timeline.queuedEntries.toSorted().at(-1)!.id; timeline.entries.unshift(...processedEntries); timeline.queuedEntries = []; timeline.queuedCount = 0; From 13e69f76589d85581a134d9a082ea076c7ba80e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Fri, 27 Mar 2026 15:06:00 +0100 Subject: [PATCH 10/11] nicolium: do not fetch notifications too fast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/nicolium/src/components/helmet.tsx | 14 ++++---------- packages/nicolium/src/components/ui/column.tsx | 17 ++++++++--------- packages/nicolium/src/init/nicolium-head.tsx | 6 ++++-- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/packages/nicolium/src/components/helmet.tsx b/packages/nicolium/src/components/helmet.tsx index f9dac5bd0..9bcd6f166 100644 --- a/packages/nicolium/src/components/helmet.tsx +++ b/packages/nicolium/src/components/helmet.tsx @@ -10,12 +10,11 @@ import FaviconService from '@/utils/favicon-service'; FaviconService.initFaviconService(); -interface IHelmet { +interface IHeadTitle { title?: string; - children?: React.ReactNode; } -const Helmet: React.FC = ({ title, children }) => { +const HeadTitle: React.FC = ({ title }) => { const instance = useInstance(); const { unreadChatsCount } = useStatContext(); const { data: awaitingApprovalCount = 0 } = usePendingUsersCount(); @@ -45,12 +44,7 @@ const Helmet: React.FC = ({ title, children }) => { ? addCounter(`${title} | ${instance.title}`) : addCounter(instance.title); - return ( - <> - {formattedTitle} - {children} - - ); + return {formattedTitle}; }; -export { Helmet as default }; +export { HeadTitle as default }; diff --git a/packages/nicolium/src/components/ui/column.tsx b/packages/nicolium/src/components/ui/column.tsx index 60cf9bdf2..273d61397 100644 --- a/packages/nicolium/src/components/ui/column.tsx +++ b/packages/nicolium/src/components/ui/column.tsx @@ -3,7 +3,7 @@ import clsx from 'clsx'; import throttle from 'lodash/throttle'; import React, { useCallback, useEffect, useState } from 'react'; -import Helmet from '@/components/helmet'; +import HeadTitle from '@/components/helmet'; import { useFrontendConfig } from '@/hooks/use-frontend-config'; import { Card, CardBody, CardHeader, CardTitle, type CardSizes } from './card'; @@ -125,14 +125,13 @@ const Column: React.FC = (props): React.JSX.Element => { variant={transparent ? undefined : 'rounded'} className={clsx('⁂-column', className)} > - - {frontendConfig.appleAppId && ( - - )} - + + {frontendConfig.appleAppId && ( + + )} {withHeader && ( import('@/components/helmet')); +const HeadTitle = React.lazy(() => import('@/components/helmet')); const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)'); @@ -30,6 +31,7 @@ const NicoliumHead = () => { const theme = useTheme(); const [wcoVisible, setWcoVisible] = React.useState(false); const [wcoRight, setWcoRight] = React.useState(false); + const instanceFetched = useInstanceStore((state) => state.fetched); const withModals = useHasModals(); @@ -96,7 +98,7 @@ const NicoliumHead = () => { return ( <> - + {instanceFetched && } {`:root { ${themeCss} }`} {['dark', 'black'].includes(theme) && ( From 9b4ff014a992af1d8cbabdd7fc5744c2a3fd6e85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Fri, 27 Mar 2026 15:10:29 +0100 Subject: [PATCH 11/11] Nicolium: fix unread notification count on Hollo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/nicolium/src/utils/comparators.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/nicolium/src/utils/comparators.ts b/packages/nicolium/src/utils/comparators.ts index aaa43e21d..b0b4f5fc5 100644 --- a/packages/nicolium/src/utils/comparators.ts +++ b/packages/nicolium/src/utils/comparators.ts @@ -13,6 +13,10 @@ const compareId = (id1: string, id2: string) => { } if (id1.length === id2.length) { return id1 > id2 ? 1 : -1; + } else if (id1.includes('/') && id2.includes('/')) { + // Hollo notification IDs consist of a date, notification type and a UUID. + // If both IDs start with a date, we can compare just the date part. + return id1.split('/')[0] > id2.split('/')[0] ? 1 : -1; } else { return id1.length > id2.length ? 1 : -1; }