Skip to content

Commit

Permalink
Merge pull request #268 from codex-team/note-history
Browse files Browse the repository at this point in the history
feat(note-history): implement history version page
  • Loading branch information
e11sy authored Aug 1, 2024
2 parents c84e77a + f9eb34c commit 258d7b5
Show file tree
Hide file tree
Showing 15 changed files with 342 additions and 24 deletions.
2 changes: 1 addition & 1 deletion codex-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"vue-tsc": "latest"
},
"dependencies": {
"@editorjs/editorjs": "2.30.2-rc.0",
"@editorjs/editorjs": "2.30.2",
"vue": "^3.4.16"
}
}
32 changes: 26 additions & 6 deletions codex-ui/src/vue/components/avatar/Avatar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
<img
:src="src"
:alt="`Avatar of ${username}`"
:class="$style.avatar"
:class="$style[`avatar--${size}`]"
referrerpolicy="no-referrer"
>
</template>

<script setup lang="ts">
import { defineProps } from 'vue';
defineProps<{
withDefaults(defineProps<{
/**
* Path to the image
*/
Expand All @@ -21,13 +21,33 @@ defineProps<{
* In future, we can use this to generate initials
*/
username: string;
}>();
/**
* Size of the avatar image
* medium by default
*/
size: 'medium' | 'small';
}>(),
{
src: undefined,
username: undefined,
size: 'medium',
});
</script>

<style module>
.avatar {
width: var(--size-avatar);
height: var(--size-avatar);
border-radius: var(--radius-m);
&--small {
width: var(--size-icon);
height: var(--size-icon);
border-radius: var(--radius-s);
}
&--medium {
width: var(--size-avatar);
height: var(--size-avatar);
border-radius: var(--radius-m);
}
}
</style>
8 changes: 6 additions & 2 deletions src/application/i18n/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@
},
"history": {
"title": "Versions history",
"view": "View"
"view": "View",
"useVersion": "Use this version",
"confirmVersionRestore": "Do you really want to use this version?",
"editedTime": "edited on"
},
"noteList": {
"emptyNoteList": "No Notes yet. Make your first note",
Expand Down Expand Up @@ -175,6 +178,7 @@
"notFound": "Not found",
"joinTeam": "Join",
"authorization": "Authorize",
"history": "History"
"history": "History",
"historyVersion": "Version"
}
}
15 changes: 15 additions & 0 deletions src/application/router/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { RouteRecordRaw } from 'vue-router';
import AddTool from '@/presentation/pages/marketplace/AddTool.vue';
import MarketplacePage from '@/presentation/pages/marketplace/MarketplacePage.vue';
import History from '@/presentation/pages/History.vue';
import HistoryVersion from '@/presentation/pages/HistoryVersion.vue';
import MarketplaceTools from '@/presentation/pages/marketplace/MarketplaceTools.vue';

