1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-27 10:32:58 +02:00

Chore: Refactor Note.tsx and note-screen-shared.tsx to improve type safety (#9467)

This commit is contained in:
Henry Heino 2023-12-08 02:12:23 -08:00 committed by GitHub
parent b70589ef56
commit 27a0959f30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 108 additions and 32 deletions

View File

@ -22,7 +22,7 @@ import Folder from '@joplin/lib/models/Folder';
const Clipboard = require('@react-native-community/clipboard').default;
const md5 = require('md5');
const { BackButtonService } = require('../../services/back-button.js');
import NavService from '@joplin/lib/services/NavService';
import NavService, { OnNavigateCallback as OnNavigateCallback } from '@joplin/lib/services/NavService';
import BaseModel from '@joplin/lib/BaseModel';
import ActionButton from '../ActionButton';
const { fileExtension, safeFileExtension } = require('@joplin/lib/path-utils');
@ -34,17 +34,17 @@ const { Checkbox } = require('../checkbox.js');
import { _, currentLocale } from '@joplin/lib/locale';
import { reg } from '@joplin/lib/registry';
import ResourceFetcher from '@joplin/lib/services/ResourceFetcher';
const { BaseScreenComponent } = require('../base-screen');
import { BaseScreenComponent } from '../base-screen';
const { themeStyle, editorFont } = require('../global-style.js');
const { dialogs } = require('../../utils/dialogs.js');
const DialogBox = require('react-native-dialogbox').default;
import ImageResizer from '@bam.tech/react-native-image-resizer';
import shared from '@joplin/lib/components/shared/note-screen-shared';
import shared, { BaseNoteScreenComponent } from '@joplin/lib/components/shared/note-screen-shared';
import { Asset, ImagePickerResponse, launchImageLibrary } from 'react-native-image-picker';
import SelectDateTimeDialog from '../SelectDateTimeDialog';
import ShareExtension from '../../utils/ShareExtension.js';
import CameraView from '../CameraView';
import { NoteEntity, ResourceEntity } from '@joplin/lib/services/database/types';
import { FolderEntity, NoteEntity, ResourceEntity } from '@joplin/lib/services/database/types';
import Logger from '@joplin/utils/Logger';
import ImageEditor from '../NoteEditor/ImageEditor/ImageEditor';
import promptRestoreAutosave from '../NoteEditor/ImageEditor/promptRestoreAutosave';
@ -54,6 +54,8 @@ import { voskEnabled } from '../../services/voiceTyping/vosk';
import { isSupportedLanguage } from '../../services/voiceTyping/vosk.android';
import { ChangeEvent as EditorChangeEvent, UndoRedoDepthChangeEvent } from '@joplin/editor/events';
import { join } from 'path';
import { Dispatch } from 'redux';
import { RefObject } from 'react';
const urlUtils = require('@joplin/lib/urlUtils');
const emptyArray: any[] = [];
@ -114,20 +116,84 @@ const pickDocument = async (multiple: boolean): Promise<SelectedDocument[]> => {
return result;
};
class NoteScreenComponent extends BaseScreenComponent {
interface Props {
provisionalNoteIds: string[];
dispatch: Dispatch;
noteId: string;
useEditorBeta: boolean;
themeId: number;
editorFontSize: number;
editorFont: number; // e.g. Setting.FONT_MENLO
showSideMenu: boolean;
searchQuery: string[];
ftsEnabled: boolean;
highlightedWords: string[];
noteHash: string;
toolbarEnabled: boolean;
}
interface State {
note: any;
mode: 'view'|'edit';
readOnly: boolean;
folder: FolderEntity|null;
lastSavedNote: any;
isLoading: boolean;
titleTextInputHeight: number;
alarmDialogShown: boolean;
heightBumpView: number;
noteTagDialogShown: boolean;
fromShare: boolean;
showCamera: boolean;
showImageEditor: boolean;
imageEditorResource: ResourceEntity;
imageEditorResourceFilepath: string;
noteResources: Record<string, ResourceEntity>;
newAndNoTitleChangeNoteId: boolean|null;
HACK_webviewLoadingState: number;
undoRedoButtonState: {
canUndo: boolean;
canRedo: boolean;
};
voiceTypingDialogShown: boolean;
}
class NoteScreenComponent extends BaseScreenComponent<Props, State> implements BaseNoteScreenComponent {
// This isn't in this.state because we don't want changing scroll to trigger
// a re-render.
private lastBodyScroll: number|undefined = undefined;
private saveActionQueues_: any;
private doFocusUpdate_: boolean;
private styles_: any;
private editorRef: any;
private titleTextFieldRef: RefObject<TextInput>;
private navHandler: OnNavigateCallback;
private backHandler: ()=> Promise<boolean>;
private undoRedoService_: UndoRedoService;
private noteTagDialog_closeRequested: any;
private onJoplinLinkClick_: any;
private refreshResource: (resource: any, noteBody?: string)=> Promise<void>;
private selection: any;
private menuOptionsCache_: Record<string, any>;
private focusUpdateIID_: any;
private folderPickerOptions_: any;
public dialogbox: any;
public static navigationOptions(): any {
return { header: null };
}
public constructor() {
super();
public constructor(props: Props) {
super(props);
this.state = {
note: Note.new(),
mode: 'view',
readOnly: false,
folder: null,
lastSavedNote: null,
isLoading: true,
@ -140,6 +206,8 @@ class NoteScreenComponent extends BaseScreenComponent {
showImageEditor: false,
imageEditorResource: null,
noteResources: {},
imageEditorResourceFilepath: null,
newAndNoTitleChangeNoteId: null,
// HACK: For reasons I can't explain, when the WebView is present, the TextInput initially does not display (It's just a white rectangle with
// no visible text). It will only appear when tapping it or doing certain action like selecting text on the webview. The bug started to
@ -164,8 +232,6 @@ class NoteScreenComponent extends BaseScreenComponent {
this.doFocusUpdate_ = false;
this.saveButtonHasBeenShown_ = false;
this.styles_ = {};
this.editorRef = React.createRef();
@ -341,7 +407,7 @@ class NoteScreenComponent extends BaseScreenComponent {
}
}
private async undoRedo(type: string) {
private async undoRedo(type: 'undo'|'redo') {
const undoState = await this.undoRedoService_[type](this.undoState());
if (!undoState) return;
@ -811,7 +877,7 @@ class NoteScreenComponent extends BaseScreenComponent {
this.setState({ note: newNote });
this.refreshResource(resource, newNote.body);
void this.refreshResource(resource, newNote.body);
this.scheduleSave();
@ -1281,8 +1347,8 @@ class NoteScreenComponent extends BaseScreenComponent {
let fieldToFocus = this.state.note.is_todo ? 'title' : 'body';
if (this.state.mode === 'view') fieldToFocus = '';
if (fieldToFocus === 'title' && this.refs.titleTextField) {
this.refs.titleTextField.focus();
if (fieldToFocus === 'title' && this.titleTextFieldRef.current) {
this.titleTextFieldRef.current.focus();
}
// if (fieldToFocus === 'body' && this.markdownEditorRef.current) {
// if (this.markdownEditorRef.current) {
@ -1504,8 +1570,6 @@ class NoteScreenComponent extends BaseScreenComponent {
const showSaveButton = false; // this.state.mode === 'edit' || this.isModified() || this.saveButtonHasBeenShown_;
const saveButtonDisabled = true;// !this.isModified();
if (showSaveButton) this.saveButtonHasBeenShown_ = true;
const titleContainerStyle = isTodo ? this.styles().titleContainerTodo : this.styles().titleContainer;
const dueDate = Note.dueDateObject(note);
@ -1514,7 +1578,7 @@ class NoteScreenComponent extends BaseScreenComponent {
<View style={titleContainerStyle}>
{isTodo && <Checkbox style={this.styles().checkbox} checked={!!Number(note.todo_completed)} onChange={this.todoCheckbox_change} />}
<TextInput
ref="titleTextField"
ref={this.titleTextFieldRef}
underlineColorAndroid="#ffffff00"
autoCapitalize="sentences"
style={this.styles().titleTextInput}

View File

@ -12,17 +12,27 @@ import { itemIsReadOnlySync, ItemSlice } from '../../models/utils/readOnly';
import ItemChange from '../../models/ItemChange';
import BaseItem from '../../models/BaseItem';
export interface BaseNoteScreenComponent {
props: any;
state: any;
setState: (newState: any)=> void;
scheduleFocusUpdate(): void;
attachFile(asset: any, fileType: any): void;
lastLoadedNoteId_?: string;
}
interface Shared {
noteExists?: (noteId: string)=> Promise<boolean>;
handleNoteDeletedWhileEditing_?: (note: NoteEntity)=> Promise<NoteEntity>;
saveNoteButton_press?: (comp: any, folderId: string, options: any)=> Promise<void>;
saveOneProperty?: (comp: any, name: string, value: any)=> void;
noteComponent_change?: (comp: any, propName: string, propValue: any)=> void;
saveNoteButton_press?: (comp: BaseNoteScreenComponent, folderId: string, options: any)=> Promise<void>;
saveOneProperty?: (comp: BaseNoteScreenComponent, name: string, value: any)=> void;
noteComponent_change?: (comp: BaseNoteScreenComponent, propName: string, propValue: any)=> void;
clearResourceCache?: ()=> void;
attachedResources?: (noteBody: string)=> Promise<any>;
isModified?: (comp: any)=> boolean;
initState?: (comp: any)=> void;
toggleIsTodo_onPress?: (comp: any)=> void;
isModified?: (comp: BaseNoteScreenComponent)=> boolean;
initState?: (comp: BaseNoteScreenComponent)=> Promise<void>;
toggleIsTodo_onPress?: (comp: BaseNoteScreenComponent)=> void;
toggleCheckboxRange?: (ipcMessage: string, noteBody: string)=> any;
toggleCheckbox?: (ipcMessage: string, noteBody: string)=> string;
installResourceHandling?: (refreshResourceHandler: any)=> void;
@ -54,7 +64,7 @@ shared.handleNoteDeletedWhileEditing_ = async (note: NoteEntity) => {
return Note.load(newNote.id);
};
shared.saveNoteButton_press = async function(comp: any, folderId: string = null, options: any = null) {
shared.saveNoteButton_press = async function(comp: BaseNoteScreenComponent, folderId: string = null, options: any = null) {
options = { autoTitle: true, ...options };
const releaseMutex = await saveNoteMutex_.acquire();
@ -150,7 +160,7 @@ shared.saveNoteButton_press = async function(comp: any, folderId: string = null,
releaseMutex();
};
shared.saveOneProperty = async function(comp: any, name: string, value: any) {
shared.saveOneProperty = async function(comp: BaseNoteScreenComponent, name: string, value: any) {
let note = { ...comp.state.note };
const recreatedNote = await shared.handleNoteDeletedWhileEditing_(note);
@ -167,7 +177,7 @@ shared.saveOneProperty = async function(comp: any, name: string, value: any) {
});
};
shared.noteComponent_change = function(comp: any, propName: string, propValue: any) {
shared.noteComponent_change = function(comp: BaseNoteScreenComponent, propName: string, propValue: any) {
const newState: any = {};
const note = { ...comp.state.note };
@ -211,14 +221,14 @@ shared.attachedResources = async function(noteBody: string) {
return output;
};
shared.isModified = function(comp: any) {
shared.isModified = function(comp: BaseNoteScreenComponent) {
if (!comp.state.note || !comp.state.lastSavedNote) return false;
const diff = BaseModel.diffObjects(comp.state.lastSavedNote, comp.state.note);
delete diff.type_;
return !!Object.getOwnPropertyNames(diff).length;
};
shared.initState = async function(comp: any) {
shared.initState = async function(comp: BaseNoteScreenComponent) {
const isProvisionalNote = comp.props.provisionalNoteIds.includes(comp.props.noteId);
const note = await Note.load(comp.props.noteId);
@ -268,7 +278,7 @@ shared.initState = async function(comp: any) {
comp.lastLoadedNoteId_ = note.id;
};
shared.toggleIsTodo_onPress = function(comp: any) {
shared.toggleIsTodo_onPress = function(comp: BaseNoteScreenComponent) {
const newNote = Note.toggleIsTodo(comp.state.note);
const newState = { note: newNote };
comp.setState(newState);

View File

@ -1,9 +1,10 @@
export type OnNavigateCallback = ()=> Promise<boolean>;
export default class NavService {
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
public static dispatch: Function = () => {};
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
private static handlers_: Function[] = [];
private static handlers_: OnNavigateCallback[] = [];
public static async go(routeName: string) {
if (this.handlers_.length) {
@ -15,10 +16,11 @@ export default class NavService {
type: 'NAV_GO',
routeName: routeName,
});
return false;
}
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
public static addHandler(handler: Function) {
public static addHandler(handler: OnNavigateCallback) {
for (let i = this.handlers_.length - 1; i >= 0; i--) {
const h = this.handlers_[i];
if (h === handler) return;
@ -28,7 +30,7 @@ export default class NavService {
}
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
public static removeHandler(hanlder: Function) {
public static removeHandler(hanlder: OnNavigateCallback) {
for (let i = this.handlers_.length - 1; i >= 0; i--) {
const h = this.handlers_[i];
if (h === hanlder) this.handlers_.splice(i, 1);