From 78b9de869fc477d20ed480dd657e0f972cf9ee95 Mon Sep 17 00:00:00 2001
From: Henry <30578846+hjball@users.noreply.github.com>
Date: Mon, 2 Feb 2026 22:25:54 +0000
Subject: [PATCH] fix: prevent validation errors in workspace/card queries
(#356)
* fix: prevent workspace.byId validation errors when publicId is empty
* fix: add keys to shortcut elements
* fix: prevent card.byId validation errors when cardPublicId is empty
* chore: remove invalid config option from next.config.js
---
apps/web/next.config.js | 7 -----
apps/web/src/components/SideNavigation.tsx | 7 +++--
apps/web/src/providers/keyboard-shortcuts.tsx | 29 +++++++++++++++----
apps/web/src/utils/cardInvalidation.ts | 2 ++
.../views/card/components/ActivityList.tsx | 2 +-
apps/web/src/views/card/index.tsx | 14 +++++----
apps/web/src/views/members/index.tsx | 10 ++++---
apps/web/src/views/public/board/CardModal.tsx | 2 +-
.../views/settings/PermissionsSettings.tsx | 8 +++--
.../src/views/settings/WorkspaceSettings.tsx | 7 +++--
.../UpdateWorkspaceEmailVisibilityForm.tsx | 8 +++--
11 files changed, 60 insertions(+), 36 deletions(-)
diff --git a/apps/web/next.config.js b/apps/web/next.config.js
index 03003b73..ebdf42fe 100644
--- a/apps/web/next.config.js
+++ b/apps/web/next.config.js
@@ -53,7 +53,6 @@ const config = {
{
protocol: 'https',
hostname: 'cdn.discordapp.com',
- pathname: '/avatars/**',
},
];
@@ -95,12 +94,6 @@ const config = {
swcPlugins: [["@lingui/swc-plugin", {}]],
},
- api: {
- bodyParser: {
- sizeLimit: env("NEXT_API_BODY_SIZE_LIMIT") || '1mb',
- },
- },
-
async rewrites() {
return [
{
diff --git a/apps/web/src/components/SideNavigation.tsx b/apps/web/src/components/SideNavigation.tsx
index 76a3bfe1..ce032e69 100644
--- a/apps/web/src/components/SideNavigation.tsx
+++ b/apps/web/src/components/SideNavigation.tsx
@@ -55,9 +55,10 @@ export default function SideNavigation({
const [isInitialised, setIsInitialised] = useState(false);
const { openModal } = useModal();
- const { data: workspaceData } = api.workspace.byId.useQuery({
- workspacePublicId: workspace.publicId,
- });
+ const { data: workspaceData } = api.workspace.byId.useQuery(
+ { workspacePublicId: workspace.publicId },
+ { enabled: !!workspace.publicId && workspace.publicId.length >= 12 },
+ );
const subscriptions = workspaceData?.subscriptions as
| Subscription[]
diff --git a/apps/web/src/providers/keyboard-shortcuts.tsx b/apps/web/src/providers/keyboard-shortcuts.tsx
index 089dea32..0c08a043 100644
--- a/apps/web/src/providers/keyboard-shortcuts.tsx
+++ b/apps/web/src/providers/keyboard-shortcuts.tsx
@@ -1,4 +1,5 @@
import type { ReactNode } from "react";
+import React from "react";
import {
Dialog,
DialogBackdrop,
@@ -464,19 +465,37 @@ function FormattedShortcut({ shortcut }: { shortcut: KeyboardShortcut }) {
? stroke.modifiers.map(stringifyModifier)
: [];
- modifierStrings.forEach((mod) => {
- parts.push({mod});
+ modifierStrings.forEach((mod, index) => {
+ parts.push(
+
+ {mod}
+ ,
+ );
});
- parts.push({stroke.key.toUpperCase()});
+ parts.push(
+
+ {stroke.key.toUpperCase()}
+ ,
+ );
return parts;
};
if (shortcut.type === "SEQUENCE") {
const parts: ReactNode[] = [];
- shortcut.strokes.forEach((stroke) => {
- parts.push(...formatStroke(stroke));
+ shortcut.strokes.forEach((stroke, strokeIndex) => {
+ const strokeParts = formatStroke(stroke);
+ // Add stroke index to keys to ensure uniqueness across multiple strokes
+ const keyedParts = strokeParts.map((part, partIndex) => {
+ if (React.isValidElement(part)) {
+ return React.cloneElement(part, {
+ key: `stroke-${strokeIndex}-${part.key || partIndex}`,
+ });
+ }
+ return part;
+ });
+ parts.push(...keyedParts);
});
return {parts};
}
diff --git a/apps/web/src/utils/cardInvalidation.ts b/apps/web/src/utils/cardInvalidation.ts
index ce56ffe9..0e8fc7ab 100644
--- a/apps/web/src/utils/cardInvalidation.ts
+++ b/apps/web/src/utils/cardInvalidation.ts
@@ -8,6 +8,8 @@ export async function invalidateCard(
utils: ReturnType,
cardPublicId: string,
) {
+ if (!cardPublicId || cardPublicId.length < 12) return;
+
await Promise.all([
utils.card.byId.invalidate({ cardPublicId }),
utils.card.getActivities.invalidate({ cardPublicId }),
diff --git a/apps/web/src/views/card/components/ActivityList.tsx b/apps/web/src/views/card/components/ActivityList.tsx
index 5102a927..95fdfcaf 100644
--- a/apps/web/src/views/card/components/ActivityList.tsx
+++ b/apps/web/src/views/card/components/ActivityList.tsx
@@ -363,7 +363,7 @@ const ActivityList = ({
limit: ACTIVITIES_PAGE_SIZE,
},
{
- enabled: !!cardPublicId,
+ enabled: !!cardPublicId && cardPublicId.length >= 12,
},
);
diff --git a/apps/web/src/views/card/index.tsx b/apps/web/src/views/card/index.tsx
index 7a06e99d..3425f808 100644
--- a/apps/web/src/views/card/index.tsx
+++ b/apps/web/src/views/card/index.tsx
@@ -53,9 +53,10 @@ export function CardRightPanel({ isTemplate }: { isTemplate?: boolean }) {
? router.query.cardId[0]
: router.query.cardId;
- const { data: card } = api.card.byId.useQuery({
- cardPublicId: cardId ?? "",
- });
+ const { data: card } = api.card.byId.useQuery(
+ { cardPublicId: cardId ?? "" },
+ { enabled: !!cardId && cardId.length >= 12 },
+ );
const isCreator = card?.createdBy && session?.user.id === card.createdBy;
const canEdit = canEditCard || isCreator;
@@ -184,9 +185,10 @@ export default function CardPage({ isTemplate }: { isTemplate?: boolean }) {
? router.query.cardId[0]
: router.query.cardId;
- const { data: card, isLoading } = api.card.byId.useQuery({
- cardPublicId: cardId ?? "",
- });
+ const { data: card, isLoading } = api.card.byId.useQuery(
+ { cardPublicId: cardId ?? "" },
+ { enabled: !!cardId && cardId.length >= 12 },
+ );
const isCreator = card?.createdBy && session?.user.id === card.createdBy;
const canEdit = canEditCard || isCreator;
diff --git a/apps/web/src/views/members/index.tsx b/apps/web/src/views/members/index.tsx
index ca91c0d4..035be336 100644
--- a/apps/web/src/views/members/index.tsx
+++ b/apps/web/src/views/members/index.tsx
@@ -37,7 +37,7 @@ export default function MembersPage() {
const { data, isLoading } = api.workspace.byId.useQuery(
{ workspacePublicId: workspace.publicId },
- // { enabled: workspace?.publicId ? true : false },
+ { enabled: !!workspace.publicId && workspace.publicId.length >= 12 },
);
const { data: session } = authClient.useSession();
@@ -48,9 +48,11 @@ export default function MembersPage() {
const updateRoleMutation = api.member.updateRole.useMutation({
onSuccess: async () => {
- await utils.workspace.byId.invalidate({
- workspacePublicId: workspace.publicId,
- });
+ if (workspace.publicId && workspace.publicId.length >= 12) {
+ await utils.workspace.byId.invalidate({
+ workspacePublicId: workspace.publicId,
+ });
+ }
showPopup({
header: t`Role updated`,
diff --git a/apps/web/src/views/public/board/CardModal.tsx b/apps/web/src/views/public/board/CardModal.tsx
index 6e6ef963..35e65a61 100644
--- a/apps/web/src/views/public/board/CardModal.tsx
+++ b/apps/web/src/views/public/board/CardModal.tsx
@@ -32,7 +32,7 @@ export function CardModal({
cardPublicId: cardPublicId ?? "",
},
{
- enabled: isOpen && !!cardPublicId,
+ enabled: isOpen && !!cardPublicId && cardPublicId.length >= 12,
},
);
diff --git a/apps/web/src/views/settings/PermissionsSettings.tsx b/apps/web/src/views/settings/PermissionsSettings.tsx
index 10e98750..66647098 100644
--- a/apps/web/src/views/settings/PermissionsSettings.tsx
+++ b/apps/web/src/views/settings/PermissionsSettings.tsx
@@ -28,9 +28,11 @@ export default function PermissionsSettings() {
});
// Refresh any relevant workspace data
- await utils.workspace.byId.invalidate({
- workspacePublicId: workspace.publicId,
- });
+ if (workspace.publicId && workspace.publicId.length >= 12) {
+ await utils.workspace.byId.invalidate({
+ workspacePublicId: workspace.publicId,
+ });
+ }
},
onError: () => {
showPopup({
diff --git a/apps/web/src/views/settings/WorkspaceSettings.tsx b/apps/web/src/views/settings/WorkspaceSettings.tsx
index 175eeb84..008f89d7 100644
--- a/apps/web/src/views/settings/WorkspaceSettings.tsx
+++ b/apps/web/src/views/settings/WorkspaceSettings.tsx
@@ -31,9 +31,10 @@ export default function WorkspaceSettings() {
const { data } = api.user.getUser.useQuery();
const [hasOpenedUpgradeModal, setHasOpenedUpgradeModal] = useState(false);
- const { data: workspaceData } = api.workspace.byId.useQuery({
- workspacePublicId: workspace.publicId,
- });
+ const { data: workspaceData } = api.workspace.byId.useQuery(
+ { workspacePublicId: workspace.publicId },
+ { enabled: !!workspace.publicId && workspace.publicId.length >= 12 },
+ );
const subscriptions = workspaceData?.subscriptions as
| Subscription[]
diff --git a/apps/web/src/views/settings/components/UpdateWorkspaceEmailVisibilityForm.tsx b/apps/web/src/views/settings/components/UpdateWorkspaceEmailVisibilityForm.tsx
index 1766cefb..2c783f0e 100644
--- a/apps/web/src/views/settings/components/UpdateWorkspaceEmailVisibilityForm.tsx
+++ b/apps/web/src/views/settings/components/UpdateWorkspaceEmailVisibilityForm.tsx
@@ -22,9 +22,11 @@ export default function UpdateWorkspaceEmailVisibilityForm({
const updateWorkspace = api.workspace.update.useMutation({
onSuccess: () => {
- void utils.workspace.byId.invalidate({
- workspacePublicId,
- });
+ if (workspacePublicId && workspacePublicId.length >= 12) {
+ void utils.workspace.byId.invalidate({
+ workspacePublicId,
+ });
+ }
},
});