// Default production hostname for homepage. If different, then custom hostname used
Expand Down Expand Up @@ -57,6 +58,20 @@ const routes: RouteRecordRaw[] = [
noteId: String(route.params.noteId),
}),
},
{
name: 'history_version',
path: '/note/:noteId/history/:historyId',
component: HistoryVersion,
meta: {
layout: 'fullpage',
pageTitleI18n: 'pages.historyVersion',
authRequired: true,
},
props: route => ({
noteId: String(route.params.noteId),
historyId: Number(route.params.historyId),
}),
},
{
name: 'new',
path: '/new',
Expand Down
89 changes: 85 additions & 4 deletions src/application/services/useNoteHistory.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,123 @@
import type { MaybeRefOrGetter, Ref } from 'vue';
import { computed, onMounted, ref, toValue } from 'vue';
import { computed, onMounted, ref, toValue, watch } from 'vue';
import type { NoteHistoryRecord, NoteHistoryMeta } from '@/domain/entities/History';
import type { Note } from '@/domain/entities/Note';
import { noteHistoryService } from '@/domain';
import { notEmpty } from '@/infrastructure/utils/empty';

interface UseNoteHistoryComposableState {
/**
* Note hisotry is array of the history meta used for history preview
*/
noteHistory: Ref<NoteHistoryMeta[] | null>;

/**
* Content of the certain history record
*/
historyContent: Ref<NoteHistoryRecord['content'] | undefined>;

/**
* Tools that are used in current history content
*/
historyTools: Ref<NoteHistoryRecord['tools'] | undefined>;

/**
* Metadata of the history record
*/
historyMeta: Ref<NoteHistoryMeta | undefined>;
}

interface UseNoteHistoryComposableOptions {
/**
* Id of the note
*/
noteId: MaybeRefOrGetter<NoteHistoryRecord['noteId'] | null>;

/**
* Id of the history record
*/
historyId?: Ref<NoteHistoryRecord['id'] | null>;
}

export default function useNoteHistory(options: UseNoteHistoryComposableOptions): UseNoteHistoryComposableState {
/**
* Array of the note history metadata
* Used fot presentation of the note history
*/
const noteHistory = ref<NoteHistoryMeta[] | null>(null);

/**
* Content of the current note history record
* Used for the presentation of certain history record
*/
const historyContent = ref<NoteHistoryRecord['content'] | undefined>(undefined);

/**
* Note tools used in current note history content
* Used for the content displaying in editor
*/
const historyTools = ref<NoteHistoryRecord['tools'] | undefined>(undefined);

/**
* Meta data of the note history record
* Used fot informative presnetation of the note history record
*/
const historyMeta = ref<NoteHistoryMeta | undefined>(undefined);

const currentNoteId = computed(() => toValue(options.noteId));
const currentHistoryId = computed(() => toValue(options.historyId));

/**
* Loads full note history meta for certain note
* @param noteId - id of the note with history
*/
async function loadNoteHistory(noteId: Note['id']): Promise<void> {
noteHistory.value = await noteHistoryService.loadNoteHistory(noteId);
}

/**
* Get full note history record with user info
* @param noteId - id of the note with history
* @param historyId - id of the history record
* @returns - full note history record with user info
*/
async function loadNoteHistoryRecord(noteId: Note['id'], historyId: NoteHistoryRecord['id']): Promise<void> {
const historyRecord = await noteHistoryService.getNoteHistoryRecordById(noteId, historyId);

historyContent.value = historyRecord.content;
historyTools.value = historyRecord.tools;
historyMeta.value = {
id: historyRecord.id,
userId: historyRecord.userId,
createdAt: historyRecord.createdAt,
user: historyRecord.user,
};
}

/**
* When page is mounted, we should load note history
*/
onMounted(() => {
onMounted(async () => {
if (currentNoteId.value !== null) {
void loadNoteHistory(currentNoteId.value);
await loadNoteHistory(currentNoteId.value);
}
});

/**
* Watch fot the history id to load new history record when it is needed
*/
watch(currentHistoryId, async (historyId) => {
if (notEmpty(historyId) && currentNoteId.value !== null) {
await loadNoteHistoryRecord(currentNoteId.value, historyId);
}
}, {
immediate: true,
});

return {
noteHistory: noteHistory,
noteHistory,
historyContent,
historyTools,
historyMeta,
};
}
5 changes: 5 additions & 0 deletions src/domain/entities/History.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,8 @@ export type NoteHistoryMeta = Omit<NoteHistoryRecord, 'content' | 'noteId' | 'to
*/
user: UserMeta;
};

/**
* Note history record with user meta data
*/
export type NoteHistoryView = NoteHistoryRecord & { user: UserMeta };
10 changes: 9 additions & 1 deletion src/domain/noteHistory.repository.interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { NoteHistoryMeta } from './entities/History';
import type { NoteHistoryMeta, NoteHistoryRecord, NoteHistoryView } from './entities/History';
import type { Note } from './entities/Note';

/**
Expand All @@ -10,4 +10,12 @@ export default interface NoteHistoryRepositoryInterface {
* @param noteId - id of the note
*/
loadNoteHistory(noteId: Note['id']): Promise<NoteHistoryMeta[]>;

/**
* Get full note history record with user info
* @param noteId - id of the note with history
* @param historyId - id of the history record
* @returns - full note history record with user info
*/
getNoteHistoryRecordById(noteId: Note['id'], historyId: NoteHistoryRecord['id']): Promise<NoteHistoryView>;
}
12 changes: 11 additions & 1 deletion src/domain/noteHistory.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { NoteHistoryMeta } from './entities/History';
import type { NoteHistoryMeta, NoteHistoryRecord, NoteHistoryView } from './entities/History';
import type { Note } from './entities/Note';
import type NoteHistoryRepository from './noteHistory.repository.interface';

Expand All @@ -23,4 +23,14 @@ export default class NoteHistoryService {
public async loadNoteHistory(noteId: Note['id']): Promise<NoteHistoryMeta[]> {
return await this.noteHistoryRepository.loadNoteHistory(noteId);
}

/**
* Get full note history record with user info
* @param noteId - id of the note with history
* @param historyId - id of the history record
* @returns - full note history record with user info
*/
public async getNoteHistoryRecordById(noteId: Note['id'], historyId: NoteHistoryRecord['id']): Promise<NoteHistoryView> {
return await this.noteHistoryRepository.getNoteHistoryRecordById(noteId, historyId);
}
}
14 changes: 13 additions & 1 deletion src/infrastructure/noteHistory.repository.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type NoteHistoryRepositoryInterface from '@/domain/noteHistory.repository.interface';
import type NotesApiTransport from './transport/notes-api';
import type { Note } from '@/domain/entities/Note';
import type { NoteHistoryMeta } from '@/domain/entities/History';
import type { NoteHistoryMeta, NoteHistoryRecord, NoteHistoryView } from '@/domain/entities/History';

/**
* Note history repository class used for data delivery from transport to service
Expand All @@ -25,4 +25,16 @@ export default class NoteHistoryRepository implements NoteHistoryRepositoryInter

return response.noteHistoryMeta;
}

/**
* Get full note history record with user info
* @param noteId - id of the note with history
* @param historyId - id of the history record
* @returns - full note history record with user info
*/
public async getNoteHistoryRecordById(noteId: Note['id'], historyId: NoteHistoryRecord['id']): Promise<NoteHistoryView> {
const response = await this.transport.get<{ noteHistoryRecord: NoteHistoryView }>(`/note/${noteId}/history/${historyId}`);

return response.noteHistoryRecord;
}
}
1 change: 1 addition & 0 deletions src/presentation/components/note-header/NoteHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
&__right {
display: flex;
gap: var(--spacing-s);
align-items: center;
}
}
</style>
2 changes: 1 addition & 1 deletion src/presentation/pages/AuthorizationPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Button
@click="showGoogleAuthPopup"
>
{{ t('auth.login') }}
{{ t('auth.login') }}
</Button>
</div>
</template>
Expand Down
7 changes: 5 additions & 2 deletions src/presentation/pages/History.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
:subtitle="parseDate(new Date(historyRecord.createdAt))"
:has-delimiter="noteHistory !== null && index !== noteHistory?.length - 1"
:class="$style['history-items__row']"
@click="router.push(`/note/${props.noteId}/history/${historyRecord.id}`)"
>
<template #left>
<Avatar
Expand Down Expand Up @@ -59,7 +60,7 @@ import { parseDate } from '@/infrastructure/utils/date';
import { watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { NoteId } from '@/domain/entities/Note';
import { useRoute } from 'vue-router';
import { useRoute, useRouter } from 'vue-router';
const props = defineProps<{
/**
Expand All @@ -74,14 +75,15 @@ const { noteHistory } = useNoteHistory({ noteId: props.noteId });
const { patchOpenedPageByUrl } = useHeader();
const route = useRoute();
const router = useRouter();
const { noteTitle } = useNote({ id: props.noteId });
watch(noteTitle, (currentNoteTitle) => {
patchOpenedPageByUrl(
route.path,
{
title: `Version hisotry (${currentNoteTitle})`,
title: `Version history (${currentNoteTitle})`,
url: route.path,
});
});
Expand Down Expand Up @@ -120,6 +122,7 @@ watch(noteTitle, (currentNoteTitle) => {
display: flex;
flex-direction: column;
align-items: flex-start;
cursor: pointer;
&__row {
align-self: stretch;
Expand Down
Loading

0 comments on commit 258d7b5

Please sign in to comment.