Migrate ModalContext and WorkspaceContext

This commit is contained in:
2025-05-08 21:42:13 +02:00
parent 1a06c31705
commit 14b1a46508
5 changed files with 160 additions and 115 deletions

View File

@@ -1,11 +1,6 @@
import { API_BASE_URL } from '@/types/authApi';
import { apiCall } from './api';
import {
DeleteWorkspaceResponse,
isWorkspace,
LastWorkspaceNameResponse,
Workspace,
} from '@/types/workspace';
import { isWorkspace, Workspace } from '@/types/workspace';
/**
* listWorkspaces fetches the list of workspaces
@@ -97,12 +92,12 @@ export const updateWorkspace = async (
/**
* deleteWorkspace deletes the workspace with the given name
* @param workspaceName - The name of the workspace to delete
* @returns {Promise<DeleteWorkspaceResponse>} A promise that resolves to the response object
* @returns {Promise<string>} A promise that resolves to the next workspace name to switch to
* @throws {Error} If the API call fails or returns an invalid response
*/
export const deleteWorkspace = async (
workspaceName: string
): Promise<DeleteWorkspaceResponse> => {
): Promise<string> => {
const response = await apiCall(
`${API_BASE_URL}/workspaces/${encodeURIComponent(workspaceName)}`,
{
@@ -113,23 +108,22 @@ export const deleteWorkspace = async (
if (!('nextWorkspaceName' in data)) {
throw new Error('Invalid delete workspace response received from API');
}
return data as DeleteWorkspaceResponse;
return data.nextWorkspaceName as string;
};
/**
* getLastWorkspaceName fetches the last workspace name
* @returns {Promise<LastWorkspaceNameResponse>} A promise that resolves to the last workspace name
* @returns {Promise<string>} A promise that resolves to the last workspace name
* @throws {Error} If the API call fails or returns an invalid response
*/
export const getLastWorkspaceName =
async (): Promise<LastWorkspaceNameResponse> => {
export const getLastWorkspaceName = async (): Promise<string> => {
const response = await apiCall(`${API_BASE_URL}/workspaces/last`);
const data = await response.json();
if (!('lastWorkspaceName' in data)) {
throw new Error('Invalid last workspace name response received from API');
}
return data as LastWorkspaceNameResponse;
};
return data.lastWorkspaceName as string;
};
/**
* updateLastWorkspaceName updates the last workspace name

View File

@@ -1,36 +0,0 @@
import React, { createContext, useContext, useState } from 'react';
const ModalContext = createContext();
export const ModalProvider = ({ children }) => {
const [newFileModalVisible, setNewFileModalVisible] = useState(false);
const [deleteFileModalVisible, setDeleteFileModalVisible] = useState(false);
const [commitMessageModalVisible, setCommitMessageModalVisible] =
useState(false);
const [settingsModalVisible, setSettingsModalVisible] = useState(false);
const [switchWorkspaceModalVisible, setSwitchWorkspaceModalVisible] =
useState(false);
const [createWorkspaceModalVisible, setCreateWorkspaceModalVisible] =
useState(false);
const value = {
newFileModalVisible,
setNewFileModalVisible,
deleteFileModalVisible,
setDeleteFileModalVisible,
commitMessageModalVisible,
setCommitMessageModalVisible,
settingsModalVisible,
setSettingsModalVisible,
switchWorkspaceModalVisible,
setSwitchWorkspaceModalVisible,
createWorkspaceModalVisible,
setCreateWorkspaceModalVisible,
};
return (
<ModalContext.Provider value={value}>{children}</ModalContext.Provider>
);
};
export const useModalContext = () => useContext(ModalContext);

View File

@@ -0,0 +1,62 @@
import React, { createContext, useContext, useState, ReactNode } from 'react';
interface ModalContextType {
newFileModalVisible: boolean;
setNewFileModalVisible: React.Dispatch<React.SetStateAction<boolean>>;
deleteFileModalVisible: boolean;
setDeleteFileModalVisible: React.Dispatch<React.SetStateAction<boolean>>;
commitMessageModalVisible: boolean;
setCommitMessageModalVisible: React.Dispatch<React.SetStateAction<boolean>>;
settingsModalVisible: boolean;
setSettingsModalVisible: React.Dispatch<React.SetStateAction<boolean>>;
switchWorkspaceModalVisible: boolean;
setSwitchWorkspaceModalVisible: React.Dispatch<React.SetStateAction<boolean>>;
createWorkspaceModalVisible: boolean;
setCreateWorkspaceModalVisible: React.Dispatch<React.SetStateAction<boolean>>;
}
// Create the context with a default undefined value
const ModalContext = createContext<ModalContextType | null>(null);
interface ModalProviderProps {
children: ReactNode;
}
export const ModalProvider: React.FC<ModalProviderProps> = ({ children }) => {
const [newFileModalVisible, setNewFileModalVisible] = useState(false);
const [deleteFileModalVisible, setDeleteFileModalVisible] = useState(false);
const [commitMessageModalVisible, setCommitMessageModalVisible] =
useState(false);
const [settingsModalVisible, setSettingsModalVisible] = useState(false);
const [switchWorkspaceModalVisible, setSwitchWorkspaceModalVisible] =
useState(false);
const [createWorkspaceModalVisible, setCreateWorkspaceModalVisible] =
useState(false);
const value: ModalContextType = {
newFileModalVisible,
setNewFileModalVisible,
deleteFileModalVisible,
setDeleteFileModalVisible,
commitMessageModalVisible,
setCommitMessageModalVisible,
settingsModalVisible,
setSettingsModalVisible,
switchWorkspaceModalVisible,
setSwitchWorkspaceModalVisible,
createWorkspaceModalVisible,
setCreateWorkspaceModalVisible,
};
return (
<ModalContext.Provider value={value}>{children}</ModalContext.Provider>
);
};
export const useModalContext = (): ModalContextType => {
const context = useContext(ModalContext);
if (context === null) {
throw new Error('useModalContext must be used within a ModalProvider');
}
return context;
};

View File

@@ -4,28 +4,53 @@ import React, {
useState,
useEffect,
useCallback,
ReactNode,
} from 'react';
import { useMantineColorScheme } from '@mantine/core';
import { MantineColorScheme, useMantineColorScheme } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import {
fetchLastWorkspaceName,
getLastWorkspaceName,
getWorkspace,
updateWorkspace,
updateLastWorkspaceName,
deleteWorkspace,
listWorkspaces,
} from '../api/git';
import { DEFAULT_WORKSPACE_SETTINGS } from '../utils/constants';
} from '@/api/workspace';
import {
Workspace,
DEFAULT_WORKSPACE_SETTINGS,
WorkspaceSettings,
} from '@/types/workspace';
const WorkspaceContext = createContext();
interface WorkspaceContextType {
currentWorkspace: Workspace | null;
workspaces: Workspace[];
settings: Workspace | typeof DEFAULT_WORKSPACE_SETTINGS;
updateSettings: (newSettings: Partial<Workspace>) => Promise<void>;
loading: boolean;
colorScheme: MantineColorScheme;
updateColorScheme: (newTheme: MantineColorScheme) => void;
switchWorkspace: (workspaceName: string) => Promise<void>;
deleteCurrentWorkspace: () => Promise<void>;
}
export const WorkspaceProvider = ({ children }) => {
const [currentWorkspace, setCurrentWorkspace] = useState(null);
const [workspaces, setWorkspaces] = useState([]);
const [loading, setLoading] = useState(true);
const WorkspaceContext = createContext<WorkspaceContextType | null>(null);
interface WorkspaceProviderProps {
children: ReactNode;
}
export const WorkspaceProvider: React.FC<WorkspaceProviderProps> = ({
children,
}) => {
const [currentWorkspace, setCurrentWorkspace] = useState<Workspace | null>(
null
);
const [workspaces, setWorkspaces] = useState<Workspace[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const { colorScheme, setColorScheme } = useMantineColorScheme();
const loadWorkspaces = useCallback(async () => {
const loadWorkspaces = useCallback(async (): Promise<Workspace[]> => {
try {
const workspaceList = await listWorkspaces();
setWorkspaces(workspaceList);
@@ -41,11 +66,12 @@ export const WorkspaceProvider = ({ children }) => {
}
}, []);
const loadWorkspaceData = useCallback(async (workspaceName) => {
const loadWorkspaceData = useCallback(
async (workspaceName: string): Promise<void> => {
try {
const workspace = await getWorkspace(workspaceName);
setCurrentWorkspace(workspace);
setColorScheme(workspace.theme);
setColorScheme(workspace.theme as MantineColorScheme);
} catch (error) {
console.error('Failed to load workspace data:', error);
notifications.show({
@@ -54,9 +80,11 @@ export const WorkspaceProvider = ({ children }) => {
color: 'red',
});
}
}, []);
},
[]
);
const loadFirstAvailableWorkspace = useCallback(async () => {
const loadFirstAvailableWorkspace = useCallback(async (): Promise<void> => {
try {
const allWorkspaces = await listWorkspaces();
if (allWorkspaces.length > 0) {
@@ -75,9 +103,9 @@ export const WorkspaceProvider = ({ children }) => {
}, []);
useEffect(() => {
const initializeWorkspace = async () => {
const initializeWorkspace = async (): Promise<void> => {
try {
const { lastWorkspaceName } = await fetchLastWorkspaceName();
const lastWorkspaceName = await getLastWorkspaceName();
if (lastWorkspaceName) {
await loadWorkspaceData(lastWorkspaceName);
} else {
@@ -95,7 +123,8 @@ export const WorkspaceProvider = ({ children }) => {
initializeWorkspace();
}, []);
const switchWorkspace = useCallback(async (workspaceName) => {
const switchWorkspace = useCallback(
async (workspaceName: string): Promise<void> => {
try {
setLoading(true);
await updateLastWorkspaceName(workspaceName);
@@ -111,9 +140,11 @@ export const WorkspaceProvider = ({ children }) => {
} finally {
setLoading(false);
}
}, []);
},
[]
);
const deleteCurrentWorkspace = useCallback(async () => {
const deleteCurrentWorkspace = useCallback(async (): Promise<void> => {
if (!currentWorkspace) return;
try {
@@ -129,10 +160,12 @@ export const WorkspaceProvider = ({ children }) => {
}
// Delete workspace and get the next workspace ID
const response = await deleteWorkspace(currentWorkspace.name);
const nextWorkspaceName: string = await deleteWorkspace(
currentWorkspace.name
);
// Load the new workspace data
await loadWorkspaceData(response.nextWorkspaceName);
await loadWorkspaceData(nextWorkspaceName);
notifications.show({
title: 'Success',
@@ -152,7 +185,7 @@ export const WorkspaceProvider = ({ children }) => {
}, [currentWorkspace]);
const updateSettings = useCallback(
async (newSettings) => {
async (newSettings: Partial<Workspace>): Promise<void> => {
if (!currentWorkspace) return;
try {
@@ -177,13 +210,13 @@ export const WorkspaceProvider = ({ children }) => {
);
const updateColorScheme = useCallback(
(newTheme) => {
(newTheme: MantineColorScheme): void => {
setColorScheme(newTheme);
},
[setColorScheme]
);
const value = {
const value: WorkspaceContextType = {
currentWorkspace,
workspaces,
settings: currentWorkspace || DEFAULT_WORKSPACE_SETTINGS,
@@ -202,9 +235,9 @@ export const WorkspaceProvider = ({ children }) => {
);
};
export const useWorkspace = () => {
export const useWorkspace = (): WorkspaceContextType => {
const context = useContext(WorkspaceContext);
if (context === undefined) {
if (!context) {
throw new Error('useWorkspace must be used within a WorkspaceProvider');
}
return context;

View File

@@ -1,13 +1,5 @@
import { Theme } from './theme';
export interface DeleteWorkspaceResponse {
nextWorkspaceName: string;
}
export interface LastWorkspaceNameResponse {
lastWorkspaceName: string;
}
export interface WorkspaceSettings {
theme: Theme;
autoSave: boolean;