Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Version Management (without sharing modes) #111

Merged
merged 57 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
1764184
Add initial design of version management sidebar
farisd16 Sep 26, 2024
26ab39a
Fix reverse bug
farisd16 Sep 26, 2024
e568f6e
Adjust sidebar for dark mode
farisd16 Sep 26, 2024
0b4e99a
feature: slices created
egenerse Oct 1, 2024
c75e7ec
feature: refactor whole the frontend
egenerse Oct 1, 2024
653fc50
feature: remove unused redux and redux-observable packages
egenerse Oct 1, 2024
acd1598
feat: convert modals functional component
egenerse Oct 3, 2024
fc0fdb7
fix: remove console.logs set new title after changing the diagram type
egenerse Oct 3, 2024
d724739
feat: convert lastUpdate value from moment isntance to ISO date string
egenerse Oct 6, 2024
7837689
fix creating new editor for when user creates new diagram
egenerse Oct 6, 2024
073a339
create 2 different component alone and collaboration
egenerse Oct 13, 2024
077dafc
feat: separtion of diagrams and double click handle in create new dia…
egenerse Oct 15, 2024
f3f39ee
fix: render collaboration
egenerse Oct 15, 2024
c160894
fix: render editor from token
egenerse Oct 15, 2024
adb85f8
fix: subscripte to editor patchers are not working
egenerse Oct 17, 2024
6f98e97
fix: collaborator name modal handle
egenerse Oct 18, 2024
3160340
fix: fixing collaboration
egenerse Oct 18, 2024
0abe5a5
feat: use .nvmrc in the project for node version
egenerse Oct 18, 2024
19c7db4
fix: selection fix
egenerse Oct 18, 2024
f339591
fix: rerender issue while changing sgared view version
egenerse Oct 18, 2024
e137040
feat: new apollon version is used
egenerse Oct 22, 2024
b8f5d41
Merge branch 'main' into feature/redux-toolkit-migration
egenerse Oct 22, 2024
b915658
fix: modal input handle
egenerse Oct 22, 2024
5798bca
chore: npm run prettier:write
egenerse Oct 22, 2024
8e68a13
Merge branch 'main' into feature/redux-toolkit-migration
egenerse Oct 22, 2024
f497f1e
fix: hide yourself
egenerse Oct 22, 2024
cd56db0
fix: hide user label
egenerse Oct 22, 2024
47b84e3
fix: return to initial path if there is no diagram
egenerse Oct 22, 2024
e603751
fix: diagram not found handled
egenerse Oct 22, 2024
61b68d8
fix: navigate to route in load diagram
egenerse Oct 22, 2024
cdb242c
fix: import diagram in collaboration fix
egenerse Oct 22, 2024
239a6d0
Add modals and routes to server
farisd16 Oct 22, 2024
5f148aa
fix: set initial modal redux state
egenerse Oct 22, 2024
791d548
Merge remote-tracking branch 'origin/feature/redux-toolkit-migration'…
egenerse Oct 22, 2024
e3b0244
Add action functionalities
farisd16 Oct 26, 2024
59a74be
fix: application bar open-close
egenerse Oct 26, 2024
1ca5f9e
fix: pass custom prop to styled element
egenerse Oct 26, 2024
44b61d6
fix: ficed
egenerse Oct 26, 2024
4366c45
Implement version management without connection
farisd16 Oct 28, 2024
7ad8308
Fix minor bugs
farisd16 Oct 30, 2024
5107e82
Fix minor bugs for version actions
farisd16 Oct 30, 2024
5e6dd80
Merge remote-tracking branch 'origin/main' into feature/version-manag…
farisd16 Oct 30, 2024
f7232c2
Disable version management in sharing modes
farisd16 Nov 4, 2024
54331b9
Fix errors not being hidden when sidebar is open
farisd16 Nov 4, 2024
79ce9b1
Simplify initilization of editor
farisd16 Nov 5, 2024
b849c72
Split up the version management sidebar into more components
farisd16 Nov 5, 2024
085f7cf
feat: use only one useeffect to handle previews and version managemen…
egenerse Nov 5, 2024
532430f
Merge branch 'feature/version-management' of github.com:ls1intum/Apol…
egenerse Nov 5, 2024
3007099
Improve server-side error handling
farisd16 Nov 5, 2024
1eb71f4
Merge remote-tracking branch 'origin/main' into feature/version-manag…
farisd16 Nov 11, 2024
e5b5f4a
Fix sharing bug
farisd16 Nov 11, 2024
a9f23b9
Fix bug of creating/loading diagram while previewing a version
farisd16 Nov 11, 2024
bc35095
Fix dark mode
farisd16 Nov 15, 2024
16361b3
Fix bugs
farisd16 Nov 15, 2024
2631d84
Hide unpublished version after creating a new version
farisd16 Nov 16, 2024
e8a1198
Include description of restored version
farisd16 Nov 16, 2024
f96ad52
Enable previewing when a single version is published
farisd16 Nov 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 43 additions & 5 deletions packages/server/src/main/resources/diagram-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,49 @@ export class DiagramResource {
}
};

