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

Chore: Mobile: Migrate NoteItem and Checkbox to TypeScript (#11094)

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

View File

@ -548,6 +548,7 @@ packages/app-mobile/commands/util/showResource.js
packages/app-mobile/components/BackButtonDialogBox.js
packages/app-mobile/components/BetaChip.js
packages/app-mobile/components/CameraView.js
packages/app-mobile/components/Checkbox.js
packages/app-mobile/components/DialogManager.js
packages/app-mobile/components/DismissibleDialog.js
packages/app-mobile/components/Dropdown.test.js
@ -611,6 +612,7 @@ packages/app-mobile/components/NoteEditor/hooks/useEditorCommandHandler.test.js
packages/app-mobile/components/NoteEditor/hooks/useEditorCommandHandler.js
packages/app-mobile/components/NoteEditor/hooks/useKeyboardVisible.js
packages/app-mobile/components/NoteEditor/types.js
packages/app-mobile/components/NoteItem.js
packages/app-mobile/components/NoteList.js
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.js
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.js

2
.gitignore vendored
View File

@ -525,6 +525,7 @@ packages/app-mobile/commands/util/showResource.js
packages/app-mobile/components/BackButtonDialogBox.js
packages/app-mobile/components/BetaChip.js
packages/app-mobile/components/CameraView.js
packages/app-mobile/components/Checkbox.js
packages/app-mobile/components/DialogManager.js
packages/app-mobile/components/DismissibleDialog.js
packages/app-mobile/components/Dropdown.test.js
@ -588,6 +589,7 @@ packages/app-mobile/components/NoteEditor/hooks/useEditorCommandHandler.test.js
packages/app-mobile/components/NoteEditor/hooks/useEditorCommandHandler.js
packages/app-mobile/components/NoteEditor/hooks/useKeyboardVisible.js
packages/app-mobile/components/NoteEditor/types.js
packages/app-mobile/components/NoteItem.js
packages/app-mobile/components/NoteList.js
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.js
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.js

View File

@ -0,0 +1,67 @@
import * as React from 'react';
import { useState, useEffect, useCallback, useMemo } from 'react';
import { TouchableHighlight, StyleSheet, TextStyle } from 'react-native';
const Icon = require('react-native-vector-icons/Ionicons').default;
interface Props {
checked: boolean;
accessibilityLabel?: string;
onChange?: (checked: boolean)=> void;
style?: TextStyle;
iconStyle?: TextStyle;
}
const useStyles = (baseStyles: TextStyle|undefined, iconStyle: TextStyle|undefined) => {
return useMemo(() => {
return StyleSheet.create({
container: {
...(baseStyles ?? {}),
justifyContent: 'center',
alignItems: 'center',
},
icon: {
fontSize: 20,
height: 22,
color: baseStyles?.color,
...iconStyle,
},
});
}, [baseStyles, iconStyle]);
};
const Checkbox: React.FC<Props> = props => {
const [checked, setChecked] = useState(props.checked);
useEffect(() => {
setChecked(props.checked);
}, [props.checked]);
const onPress = useCallback(() => {
setChecked(checked => {
const newChecked = !checked;
props.onChange?.(newChecked);
return newChecked;
});
}, [props.onChange]);
const iconName = checked ? 'checkbox-outline' : 'square-outline';
const styles = useStyles(props.style, props.iconStyle);
const accessibilityState = useMemo(() => ({
checked,
}), [checked]);
return (
<TouchableHighlight
onPress={onPress}
style={styles.container}
accessibilityRole="checkbox"
accessibilityState={accessibilityState}
accessibilityLabel={props.accessibilityLabel ?? ''}
>
<Icon name={iconName} style={styles.icon} />
</TouchableHighlight>
);
};
export default Checkbox;

View File

@ -1,34 +1,40 @@
const React = require('react');
const Component = React.Component;
const { connect } = require('react-redux');
const { Text, TouchableOpacity, View, StyleSheet } = require('react-native');
const { Checkbox } = require('./checkbox.js');
const Note = require('@joplin/lib/models/Note').default;
const time = require('@joplin/lib/time').default;
const { themeStyle } = require('./global-style');
const { _ } = require('@joplin/lib/locale');
import * as React from 'react';
import { PureComponent } from 'react';
import { connect } from 'react-redux';
import { Text, TouchableOpacity, View, StyleSheet, TextStyle, ViewStyle } from 'react-native';
import Checkbox from './Checkbox';
import Note from '@joplin/lib/models/Note';
import time from '@joplin/lib/time';
import { themeStyle } from './global-style';
import { _ } from '@joplin/lib/locale';
import { AppState } from '../utils/types';
import { Dispatch } from 'redux';
import { NoteEntity } from '@joplin/lib/services/database/types';
class NoteItemComponent extends Component {
constructor() {
super();
this.styles_ = {};
interface Props {
dispatch: Dispatch;
themeId: number;
note: NoteEntity;
noteSelectionEnabled: boolean;
selectedNoteIds: string[];
}
interface State {}
type Styles = Record<string, TextStyle|ViewStyle>;
class NoteItemComponent extends PureComponent<Props, State> {
private styles_: Record<string, Styles> = {};
public constructor(props: Props) {
super(props);
}
noteItem_press(noteId) {
this.props.dispatch({
type: 'NAV_GO',
routeName: 'Note',
noteId: noteId,
});
}
styles() {
private styles() {
const theme = themeStyle(this.props.themeId);
if (this.styles_[this.props.themeId]) return this.styles_[this.props.themeId];
this.styles_ = {};
const styles = {
const styles: Record<string, TextStyle|ViewStyle> = {
listItem: {
flexDirection: 'row',
// height: 40,
@ -49,6 +55,17 @@ class NoteItemComponent extends Component {
selectionWrapper: {
backgroundColor: theme.backgroundColor,
},
checkboxStyle: {
color: theme.color,
paddingRight: 10,
paddingTop: theme.itemMarginTop,
paddingBottom: theme.itemMarginBottom,
paddingLeft: theme.marginLeft,
},
checkedOpacityStyle: {
opacity: 0.4,
},
uncheckedOpacityStyle: { },
};
styles.listItemWithCheckbox = { ...styles.listItem };
@ -57,7 +74,7 @@ class NoteItemComponent extends Component {
delete styles.listItemWithCheckbox.paddingLeft;
styles.listItemTextWithCheckbox = { ...styles.listItemText };
styles.listItemTextWithCheckbox.marginTop = styles.listItem.paddingTop - 1;
styles.listItemTextWithCheckbox.marginTop = theme.itemMarginTop - 1;
styles.listItemTextWithCheckbox.marginBottom = styles.listItem.paddingBottom;
styles.selectionWrapperSelected = { ...styles.selectionWrapper };
@ -67,7 +84,7 @@ class NoteItemComponent extends Component {
return this.styles_[this.props.themeId];
}
async todoCheckbox_change(checked) {
private todoCheckbox_change = async (checked: boolean) => {
if (!this.props.note) return;
const newNote = {
@ -77,9 +94,9 @@ class NoteItemComponent extends Component {
await Note.save(newNote);
this.props.dispatch({ type: 'NOTE_SORT' });
}
};
onPress() {
private onPress = () => {
if (!this.props.note) return;
if (this.props.note.encryption_applied) return;
@ -95,38 +112,26 @@ class NoteItemComponent extends Component {
noteId: this.props.note.id,
});
}
}
};
onLongPress() {
private onLongPress = () => {
if (!this.props.note) return;
this.props.dispatch({
type: this.props.noteSelectionEnabled ? 'NOTE_SELECTION_TOGGLE' : 'NOTE_SELECTION_START',
id: this.props.note.id,
});
}
};
render() {
public render() {
const note = this.props.note ? this.props.note : {};
const isTodo = !!Number(note.is_todo);
const theme = themeStyle(this.props.themeId);
// IOS: display: none crashes the app
const checkboxStyle = !isTodo ? { display: 'none' } : { color: theme.color };
if (isTodo) {
checkboxStyle.paddingRight = 10;
checkboxStyle.paddingTop = theme.itemMarginTop;
checkboxStyle.paddingBottom = theme.itemMarginBottom;
checkboxStyle.paddingLeft = theme.marginLeft;
}
const checkboxChecked = !!Number(note.todo_completed);
const checkboxStyle = this.styles().checkboxStyle;
const listItemStyle = isTodo ? this.styles().listItemWithCheckbox : this.styles().listItem;
const listItemTextStyle = isTodo ? this.styles().listItemTextWithCheckbox : this.styles().listItemText;
const opacityStyle = isTodo && checkboxChecked ? { opacity: 0.4 } : {};
const opacityStyle = isTodo && checkboxChecked ? this.styles().checkedOpacityStyle : this.styles().uncheckedOpacityStyle;
const isSelected = this.props.noteSelectionEnabled && this.props.selectedNoteIds.indexOf(note.id) >= 0;
const selectionWrapperStyle = isSelected ? this.styles().selectionWrapperSelected : this.styles().selectionWrapper;
@ -134,16 +139,16 @@ class NoteItemComponent extends Component {
const noteTitle = Note.displayTitle(note);
return (
<TouchableOpacity onPress={() => this.onPress()} onLongPress={() => this.onLongPress()} activeOpacity={0.5}>
<TouchableOpacity onPress={this.onPress} onLongPress={this.onLongPress} activeOpacity={0.5}>
<View style={selectionWrapperStyle}>
<View style={opacityStyle}>
<View style={listItemStyle}>
<Checkbox
{isTodo ? <Checkbox
style={checkboxStyle}
checked={checkboxChecked}
onChange={checked => this.todoCheckbox_change(checked)}
onChange={this.todoCheckbox_change}
accessibilityLabel={_('to-do: %s', noteTitle)}
/>
/> : null }
<Text style={listItemTextStyle}>{noteTitle}</Text>
</View>
</View>
@ -153,7 +158,7 @@ class NoteItemComponent extends Component {
}
}
const NoteItem = connect(state => {
export default connect((state: AppState) => {
return {
themeId: state.settings.theme,
noteSelectionEnabled: state.noteSelectionEnabled,
@ -161,4 +166,3 @@ const NoteItem = connect(state => {
};
})(NoteItemComponent);
module.exports = { NoteItem };

View File

@ -10,7 +10,7 @@ import getEmptyFolderMessage from '@joplin/lib/components/shared/NoteList/getEmp
import Folder from '@joplin/lib/models/Folder';
const { _ } = require('@joplin/lib/locale');
const { NoteItem } = require('./note-item.js');
import NoteItem from './NoteItem';
import { themeStyle } from './global-style';
interface NoteListProps {

View File

@ -1,77 +0,0 @@
const React = require('react');
const Component = React.Component;
const { View, TouchableHighlight } = require('react-native');
const Icon = require('react-native-vector-icons/Ionicons').default;
const styles = {
checkboxIcon: {
fontSize: 20,
height: 22,
// marginRight: 10,
},
};
class Checkbox extends Component {
constructor() {
super();
this.state = {
checked: false,
};
}
UNSAFE_componentWillMount() {
this.setState({ checked: this.props.checked });
}
UNSAFE_componentWillReceiveProps(newProps) {
if ('checked' in newProps) {
this.setState({ checked: newProps.checked });
}
}
onPress() {
const newChecked = !this.state.checked;
this.setState({ checked: newChecked });
if (this.props.onChange) this.props.onChange(newChecked);
}
render() {
const iconName = this.state.checked ? 'checkbox-outline' : 'square-outline';
const style = this.props.style ? { ...this.props.style } : {};
style.justifyContent = 'center';
style.alignItems = 'center';
const checkboxIconStyle = { ...styles.checkboxIcon };
if (style.color) checkboxIconStyle.color = style.color;
if (style.paddingTop) checkboxIconStyle.marginTop = style.paddingTop;
if (style.paddingBottom) checkboxIconStyle.marginBottom = style.paddingBottom;
if (style.paddingLeft) checkboxIconStyle.marginLeft = style.paddingLeft;
if (style.paddingRight) checkboxIconStyle.marginRight = style.paddingRight;
const thStyle = {
justifyContent: 'center',
alignItems: 'center',
};
if (style && style.display === 'none') return <View />;
// if (style.display) thStyle.display = style.display;
return (
<TouchableHighlight
onPress={() => this.onPress()}
style={thStyle}
accessibilityRole="checkbox"
accessibilityState={{
checked: this.state.checked,
}}
accessibilityLabel={this.props.accessibilityLabel ?? ''}>
<Icon name={iconName} style={checkboxIconStyle} />
</TouchableHighlight>
);
}
}
module.exports = { Checkbox };

View File

@ -26,7 +26,7 @@ import * as mimeUtils from '@joplin/lib/mime-utils';
import ScreenHeader, { MenuOptionType } from '../ScreenHeader';
import NoteTagsDialog from './NoteTagsDialog';
import time from '@joplin/lib/time';
const { Checkbox } = require('../checkbox.js');
import Checkbox from '../Checkbox';
import { _, currentLocale } from '@joplin/lib/locale';
import { reg } from '@joplin/lib/registry';
import ResourceFetcher from '@joplin/lib/services/ResourceFetcher';

View File

@ -6,7 +6,7 @@ import ScreenHeader from '../ScreenHeader';
const Icon = require('react-native-vector-icons/Ionicons').default;
import { _ } from '@joplin/lib/locale';
import Note from '@joplin/lib/models/Note';
const { NoteItem } = require('../note-item.js');
import NoteItem from '../NoteItem';
const { BaseScreenComponent } = require('../base-screen');
import { themeStyle } from '../global-style';
const DialogBox = require('react-native-dialogbox').default;