Skip to content

Commit cebc183

Browse files
authored
chore: restrict project creation and renaming to 240 characters or less (#7926)
* chore: implemented systemio guard for project create and rename * fix: improving display of 239 character project name * fix: display truncated project name * fix: displaying project name in delete modal * fix: auto fixes * fix: typo
1 parent 6e9c46a commit cebc183

File tree

5 files changed

+74
-20
lines changed

5 files changed

+74
-20
lines changed

src/components/ProjectCard/ProjectCard.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ function ProjectCard({
8888
}
8989
}, [isEditing, inputRef.current])
9090

91+
const projectName = project.name?.replace(FILE_EXT, '')
92+
9193
return (
9294
<li
9395
{...props}
@@ -127,10 +129,11 @@ function ProjectCard({
127129
/>
128130
) : (
129131
<h3
130-
className="font-sans relative z-0 p-2"
132+
className="font-sans relative z-0 p-2 truncate"
131133
data-testid="project-title"
134+
title={projectName}
132135
>
133-
{project.name?.replace(FILE_EXT, '')}
136+
{projectName}
134137
</h3>
135138
)}
136139
{project.readWriteAccess && (
@@ -208,11 +211,11 @@ function ProjectCard({
208211
}, reportRejection)}
209212
onDismiss={() => setIsConfirmingDelete(false)}
210213
>
211-
<p className="my-4">
214+
<p className="my-4 text-wrap break-words">
212215
This will permanently delete "{project.name || 'this file'}
213216
".
214217
</p>
215-
<p className="my-4">
218+
<p className="my-4 text-wrap break-words">
216219
Are you sure you want to delete "{project.name || 'this file'}
217220
"? This action cannot be undone.
218221
</p>

src/components/ProjectSidebarMenu.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -234,33 +234,43 @@ function ProjectMenuPopover({
234234
]
235235
)
236236

237+
// Breadcrumb for project and file
238+
const breadCrumb = {
239+
projectName: project?.name || '',
240+
sep: '/',
241+
filename:
242+
isDesktop() && file?.name
243+
? file.name.slice(file.name.lastIndexOf(window.electron.path.sep) + 1)
244+
: APP_NAME,
245+
}
246+
const breadCrumbTooltip = `${breadCrumb.projectName}${breadCrumb.sep}${breadCrumb.filename}`
247+
237248
return (
238249
<Popover className="relative">
239250
<Popover.Button
240251
className="gap-1 rounded-sm mr-auto max-h-min min-w-max border-0 py-1 px-2 flex items-center focus-visible:outline-appForeground dark:hover:bg-chalkboard-90"
241252
data-testid="project-sidebar-toggle"
242253
>
243-
<div className="flex items-baseline py-0.5 text-sm gap-1">
254+
<div
255+
className="flex items-baseline py-0.5 text-sm gap-1"
256+
title={breadCrumbTooltip}
257+
>
244258
{isDesktop() && project?.name && (
245259
<>
246260
<span
247-
className="hidden whitespace-nowrap md:block"
261+
className="hidden whitespace-nowrap md:block max-w-80 truncate"
248262
data-testid="app-header-project-name"
249263
>
250-
{project.name}
264+
{breadCrumb.projectName}
251265
</span>
252-
<span className="hidden md:block">/</span>
266+
<span className="hidden md:block">{breadCrumb.sep}</span>
253267
</>
254268
)}
255269
<span
256270
className="text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap"
257271
data-testid="app-header-file-name"
258272
>
259-
{isDesktop() && file?.name
260-
? file.name.slice(
261-
file.name.lastIndexOf(window.electron.path.sep) + 1
262-
)
263-
: APP_NAME}
273+
{breadCrumb.filename}
264274
</span>
265275
</div>
266276
<CustomIcon

src/lib/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,5 @@ export type EnvironmentConfigurationRuntime = {
247247
}
248248

249249
export const ENVIRONMENT_CONFIGURATION_FOLDER = 'envs'
250+
251+
export const MAX_PROJECT_NAME_LENGTH = 240

src/machines/systemIO/systemIOMachine.ts

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { DEFAULT_PROJECT_NAME } from '@src/lib/constants'
1+
import {
2+
DEFAULT_PROJECT_NAME,
3+
MAX_PROJECT_NAME_LENGTH,
4+
} from '@src/lib/constants'
25
import type { Project } from '@src/lib/project'
36
import type {
47
SystemIOContext,
@@ -9,6 +12,7 @@ import {
912
SystemIOMachineActions,
1013
SystemIOMachineActors,
1114
SystemIOMachineEvents,
15+
SystemIOMachineGuards,
1216
SystemIOMachineStates,
1317
} from '@src/machines/systemIO/utils'
1418
import toast from 'react-hot-toast'
@@ -142,6 +146,19 @@ export const systemIOMachine = setup({
142146
}
143147
},
144148
},
149+
guards: {
150+
[SystemIOMachineGuards.projectNameIsValidLength]: ({
151+
context,
152+
event,
153+
}): boolean => {
154+
assertEvent(event, [
155+
SystemIOMachineEvents.createProject,
156+
SystemIOMachineEvents.renameProject,
157+
])
158+
const { requestedProjectName } = event.data
159+
return requestedProjectName.length <= MAX_PROJECT_NAME_LENGTH
160+
},
161+
},
145162
actions: {
146163
[SystemIOMachineActions.setFolders]: assign({
147164
folders: ({ event }) => {
@@ -222,6 +239,11 @@ export const systemIOMachine = setup({
222239
return { project: event.output.name }
223240
},
224241
}),
242+
[SystemIOMachineActions.toastProjectNameTooLong]: () => {
243+
toast.error(
244+
`Project name is too long, must be less than or equal to ${MAX_PROJECT_NAME_LENGTH} characters`
245+
)
246+
},
225247
},
226248
actors: {
227249
[SystemIOMachineActors.readFoldersFromProjectDirectory]: fromPromise(
@@ -414,12 +436,24 @@ export const systemIOMachine = setup({
414436
[SystemIOMachineEvents.navigateToFile]: {
415437
actions: [SystemIOMachineActions.setRequestedFileName],
416438
},
417-
[SystemIOMachineEvents.createProject]: {
418-
target: SystemIOMachineStates.creatingProject,
419-
},
420-
[SystemIOMachineEvents.renameProject]: {
421-
target: SystemIOMachineStates.renamingProject,
422-
},
439+
[SystemIOMachineEvents.createProject]: [
440+
{
441+
guard: SystemIOMachineGuards.projectNameIsValidLength,
442+
target: SystemIOMachineStates.creatingProject,
443+
},
444+
{
445+
actions: [SystemIOMachineActions.toastProjectNameTooLong],
446+
},
447+
],
448+
[SystemIOMachineEvents.renameProject]: [
449+
{
450+
target: SystemIOMachineStates.renamingProject,
451+
guard: SystemIOMachineGuards.projectNameIsValidLength,
452+
},
453+
{
454+
actions: [SystemIOMachineActions.toastProjectNameTooLong],
455+
},
456+
],
423457
[SystemIOMachineEvents.deleteProject]: {
424458
target: SystemIOMachineStates.deletingProject,
425459
},

src/machines/systemIO/utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ export enum SystemIOMachineActions {
7474
setReadWriteProjectDirectory = 'set read write project directory',
7575
setRequestedTextToCadGeneration = 'set requested text to cad generation',
7676
setLastProjectDeleteRequest = 'set last project delete request',
77+
toastProjectNameTooLong = 'toast project name too long',
78+
}
79+
80+
export enum SystemIOMachineGuards {
81+
projectNameIsValidLength = 'project name is valid length',
7782
}
7883

7984
export const NO_PROJECT_DIRECTORY = ''

0 commit comments

Comments
 (0)