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:
parent
b9dc226031
commit
8c4bf057d6
@ -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;
|
||||||
|
@ -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 }} />
|
||||||
|
@ -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;
|
|
||||||
|
@ -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[];
|
||||||
|
Loading…
Reference in New Issue
Block a user