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

Chore: Mobile: Improve Notes screen type safety (#11093)

This commit is contained in:
Henry Heino 2024-09-21 04:57:38 -07:00 committed by GitHub
parent b9dc226031
commit 8c4bf057d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 114 additions and 84 deletions

View File

@ -36,6 +36,13 @@ const PADDING_V = 10;
type OnPressCallback=()=> void; type OnPressCallback=()=> void;
export interface FolderPickerOptions {
enabled: boolean;
selectedFolderId?: string;
onValueChange?: OnValueChangedListener;
mustSelect?: boolean;
}
interface ScreenHeaderProps { interface ScreenHeaderProps {
selectedNoteIds: string[]; selectedNoteIds: string[];
selectedFolderId: string; selectedFolderId: string;
@ -49,12 +56,7 @@ interface ScreenHeaderProps {
menuOptions: MenuOptionType[]; menuOptions: MenuOptionType[];
title?: string|null; title?: string|null;
folders: FolderEntity[]; folders: FolderEntity[];
folderPickerOptions?: { folderPickerOptions?: FolderPickerOptions;
enabled: boolean;
selectedFolderId?: string;
onValueChange?: OnValueChangedListener;
mustSelect?: boolean;
};
plugins: PluginStates; plugins: PluginStates;
dispatch: Dispatch; dispatch: Dispatch;

View File

@ -115,7 +115,7 @@ class AppNavComponent extends Component<Props, State> {
behavior={Platform.OS === 'ios' ? 'padding' : null} behavior={Platform.OS === 'ios' ? 'padding' : null}
style={style} style={style}
> >
<NotesScreen visible={notesScreenVisible} navigation={{ state: route }} /> <NotesScreen visible={notesScreenVisible} />
{searchScreenLoaded && <SearchScreen visible={searchScreenVisible} navigation={{ state: route }} />} {searchScreenLoaded && <SearchScreen visible={searchScreenVisible} navigation={{ state: route }} />}
{!notesScreenVisible && !searchScreenVisible && <Screen navigation={{ state: route }} themeId={this.props.themeId} dispatch={this.props.dispatch} />} {!notesScreenVisible && !searchScreenVisible && <Screen navigation={{ state: route }} themeId={this.props.themeId} dispatch={this.props.dispatch} />}
<View style={{ height: this.state.autoCompletionBarExtraHeight }} /> <View style={{ height: this.state.autoCompletionBarExtraHeight }} />

View File

@ -1,87 +1,118 @@
const React = require('react'); import * as React from 'react';
import { AppState as RNAppState, View, StyleSheet, NativeEventSubscription } from 'react-native'; import { AppState as RNAppState, View, StyleSheet, NativeEventSubscription, ViewStyle, TextStyle } from 'react-native';
import { stateUtils } from '@joplin/lib/reducer'; import { stateUtils } from '@joplin/lib/reducer';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import NoteList from '../NoteList'; import NoteList from '../NoteList';
import Folder from '@joplin/lib/models/Folder'; import Folder from '@joplin/lib/models/Folder';
import Tag from '@joplin/lib/models/Tag'; import Tag from '@joplin/lib/models/Tag';
import Note from '@joplin/lib/models/Note'; import Note, { PreviewsOrder } from '@joplin/lib/models/Note';
import Setting from '@joplin/lib/models/Setting'; import Setting from '@joplin/lib/models/Setting';
import { themeStyle } from '../global-style'; import { themeStyle } from '../global-style';
import { ScreenHeader } from '../ScreenHeader'; import { FolderPickerOptions, ScreenHeader } from '../ScreenHeader';
import { _ } from '@joplin/lib/locale'; import { _ } from '@joplin/lib/locale';
import ActionButton from '../buttons/FloatingActionButton'; import ActionButton from '../buttons/FloatingActionButton';
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;
const { BaseScreenComponent } = require('../base-screen'); import { BaseScreenComponent } from '../base-screen';
const { BackButtonService } = require('../../services/back-button.js'); const { BackButtonService } = require('../../services/back-button.js');
import { AppState } from '../../utils/types'; import { AppState } from '../../utils/types';
import { NoteEntity } from '@joplin/lib/services/database/types'; import { FolderEntity, NoteEntity, TagEntity } from '@joplin/lib/services/database/types';
import { itemIsInTrash } from '@joplin/lib/services/trash'; import { itemIsInTrash } from '@joplin/lib/services/trash';
import AccessibleView from '../accessibility/AccessibleView'; import AccessibleView from '../accessibility/AccessibleView';
import { Dispatch } from 'redux';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied interface Props {
class NotesScreenComponent extends BaseScreenComponent<any> { dispatch: Dispatch;
themeId: number;
visible: boolean;
folders: FolderEntity[];
tags: TagEntity[];
notesSource: string;
notesOrder: PreviewsOrder[];
uncompletedTodosOnTop: boolean;
showCompletedTodos: boolean;
noteSelectionEnabled: boolean;
activeFolderId: string;
selectedFolderId: string;
selectedTagId: string;
selectedSmartFilterId: string;
notesParentType: string;
}
interface State {
}
type Styles = Record<string, ViewStyle|TextStyle>;
class NotesScreenComponent extends BaseScreenComponent<Props, State> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partial refactor of old code from before rule was applied
private dialogbox: any;
private onAppStateChangeSub_: NativeEventSubscription = null; private onAppStateChangeSub_: NativeEventSubscription = null;
private styles_: Record<number, Styles> = {};
private folderPickerOptions_: FolderPickerOptions;
public constructor() { public constructor(props: Props) {
super(); super(props);
this.onAppStateChange_ = async () => {
// Force an update to the notes list when app state changes
const newProps = { ...this.props };
newProps.notesSource = '';
await this.refreshNotes(newProps);
};
this.sortButton_press = async () => {
const buttons = [];
const sortNoteOptions = Setting.enumOptions('notes.sortOrder.field');
const makeCheckboxText = function(selected: boolean, sign: string, label: string) {
const s = sign === 'tick' ? '✓' : '⬤';
return (selected ? `${s} ` : '') + label;
};
for (const field in sortNoteOptions) {
if (!sortNoteOptions.hasOwnProperty(field)) continue;
buttons.push({
text: makeCheckboxText(Setting.value('notes.sortOrder.field') === field, 'bullet', sortNoteOptions[field]),
id: { name: 'notes.sortOrder.field', value: field },
});
}
buttons.push({
text: makeCheckboxText(Setting.value('notes.sortOrder.reverse'), 'tick', `[ ${Setting.settingMetadata('notes.sortOrder.reverse').label()} ]`),
id: { name: 'notes.sortOrder.reverse', value: !Setting.value('notes.sortOrder.reverse') },
});
buttons.push({
text: makeCheckboxText(Setting.value('uncompletedTodosOnTop'), 'tick', `[ ${Setting.settingMetadata('uncompletedTodosOnTop').label()} ]`),
id: { name: 'uncompletedTodosOnTop', value: !Setting.value('uncompletedTodosOnTop') },
});
buttons.push({
text: makeCheckboxText(Setting.value('showCompletedTodos'), 'tick', `[ ${Setting.settingMetadata('showCompletedTodos').label()} ]`),
id: { name: 'showCompletedTodos', value: !Setting.value('showCompletedTodos') },
});
const r = await dialogs.pop(this, Setting.settingMetadata('notes.sortOrder.field').label(), buttons);
if (!r) return;
Setting.setValue(r.name, r.value);
};
this.backHandler = () => {
if (this.dialogbox && this.dialogbox.state && this.dialogbox.state.isVisible) {
this.dialogbox.close();
return true;
}
return false;
};
} }
private onAppStateChange_ = async () => {
// Force an update to the notes list when app state changes
const newProps = { ...this.props };
newProps.notesSource = '';
await this.refreshNotes(newProps);
};
private sortButton_press = async () => {
const buttons = [];
const sortNoteOptions = Setting.enumOptions('notes.sortOrder.field');
const makeCheckboxText = function(selected: boolean, sign: string, label: string) {
const s = sign === 'tick' ? '✓' : '⬤';
return (selected ? `${s} ` : '') + label;
};
for (const field in sortNoteOptions) {
if (!sortNoteOptions.hasOwnProperty(field)) continue;
buttons.push({
text: makeCheckboxText(Setting.value('notes.sortOrder.field') === field, 'bullet', sortNoteOptions[field]),
id: { name: 'notes.sortOrder.field', value: field },
});
}
buttons.push({
text: makeCheckboxText(Setting.value('notes.sortOrder.reverse'), 'tick', `[ ${Setting.settingMetadata('notes.sortOrder.reverse').label()} ]`),
id: { name: 'notes.sortOrder.reverse', value: !Setting.value('notes.sortOrder.reverse') },
});
buttons.push({
text: makeCheckboxText(Setting.value('uncompletedTodosOnTop'), 'tick', `[ ${Setting.settingMetadata('uncompletedTodosOnTop').label()} ]`),
id: { name: 'uncompletedTodosOnTop', value: !Setting.value('uncompletedTodosOnTop') },
});
buttons.push({
text: makeCheckboxText(Setting.value('showCompletedTodos'), 'tick', `[ ${Setting.settingMetadata('showCompletedTodos').label()} ]`),
id: { name: 'showCompletedTodos', value: !Setting.value('showCompletedTodos') },
});
const r = await dialogs.pop(this, Setting.settingMetadata('notes.sortOrder.field').label(), buttons);
if (!r) return;
Setting.setValue(r.name, r.value);
};
private backHandler = () => {
if (this.dialogbox && this.dialogbox.state && this.dialogbox.state.isVisible) {
this.dialogbox.close();
return true;
}
return false;
};
public styles() { public styles() {
if (!this.styles_) this.styles_ = {}; if (!this.styles_) this.styles_ = {};
const themeId = this.props.themeId; const themeId = this.props.themeId;
@ -111,15 +142,13 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
BackButtonService.removeHandler(this.backHandler); BackButtonService.removeHandler(this.backHandler);
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied public async componentDidUpdate(prevProps: Props) {
public async componentDidUpdate(prevProps: any) {
if (prevProps.notesOrder !== this.props.notesOrder || prevProps.selectedFolderId !== this.props.selectedFolderId || prevProps.selectedTagId !== this.props.selectedTagId || prevProps.selectedSmartFilterId !== this.props.selectedSmartFilterId || prevProps.notesParentType !== this.props.notesParentType || prevProps.uncompletedTodosOnTop !== this.props.uncompletedTodosOnTop || prevProps.showCompletedTodos !== this.props.showCompletedTodos) { if (prevProps.notesOrder !== this.props.notesOrder || prevProps.selectedFolderId !== this.props.selectedFolderId || prevProps.selectedTagId !== this.props.selectedTagId || prevProps.selectedSmartFilterId !== this.props.selectedSmartFilterId || prevProps.notesParentType !== this.props.notesParentType || prevProps.uncompletedTodosOnTop !== this.props.uncompletedTodosOnTop || prevProps.showCompletedTodos !== this.props.showCompletedTodos) {
await this.refreshNotes(this.props); await this.refreshNotes(this.props);
} }
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied public async refreshNotes(props: Props|null = null) {
public async refreshNotes(props: any = null) {
if (props === null) props = this.props; if (props === null) props = this.props;
const options = { const options = {
@ -172,8 +201,7 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
} }
}; };
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied public parentItem(props: Props|null = null) {
public parentItem(props: any = null) {
if (!props) props = this.props; if (!props) props = this.props;
let output = null; let output = null;
@ -305,8 +333,6 @@ const NotesScreen = connect((state: AppState) => {
noteSelectionEnabled: state.noteSelectionEnabled, noteSelectionEnabled: state.noteSelectionEnabled,
notesOrder: stateUtils.notesOrder(state.settings), notesOrder: stateUtils.notesOrder(state.settings),
}; };
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied })(NotesScreenComponent);
})(NotesScreenComponent as any);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied export default NotesScreen;
export default NotesScreen as any;

View File

@ -24,11 +24,13 @@ const { isImageMimeType } = require('../resourceUtils');
const { MarkupToHtml } = require('@joplin/renderer'); const { MarkupToHtml } = require('@joplin/renderer');
const { ALL_NOTES_FILTER_ID } = require('../reserved-ids'); const { ALL_NOTES_FILTER_ID } = require('../reserved-ids');
export interface PreviewsOrder {
by: string;
dir: string;
}
interface PreviewsOptions { interface PreviewsOptions {
order?: { order?: PreviewsOrder[];
by: string;
dir: string;
}[];
conditions?: string[]; conditions?: string[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
conditionsParams?: any[]; conditionsParams?: any[];