diff --git a/next.config.js b/next.config.js index 1bdc97c7..5373e6a7 100644 --- a/next.config.js +++ b/next.config.js @@ -2,7 +2,7 @@ const nextConfig = { eslint: { // Allows production builds to successfully complete even if it has linting errors. - // This is only OK bevause we do linting as part of our CI setup. + // This is only OK because we do linting as part of our CI setup. ignoreDuringBuilds: true, } } diff --git a/src/common/client/startup.ts b/src/common/client/startup.ts deleted file mode 100644 index 325e4e60..00000000 --- a/src/common/client/startup.ts +++ /dev/null @@ -1,3 +0,0 @@ -import SettingsStore from "@/features/settings/data/SettingsStore" - -export const settingsStore = new SettingsStore() \ No newline at end of file diff --git a/src/common/events/BaseEvent.ts b/src/common/events/BaseEvent.ts deleted file mode 100644 index d2f23262..00000000 --- a/src/common/events/BaseEvent.ts +++ /dev/null @@ -1,10 +0,0 @@ -export enum Events { - SETTINGS_CHANGED = "SETTINGS_CHANGED", - PROJECT_CHANGED = "PROJECT_CHANGED", - VERSION_CHANGED = "VERSION_CHANGED", - OPEN_API_SPECIFICATION_CHANGED = "OPEN_API_SPECIFICATION_CHANGED", -} - -export default abstract class BaseEvent { - constructor(public name: Events, public data: T) {} -} diff --git a/src/common/events/OpenApiSpecificationChangedEvent.ts b/src/common/events/OpenApiSpecificationChangedEvent.ts deleted file mode 100644 index 729a3c96..00000000 --- a/src/common/events/OpenApiSpecificationChangedEvent.ts +++ /dev/null @@ -1,12 +0,0 @@ -import IOpenApiSpecification from "@/features/projects/domain/IOpenApiSpecification" -import BaseEvent, { Events } from "./BaseEvent" - -export interface OpenApiSpecificationChangedEventData { - openApiSpecification: IOpenApiSpecification -} - -export default class OpenApiSpecificationChangedEvent extends BaseEvent { - constructor(openApiSpecification: IOpenApiSpecification) { - super(Events.OPEN_API_SPECIFICATION_CHANGED, { openApiSpecification }) - } -} diff --git a/src/common/events/ProjectChangedEvent.ts b/src/common/events/ProjectChangedEvent.ts deleted file mode 100644 index 9031a250..00000000 --- a/src/common/events/ProjectChangedEvent.ts +++ /dev/null @@ -1,11 +0,0 @@ -import BaseEvent, { Events } from "./BaseEvent" - -export interface ProjectChangedEventData { - projectName: string -} - -export default class ProjectChangedEvent extends BaseEvent { - constructor(projectName: string) { - super(Events.PROJECT_CHANGED, {projectName}) - } -} diff --git a/src/common/events/SettingsChangedEvent.ts b/src/common/events/SettingsChangedEvent.ts deleted file mode 100644 index 3be43c6a..00000000 --- a/src/common/events/SettingsChangedEvent.ts +++ /dev/null @@ -1,7 +0,0 @@ -import BaseEvent, { Events } from "./BaseEvent" - -export default class SettingsChangedEvent extends BaseEvent { - constructor() { - super(Events.SETTINGS_CHANGED, undefined) - } -} diff --git a/src/common/events/VersionChangedEvent.ts b/src/common/events/VersionChangedEvent.ts deleted file mode 100644 index 69ec1953..00000000 --- a/src/common/events/VersionChangedEvent.ts +++ /dev/null @@ -1,11 +0,0 @@ -import BaseEvent, { Events } from "./BaseEvent" - -export interface VersionChangedEventData { - versionName: string -} - -export default class VersionChangedEvent extends BaseEvent { - constructor(versionName: string) { - super(Events.VERSION_CHANGED, { versionName }) - } -} \ No newline at end of file diff --git a/src/common/events/utils.ts b/src/common/events/utils.ts deleted file mode 100644 index e81fa739..00000000 --- a/src/common/events/utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import BaseEvent, { Events } from "./BaseEvent" - -function subscribe(eventName: Events, listener: () => void) { - document.addEventListener(eventName, listener) -} - -function unsubscribe(eventName: Events, listener: () => void) { - document.removeEventListener(eventName, listener) -} - -function publish(event: BaseEvent) { - const customEvent = new CustomEvent(event.name, { - detail: event.data - }) - document.dispatchEvent(customEvent) -} - -export { publish, subscribe, unsubscribe } \ No newline at end of file diff --git a/src/features/projects/domain/projectNavigator.ts b/src/features/projects/domain/projectNavigator.ts index 04427567..8121be7a 100644 --- a/src/features/projects/domain/projectNavigator.ts +++ b/src/features/projects/domain/projectNavigator.ts @@ -34,26 +34,7 @@ const projectNavigator = { router: IProjectRouter ) { router.push(`/${selection.project.id}/${selection.version.id}/${specificationId}`) - }, - navigateToCurrentSelection( - candidateSelection: { - projectId?: string, - versionId?: string, - specificationId?: string - }, - actualSelection: ProjectPageSelection, - router: IProjectRouter - ) { - if ( - actualSelection.project.id != candidateSelection.projectId || - actualSelection.version.id != candidateSelection.versionId || - actualSelection.specification.id != candidateSelection.specificationId - ) { - router.replace( - `/${actualSelection.project.id}/${actualSelection.version.id}/${actualSelection.specification.id}` - ) - } - } + } } export default projectNavigator \ No newline at end of file diff --git a/src/features/projects/view/ProjectList.tsx b/src/features/projects/view/ProjectList.tsx index 2140232d..929ab042 100644 --- a/src/features/projects/view/ProjectList.tsx +++ b/src/features/projects/view/ProjectList.tsx @@ -3,18 +3,20 @@ import ProjectListItem from "./ProjectListItem" import ProjectListItemPlaceholder from "./ProjectListItemPlaceholder" import IProject from "../domain/IProject" -interface ProjectListProps { +interface ProjectListProps { readonly isLoading: boolean - readonly projects: ProjectType[] + readonly projects: IProject[] readonly selectedProjectId?: string + readonly onSelectProject: (project: IProject) => void } -const ProjectList = ( +const ProjectList = ( { isLoading, projects, - selectedProjectId - }: ProjectListProps + selectedProjectId, + onSelectProject + }: ProjectListProps ) => { const loadingItemCount = 6 if (isLoading || projects.length > 0) { @@ -33,6 +35,7 @@ const ProjectList = ( key={project.id} project={project} isSelected={project.id === selectedProjectId} + onSelectProject={onSelectProject} /> ))} diff --git a/src/features/projects/view/ProjectListItem.tsx b/src/features/projects/view/ProjectListItem.tsx index 77796d0d..ab64db85 100644 --- a/src/features/projects/view/ProjectListItem.tsx +++ b/src/features/projects/view/ProjectListItem.tsx @@ -1,4 +1,3 @@ -import { useRouter } from "next/navigation" import { ListItem, ListItemButton, ListItemText, Typography } from "@mui/material" import IProject from "../domain/IProject" import ProjectAvatar from "./ProjectAvatar" @@ -6,19 +5,20 @@ import ProjectAvatar from "./ProjectAvatar" interface ProjectListItemProps { readonly project: ProjectType readonly isSelected: boolean + readonly onSelectProject: (project: IProject) => void } const ProjectListItem = ( { project, - isSelected + isSelected, + onSelectProject }: ProjectListItemProps ) => { - const router = useRouter() return ( router.push(`/${project.id}`)} + onClick={() => onSelectProject(project)} selected={isSelected} sx={{ paddingLeft: "15px", diff --git a/src/features/projects/view/ProjectsPage.tsx b/src/features/projects/view/ProjectsPage.tsx index b0177af0..8f856cd8 100644 --- a/src/features/projects/view/ProjectsPage.tsx +++ b/src/features/projects/view/ProjectsPage.tsx @@ -5,9 +5,10 @@ import SidebarContainer from "@/common/SidebarContainer" import ProjectList from "./ProjectList" import ProjectsPageSecondaryContent from "./ProjectsPageSecondaryContent" import ProjectsPageTrailingToolbarItem from "./ProjectsPageTrailingToolbarItem" -import useProjects from "../data/useProjects" +import IProject from "../domain/IProject" import { getProjectPageState } from "../domain/ProjectPageState" import projectNavigator from "../domain/projectNavigator" +import useProjects from "../data/useProjects" interface ProjectsPageProps { readonly projectId?: string @@ -28,14 +29,10 @@ export default function ProjectsPage( selectedVersionId: versionId, selectedSpecificationId: specificationId }) - // Ensure the URL reflects the current selection of project, version, and specification. - if (stateContainer.selection) { - const candidateSelection = { projectId, versionId, specificationId } - projectNavigator.navigateToCurrentSelection( - candidateSelection, - stateContainer.selection, - router - ) + const handleProjectSelected = (project: IProject) => { + const version = project.versions[0] + const specification = version.specifications[0] + router.push(`/${project.id}/${version.id}/${specification.id}`) } return ( } secondary={ diff --git a/src/features/projects/view/docs/DocumentationViewer.tsx b/src/features/projects/view/docs/DocumentationViewer.tsx index 6d501cdb..39d0f113 100644 --- a/src/features/projects/view/docs/DocumentationViewer.tsx +++ b/src/features/projects/view/docs/DocumentationViewer.tsx @@ -1,27 +1,14 @@ -import { useEffect } from "react" -import { Events } from "@/common/events/BaseEvent" -import { subscribe, unsubscribe } from "@/common/events/utils" -import { useForceUpdate } from "@/common/useForceUpdate" -import { settingsStore } from "@/common/client/startup" import Swagger from "./Swagger" import Redocly from "./Redocly" import DocumentationVisualizer from "@/features/settings/domain/DocumentationVisualizer" +import useDocumentationVisualizer from "@/features/settings/data/useDocumentationVisualizer" -const DocumentationViewer: React.FC<{ url: string }> = ({ url }) => { - const forceUpdate = useForceUpdate() - const visualizer = settingsStore.documentationVisualizer - - useEffect(() => { - subscribe(Events.SETTINGS_CHANGED, forceUpdate) - return () => { - unsubscribe(Events.SETTINGS_CHANGED, forceUpdate) - } - }) - - switch (visualizer.toString()) { - case DocumentationVisualizer.SWAGGER.toString(): +const DocumentationViewer = ({ url }: { url: string }) => { + const [documentationVisualizer] = useDocumentationVisualizer() + switch (documentationVisualizer) { + case DocumentationVisualizer.SWAGGER: return - case DocumentationVisualizer.REDOCLY.toString(): + case DocumentationVisualizer.REDOCLY: return } } diff --git a/src/features/settings/data/SettingsStore.ts b/src/features/settings/data/SettingsStore.ts deleted file mode 100644 index c7f8c561..00000000 --- a/src/features/settings/data/SettingsStore.ts +++ /dev/null @@ -1,36 +0,0 @@ -import DocumentationVisualizer from "../domain/DocumentationVisualizer" -import ISettingsStore from "../domain/ISettingsStore" -import SettingsChangedEvent from "@/common/events/SettingsChangedEvent" -import { publish } from "@/common/events/utils" - -const LOCAL_STORAGE_SETTINGS_KEY = "settings" - -export default class SettingsStore implements ISettingsStore { - get documentationVisualizer(): DocumentationVisualizer { - return getSettings().documentationVisualizer - } - - set documentationVisualizer(documentationVisualizer: DocumentationVisualizer) { - setSettings({ ...getSettings(), documentationVisualizer }) - } -} - -interface ISettings { - documentationVisualizer: DocumentationVisualizer -} - -function getSettings(): ISettings { - const savedSettings = window.localStorage.getItem(LOCAL_STORAGE_SETTINGS_KEY) - return savedSettings ? JSON.parse(savedSettings) : getDefaultSettings() -} - -function setSettings(settings: ISettings) { - window.localStorage.setItem(LOCAL_STORAGE_SETTINGS_KEY, JSON.stringify(settings)) - publish(new SettingsChangedEvent()) -} - -function getDefaultSettings(): ISettings { - return { - documentationVisualizer: DocumentationVisualizer.SWAGGER - } -} diff --git a/src/features/settings/data/useDocumentationVisualizer.ts b/src/features/settings/data/useDocumentationVisualizer.ts new file mode 100644 index 00000000..dcd8c2ba --- /dev/null +++ b/src/features/settings/data/useDocumentationVisualizer.ts @@ -0,0 +1,6 @@ +import { useLocalStorage } from "usehooks-ts" +import DocumentationVisualizer from "@/features/settings/domain/DocumentationVisualizer" + +export default function useDocumentationVisualizer() { + return useLocalStorage("documentationVisualizer", DocumentationVisualizer.SWAGGER) +} diff --git a/src/features/settings/domain/ISettingsStore.ts b/src/features/settings/domain/ISettingsStore.ts deleted file mode 100644 index 3d68d3a8..00000000 --- a/src/features/settings/domain/ISettingsStore.ts +++ /dev/null @@ -1,5 +0,0 @@ -import DocumentationVisualizer from "./DocumentationVisualizer" - -export default interface ISettingsStore { - documentationVisualizer: DocumentationVisualizer -} diff --git a/src/features/settings/view/DocumentationVisualizationPicker.tsx b/src/features/settings/view/DocumentationVisualizationPicker.tsx index 2c91714b..31598b49 100644 --- a/src/features/settings/view/DocumentationVisualizationPicker.tsx +++ b/src/features/settings/view/DocumentationVisualizationPicker.tsx @@ -1,32 +1,28 @@ -import { useState } from "react" import { ToggleButtonGroup, ToggleButton } from "@mui/material" import DocumentationVisualizer from "../domain/DocumentationVisualizer" -import { settingsStore } from "@/common/client/startup" +import useDocumentationVisualizer from "@/features/settings/data/useDocumentationVisualizer" const DocumentationVisualizationPicker: React.FC = () => { - const [value, setValue] = useState(settingsStore.documentationVisualizer) + const [value, setValue] = useDocumentationVisualizer() const handleChange = ( _event: React.MouseEvent, documentationVisualizer: DocumentationVisualizer ) => { setValue(documentationVisualizer) - setTimeout(() => { - settingsStore.documentationVisualizer = documentationVisualizer - }) } return ( - + Swagger - + Redocly