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

feat(note): Merge user and note tools #182

Merged
merged 5 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 14 additions & 6 deletions src/application/services/useNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ import { onMounted, ref, type Ref, type MaybeRefOrGetter, computed, toValue, wat
import { noteService } from '@/domain';
import type { Note, NoteContent, NoteId } from '@/domain/entities/Note';
import { useRouter } from 'vue-router';

/**
* On new note creation, we use predefined structure of the Editor: header + paragraph
* We call it NoteDraft
*/
type NoteDraft = Pick<Note, 'content'>;
import type { NoteDraft } from '@/domain/entities/NoteDraft';
import type EditorTool from '@/domain/entities/EditorTool';

/**
* Creates base structure for the empty note:
Expand Down Expand Up @@ -46,6 +42,11 @@ interface UseNoteComposableState {
*/
note: Ref<Note | NoteDraft | null>;

/**
* List of tools used in the note
*/
noteTools: Ref<EditorTool[]>;

/**
* Creates/updates the note
*/
Expand Down Expand Up @@ -102,6 +103,11 @@ export default function (options: UseNoteComposableOptions): UseNoteComposableSt
*/
const note = ref<Note | NoteDraft | null>(currentId.value === null ? createDraft() : null);

/**
* List of tools used in the note
*/
const noteTools = ref<EditorTool[]>([]);

/**
* Router instance used to replace the current route with note id
*/
Expand Down Expand Up @@ -149,6 +155,7 @@ export default function (options: UseNoteComposableOptions): UseNoteComposableSt

note.value = response.note;
canEdit.value = response.accessRights.canEdit;
noteTools.value = response.tools;
parentNote.value = response.parentNote;
}

Expand Down Expand Up @@ -244,6 +251,7 @@ export default function (options: UseNoteComposableOptions): UseNoteComposableSt

return {
note,
noteTools,
noteTitle,
canEdit,
resolveHostname,
Expand Down
82 changes: 82 additions & 0 deletions src/application/services/useTools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { type Ref, ref, watch } from 'vue';
import { useAppState } from './useAppState';
import type EditorTool from '@/domain/entities/EditorTool';
import { loadScript } from '@/infrastructure/utils/load-script';
import type { EditorConfig } from '@editorjs/editorjs';

/**
* Downloaded tools data structure
*/
type DownloadedTools = EditorConfig['tools'];

/**
* Service for load editor tools
*
* @param noteTools - note tools
*/
export function useTools(noteTools: Ref<EditorTool[]>): {
tools: Ref<DownloadedTools | undefined>;
} {
/**
* User notes tools
*/
const { userEditorTools } = useAppState();
const tools = ref<DownloadedTools | undefined>();

/**
* Download all the user tools and return a map
*
* @param toolsList - tools data
*/
async function downloadTools(toolsList: EditorTool[]): Promise<DownloadedTools> {
neSpecc marked this conversation as resolved.
Show resolved Hide resolved
const downloadedTools: DownloadedTools = {};

for (const tool of toolsList) {
if (tool.source.cdn === undefined) {
continue;
}

await loadScript(tool.source.cdn);

downloadedTools[tool.name] = window[tool.exportName as keyof typeof window];
}

return downloadedTools;
}

/**
* Merge two arrays of tools, removing duplicates
*
* @param toolsA – first array of tools
* @param toolsB – second array of tools
*/
function mergeTools(toolsA: EditorTool[], toolsB: EditorTool[]): EditorTool[] {
const uniqueTools = new Map<string, EditorTool>();

[...toolsA, ...toolsB].forEach((tool) => {
uniqueTools.set(tool.name, tool);
});

return Array.from(uniqueTools.values());
}

watch(
[userEditorTools, noteTools],
async () => {
const allTools = mergeTools(userEditorTools.value || [], noteTools.value);

/**
* If tools are not loaded yet or empty, skip downloading their scripts
*/
if (allTools.length === 0) {
return;
}
tools.value = await downloadTools(allTools);
},
{ immediate: true }
);

return {
tools,
};
}
62 changes: 0 additions & 62 deletions src/application/services/useUserTools.ts

This file was deleted.

28 changes: 28 additions & 0 deletions src/domain/entities/NoteDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type EditorTool from './EditorTool';
import type { Note } from './Note';
import type NoteAccessRights from './NoteAccessRights';

/**
* Note Datsa Transfer Object used to pass Note data between layers
*/
export interface NoteDTO {
/**
* Note data
*/
note: Note;

/**
* Note access rights
*/
accessRights: NoteAccessRights;

/**
* Parent note if exists
*/
parentNote: Note | undefined;

/**
* Editor tools
*/
tools: EditorTool[];
}
7 changes: 7 additions & 0 deletions src/domain/entities/NoteDraft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { Note } from './Note';

/**
* On new note creation, we use predefined structure of the Editor: header + paragraph
* We call it NoteDraft
*/
export type NoteDraft = Pick<Note, 'content'>;
3 changes: 2 additions & 1 deletion src/domain/note.repository.interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Note, NoteContent, NoteId } from '@/domain/entities/Note';
import type { NoteList } from './entities/NoteList';
import type NoteAccessRights from '@/domain/entities/NoteAccessRights';
import type { NoteDTO } from './entities/NoteDTO';

