You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Chore: Refactor Note.tsx and note-screen-shared.tsx to improve type safety (#9467)
This commit is contained in:
		| @@ -22,7 +22,7 @@ import Folder from '@joplin/lib/models/Folder'; | |||||||
| const Clipboard = require('@react-native-community/clipboard').default; | const Clipboard = require('@react-native-community/clipboard').default; | ||||||
| const md5 = require('md5'); | const md5 = require('md5'); | ||||||
| const { BackButtonService } = require('../../services/back-button.js'); | 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 BaseModel from '@joplin/lib/BaseModel'; | ||||||
| import ActionButton from '../ActionButton'; | import ActionButton from '../ActionButton'; | ||||||
| const { fileExtension, safeFileExtension } = require('@joplin/lib/path-utils'); | const { fileExtension, safeFileExtension } = require('@joplin/lib/path-utils'); | ||||||
| @@ -34,17 +34,17 @@ const { Checkbox } = require('../checkbox.js'); | |||||||
| import { _, currentLocale } from '@joplin/lib/locale'; | import { _, currentLocale } from '@joplin/lib/locale'; | ||||||
| import { reg } from '@joplin/lib/registry'; | import { reg } from '@joplin/lib/registry'; | ||||||
| import ResourceFetcher from '@joplin/lib/services/ResourceFetcher'; | 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 { themeStyle, editorFont } = require('../global-style.js'); | ||||||
| const { dialogs } = require('../../utils/dialogs.js'); | const { dialogs } = require('../../utils/dialogs.js'); | ||||||
| const DialogBox = require('react-native-dialogbox').default; | const DialogBox = require('react-native-dialogbox').default; | ||||||
| import ImageResizer from '@bam.tech/react-native-image-resizer'; | 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 { Asset, ImagePickerResponse, launchImageLibrary } from 'react-native-image-picker'; | ||||||
| import SelectDateTimeDialog from '../SelectDateTimeDialog'; | import SelectDateTimeDialog from '../SelectDateTimeDialog'; | ||||||
| import ShareExtension from '../../utils/ShareExtension.js'; | import ShareExtension from '../../utils/ShareExtension.js'; | ||||||
| import CameraView from '../CameraView'; | 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 Logger from '@joplin/utils/Logger'; | ||||||
| import ImageEditor from '../NoteEditor/ImageEditor/ImageEditor'; | import ImageEditor from '../NoteEditor/ImageEditor/ImageEditor'; | ||||||
| import promptRestoreAutosave from '../NoteEditor/ImageEditor/promptRestoreAutosave'; | import promptRestoreAutosave from '../NoteEditor/ImageEditor/promptRestoreAutosave'; | ||||||
| @@ -54,6 +54,8 @@ import { voskEnabled } from '../../services/voiceTyping/vosk'; | |||||||
| import { isSupportedLanguage } from '../../services/voiceTyping/vosk.android'; | import { isSupportedLanguage } from '../../services/voiceTyping/vosk.android'; | ||||||
| import { ChangeEvent as EditorChangeEvent, UndoRedoDepthChangeEvent } from '@joplin/editor/events'; | import { ChangeEvent as EditorChangeEvent, UndoRedoDepthChangeEvent } from '@joplin/editor/events'; | ||||||
| import { join } from 'path'; | import { join } from 'path'; | ||||||
|  | import { Dispatch } from 'redux'; | ||||||
|  | import { RefObject } from 'react'; | ||||||
| const urlUtils = require('@joplin/lib/urlUtils'); | const urlUtils = require('@joplin/lib/urlUtils'); | ||||||
|  |  | ||||||
| const emptyArray: any[] = []; | const emptyArray: any[] = []; | ||||||
| @@ -114,20 +116,84 @@ const pickDocument = async (multiple: boolean): Promise<SelectedDocument[]> => { | |||||||
| 	return result; | 	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 | 	// This isn't in this.state because we don't want changing scroll to trigger | ||||||
| 	// a re-render. | 	// a re-render. | ||||||
| 	private lastBodyScroll: number|undefined = undefined; | 	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 { | 	public static navigationOptions(): any { | ||||||
| 		return { header: null }; | 		return { header: null }; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public constructor() { | 	public constructor(props: Props) { | ||||||
| 		super(); | 		super(props); | ||||||
|  |  | ||||||
| 		this.state = { | 		this.state = { | ||||||
| 			note: Note.new(), | 			note: Note.new(), | ||||||
| 			mode: 'view', | 			mode: 'view', | ||||||
|  | 			readOnly: false, | ||||||
| 			folder: null, | 			folder: null, | ||||||
| 			lastSavedNote: null, | 			lastSavedNote: null, | ||||||
| 			isLoading: true, | 			isLoading: true, | ||||||
| @@ -140,6 +206,8 @@ class NoteScreenComponent extends BaseScreenComponent { | |||||||
| 			showImageEditor: false, | 			showImageEditor: false, | ||||||
| 			imageEditorResource: null, | 			imageEditorResource: null, | ||||||
| 			noteResources: {}, | 			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 | 			// 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 | 			// 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.doFocusUpdate_ = false; | ||||||
|  |  | ||||||
| 		this.saveButtonHasBeenShown_ = false; |  | ||||||
|  |  | ||||||
| 		this.styles_ = {}; | 		this.styles_ = {}; | ||||||
|  |  | ||||||
| 		this.editorRef = React.createRef(); | 		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()); | 		const undoState = await this.undoRedoService_[type](this.undoState()); | ||||||
| 		if (!undoState) return; | 		if (!undoState) return; | ||||||
|  |  | ||||||
| @@ -811,7 +877,7 @@ class NoteScreenComponent extends BaseScreenComponent { | |||||||
|  |  | ||||||
| 		this.setState({ note: newNote }); | 		this.setState({ note: newNote }); | ||||||
|  |  | ||||||
| 		this.refreshResource(resource, newNote.body); | 		void this.refreshResource(resource, newNote.body); | ||||||
|  |  | ||||||
| 		this.scheduleSave(); | 		this.scheduleSave(); | ||||||
|  |  | ||||||
| @@ -1281,8 +1347,8 @@ class NoteScreenComponent extends BaseScreenComponent { | |||||||
| 		let fieldToFocus = this.state.note.is_todo ? 'title' : 'body'; | 		let fieldToFocus = this.state.note.is_todo ? 'title' : 'body'; | ||||||
| 		if (this.state.mode === 'view') fieldToFocus = ''; | 		if (this.state.mode === 'view') fieldToFocus = ''; | ||||||
|  |  | ||||||
| 		if (fieldToFocus === 'title' && this.refs.titleTextField) { | 		if (fieldToFocus === 'title' && this.titleTextFieldRef.current) { | ||||||
| 			this.refs.titleTextField.focus(); | 			this.titleTextFieldRef.current.focus(); | ||||||
| 		} | 		} | ||||||
| 		// if (fieldToFocus === 'body' && this.markdownEditorRef.current) { | 		// if (fieldToFocus === 'body' && this.markdownEditorRef.current) { | ||||||
| 		// 	if (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 showSaveButton = false; // this.state.mode === 'edit' || this.isModified() || this.saveButtonHasBeenShown_; | ||||||
| 		const saveButtonDisabled = true;// !this.isModified(); | 		const saveButtonDisabled = true;// !this.isModified(); | ||||||
|  |  | ||||||
| 		if (showSaveButton) this.saveButtonHasBeenShown_ = true; |  | ||||||
|  |  | ||||||
| 		const titleContainerStyle = isTodo ? this.styles().titleContainerTodo : this.styles().titleContainer; | 		const titleContainerStyle = isTodo ? this.styles().titleContainerTodo : this.styles().titleContainer; | ||||||
|  |  | ||||||
| 		const dueDate = Note.dueDateObject(note); | 		const dueDate = Note.dueDateObject(note); | ||||||
| @@ -1514,7 +1578,7 @@ class NoteScreenComponent extends BaseScreenComponent { | |||||||
| 			<View style={titleContainerStyle}> | 			<View style={titleContainerStyle}> | ||||||
| 				{isTodo && <Checkbox style={this.styles().checkbox} checked={!!Number(note.todo_completed)} onChange={this.todoCheckbox_change} />} | 				{isTodo && <Checkbox style={this.styles().checkbox} checked={!!Number(note.todo_completed)} onChange={this.todoCheckbox_change} />} | ||||||
| 				<TextInput | 				<TextInput | ||||||
| 					ref="titleTextField" | 					ref={this.titleTextFieldRef} | ||||||
| 					underlineColorAndroid="#ffffff00" | 					underlineColorAndroid="#ffffff00" | ||||||
| 					autoCapitalize="sentences" | 					autoCapitalize="sentences" | ||||||
| 					style={this.styles().titleTextInput} | 					style={this.styles().titleTextInput} | ||||||
|   | |||||||
| @@ -12,17 +12,27 @@ import { itemIsReadOnlySync, ItemSlice } from '../../models/utils/readOnly'; | |||||||
| import ItemChange from '../../models/ItemChange'; | import ItemChange from '../../models/ItemChange'; | ||||||
| import BaseItem from '../../models/BaseItem'; | 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 { | interface Shared { | ||||||
| 	noteExists?: (noteId: string)=> Promise<boolean>; | 	noteExists?: (noteId: string)=> Promise<boolean>; | ||||||
| 	handleNoteDeletedWhileEditing_?: (note: NoteEntity)=> Promise<NoteEntity>; | 	handleNoteDeletedWhileEditing_?: (note: NoteEntity)=> Promise<NoteEntity>; | ||||||
| 	saveNoteButton_press?: (comp: any, folderId: string, options: any)=> Promise<void>; | 	saveNoteButton_press?: (comp: BaseNoteScreenComponent, folderId: string, options: any)=> Promise<void>; | ||||||
| 	saveOneProperty?: (comp: any, name: string, value: any)=> void; | 	saveOneProperty?: (comp: BaseNoteScreenComponent, name: string, value: any)=> void; | ||||||
| 	noteComponent_change?: (comp: any, propName: string, propValue: any)=> void; | 	noteComponent_change?: (comp: BaseNoteScreenComponent, propName: string, propValue: any)=> void; | ||||||
| 	clearResourceCache?: ()=> void; | 	clearResourceCache?: ()=> void; | ||||||
| 	attachedResources?: (noteBody: string)=> Promise<any>; | 	attachedResources?: (noteBody: string)=> Promise<any>; | ||||||
| 	isModified?: (comp: any)=> boolean; | 	isModified?: (comp: BaseNoteScreenComponent)=> boolean; | ||||||
| 	initState?: (comp: any)=> void; | 	initState?: (comp: BaseNoteScreenComponent)=> Promise<void>; | ||||||
| 	toggleIsTodo_onPress?: (comp: any)=> void; | 	toggleIsTodo_onPress?: (comp: BaseNoteScreenComponent)=> void; | ||||||
| 	toggleCheckboxRange?: (ipcMessage: string, noteBody: string)=> any; | 	toggleCheckboxRange?: (ipcMessage: string, noteBody: string)=> any; | ||||||
| 	toggleCheckbox?: (ipcMessage: string, noteBody: string)=> string; | 	toggleCheckbox?: (ipcMessage: string, noteBody: string)=> string; | ||||||
| 	installResourceHandling?: (refreshResourceHandler: any)=> void; | 	installResourceHandling?: (refreshResourceHandler: any)=> void; | ||||||
| @@ -54,7 +64,7 @@ shared.handleNoteDeletedWhileEditing_ = async (note: NoteEntity) => { | |||||||
| 	return Note.load(newNote.id); | 	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 }; | 	options = { autoTitle: true, ...options }; | ||||||
|  |  | ||||||
| 	const releaseMutex = await saveNoteMutex_.acquire(); | 	const releaseMutex = await saveNoteMutex_.acquire(); | ||||||
| @@ -150,7 +160,7 @@ shared.saveNoteButton_press = async function(comp: any, folderId: string = null, | |||||||
| 	releaseMutex(); | 	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 }; | 	let note = { ...comp.state.note }; | ||||||
|  |  | ||||||
| 	const recreatedNote = await shared.handleNoteDeletedWhileEditing_(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 newState: any = {}; | ||||||
|  |  | ||||||
| 	const note = { ...comp.state.note }; | 	const note = { ...comp.state.note }; | ||||||
| @@ -211,14 +221,14 @@ shared.attachedResources = async function(noteBody: string) { | |||||||
| 	return output; | 	return output; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| shared.isModified = function(comp: any) { | shared.isModified = function(comp: BaseNoteScreenComponent) { | ||||||
| 	if (!comp.state.note || !comp.state.lastSavedNote) return false; | 	if (!comp.state.note || !comp.state.lastSavedNote) return false; | ||||||
| 	const diff = BaseModel.diffObjects(comp.state.lastSavedNote, comp.state.note); | 	const diff = BaseModel.diffObjects(comp.state.lastSavedNote, comp.state.note); | ||||||
| 	delete diff.type_; | 	delete diff.type_; | ||||||
| 	return !!Object.getOwnPropertyNames(diff).length; | 	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 isProvisionalNote = comp.props.provisionalNoteIds.includes(comp.props.noteId); | ||||||
|  |  | ||||||
| 	const note = await Note.load(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; | 	comp.lastLoadedNoteId_ = note.id; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| shared.toggleIsTodo_onPress = function(comp: any) { | shared.toggleIsTodo_onPress = function(comp: BaseNoteScreenComponent) { | ||||||
| 	const newNote = Note.toggleIsTodo(comp.state.note); | 	const newNote = Note.toggleIsTodo(comp.state.note); | ||||||
| 	const newState = { note: newNote }; | 	const newState = { note: newNote }; | ||||||
| 	comp.setState(newState); | 	comp.setState(newState); | ||||||
|   | |||||||
| @@ -1,9 +1,10 @@ | |||||||
|  | export type OnNavigateCallback = ()=> Promise<boolean>; | ||||||
|  |  | ||||||
| export default class NavService { | export default class NavService { | ||||||
|  |  | ||||||
| 	// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied | 	// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied | ||||||
| 	public static dispatch: Function = () => {}; | 	public static dispatch: Function = () => {}; | ||||||
| 	// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied | 	private static handlers_: OnNavigateCallback[] = []; | ||||||
| 	private static handlers_: Function[] = []; |  | ||||||
|  |  | ||||||
| 	public static async go(routeName: string) { | 	public static async go(routeName: string) { | ||||||
| 		if (this.handlers_.length) { | 		if (this.handlers_.length) { | ||||||
| @@ -15,10 +16,11 @@ export default class NavService { | |||||||
| 			type: 'NAV_GO', | 			type: 'NAV_GO', | ||||||
| 			routeName: routeName, | 			routeName: routeName, | ||||||
| 		}); | 		}); | ||||||
|  | 		return false; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied | 	// 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--) { | 		for (let i = this.handlers_.length - 1; i >= 0; i--) { | ||||||
| 			const h = this.handlers_[i]; | 			const h = this.handlers_[i]; | ||||||
| 			if (h === handler) return; | 			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 | 	// 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--) { | 		for (let i = this.handlers_.length - 1; i >= 0; i--) { | ||||||
| 			const h = this.handlers_[i]; | 			const h = this.handlers_[i]; | ||||||
| 			if (h === hanlder) this.handlers_.splice(i, 1); | 			if (h === hanlder) this.handlers_.splice(i, 1); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user