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 2 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 user tools
TatianaFomina marked this conversation as resolved.
Show resolved Hide resolved
*
* @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.

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'>;
5 changes: 4 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 EditorTool from './entities/EditorTool';

/**
* Repository interface describes the methods that required by domain for its business logic implementation
Expand All @@ -13,7 +14,9 @@ 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<{ note: Note; accessRights: NoteAccessRights; parentNote: Note | undefined; tools: EditorTool[] }>;
neSpecc marked this conversation as resolved.
Show resolved Hide resolved

/**
* Returns a Note and NoteAccessRights by hostname
Expand Down
3 changes: 2 additions & 1 deletion 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 EditorTool from './entities/EditorTool';

/**
* Note Service
Expand Down Expand Up @@ -30,7 +31,7 @@ export default class NoteService {
*/
public async getNoteById(
id: string
): Promise<{ note: Note; accessRights: NoteAccessRights; parentNote: Note | undefined }> {
): Promise<{ note: Note; accessRights: NoteAccessRights; parentNote: Note | undefined; tools: EditorTool[] }> {
return await this.noteRepository.getNoteById(id);
}

Expand Down
3 changes: 2 additions & 1 deletion 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 EditorTool from '@/domain/entities/EditorTool';

/**
* Note repository
Expand Down Expand Up @@ -41,7 +42,7 @@ export default class NoteRepository implements NoteRepositoryInterface {
*/
public async getNoteById(
id: string
): Promise<{ note: Note; accessRights: NoteAccessRights; parentNote: Note | undefined }> {
): Promise<{ note: Note; accessRights: NoteAccessRights; parentNote: Note | undefined; tools: EditorTool[] }> {
/**
* 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[];
};
13 changes: 8 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 Expand Up @@ -124,3 +126,4 @@ watch(noteTitle, () => {
</script>

<style scoped></style>
@/application/services/useTools
Loading