/**
* Repository interface describes the methods that required by domain for its business logic implementation
Expand All @@ -13,7 +14,7 @@ export default interface NoteRepositoryInterface {
* @returns {{ note: Note, accessRights: NoteAccessRights, parentNote: Note | undefined }} - Returns a Note, NoteAccessRights and parent note (if exists) by id
* @throws NotFoundError
*/
getNoteById(publicId: string): Promise<{ note: Note; accessRights: NoteAccessRights; parentNote: Note | undefined }>;
getNoteById(publicId: string): Promise<NoteDTO>;

/**
* Returns a Note and NoteAccessRights by hostname
Expand Down
5 changes: 2 additions & 3 deletions src/domain/note.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type NoteRepository from '@/domain/note.repository.interface';
import type { Note, NoteContent, NoteId } from '@/domain/entities/Note';
import type NoteAccessRights from '@/domain/entities/NoteAccessRights';
import type { NoteDTO } from './entities/NoteDTO';

/**
* Note Service
Expand Down Expand Up @@ -28,9 +29,7 @@ export default class NoteService {
* @returns {{ note: Note, accessRights: NoteAccessRights, parentNote: Note | undefined }} - note data, accessRights data
* and parent note data if exists
*/
public async getNoteById(
id: string
): Promise<{ note: Note; accessRights: NoteAccessRights; parentNote: Note | undefined }> {
public async getNoteById(id: string): Promise<NoteDTO> {
return await this.noteRepository.getNoteById(id);
}

Expand Down
5 changes: 2 additions & 3 deletions src/infrastructure/note.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type NoteStorage from '@/infrastructure/storage/note.js';
import type NotesApiTransport from '@/infrastructure/transport/notes-api';
import type { GetNoteResponsePayload } from '@/infrastructure/transport/notes-api/types/GetNoteResponsePayload';
import type { NoteList } from '@/domain/entities/NoteList';
import type { NoteDTO } from '@/domain/entities/NoteDTO';

/**
* Note repository
Expand Down Expand Up @@ -39,9 +40,7 @@ export default class NoteRepository implements NoteRepositoryInterface {
* @returns {{ note: Note, accessRights: NoteAccessRights, parentNote }} - Note instance, NoteAccessRights instance
* and parent note, if exists
*/
public async getNoteById(
id: string
): Promise<{ note: Note; accessRights: NoteAccessRights; parentNote: Note | undefined }> {
public async getNoteById(id: string): Promise<NoteDTO> {
/**
* Get note data from API
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type EditorTool from '@/domain/entities/EditorTool';
import type { Note } from '@/domain/entities/Note';
import type NoteAccessRights from '@/domain/entities/NoteAccessRights.ts';

Expand All @@ -8,4 +9,5 @@ export type GetNoteResponsePayload = {
note: Note;
accessRights: NoteAccessRights;
parentNote: Note | undefined;
tools: EditorTool[];
};
12 changes: 7 additions & 5 deletions src/presentation/pages/Note.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
>
{{ t('note.createChildNote') }}
</Button>

<Button
v-if="parentNote != undefined"
@click="unlinkButton"
Expand All @@ -17,9 +18,9 @@
</div>

<Editor
v-if="userTools !== undefined"
v-if="tools !== undefined"
ref="editor"
:tools="userTools"
:tools="tools"
:content="note.content"
:read-only="!canEdit"
@change="noteChanged"
Expand All @@ -31,14 +32,13 @@
import { ref, toRef, watch } from 'vue';
import { Button, Editor } from 'codex-ui/vue';
import useNote from '@/application/services/useNote';
import { useUserTools } from '@/application/services/useUserTools.ts';
import { useTools } from '@/application/services/useTools.ts';
import { useRouter } from 'vue-router';
import { NoteContent } from '@/domain/entities/Note';
import { useHead } from 'unhead';
import { useI18n } from 'vue-i18n';

const { t } = useI18n();
const { userTools } = useUserTools();

const router = useRouter();

Expand All @@ -56,10 +56,12 @@ const props = defineProps<{

const noteId = toRef(props, 'id');

const { note, save, noteTitle, canEdit, unlinkParent, parentNote } = useNote({
const { note, noteTools, save, noteTitle, canEdit, unlinkParent, parentNote } = useNote({
id: noteId,
});

const { tools } = useTools(noteTools);

/**
* Editor component reference
*/
Expand Down
Loading