You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-07-13 00:10:37 +02:00
refactor
This commit is contained in:
@ -272,6 +272,7 @@ packages/app-desktop/gui/NoteList/NoteList2.js
|
|||||||
packages/app-desktop/gui/NoteList/NoteListSource.js
|
packages/app-desktop/gui/NoteList/NoteListSource.js
|
||||||
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js
|
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js
|
||||||
packages/app-desktop/gui/NoteList/commands/index.js
|
packages/app-desktop/gui/NoteList/commands/index.js
|
||||||
|
packages/app-desktop/gui/NoteList/defaultItemRenderer.js
|
||||||
packages/app-desktop/gui/NoteList/types.js
|
packages/app-desktop/gui/NoteList/types.js
|
||||||
packages/app-desktop/gui/NoteListControls/NoteListControls.js
|
packages/app-desktop/gui/NoteListControls/NoteListControls.js
|
||||||
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js
|
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -258,6 +258,7 @@ packages/app-desktop/gui/NoteList/NoteList2.js
|
|||||||
packages/app-desktop/gui/NoteList/NoteListSource.js
|
packages/app-desktop/gui/NoteList/NoteListSource.js
|
||||||
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js
|
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js
|
||||||
packages/app-desktop/gui/NoteList/commands/index.js
|
packages/app-desktop/gui/NoteList/commands/index.js
|
||||||
|
packages/app-desktop/gui/NoteList/defaultItemRenderer.js
|
||||||
packages/app-desktop/gui/NoteList/types.js
|
packages/app-desktop/gui/NoteList/types.js
|
||||||
packages/app-desktop/gui/NoteListControls/NoteListControls.js
|
packages/app-desktop/gui/NoteListControls/NoteListControls.js
|
||||||
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js
|
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js
|
||||||
|
@ -1,21 +1,16 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useMemo, useState, useCallback, memo } from 'react';
|
import { useMemo, useState, useCallback, memo } from 'react';
|
||||||
import { AppState } from '../../app.reducer';
|
import { AppState } from '../../app.reducer';
|
||||||
// import { _ } from '@joplin/lib/locale';
|
|
||||||
import BaseModel, { ModelType } from '@joplin/lib/BaseModel';
|
import BaseModel, { ModelType } from '@joplin/lib/BaseModel';
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
import { Props } from './types';
|
import { ItemFlow, ItemRendererDepependency, Props } from './types';
|
||||||
import { itemIsReadOnlySync, ItemSlice } from '@joplin/lib/models/utils/readOnly';
|
import { itemIsReadOnlySync, ItemSlice } from '@joplin/lib/models/utils/readOnly';
|
||||||
import { FolderEntity, NoteEntity } from '@joplin/lib/services/database/types';
|
import { FolderEntity, NoteEntity } from '@joplin/lib/services/database/types';
|
||||||
import ItemChange from '@joplin/lib/models/ItemChange';
|
import ItemChange from '@joplin/lib/models/ItemChange';
|
||||||
import { Size } from '@joplin/utils/types';
|
import { Size } from '@joplin/utils/types';
|
||||||
import { htmlentities } from '@joplin/utils/html';
|
|
||||||
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
||||||
|
import defaultItemRenderer from './defaultItemRenderer';
|
||||||
// enum ItemFlow {
|
import * as Mustache from 'mustache';
|
||||||
// TopToBottom = 'topToBottom',
|
|
||||||
// LeftToRight = 'leftToRight',
|
|
||||||
// }
|
|
||||||
|
|
||||||
interface RenderedNote {
|
interface RenderedNote {
|
||||||
id: string;
|
id: string;
|
||||||
@ -32,36 +27,52 @@ const useRenderedNotes = (notes: NoteEntity[], selectedNoteIds: string[], itemSi
|
|||||||
|
|
||||||
const [renderedNotes, setRenderedNotes] = useState<RenderedNote[]>(initialValue);
|
const [renderedNotes, setRenderedNotes] = useState<RenderedNote[]>(initialValue);
|
||||||
|
|
||||||
|
const prepareViewProps = async (dependencies: ItemRendererDepependency[], note: NoteEntity, itemSize: Size, selected: boolean) => {
|
||||||
|
const output: any = {};
|
||||||
|
for (const dep of dependencies) {
|
||||||
|
|
||||||
|
if (dep.startsWith('note.')) {
|
||||||
|
const splitted = dep.split('.');
|
||||||
|
if (splitted.length !== 2) throw new Error(`Invalid dependency name: ${dep}`);
|
||||||
|
const propName = splitted.pop();
|
||||||
|
if (!output.note) output.note = {};
|
||||||
|
if (!(propName in note)) throw new Error(`Invalid dependency name: ${dep}`);
|
||||||
|
output.note[propName] = (note as any)[propName];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dep.startsWith('item.size.')) {
|
||||||
|
const splitted = dep.split('.');
|
||||||
|
if (splitted.length !== 3) throw new Error(`Invalid dependency name: ${dep}`);
|
||||||
|
const propName = splitted.pop();
|
||||||
|
if (!output.item) output.item = {};
|
||||||
|
if (!output.item.size) output.item.size = {};
|
||||||
|
if (!(propName in itemSize)) throw new Error(`Invalid dependency name: ${dep}`);
|
||||||
|
output.item.size[propName] = (itemSize as any)[propName];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dep === 'item.selected') {
|
||||||
|
if (!output.item) output.item = {};
|
||||||
|
output.item.selected = selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
useAsyncEffect(async (event) => {
|
useAsyncEffect(async (event) => {
|
||||||
const newRenderedNotes: RenderedNote[] = [];
|
const newRenderedNotes: RenderedNote[] = [];
|
||||||
|
|
||||||
const renderCheckbox = (itemHeight: number) => {
|
|
||||||
return `
|
|
||||||
<div class="checkbox" style="height: ${itemHeight}px;">
|
|
||||||
<input type="checkbox" style="margin: 0px 5px 1px 0px;">
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderTitle = (noteId: string, title: string) => {
|
|
||||||
return `
|
|
||||||
<a href="#" class="title" draggable="true" data-id="${noteId}" style="">
|
|
||||||
<span>${htmlentities(title)}</span
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
const selected = selectedNoteIds.includes(note.id);
|
const view = await defaultItemRenderer.onRenderNote(await prepareViewProps(
|
||||||
|
defaultItemRenderer.dependencies,
|
||||||
|
note,
|
||||||
|
itemSize,
|
||||||
|
selectedNoteIds.includes(note.id)
|
||||||
|
));
|
||||||
|
|
||||||
newRenderedNotes.push({
|
newRenderedNotes.push({
|
||||||
id: note.id,
|
id: note.id,
|
||||||
html: `
|
html: Mustache.render(defaultItemRenderer.itemTemplate, view),
|
||||||
<div class="content -default ${selected ? '-selected' : ''}">
|
|
||||||
${renderCheckbox(itemSize.height)}
|
|
||||||
${renderTitle(note.id, note.title)}
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,8 +101,10 @@ const NoteItem = memo((props: NoteItemProps) => {
|
|||||||
></div>;
|
></div>;
|
||||||
});
|
});
|
||||||
|
|
||||||
const NoteListComponent = (props: Props) => {
|
const NoteList = (props: Props) => {
|
||||||
// const itemDirection:ItemFlow = ItemFlow.TopToBottom;
|
const itemFlow = ItemFlow.TopToBottom;
|
||||||
|
|
||||||
|
if (itemFlow !== ItemFlow.TopToBottom) throw new Error('Not implemented');
|
||||||
|
|
||||||
const itemSize: Size = useMemo(() => {
|
const itemSize: Size = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
@ -191,4 +204,4 @@ const mapStateToProps = (state: AppState) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps)(NoteListComponent);
|
export default connect(mapStateToProps)(NoteList);
|
||||||
|
51
packages/app-desktop/gui/NoteList/defaultItemRenderer.ts
Normal file
51
packages/app-desktop/gui/NoteList/defaultItemRenderer.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { ItemFlow, ItemRenderer } from './types';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
note: {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
is_todo: number;
|
||||||
|
todo_completed: number;
|
||||||
|
};
|
||||||
|
item: {
|
||||||
|
size: {
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
selected: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultItemRenderer: ItemRenderer = {
|
||||||
|
flow: ItemFlow.TopToBottom,
|
||||||
|
|
||||||
|
itemSize: {
|
||||||
|
width: 0,
|
||||||
|
height: 34,
|
||||||
|
},
|
||||||
|
|
||||||
|
dependencies: [
|
||||||
|
'note.id',
|
||||||
|
'note.title',
|
||||||
|
'note.is_todo',
|
||||||
|
'note.todo_completed',
|
||||||
|
'item.size.height',
|
||||||
|
'item.selected',
|
||||||
|
],
|
||||||
|
|
||||||
|
itemTemplate: `
|
||||||
|
<div class="content -default {{#item.selected}}-selected{{/item.selected}}">
|
||||||
|
<div class="checkbox" style="height: {{item.size.height}}px;">
|
||||||
|
<input type="checkbox" style="margin: 0px 5px 1px 0px;">
|
||||||
|
</div>
|
||||||
|
<a href="#" class="title" draggable="true" data-id="{{note.id}}">
|
||||||
|
<span>{{note.title}}</span
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
|
||||||
|
onRenderNote: async (props: Props) => {
|
||||||
|
return props;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defaultItemRenderer;
|
@ -1,5 +1,6 @@
|
|||||||
import { FolderEntity, NoteEntity } from '@joplin/lib/services/database/types';
|
import { FolderEntity, ItemRendererDatabaseDependency, NoteEntity } from '@joplin/lib/services/database/types';
|
||||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||||
|
import { Size } from '@joplin/utils/types';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
themeId: any;
|
themeId: any;
|
||||||
@ -27,3 +28,22 @@ export interface Props {
|
|||||||
focusedField: string;
|
focusedField: string;
|
||||||
parentFolderIsReadOnly: boolean;
|
parentFolderIsReadOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ItemFlow {
|
||||||
|
TopToBottom = 'topToBottom',
|
||||||
|
LeftToRight = 'leftToRight',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RenderNoteView = Record<string, any>;
|
||||||
|
|
||||||
|
export type OnRenderNoteHandler = (props: any)=> Promise<RenderNoteView>;
|
||||||
|
|
||||||
|
export type ItemRendererDepependency = ItemRendererDatabaseDependency | 'item.size.width' | 'item.size.height' | 'item.selected';
|
||||||
|
|
||||||
|
export interface ItemRenderer {
|
||||||
|
flow: ItemFlow;
|
||||||
|
itemSize: Size;
|
||||||
|
dependencies: ItemRendererDepependency[];
|
||||||
|
itemTemplate: string;
|
||||||
|
onRenderNote: OnRenderNoteHandler;
|
||||||
|
}
|
||||||
|
@ -139,6 +139,7 @@
|
|||||||
"@joplin/lib": "~2.12",
|
"@joplin/lib": "~2.12",
|
||||||
"@joplin/renderer": "~2.12",
|
"@joplin/renderer": "~2.12",
|
||||||
"@joplin/utils": "~2.12",
|
"@joplin/utils": "~2.12",
|
||||||
|
"@types/mustache": "4.2.2",
|
||||||
"async-mutex": "0.4.0",
|
"async-mutex": "0.4.0",
|
||||||
"codemirror": "5.65.9",
|
"codemirror": "5.65.9",
|
||||||
"color": "3.2.1",
|
"color": "3.2.1",
|
||||||
@ -154,6 +155,7 @@
|
|||||||
"mark.js": "8.11.1",
|
"mark.js": "8.11.1",
|
||||||
"md5": "2.3.0",
|
"md5": "2.3.0",
|
||||||
"moment": "2.29.4",
|
"moment": "2.29.4",
|
||||||
|
"mustache": "4.2.0",
|
||||||
"node-fetch": "2.6.7",
|
"node-fetch": "2.6.7",
|
||||||
"node-notifier": "10.0.1",
|
"node-notifier": "10.0.1",
|
||||||
"node-rsa": "1.1.1",
|
"node-rsa": "1.1.1",
|
||||||
|
@ -46,6 +46,34 @@ export interface UserDataValue {
|
|||||||
|
|
||||||
export type UserData = Record<string, Record<string, UserDataValue>>;
|
export type UserData = Record<string, Record<string, UserDataValue>>;
|
||||||
|
|
||||||
|
interface DatabaseTableColumn {
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DatabaseTable {
|
||||||
|
[key: string]: DatabaseTableColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DatabaseTables {
|
||||||
|
[key: string]: DatabaseTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -300,3 +328,233 @@ export interface VersionEntity {
|
|||||||
'version'?: number;
|
'version'?: number;
|
||||||
'type_'?: number;
|
'type_'?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const databaseSchema: DatabaseTables = {
|
||||||
|
folders: {
|
||||||
|
created_time: { type: 'number' },
|
||||||
|
encryption_applied: { type: 'number' },
|
||||||
|
encryption_cipher_text: { type: 'string' },
|
||||||
|
icon: { type: 'string' },
|
||||||
|
id: { type: 'string' },
|
||||||
|
is_shared: { type: 'number' },
|
||||||
|
master_key_id: { type: 'string' },
|
||||||
|
parent_id: { type: 'string' },
|
||||||
|
share_id: { type: 'string' },
|
||||||
|
title: { type: 'string' },
|
||||||
|
updated_time: { type: 'number' },
|
||||||
|
user_created_time: { type: 'number' },
|
||||||
|
user_updated_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
tags: {
|
||||||
|
created_time: { type: 'number' },
|
||||||
|
encryption_applied: { type: 'number' },
|
||||||
|
encryption_cipher_text: { type: 'string' },
|
||||||
|
id: { type: 'string' },
|
||||||
|
is_shared: { type: 'number' },
|
||||||
|
parent_id: { type: 'string' },
|
||||||
|
title: { type: 'string' },
|
||||||
|
updated_time: { type: 'number' },
|
||||||
|
user_created_time: { type: 'number' },
|
||||||
|
user_updated_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
note_tags: {
|
||||||
|
created_time: { type: 'number' },
|
||||||
|
encryption_applied: { type: 'number' },
|
||||||
|
encryption_cipher_text: { type: 'string' },
|
||||||
|
id: { type: 'string' },
|
||||||
|
is_shared: { type: 'number' },
|
||||||
|
note_id: { type: 'string' },
|
||||||
|
tag_id: { type: 'string' },
|
||||||
|
updated_time: { type: 'number' },
|
||||||
|
user_created_time: { type: 'number' },
|
||||||
|
user_updated_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
table_fields: {
|
||||||
|
field_default: { type: 'string' },
|
||||||
|
field_name: { type: 'string' },
|
||||||
|
field_type: { type: 'number' },
|
||||||
|
id: { type: 'number' },
|
||||||
|
table_name: { type: 'string' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
sync_items: {
|
||||||
|
force_sync: { type: 'number' },
|
||||||
|
id: { type: 'number' },
|
||||||
|
item_id: { type: 'string' },
|
||||||
|
item_location: { type: 'number' },
|
||||||
|
item_type: { type: 'number' },
|
||||||
|
sync_disabled: { type: 'number' },
|
||||||
|
sync_disabled_reason: { type: 'string' },
|
||||||
|
sync_target: { type: 'number' },
|
||||||
|
sync_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
table_fields_version: { type: 'number' },
|
||||||
|
version: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
deleted_items: {
|
||||||
|
deleted_time: { type: 'number' },
|
||||||
|
id: { type: 'number' },
|
||||||
|
item_id: { type: 'string' },
|
||||||
|
item_type: { type: 'number' },
|
||||||
|
sync_target: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
key: { type: 'string' },
|
||||||
|
value: { type: 'string' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
alarms: {
|
||||||
|
id: { type: 'number' },
|
||||||
|
note_id: { type: 'string' },
|
||||||
|
trigger_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
item_changes: {
|
||||||
|
before_change_item: { type: 'string' },
|
||||||
|
created_time: { type: 'number' },
|
||||||
|
id: { type: 'number' },
|
||||||
|
item_id: { type: 'string' },
|
||||||
|
item_type: { type: 'number' },
|
||||||
|
source: { type: 'number' },
|
||||||
|
type: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
note_resources: {
|
||||||
|
id: { type: 'number' },
|
||||||
|
is_associated: { type: 'number' },
|
||||||
|
last_seen_time: { type: 'number' },
|
||||||
|
note_id: { type: 'string' },
|
||||||
|
resource_id: { type: 'string' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
resource_local_states: {
|
||||||
|
fetch_error: { type: 'string' },
|
||||||
|
fetch_status: { type: 'number' },
|
||||||
|
id: { type: 'number' },
|
||||||
|
resource_id: { type: 'string' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
resources: {
|
||||||
|
created_time: { type: 'number' },
|
||||||
|
encryption_applied: { type: 'number' },
|
||||||
|
encryption_blob_encrypted: { type: 'number' },
|
||||||
|
encryption_cipher_text: { type: 'string' },
|
||||||
|
file_extension: { type: 'string' },
|
||||||
|
filename: { type: 'string' },
|
||||||
|
id: { type: 'string' },
|
||||||
|
is_shared: { type: 'number' },
|
||||||
|
master_key_id: { type: 'string' },
|
||||||
|
mime: { type: 'string' },
|
||||||
|
share_id: { type: 'string' },
|
||||||
|
size: { type: 'number' },
|
||||||
|
title: { type: 'string' },
|
||||||
|
updated_time: { type: 'number' },
|
||||||
|
user_created_time: { type: 'number' },
|
||||||
|
user_updated_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
revisions: {
|
||||||
|
body_diff: { type: 'string' },
|
||||||
|
created_time: { type: 'number' },
|
||||||
|
encryption_applied: { type: 'number' },
|
||||||
|
encryption_cipher_text: { type: 'string' },
|
||||||
|
id: { type: 'string' },
|
||||||
|
item_id: { type: 'string' },
|
||||||
|
item_type: { type: 'number' },
|
||||||
|
item_updated_time: { type: 'number' },
|
||||||
|
metadata_diff: { type: 'string' },
|
||||||
|
parent_id: { type: 'string' },
|
||||||
|
title_diff: { type: 'string' },
|
||||||
|
updated_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
created_time: { type: 'number' },
|
||||||
|
id: { type: 'number' },
|
||||||
|
number: { type: 'number' },
|
||||||
|
updated_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
resources_to_download: {
|
||||||
|
created_time: { type: 'number' },
|
||||||
|
id: { type: 'number' },
|
||||||
|
resource_id: { type: 'string' },
|
||||||
|
updated_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
key_values: {
|
||||||
|
id: { type: 'number' },
|
||||||
|
key: { type: 'string' },
|
||||||
|
type: { type: 'number' },
|
||||||
|
updated_time: { type: 'number' },
|
||||||
|
value: { type: 'string' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
notes: {
|
||||||
|
altitude: { type: 'number' },
|
||||||
|
application_data: { type: 'string' },
|
||||||
|
author: { type: 'string' },
|
||||||
|
body: { type: 'string' },
|
||||||
|
conflict_original_id: { type: 'string' },
|
||||||
|
created_time: { type: 'number' },
|
||||||
|
encryption_applied: { type: 'number' },
|
||||||
|
encryption_cipher_text: { type: 'string' },
|
||||||
|
id: { type: 'string' },
|
||||||
|
is_conflict: { type: 'number' },
|
||||||
|
is_shared: { type: 'number' },
|
||||||
|
is_todo: { type: 'number' },
|
||||||
|
latitude: { type: 'number' },
|
||||||
|
longitude: { type: 'number' },
|
||||||
|
markup_language: { type: 'number' },
|
||||||
|
master_key_id: { type: 'string' },
|
||||||
|
order: { type: 'number' },
|
||||||
|
parent_id: { type: 'string' },
|
||||||
|
share_id: { type: 'string' },
|
||||||
|
source: { type: 'string' },
|
||||||
|
source_application: { type: 'string' },
|
||||||
|
source_url: { type: 'string' },
|
||||||
|
title: { type: 'string' },
|
||||||
|
todo_completed: { type: 'number' },
|
||||||
|
todo_due: { type: 'number' },
|
||||||
|
updated_time: { type: 'number' },
|
||||||
|
user_created_time: { type: 'number' },
|
||||||
|
user_data: { type: 'string' },
|
||||||
|
user_updated_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
notes_normalized: {
|
||||||
|
altitude: { type: 'number' },
|
||||||
|
body: { type: 'string' },
|
||||||
|
id: { type: 'string' },
|
||||||
|
is_todo: { type: 'number' },
|
||||||
|
latitude: { type: 'number' },
|
||||||
|
longitude: { type: 'number' },
|
||||||
|
parent_id: { type: 'string' },
|
||||||
|
source_url: { type: 'string' },
|
||||||
|
title: { type: 'string' },
|
||||||
|
todo_completed: { type: 'number' },
|
||||||
|
todo_due: { type: 'number' },
|
||||||
|
user_created_time: { type: 'number' },
|
||||||
|
user_updated_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
tags_with_note_count: {
|
||||||
|
created_time: { type: 'number' },
|
||||||
|
id: { type: 'string' },
|
||||||
|
note_count: { type: 'any' },
|
||||||
|
title: { type: 'string' },
|
||||||
|
todo_completed_count: { type: 'any' },
|
||||||
|
updated_time: { type: 'number' },
|
||||||
|
type_: { type: 'number' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ItemRendererDatabaseDependency = 'folder.created_time' | 'folder.encryption_applied' | 'folder.encryption_cipher_text' | 'folder.icon' | 'folder.id' | 'folder.is_shared' | 'folder.master_key_id' | 'folder.parent_id' | 'folder.share_id' | 'folder.title' | 'folder.updated_time' | 'folder.user_created_time' | 'folder.user_updated_time' | 'folder.type_' | 'note.altitude' | 'note.application_data' | 'note.author' | 'note.body' | 'note.conflict_original_id' | 'note.created_time' | 'note.encryption_applied' | 'note.encryption_cipher_text' | 'note.id' | 'note.is_conflict' | 'note.is_shared' | 'note.is_todo' | 'note.latitude' | 'note.longitude' | 'note.markup_language' | 'note.master_key_id' | 'note.order' | 'note.parent_id' | 'note.share_id' | 'note.source' | 'note.source_application' | 'note.source_url' | 'note.title' | 'note.todo_completed' | 'note.todo_due' | 'note.updated_time' | 'note.user_created_time' | 'note.user_data' | 'note.user_updated_time' | 'note.type_';
|
@ -4,6 +4,37 @@ import { rootDir } from './tool-utils';
|
|||||||
const sqlts = require('@rmp135/sql-ts').default;
|
const sqlts = require('@rmp135/sql-ts').default;
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
|
||||||
|
function createRuntimeObject(table: any) {
|
||||||
|
const colStrings = [];
|
||||||
|
for (const col of table.columns) {
|
||||||
|
const name = col.propertyName;
|
||||||
|
const type = col.propertyType;
|
||||||
|
colStrings.push(`\t\t${name}: { type: '${type}' },`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `\t${table.name}: {\n${colStrings.join('\n')}\n\t},`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stringToSingular = (word: string) => {
|
||||||
|
if (word.endsWith('s')) return word.substring(0, word.length - 1);
|
||||||
|
return word;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateItemRenderDependencyType = (tables: any[]) => {
|
||||||
|
const output: string[] = [];
|
||||||
|
|
||||||
|
for (const table of tables) {
|
||||||
|
if (!['notes', 'folders'].includes(table.name)) continue;
|
||||||
|
|
||||||
|
for (const col of table.columns) {
|
||||||
|
const name = col.propertyName;
|
||||||
|
output.push(`'${stringToSingular(table.name)}.${name}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.join(' | ');
|
||||||
|
};
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
// Run the CLI app once so as to generate the database file
|
// Run the CLI app once so as to generate the database file
|
||||||
process.chdir(`${rootDir}/packages/app-cli`);
|
process.chdir(`${rootDir}/packages/app-cli`);
|
||||||
@ -54,6 +85,11 @@ async function main() {
|
|||||||
return table;
|
return table;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tableStrings = [];
|
||||||
|
for (const table of definitions.tables) {
|
||||||
|
tableStrings.push(createRuntimeObject(table));
|
||||||
|
}
|
||||||
|
|
||||||
const tsString = sqlts.fromObject(definitions, sqlTsConfig)
|
const tsString = sqlts.fromObject(definitions, sqlTsConfig)
|
||||||
.replace(/": /g, '"?: ');
|
.replace(/": /g, '"?: ');
|
||||||
const header = `// AUTO-GENERATED BY ${__filename.substr(rootDir.length + 1)}`;
|
const header = `// AUTO-GENERATED BY ${__filename.substr(rootDir.length + 1)}`;
|
||||||
@ -65,7 +101,11 @@ async function main() {
|
|||||||
const splitted = existingContent.split('// AUTO-GENERATED BY');
|
const splitted = existingContent.split('// AUTO-GENERATED BY');
|
||||||
const staticContent = splitted[0];
|
const staticContent = splitted[0];
|
||||||
|
|
||||||
await fs.writeFile(targetFile, `${staticContent}\n\n${header}\n\n${tsString}`, 'utf8');
|
const runtimeContent = `export const databaseSchema: DatabaseTables = {\n${tableStrings.join('\n')}\n};`;
|
||||||
|
|
||||||
|
const itemRendererDependency = `export type ItemRendererDatabaseDependency = ${generateItemRenderDependencyType(definitions.tables)};`;
|
||||||
|
|
||||||
|
await fs.writeFile(targetFile, `${staticContent}\n\n${header}\n\n${tsString}\n\n${runtimeContent}\n\n${itemRendererDependency}`, 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch((error) => {
|
main().catch((error) => {
|
||||||
|
@ -4435,6 +4435,7 @@ __metadata:
|
|||||||
"@joplin/utils": ~2.12
|
"@joplin/utils": ~2.12
|
||||||
"@testing-library/react-hooks": 8.0.1
|
"@testing-library/react-hooks": 8.0.1
|
||||||
"@types/jest": 29.5.3
|
"@types/jest": 29.5.3
|
||||||
|
"@types/mustache": 4.2.2
|
||||||
"@types/node": 18.16.18
|
"@types/node": 18.16.18
|
||||||
"@types/react": 18.0.24
|
"@types/react": 18.0.24
|
||||||
"@types/react-redux": 7.1.25
|
"@types/react-redux": 7.1.25
|
||||||
@ -4461,6 +4462,7 @@ __metadata:
|
|||||||
mark.js: 8.11.1
|
mark.js: 8.11.1
|
||||||
md5: 2.3.0
|
md5: 2.3.0
|
||||||
moment: 2.29.4
|
moment: 2.29.4
|
||||||
|
mustache: 4.2.0
|
||||||
nan: 2.17.0
|
nan: 2.17.0
|
||||||
node-fetch: 2.6.7
|
node-fetch: 2.6.7
|
||||||
node-notifier: 10.0.1
|
node-notifier: 10.0.1
|
||||||
|
Reference in New Issue
Block a user