publishDiagram = (req: Request, res: Response) => {
const diagram: DiagramDTO = req.body;
this.diagramService.saveDiagramAndGenerateTokens(diagram).then((token: string) => {
res.status(200).send(token);
});
publishDiagramVersion = (req: Request, res: Response) => {
const diagram: DiagramDTO = req.body.diagram;
const existingToken: string | undefined = req.body.token;
this.diagramService
.saveDiagramVersion(diagram, existingToken)
.then((savedDiagram) => {
res.status(200).send(savedDiagram);
})
.catch((error) => {
console.error(error);
res.status(503).send('Error occurred while publishing');
});
};

deleteDiagramVersion = (req: Request, res: Response) => {
const token: string = req.params.token;
const versionIndex: number = req.body.versionIndex;

this.diagramService
.deleteDiagramVersion(token, versionIndex)
.then((deletedDiagramVersion) => {
res.status(200).send(deletedDiagramVersion);
})
.catch((error) => {
console.error(error);
res.status(503).send('Error occurred while deleting version');
});
};

editDiagramVersion = (req: Request, res: Response) => {
const token: string = req.params.token;
const versionIndex: number = req.body.versionIndex;
const title: string = req.body.title;
const description: string = req.body.description;
this.diagramService
.editDiagramVersion(token, versionIndex, title, description)
.then((editedDiagram) => {
res.status(200).send(editedDiagram);
})
.catch((error) => {
console.error(error);
res.status(503).send('Error occurred while editing version');
});
};

convertSvgToPdf = (req: Request, res: Response) => {
Expand Down
4 changes: 3 additions & 1 deletion packages/server/src/main/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export const register = (app: express.Application) => {
// routes

router.get('/diagrams/:token', (req, res) => diagramResource.getDiagram(req, res));
router.post('/diagrams/publish', (req, res) => diagramResource.publishDiagram(req, res));
router.post('/diagrams/publish', (req, res) => diagramResource.publishDiagramVersion(req, res));
router.delete('/diagrams/:token', (req, res) => diagramResource.deleteDiagramVersion(req, res));
router.post('/diagrams/:token', (req, res) => diagramResource.editDiagramVersion(req, res));
router.post('/diagrams/pdf', (req, res) => diagramResource.convertSvgToPdf(req, res));
app.use('/api', router);
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,84 @@ export class DiagramService {
this.storageService = storageService;
}

saveDiagramAndGenerateTokens(diagramDTO: DiagramDTO): Promise<string> {
// alpha numeric token with length = tokenLength
const token = randomString(tokenLength);
return this.storageService.saveDiagram(diagramDTO, token);
async saveDiagramVersion(
diagramDTO: DiagramDTO,
existingToken?: string,
): Promise<{ diagramToken: string; diagram: DiagramDTO }> {
const diagramExists = existingToken !== undefined && (await this.storageService.diagramExists(existingToken));
const diagramToken = diagramExists ? existingToken : randomString(tokenLength);
const diagram = !diagramExists ? diagramDTO : await this.getDiagramByLink(diagramToken);
const model = diagramDTO.model;
const title = diagramDTO.title;
const description = diagramDTO.description;

if (!diagram) {
throw Error(`Could not retrieve a saved diagram with the token ${diagramToken}`);
farisd16 marked this conversation as resolved.
Show resolved Hide resolved
}

if (!diagram.versions) {
diagram.versions = [];
}

diagram.model = model;
diagram.token = diagramToken;
diagram.title = title;
diagram.description = description;

const newDiagramVersion = {
...diagramDTO,
lastUpdate: new Date().toISOString(),
};
// Remove token and versions fields from newDiagramVersion
delete newDiagramVersion.token;
delete newDiagramVersion.versions;

diagram.versions.push(newDiagramVersion);
await this.storageService.saveDiagram(diagram, diagramToken);

return { diagramToken, diagram };
}

async deleteDiagramVersion(token: string, versionIndex: number): Promise<DiagramDTO> {
const diagram = await this.getDiagramByLink(token);

if (!diagram) {
throw Error(`Could not retrieve a saved diagram with the token ${token}`);
farisd16 marked this conversation as resolved.
Show resolved Hide resolved
}

if (!diagram.versions) {
throw Error(`Diagram with the token ${token} doesn't have any versions`);
}

diagram.versions.splice(versionIndex, 1);
await this.storageService.saveDiagram(diagram, token);

return diagram;
}

async editDiagramVersion(
token: string,
versionIndex: number,
title: string,
description: string,
): Promise<DiagramDTO> {
const diagram = await this.getDiagramByLink(token);

if (!diagram) {
throw Error(`Could not retrieve a saved diagram with the token ${token}`);
}

if (!diagram.versions) {
throw Error(`Diagram with the token ${token} doesn't have any versions`);
}

diagram.versions[versionIndex].title = title;
diagram.versions[versionIndex].description = description;
await this.storageService.saveDiagram(diagram, token);

return diagram;
}

getDiagramByLink(token: string): Promise<DiagramDTO | undefined> {
return this.storageService.getDiagramByLink(token);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class DiagramFileStorageService implements DiagramStorageService {
);
}

async saveDiagram(diagramDTO: DiagramDTO, token: string, shared: boolean = false): Promise<string> {
async saveDiagram(diagramDTO: DiagramDTO, token: string, shared: boolean = true): Promise<string> {
const path = this.getFilePathForToken(token);
const exists = await this.diagramExists(path);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export class DiagramRedisStorageService implements DiagramStorageService {
);
}

async saveDiagram(diagramDTO: DiagramDTO, token: string, shared: boolean = false): Promise<string> {
async saveDiagram(diagramDTO: DiagramDTO, token: string, shared: boolean = true): Promise<string> {
const key = this.getKeyForToken(token);
const exists = await this.diagramExists(key);

Expand Down
7 changes: 6 additions & 1 deletion packages/shared/src/diagram-dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ export class DiagramDTO {
title: string;
model: UMLModel;
lastUpdate: string;
versions?: DiagramDTO[];
description?: string;
token?: string;

constructor(id: string, title: string, model: UMLModel, lastUpdate: string) {
constructor(id: string, title: string, model: UMLModel, lastUpdate: string, versions: DiagramDTO[], token: string) {
this.id = id;
this.title = title;
this.model = model;
this.lastUpdate = lastUpdate;
this.versions = versions;
this.token = token;
}
}
2 changes: 2 additions & 0 deletions packages/webapp/src/main/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ToastContainer } from 'react-toastify';
import { PostHogProvider } from 'posthog-js/react';
import { ApplicationStore } from './components/store/application-store';
import { ApollonEditorComponentWithConnection } from './components/apollon-editor-component/ApollonEditorComponentWithConnection';
import { VersionManagementSidebar } from './components/version-management-sidebar/VersionManagementSidebar';

const postHogOptions = {
api_host: POSTHOG_HOST,
Expand All @@ -31,6 +32,7 @@ export function RoutedApplication() {
<ApollonEditorProvider value={{ editor, setEditor: handleSetEditor }}>
<ApplicationBar />
<ApplicationModal />
<VersionManagementSidebar />
{isFirefox && <FirefoxIncompatibilityHint />}
<Routes>
<Route path={'/:token'} element={<ApollonEditorComponentWithConnection />} />
Expand Down
Binary file added packages/webapp/src/main/components/.DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { uuid } from '../../utils/uuid';
import { setCreateNewEditor, updateDiagramThunk, selectCreatenewEditor } from '../../services/diagram/diagramSlice';
import { ApollonEditorContext } from './apollon-editor-context';
import { useAppDispatch, useAppSelector } from '../store/hooks';
import { selectPreviewedDiagramIndex } from '../../services/version-management/versionManagementSlice';

const ApollonContainer = styled.div`
display: flex;
Expand All @@ -21,30 +22,51 @@ export const ApollonEditorComponent: React.FC = () => {
const { diagram: reduxDiagram } = useAppSelector((state) => state.diagram);
const options = useAppSelector((state) => state.diagram.editorOptions);
const createNewEditor = useAppSelector(selectCreatenewEditor);
const editorContext = useContext(ApollonEditorContext);
const setEditor = editorContext?.setEditor;
const previewedDiagramIndex = useAppSelector(selectPreviewedDiagramIndex);
const { setEditor } = useContext(ApollonEditorContext);

useEffect(() => {
const initializeEditor = async () => {
if (containerRef.current != null && createNewEditor) {
const setupEditor = async () => {
if (!containerRef.current) return;

if (createNewEditor || previewedDiagramIndex === -1) {
// Initialize or reset editor
if (editorRef.current) {
await editorRef.current.nextRender;
editorRef.current.destroy();
}
editorRef.current = new ApollonEditor(containerRef.current, options);
await editorRef.current?.nextRender;
await editorRef.current.nextRender;

// Load diagram model if available
if (reduxDiagram.model) {
editorRef.current.model = reduxDiagram.model;
}

editorRef.current.subscribeToModelChange((model: UMLModel) => {
const diagram = { ...reduxDiagram, model };
dispatch(updateDiagramThunk(diagram));
});

setEditor!(editorRef.current);
dispatch(setCreateNewEditor(false));
} else if (previewedDiagramIndex !== -1 && editorRef.current) {
// Handle preview mode
const editorOptions = { ...options, readonly: true };
await editorRef.current.nextRender;
editorRef.current.destroy();
editorRef.current = new ApollonEditor(containerRef.current, editorOptions);
await editorRef.current.nextRender;

const modelToPreview = reduxDiagram?.versions && reduxDiagram.versions[previewedDiagramIndex]?.model;
if (modelToPreview) {
editorRef.current.model = modelToPreview;
}
}
};

initializeEditor();
}, [containerRef.current, createNewEditor]);
setupEditor();
}, [createNewEditor, previewedDiagramIndex]);

const key = reduxDiagram?.id || uuid();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ export const ApollonEditorComponentWithConnection: React.FC = () => {
const { diagram: reduxDiagram } = useAppSelector((state) => state.diagram);
const options = useAppSelector((state) => state.diagram.editorOptions);
const createNewEditor = useAppSelector(selectCreatenewEditor);
const editorContext = useContext(ApollonEditorContext);
const setEditor = editorContext!.setEditor;
const { setEditor } = useContext(ApollonEditorContext);
const [searchParams] = useSearchParams();
const view = searchParams.get('view');
const navigate = useNavigate();
Expand Down Expand Up @@ -120,6 +119,7 @@ export const ApollonEditorComponentWithConnection: React.FC = () => {
});
}
};

useEffect(() => {
const initializeEditor = async () => {
const shouldConnectToServer =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export type ApollonEditorContextType = {
setEditor: (editor: ApollonEditor) => void;
};

export const ApollonEditorContext = createContext<ApollonEditorContextType | null>(null);
// Provide a default no-op function for `setEditor`
export const ApollonEditorContext = createContext<ApollonEditorContextType>({
setEditor: () => {
throw new Error("setEditor is not defined. Make sure to wrap your component within ApollonEditorProvider.");
},
});

export const { Consumer: ApollonEditorConsumer, Provider: ApollonEditorProvider } = ApollonEditorContext;
Loading