1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-24 20:19:10 +02:00

Compare commits

...

21 Commits

Author SHA1 Message Date
Laurent Cozic
9b88c7c8a3 Slow down Renovate updates 2023-01-06 20:06:55 +00:00
Laurent Cozic
8b7d1c463f update 2023-01-06 19:55:04 +00:00
Laurent Cozic
fce9d6429a update 2023-01-06 19:24:40 +00:00
Laurent Cozic
c895601712 udpate 2023-01-06 18:03:59 +00:00
Laurent Cozic
98f22d49d0 update 2023-01-06 17:21:10 +00:00
Laurent Cozic
2331d66a6d update 2023-01-06 12:26:23 +00:00
Laurent Cozic
b091499b09 Profile switcher: Init 2023-01-05 23:36:17 +00:00
Laurent Cozic
b60a94bb6e Merge branch 'pr/react-native-paper-action-buttons' of https://github.com/personalizedrefrigerator/joplin into personalizedrefrigerator-pr/react-native-paper-action-buttons 2023-01-05 23:31:53 +00:00
Henry Heino
c0d0c16d7a Update Podfile.lock after merge 2022-12-28 19:56:40 -08:00
Laurent Cozic
d3ca725395 Merge branch 'dev' into pr/react-native-paper-action-buttons 2022-12-27 14:30:30 +00:00
Henry Heino
d14ec824b4 Merge remote-tracking branch 'upstream/dev' into pr/react-native-paper-action-buttons 2022-12-25 00:27:18 -08:00
Henry Heino
f7a003fbf1 Merge remote-tracking branch 'upstream/dev' into pr/react-native-paper-action-buttons 2022-12-18 14:07:11 -08:00
Henry Heino
887e03e338 Run npx pod-install 2022-12-18 13:22:40 -08:00
Henry Heino
b169283ea8 Update yarn.lock 2022-12-18 13:19:24 -08:00
Henry Heino
b0c898839d Reset to newer version ios directory 2022-12-18 12:41:03 -08:00
Henry Heino
54aa68baad Reset entire iOS directory. npx pod-install will need to be re-run 2022-12-18 12:37:29 -08:00
Henry Heino
3b7fdd5019 Restore project.pbxproj 2022-12-18 12:31:17 -08:00
Henry Heino
63a9c571bb Spaces -> tabs in ActionButton.tsx 2022-12-17 16:52:53 -08:00
Henry Heino
22a10b0abb Update react-native-paper and Merge remote-tracking branch 'upstream/dev' into pr/react-native-paper-action-buttons 2022-12-17 16:45:11 -08:00
Henry Heino
ad5a21193e Commit yarn.lock 2022-10-01 15:28:56 -07:00
Henry Heino
7af794d8c6 Migrate to React Native Paper 2022-10-01 15:28:55 -07:00
27 changed files with 751 additions and 215 deletions

View File

@@ -864,6 +864,9 @@ packages/app-desktop/utils/markupLanguageUtils.js.map
packages/app-mobile/PluginAssetsLoader.d.ts
packages/app-mobile/PluginAssetsLoader.js
packages/app-mobile/PluginAssetsLoader.js.map
packages/app-mobile/components/ActionButton.d.ts
packages/app-mobile/components/ActionButton.js
packages/app-mobile/components/ActionButton.js.map
packages/app-mobile/components/BackButtonDialogBox.d.ts
packages/app-mobile/components/BackButtonDialogBox.js
packages/app-mobile/components/BackButtonDialogBox.js.map
@@ -981,6 +984,15 @@ packages/app-mobile/components/NoteEditor/SelectionFormatting.js.map
packages/app-mobile/components/NoteEditor/types.d.ts
packages/app-mobile/components/NoteEditor/types.js
packages/app-mobile/components/NoteEditor/types.js.map
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.d.ts
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.js
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.js.map
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.d.ts
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.js
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.js.map
packages/app-mobile/components/ProfileSwitcher/useProfileConfig.d.ts
packages/app-mobile/components/ProfileSwitcher/useProfileConfig.js
packages/app-mobile/components/ProfileSwitcher/useProfileConfig.js.map
packages/app-mobile/components/ScreenHeader.d.ts
packages/app-mobile/components/ScreenHeader.js
packages/app-mobile/components/ScreenHeader.js.map
@@ -990,6 +1002,9 @@ packages/app-mobile/components/SelectDateTimeDialog.js.map
packages/app-mobile/components/SideMenu.d.ts
packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/SideMenu.js.map
packages/app-mobile/components/TextInput.d.ts
packages/app-mobile/components/TextInput.js
packages/app-mobile/components/TextInput.js.map
packages/app-mobile/components/biometrics/BiometricPopup.d.ts
packages/app-mobile/components/biometrics/BiometricPopup.js
packages/app-mobile/components/biometrics/BiometricPopup.js.map
@@ -1035,6 +1050,9 @@ packages/app-mobile/services/AlarmServiceDriver.ios.js.map
packages/app-mobile/services/e2ee/RSA.react-native.d.ts
packages/app-mobile/services/e2ee/RSA.react-native.js
packages/app-mobile/services/e2ee/RSA.react-native.js.map
packages/app-mobile/services/profiles/index.d.ts
packages/app-mobile/services/profiles/index.js
packages/app-mobile/services/profiles/index.js.map
packages/app-mobile/setupQuickActions.d.ts
packages/app-mobile/setupQuickActions.js
packages/app-mobile/setupQuickActions.js.map
@@ -1053,6 +1071,9 @@ packages/app-mobile/utils/TlsUtils.js.map
packages/app-mobile/utils/checkPermissions.d.ts
packages/app-mobile/utils/checkPermissions.js
packages/app-mobile/utils/checkPermissions.js.map
packages/app-mobile/utils/createRootStyle.d.ts
packages/app-mobile/utils/createRootStyle.js
packages/app-mobile/utils/createRootStyle.js.map
packages/app-mobile/utils/debounce.d.ts
packages/app-mobile/utils/debounce.js
packages/app-mobile/utils/debounce.js.map

25
.gitignore vendored
View File

@@ -852,6 +852,9 @@ packages/app-desktop/utils/markupLanguageUtils.js.map
packages/app-mobile/PluginAssetsLoader.d.ts
packages/app-mobile/PluginAssetsLoader.js
packages/app-mobile/PluginAssetsLoader.js.map
packages/app-mobile/components/ActionButton.d.ts
packages/app-mobile/components/ActionButton.js
packages/app-mobile/components/ActionButton.js.map
packages/app-mobile/components/BackButtonDialogBox.d.ts
packages/app-mobile/components/BackButtonDialogBox.js
packages/app-mobile/components/BackButtonDialogBox.js.map
@@ -969,6 +972,15 @@ packages/app-mobile/components/NoteEditor/SelectionFormatting.js.map
packages/app-mobile/components/NoteEditor/types.d.ts
packages/app-mobile/components/NoteEditor/types.js
packages/app-mobile/components/NoteEditor/types.js.map
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.d.ts
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.js
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.js.map
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.d.ts
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.js
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.js.map
packages/app-mobile/components/ProfileSwitcher/useProfileConfig.d.ts
packages/app-mobile/components/ProfileSwitcher/useProfileConfig.js
packages/app-mobile/components/ProfileSwitcher/useProfileConfig.js.map
packages/app-mobile/components/ScreenHeader.d.ts
packages/app-mobile/components/ScreenHeader.js
packages/app-mobile/components/ScreenHeader.js.map
@@ -978,6 +990,9 @@ packages/app-mobile/components/SelectDateTimeDialog.js.map
packages/app-mobile/components/SideMenu.d.ts
packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/SideMenu.js.map
packages/app-mobile/components/TextInput.d.ts
packages/app-mobile/components/TextInput.js
packages/app-mobile/components/TextInput.js.map
packages/app-mobile/components/biometrics/BiometricPopup.d.ts
packages/app-mobile/components/biometrics/BiometricPopup.js
packages/app-mobile/components/biometrics/BiometricPopup.js.map
@@ -1023,6 +1038,9 @@ packages/app-mobile/services/AlarmServiceDriver.ios.js.map
packages/app-mobile/services/e2ee/RSA.react-native.d.ts
packages/app-mobile/services/e2ee/RSA.react-native.js
packages/app-mobile/services/e2ee/RSA.react-native.js.map
packages/app-mobile/services/profiles/index.d.ts
packages/app-mobile/services/profiles/index.js
packages/app-mobile/services/profiles/index.js.map
packages/app-mobile/setupQuickActions.d.ts
packages/app-mobile/setupQuickActions.js
packages/app-mobile/setupQuickActions.js.map
@@ -1041,6 +1059,9 @@ packages/app-mobile/utils/TlsUtils.js.map
packages/app-mobile/utils/checkPermissions.d.ts
packages/app-mobile/utils/checkPermissions.js
packages/app-mobile/utils/checkPermissions.js.map
packages/app-mobile/utils/createRootStyle.d.ts
packages/app-mobile/utils/createRootStyle.js
packages/app-mobile/utils/createRootStyle.js.map
packages/app-mobile/utils/debounce.d.ts
packages/app-mobile/utils/debounce.js
packages/app-mobile/utils/debounce.js.map
@@ -2368,6 +2389,4 @@ packages/tools/website/utils/types.d.ts
packages/tools/website/utils/types.js
packages/tools/website/utils/types.js.map
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
packages/app-mobile/components/get-responsive-value.test.js
packages/app-mobile/components/get-responsive-value.test.js
packages/app-mobile/components/get-responsive-value.test.js

View File

@@ -9,6 +9,7 @@ import androidx.multidex.MultiDex;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.oblador.vectoricons.VectorIconsPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;

View File

@@ -1,4 +1,6 @@
rootProject.name = 'Joplin'
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../node_modules/react-native-gradle-plugin')

View File

@@ -0,0 +1,73 @@
const React = require('react');
import { useState, useCallback, useMemo } from 'react';
const Icon = require('react-native-vector-icons/Ionicons').default;
import { FAB, Portal } from 'react-native-paper';
import { _ } from '@joplin/lib/locale';
type OnButtonPress = ()=> void;
interface ButtonSpec {
icon: string;
label: string;
color?: string;
onPress?: OnButtonPress;
}
interface ActionButtonProps {
buttons?: ButtonSpec[];
// If not given, an "add" button will be used.
mainButton?: ButtonSpec;
}
const defaultOnPress = () => {};
// Returns a render function compatible with React Native Paper.
const getIconRenderFunction = (iconName: string) => {
return (props: any) => <Icon name={iconName} {...props} />;
};
const useIcon = (iconName: string) => {
return useMemo(() => {
return getIconRenderFunction(iconName);
}, [iconName]);
};
const ActionButton = (props: ActionButtonProps) => {
const [open, setOpen] = useState(false);
const onMenuToggled = useCallback(
(state: { open: boolean }) => setOpen(state.open)
, [setOpen]);
const actions = useMemo(() => (props.buttons ?? []).map(button => {
return {
...button,
icon: getIconRenderFunction(button.icon),
onPress: button.onPress ?? defaultOnPress,
};
}), [props.buttons]);
const closedIcon = useIcon(props.mainButton?.icon ?? 'md-add');
const openIcon = useIcon('close');
return (
<Portal>
<FAB.Group
open={open}
accessibilityLabel={props.mainButton?.label ?? _('Add new')}
icon={ open ? openIcon : closedIcon }
fabStyle={{
backgroundColor: props.mainButton?.color ?? 'rgba(231,76,60,1)',
}}
onStateChange={onMenuToggled}
actions={actions}
onPress={props.mainButton?.onPress ?? defaultOnPress}
visible={true}
/>
</Portal>
);
};
export default ActionButton;

View File

@@ -0,0 +1,102 @@
const React = require('react');
import { useCallback, useEffect, useMemo, useState } from 'react';
const { View, StyleSheet } = require('react-native');
import createRootStyle from '../../utils/createRootStyle';
import ScreenHeader from '../ScreenHeader';
import { _ } from '@joplin/lib/locale';
import { loadProfileConfig, saveProfileConfig } from '../../services/profiles';
import { createNewProfile } from '@joplin/lib/services/profileConfig';
import useProfileConfig from './useProfileConfig';
const { TextInput } = require('react-native-paper');
interface NavigationState {
profileId: string;
}
interface Navigation {
state: NavigationState;
}
interface Props {
themeId: number;
dispatch: Function;
navigation: Navigation;
}
const useStyle = (themeId: number) => {
return useMemo(() => {
return StyleSheet.create({
...createRootStyle(themeId),
});
}, [themeId]);
};
export default (props: Props) => {
const profileId = props.navigation.state?.profileId;
const isNew = !profileId;
const profileConfig = useProfileConfig();
const style = useStyle(props.themeId);
const [name, setName] = useState('');
const profile = !isNew && profileConfig ? profileConfig.profiles.find(p => p.id === profileId) : null;
useEffect(() => {
if (!profile) return;
setName(profile.name);
}, [profile]);
const onSaveButtonPress = useCallback(async () => {
if (isNew) {
const profileConfig = await loadProfileConfig();
const result = createNewProfile(profileConfig, name);
await saveProfileConfig(result.newConfig);
} else {
const newProfiles = profileConfig.profiles.map(p => {
if (p.id === profile.id) {
return {
...profile,
name,
};
}
return p;
});
const newProfileConfig = {
...profileConfig,
profiles: newProfiles,
};
await saveProfileConfig(newProfileConfig);
}
props.dispatch({
type: 'NAV_BACK',
});
}, [name, isNew, profileConfig, profile, props.dispatch]);
const isModified = useMemo(() => {
if (isNew) return true;
if (!profile) return false;
return profile.name !== name;
}, [isNew, profile, name]);
return (
<View style={style.root}>
<ScreenHeader
title={isNew ? _('Create new profile...') : _('Edit profile')}
onSaveButtonPress={onSaveButtonPress}
saveButtonDisabled={!isModified}
showSaveButton={true}
showSideMenuButton={false}
showSearchButton={false}
/>
<View style={{}}>
<TextInput label={_('Profile name')}
value={name}
onChangeText={(text: string) => setName(text)}
/>
</View>
</View>
);
};

View File

@@ -0,0 +1,176 @@
const React = require('react');
import { useCallback, useMemo, useState } from 'react';
const { View, FlatList, StyleSheet } = require('react-native');
import createRootStyle from '../../utils/createRootStyle';
import ScreenHeader from '../ScreenHeader';
const { FAB, List } = require('react-native-paper');
import { Profile } from '@joplin/lib/services/profileConfig/types';
import useProfileConfig from './useProfileConfig';
import { Alert } from 'react-native';
import { _ } from '@joplin/lib/locale';
import { deleteProfileById } from '@joplin/lib/services/profileConfig';
import { saveProfileConfig, switchProfile } from '../../services/profiles';
const { themeStyle } = require('../global-style');
interface Props {
themeId: number;
dispatch: Function;
}
const useStyle = (themeId: number) => {
return useMemo(() => {
const theme = themeStyle(themeId);
return StyleSheet.create({
...createRootStyle(themeId),
fab: {
position: 'absolute',
margin: 16,
right: 0,
bottom: 0,
},
profileListItem: {
paddingLeft: theme.margin,
paddingRight: theme.margin,
},
});
}, [themeId]);
};
export default (props: Props) => {
const style = useStyle(props.themeId);
const [profileConfigTime, setProfileConfigTime] = useState(Date.now());
const profileConfig = useProfileConfig(profileConfigTime);
const profiles = useMemo(() => {
return profileConfig ? profileConfig.profiles : [];
}, [profileConfig]);
const onProfileItemPress = useCallback(async (profile: Profile) => {
const doIt = async () => {
try {
await switchProfile(profile.id);
} catch (error) {
Alert.alert(_('Could not switch profile: %s', error.message));
}
};
Alert.alert(
_('Confirmation'),
_('To switch the profile, the app is going to close and you will need to restart it.'),
[
{
text: _('Continue'),
onPress: () => doIt(),
style: 'default',
},
{
text: _('Cancel'),
onPress: () => {},
style: 'cancel',
},
]
);
}, []);
const onEditProfile = useCallback(async (profileId: string) => {
props.dispatch({
type: 'NAV_GO',
routeName: 'ProfileEditor',
profileId: profileId,
});
}, [props.dispatch]);
const onDeleteProfile = useCallback(async (profile: Profile) => {
const doIt = async () => {
try {
const newConfig = deleteProfileById(profileConfig, profile.id);
await saveProfileConfig(newConfig);
setProfileConfigTime(Date.now());
} catch (error) {
Alert.alert(error.message);
}
};
Alert.alert(
_('Delete this profile?'),
_('All data, including notes, notebooks and tags will be permanently deleted.'),
[
{
text: _('Delete profile "%s"', profile.name),
onPress: () => doIt(),
style: 'destructive',
},
{
text: _('Cancel'),
onPress: () => {},
style: 'cancel',
},
]
);
}, [profileConfig]);
const renderProfileItem = (event: any) => {
const profile = event.item as Profile;
const titleStyle = { fontWeight: profile.id === profileConfig.currentProfileId ? 'bold' : 'normal' };
return (
<List.Item
title={profile.name}
style={style.profileListItem}
titleStyle={titleStyle}
left={() => <List.Icon icon="file-account-outline" />}
key={profile.id}
profileId={profile.id}
onPress={() => { void onProfileItemPress(profile); }}
onLongPress={() => {
Alert.alert(
_('Configuration'),
'',
[
{
text: _('Edit'),
onPress: () => onEditProfile(profile.id),
style: 'default',
},
{
text: _('Delete'),
onPress: () => onDeleteProfile(profile),
style: 'default',
},
{
text: _('Close'),
onPress: () => {},
style: 'cancel',
},
]
);
}}
/>
);
};
return (
<View style={style.root}>
<ScreenHeader title={_('Profiles')} showSaveButton={false} showSideMenuButton={false} showSearchButton={false} />
<View>
<FlatList
data={profiles}
renderItem={renderProfileItem}
keyExtractor={(profile: Profile) => profile.id}
/>
</View>
<FAB
icon="plus"
style={style.fab}
onPress={() => {
props.dispatch({
type: 'NAV_GO',
routeName: 'ProfileEditor',
});
}}
/>
</View>
);
};

View File

@@ -0,0 +1,20 @@
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
import { ProfileConfig } from '@joplin/lib/services/profileConfig/types';
import { useState } from 'react';
import { loadProfileConfig } from '../../services/profiles';
export default (timestamp: number = 0) => {
const [profileConfig, setProfileConfig] = useState<ProfileConfig>(null);
useAsyncEffect(async (event: AsyncEffectEvent) => {
const load = async () => {
const r = await loadProfileConfig();
if (event.cancelled) return;
setProfileConfig(r);
};
void load();
}, [timestamp]);
return profileConfig;
};

View File

@@ -0,0 +1,37 @@
const React = require('react');
import { useMemo } from 'react';
import { themeStyle } from '@joplin/lib/theme';
import { TextInput, TextInputProps, StyleSheet } from 'react-native';
interface Props extends TextInputProps {
themeId: number;
}
export default (props: Props) => {
const theme = themeStyle(props.themeId);
const finalProps = { ...props };
if (!('placeholderTextColor' in finalProps)) finalProps.placeholderTextColor = theme.colorFaded;
if (!('underlineColorAndroid' in finalProps)) finalProps.underlineColorAndroid = theme.dividerColor;
if (!('selectionColor' in finalProps)) finalProps.selectionColor = theme.textSelectionColor;
if (!('keyboardAppearance' in finalProps)) finalProps.keyboardAppearance = theme.keyboardAppearance;
if (!('style' in finalProps)) finalProps.style = {};
const defaultStyle = useMemo(() => {
const theme = themeStyle(finalProps.themeId);
return StyleSheet.create({
textInput: {
color: theme.color,
paddingLeft: 14,
paddingRight: 14,
paddingTop: 12,
paddingBottom: 12,
},
});
}, [finalProps.themeId]);
finalProps.style = [defaultStyle.textInput, finalProps.style];
return <TextInput {...finalProps} />;
};

View File

@@ -1,169 +0,0 @@
const React = require('react');
const { StyleSheet } = require('react-native');
const Note = require('@joplin/lib/models/Note').default;
const Icon = require('react-native-vector-icons/Ionicons').default;
const ReactNativeActionButton = require('react-native-action-button').default;
const { connect } = require('react-redux');
const { _ } = require('@joplin/lib/locale');
// We need this to suppress the useless warning
// https://github.com/oblador/react-native-vector-icons/issues/1465
Icon.loadFont().catch((error) => { console.info(error); });
const styles = StyleSheet.create({
actionButtonIcon: {
fontSize: 20,
height: 22,
color: 'white',
},
itemText: {
// fontSize: 14, // Cannot currently set fontsize since the bow surrounding the label has a fixed size
},
});
class ActionButtonComponent extends React.Component {
constructor() {
super();
this.state = {
buttonIndex: 0,
};
this.renderIconMultiStates = this.renderIconMultiStates.bind(this);
this.renderIcon = this.renderIcon.bind(this);
}
UNSAFE_componentWillReceiveProps(newProps) {
if ('buttonIndex' in newProps) {
this.setState({ buttonIndex: newProps.buttonIndex });
}
}
async newNoteNavigate(folderId, isTodo) {
const newNote = await Note.save({
parent_id: folderId,
is_todo: isTodo ? 1 : 0,
}, { provisional: true });
this.props.dispatch({
type: 'NAV_GO',
routeName: 'Note',
noteId: newNote.id,
});
}
newTodo_press() {
this.newNoteNavigate(this.props.parentFolderId, true);
}
newNote_press() {
this.newNoteNavigate(this.props.parentFolderId, false);
}
renderIconMultiStates() {
const button = this.props.buttons[this.state.buttonIndex];
return <Icon
name={button.icon}
style={styles.actionButtonIcon}
accessibilityLabel={button.title}
/>;
}
renderIcon() {
const mainButton = this.props.mainButton ? this.props.mainButton : {};
const iconName = mainButton.icon ?? 'md-add';
// Icons don't have alt text by default. We need to add it:
const iconTitle = mainButton.title ?? _('Add new');
// TODO: If the button toggles a sub-menu, state whether the submenu is open
// or closed.
return (
<Icon
name={iconName}
style={styles.actionButtonIcon}
accessibilityLabel={iconTitle}
/>
);
}
render() {
const buttons = this.props.buttons ? this.props.buttons : [];
if (this.props.addFolderNoteButtons) {
if (this.props.folders.length) {
buttons.push({
title: _('New to-do'),
onPress: () => {
this.newTodo_press();
},
color: '#9b59b6',
icon: 'md-checkbox-outline',
});
buttons.push({
title: _('New note'),
onPress: () => {
this.newNote_press();
},
color: '#9b59b6',
icon: 'md-document',
});
}
}
const buttonComps = [];
for (let i = 0; i < buttons.length; i++) {
const button = buttons[i];
const buttonTitle = button.title ? button.title : '';
const key = `${buttonTitle.replace(/\s/g, '_')}_${button.icon}`;
buttonComps.push(
// TODO: By default, ReactNativeActionButton also adds a title, which is focusable
// by the screen reader. As such, each item currently is double-focusable
<ReactNativeActionButton.Item key={key} buttonColor={button.color} title={buttonTitle} onPress={button.onPress}>
<Icon
name={button.icon}
style={styles.actionButtonIcon}
accessibilityLabel={buttonTitle}
/>
</ReactNativeActionButton.Item>
);
}
if (!buttonComps.length && !this.props.mainButton) {
return null;
}
if (this.props.multiStates) {
if (!this.props.buttons || !this.props.buttons.length) throw new Error('Multi-state button requires at least one state');
if (this.state.buttonIndex < 0 || this.state.buttonIndex >= this.props.buttons.length) throw new Error(`Button index out of bounds: ${this.state.buttonIndex}/${this.props.buttons.length}`);
const button = this.props.buttons[this.state.buttonIndex];
return (
<ReactNativeActionButton
renderIcon={this.renderIconMultiStates}
buttonColor="rgba(231,76,60,1)"
onPress={() => {
button.onPress();
}}
/>
);
} else {
return (
<ReactNativeActionButton textStyle={styles.itemText} renderIcon={this.renderIcon} buttonColor="rgba(231,76,60,1)" onPress={function() {}}>
{buttonComps}
</ReactNativeActionButton>
);
}
}
}
const ActionButton = connect(state => {
return {
folders: state.folders,
locale: state.settings.locale,
};
})(ActionButtonComponent);
module.exports = { ActionButton };

View File

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

View File

@@ -69,6 +69,9 @@ function addExtraStyles(style) {
style.keyboardAppearance = style.appearance;
style.color5 = style.backgroundColor4;
style.backgroundColor5 = style.color4;
return style;
}

View File

@@ -100,6 +100,13 @@ class ConfigScreenComponent extends BaseScreenComponent {
void NavService.go('Status');
};
this.manageProfilesButtonPress_ = () => {
this.props.dispatch({
type: 'NAV_GO',
routeName: 'ProfileSwitcher',
});
};
this.exportDebugButtonPress_ = async () => {
this.setState({ creatingReport: true });
const service = new ReportService();
@@ -564,6 +571,7 @@ class ConfigScreenComponent extends BaseScreenComponent {
settingComps.push(this.renderHeader('tools', _('Tools')));
settingComps.push(this.renderButton('profiles_buttons', _('Manage profiles'), this.manageProfilesButtonPress_));
settingComps.push(this.renderButton('status_button', _('Sync Status'), this.syncStatusButtonPress_));
settingComps.push(this.renderButton('log_button', _('Log'), this.logButtonPress_));
if (Platform.OS === 'android') {

View File

@@ -22,7 +22,7 @@ const md5 = require('md5');
const { BackButtonService } = require('../../services/back-button.js');
import NavService from '@joplin/lib/services/NavService';
import BaseModel from '@joplin/lib/BaseModel';
const { ActionButton } = require('../action-button.js');
import ActionButton from '../ActionButton';
const { fileExtension, safeFileExtension } = require('@joplin/lib/path-utils');
const mimeUtils = require('@joplin/lib/mime-utils.js').mime;
import ScreenHeader from '../ScreenHeader';
@@ -1145,21 +1145,19 @@ class NoteScreenComponent extends BaseScreenComponent {
}
const renderActionButton = () => {
const buttons = [];
buttons.push({
title: _('Edit'),
const editButton = {
label: _('Edit'),
icon: 'md-create',
onPress: () => {
this.setState({ mode: 'edit' });
this.doFocusUpdate_ = true;
},
});
};
if (this.state.mode === 'edit') return null;
return <ActionButton multiStates={true} buttons={buttons} buttonIndex={0} />;
return <ActionButton mainButton={editButton} />;
};
const actionButtonComp = renderActionButton();

View File

@@ -1,14 +1,14 @@
const React = require('react');
const { View, TextInput, StyleSheet } = require('react-native');
const { View } = require('react-native');
const { connect } = require('react-redux');
const Folder = require('@joplin/lib/models/Folder').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const { ScreenHeader } = require('../ScreenHeader');
const { BaseScreenComponent } = require('../base-screen.js');
const { dialogs } = require('../../utils/dialogs.js');
const { themeStyle } = require('../global-style.js');
const { _ } = require('@joplin/lib/locale');
const TextInput = require('../TextInput').default;
class FolderScreenComponent extends BaseScreenComponent {
static navigationOptions() {
@@ -21,25 +21,6 @@ class FolderScreenComponent extends BaseScreenComponent {
folder: Folder.new(),
lastSavedFolder: null,
};
this.styles_ = {};
}
styles() {
const theme = themeStyle(this.props.themeId);
if (this.styles_[this.props.themeId]) return this.styles_[this.props.themeId];
this.styles_ = {};
const styles = {
textInput: {
color: theme.color,
paddingLeft: theme.marginLeft,
marginTop: theme.marginTop,
},
};
this.styles_[this.props.themeId] = StyleSheet.create(styles);
return this.styles_[this.props.themeId];
}
UNSAFE_componentWillMount() {
@@ -103,12 +84,17 @@ class FolderScreenComponent extends BaseScreenComponent {
render() {
const saveButtonDisabled = !this.isModified();
const theme = themeStyle(this.props.themeId);
return (
<View style={this.rootStyle(this.props.themeId).root}>
<ScreenHeader title={_('Edit notebook')} showSaveButton={true} saveButtonDisabled={saveButtonDisabled} onSaveButtonPress={() => this.saveFolderButton_press()} showSideMenuButton={false} showSearchButton={false} />
<TextInput placeholder={_('Enter notebook title')} placeholderTextColor={theme.colorFaded} underlineColorAndroid={theme.dividerColor} selectionColor={theme.textSelectionColor} keyboardAppearance={theme.keyboardAppearance} style={this.styles().textInput} autoFocus={true} value={this.state.folder.title} onChangeText={text => this.title_changeText(text)} />
<TextInput
themeId={this.props.themeId}
placeholder={_('Enter notebook title')}
autoFocus={true}
value={this.state.folder.title}
onChangeText={text => this.title_changeText(text)}
/>
<dialogs.DialogBox
ref={dialogbox => {
this.dialogbox = dialogbox;

View File

@@ -11,7 +11,7 @@ const Setting = require('@joplin/lib/models/Setting').default;
const { themeStyle } = require('../global-style.js');
const { ScreenHeader } = require('../ScreenHeader');
const { _ } = require('@joplin/lib/locale');
const { ActionButton } = require('../action-button.js');
const ActionButton = require('../ActionButton').default;
const { dialogs } = require('../../utils/dialogs.js');
const DialogBox = require('react-native-dialogbox').default;
const { BaseScreenComponent } = require('../base-screen.js');
@@ -179,6 +179,19 @@ class NotesScreenComponent extends BaseScreenComponent {
});
}
newNoteNavigate = async (folderId, isTodo) => {
const newNote = await Note.save({
parent_id: folderId,
is_todo: isTodo ? 1 : 0,
}, { provisional: true });
this.props.dispatch({
type: 'NAV_GO',
routeName: 'Note',
noteId: newNote.id,
});
};
parentItem(props = null) {
if (!props) props = this.props;
@@ -238,7 +251,35 @@ class NotesScreenComponent extends BaseScreenComponent {
const addFolderNoteButtons = !!buttonFolderId;
const thisComp = this;
const actionButtonComp = this.props.noteSelectionEnabled || !this.props.visible ? null : <ActionButton addFolderNoteButtons={addFolderNoteButtons} parentFolderId={buttonFolderId}></ActionButton>;
const makeActionButtonComp = () => {
if (addFolderNoteButtons && this.props.folders.length > 0) {
const buttons = [];
buttons.push({
label: _('New to-do'),
onPress: () => {
const isTodo = true;
this.newNoteNavigate(buttonFolderId, isTodo);
},
color: '#9b59b6',
icon: 'md-checkbox-outline',
});
buttons.push({
label: _('New note'),
onPress: () => {
const isTodo = false;
this.newNoteNavigate(buttonFolderId, isTodo);
},
color: '#9b59b6',
icon: 'md-document',
});
return <ActionButton buttons={buttons}/>;
}
return null;
};
const actionButtonComp = this.props.noteSelectionEnabled || !this.props.visible ? null : makeActionButtonComp();
return (
<View style={rootStyle}>

View File

@@ -13,6 +13,7 @@ import { FolderEntity, FolderIcon } from '@joplin/lib/services/database/types';
import { AppState } from '../utils/types';
import Setting from '@joplin/lib/models/Setting';
import { reg } from '@joplin/lib/registry';
import { ProfileConfig } from '@joplin/lib/services/profileConfig/types';
// We need this to suppress the useless warning
// https://github.com/oblador/react-native-vector-icons/issues/1465
@@ -31,6 +32,7 @@ interface Props {
notesParentType: string;
folders: FolderEntity[];
opacity: number;
profileConfig: ProfileConfig;
}
const syncIconRotationValue = new Animated.Value(0);
@@ -200,6 +202,15 @@ const SideMenuContentComponent = (props: Props) => {
});
};
const switchProfileButton_press = () => {
props.dispatch({ type: 'SIDE_MENU_CLOSE' });
props.dispatch({
type: 'NAV_GO',
routeName: 'ProfileSwitcher',
});
};
const configButton_press = () => {
props.dispatch({ type: 'SIDE_MENU_CLOSE' });
void NavService.go('Config');
@@ -403,6 +414,10 @@ const SideMenuContentComponent = (props: Props) => {
items.push(renderSidebarButton('tag_button', _('Tags'), 'md-pricetag', tagButton_press));
if (props.profileConfig && props.profileConfig.profiles.length > 1) {
items.push(renderSidebarButton('switchProfile_button', _('Switch profile'), 'md-people-circle-outline', switchProfileButton_press));
}
items.push(renderSidebarButton('config_button', _('Configuration'), 'md-settings', configButton_press));
items.push(makeDivider('divider_2'));
@@ -502,5 +517,6 @@ export default connect((state: AppState) => {
resourceFetcher: state.resourceFetcher,
isOnMobileData: state.isOnMobileData,
syncOnlyOverWifi: state.settings['sync.mobileWifiOnly'],
profileConfig: state.profileConfig,
};
})(SideMenuContentComponent);

View File

@@ -248,6 +248,12 @@ PODS:
- React-Core
- react-native-rsa-native (2.0.5):
- React
- react-native-safe-area-context (4.4.1):
- RCT-Folly
- RCTRequired
- RCTTypeSafety
- React-Core
- ReactCommon/turbomodule/core
- react-native-slider (4.4.0):
- React-Core
- react-native-sqlite-storage (6.0.1):
@@ -329,6 +335,8 @@ PODS:
- React-Core
- RNDateTimePicker (6.7.1):
- React-Core
- RNExitApp (1.1.0):
- React
- RNFileViewer (2.1.5):
- React-Core
- RNFS (2.20.0):
@@ -376,6 +384,7 @@ DEPENDENCIES:
- react-native-image-resizer (from `../node_modules/react-native-image-resizer`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-rsa-native (from `../node_modules/react-native-rsa-native`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
- react-native-sqlite-storage (from `../node_modules/react-native-sqlite-storage`)
- react-native-version-info (from `../node_modules/react-native-version-info`)
@@ -396,6 +405,7 @@ DEPENDENCIES:
- "RNCClipboard (from `../node_modules/@react-native-community/clipboard`)"
- "RNCPushNotificationIOS (from `../node_modules/@react-native-community/push-notification-ios`)"
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
- RNExitApp (from `../node_modules/react-native-exit-app`)
- RNFileViewer (from `../node_modules/react-native-file-viewer`)
- RNFS (from `../node_modules/react-native-fs`)
- RNQuickAction (from `../node_modules/react-native-quick-actions`)
@@ -469,6 +479,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-community/netinfo"
react-native-rsa-native:
:path: "../node_modules/react-native-rsa-native"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-slider:
:path: "../node_modules/@react-native-community/slider"
react-native-sqlite-storage:
@@ -509,6 +521,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-community/push-notification-ios"
RNDateTimePicker:
:path: "../node_modules/@react-native-community/datetimepicker"
RNExitApp:
:path: "../node_modules/react-native-exit-app"
RNFileViewer:
:path: "../node_modules/react-native-file-viewer"
RNFS:
@@ -556,6 +570,7 @@ SPEC CHECKSUMS:
react-native-image-resizer: d9fb629a867335bdc13230ac2a58702bb8c8828f
react-native-netinfo: 2517ad504b3d303e90d7a431b0fcaef76d207983
react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a
react-native-safe-area-context: 99b24a0c5acd0d5dcac2b1a7f18c49ea317be99a
react-native-slider: d2938a12c4e439a227c70eec65d119136eb4aeb5
react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261
react-native-version-info: a106f23009ac0db4ee00de39574eb546682579b9
@@ -576,6 +591,7 @@ SPEC CHECKSUMS:
RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495
RNCPushNotificationIOS: 87b8d16d3ede4532745e05b03c42cff33a36cc45
RNDateTimePicker: 0530a73a6f3a1a85814cbde0802736993b9e675e
RNExitApp: c4e052df2568b43bec8a37c7cd61194d4cfee2c3
RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93

View File

@@ -44,6 +44,7 @@
"react-native-dialogbox": "0.6.10",
"react-native-document-picker": "8.1.3",
"react-native-dropdownalert": "4.5.1",
"react-native-exit-app": "1.1.0",
"react-native-file-viewer": "2.1.5",
"react-native-fingerprint-scanner": "6.0.0",
"react-native-fs": "2.20.0",
@@ -51,9 +52,11 @@
"react-native-image-picker": "4.10.3",
"react-native-image-resizer": "1.4.5",
"react-native-modal-datetime-picker": "14.0.1",
"react-native-paper": "5.0.2",
"react-native-popup-menu": "0.16.1",
"react-native-quick-actions": "0.3.13",
"react-native-rsa-native": "2.0.5",
"react-native-safe-area-context": "4.4.1",
"react-native-securerandom": "1.0.1",
"react-native-share": "8.1.0",
"react-native-side-menu-updated": "1.3.2",

View File

@@ -15,7 +15,6 @@ import KvStore from '@joplin/lib/services/KvStore';
import NoteScreen from './components/screens/Note';
import UpgradeSyncTargetScreen from './components/screens/UpgradeSyncTargetScreen';
import Setting, { Env } from '@joplin/lib/models/Setting';
import RNFetchBlob from 'rn-fetch-blob';
import PoorManIntervals from '@joplin/lib/PoorManIntervals';
import reducer from '@joplin/lib/reducer';
import ShareExtension from './utils/ShareExtension';
@@ -27,6 +26,7 @@ import { setLocale, closestSupportedLocale, defaultLocale } from '@joplin/lib/lo
import SyncTargetJoplinServer from '@joplin/lib/SyncTargetJoplinServer';
import SyncTargetJoplinCloud from '@joplin/lib/SyncTargetJoplinCloud';
import SyncTargetOneDrive from '@joplin/lib/SyncTargetOneDrive';
import initProfile from '@joplin/lib/services/profileConfig/initProfile';
const VersionInfo = require('react-native-version-info').default;
const { Keyboard, NativeModules, BackHandler, Animated, View, StatusBar, Linking, Platform, Dimensions } = require('react-native');
const RNAppState = require('react-native').AppState;
@@ -36,6 +36,7 @@ const DropdownAlert = require('react-native-dropdownalert').default;
const AlarmServiceDriver = require('./services/AlarmServiceDriver').default;
const SafeAreaView = require('./components/SafeAreaView');
const { connect, Provider } = require('react-redux');
import { Provider as PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-native-paper';
const { BackButtonService } = require('./services/back-button.js');
import NavService from '@joplin/lib/services/NavService';
import { createStore, applyMiddleware } from 'redux';
@@ -108,8 +109,13 @@ import SyncTargetNone from '@joplin/lib/SyncTargetNone';
import { setRSA } from '@joplin/lib/services/e2ee/ppk';
import RSA from './services/e2ee/RSA.react-native';
import { runIntegrationTests } from '@joplin/lib/services/e2ee/ppkTestUtils';
import { Theme, ThemeAppearance } from '@joplin/lib/themes/type';
import { AppState } from './utils/types';
import ProfileSwitcher from './components/ProfileSwitcher/ProfileSwitcher';
import ProfileEditor from './components/ProfileSwitcher/ProfileEditor';
import sensorInfo from './components/biometrics/sensorInfo';
import { getCurrentProfile } from '@joplin/lib/services/profileConfig';
import { getDatabaseName, getProfilesRootDir, getResourceDir, setDispatch } from './services/profiles';
let storeDispatch = function(_action: any) {};
@@ -407,11 +413,23 @@ function decryptionWorker_resourceMetadataButNotBlobDecrypted() {
async function initialize(dispatch: Function) {
shimInit();
setDispatch(dispatch);
const { profileConfig, isSubProfile } = await initProfile(getProfilesRootDir());
const currentProfile = getCurrentProfile(profileConfig);
dispatch({
type: 'PROFILE_CONFIG_SET',
value: profileConfig,
});
// @ts-ignore
Setting.setConstant('env', __DEV__ ? 'dev' : 'prod');
Setting.setConstant('appId', 'net.cozic.joplin-mobile');
Setting.setConstant('appType', 'mobile');
Setting.setConstant('resourceDir', RNFetchBlob.fs.dirs.DocumentDir);
const resourceDir = getResourceDir(currentProfile, isSubProfile);
Setting.setConstant('resourceDir', resourceDir);
await shim.fsDriver().mkdir(resourceDir);
const logDatabase = new Database(new DatabaseDriverReactNative());
await logDatabase.open({ name: 'log.sqlite' });
@@ -479,9 +497,9 @@ async function initialize(dispatch: Function) {
try {
if (Setting.value('env') === 'prod') {
await db.open({ name: 'joplin.sqlite' });
await db.open({ name: getDatabaseName(currentProfile, isSubProfile) });
} else {
await db.open({ name: 'joplin-101.sqlite' });
await db.open({ name: getDatabaseName(currentProfile, isSubProfile) });
// await db.clearForTesting();
}
@@ -769,6 +787,13 @@ class AppComponent extends React.Component {
type: 'APP_STATE_SET',
state: 'ready',
});
// setTimeout(() => {
// this.props.dispatch({
// type: 'NAV_GO',
// routeName: 'ProfileSwitcher',
// });
// }, 1000);
}
Linking.addEventListener('url', this.handleOpenURL_);
@@ -881,7 +906,7 @@ class AppComponent extends React.Component {
public render() {
if (this.props.appState !== 'ready') return null;
const theme = themeStyle(this.props.themeId);
const theme: Theme = themeStyle(this.props.themeId);
let sideMenuContent = null;
let menuPosition = 'left';
@@ -902,6 +927,8 @@ class AppComponent extends React.Component {
DropboxLogin: { screen: DropboxLoginScreen },
EncryptionConfig: { screen: EncryptionConfigScreen },
UpgradeSyncTarget: { screen: UpgradeSyncTargetScreen },
ProfileSwitcher: { screen: ProfileSwitcher },
ProfileEditor: { screen: ProfileEditor },
Log: { screen: LogScreen },
Status: { screen: StatusScreen },
Search: { screen: SearchScreen },
@@ -912,7 +939,7 @@ class AppComponent extends React.Component {
// const statusBarStyle = theme.appearance === 'light-content';
const statusBarStyle = 'light-content';
return (
const mainContent = (
<View style={{ flex: 1, backgroundColor: theme.backgroundColor }}>
<SideMenu
menu={sideMenuContent}
@@ -932,7 +959,7 @@ class AppComponent extends React.Component {
<SafeAreaView style={{ flex: 0, backgroundColor: theme.backgroundColor2 }}/>
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1, backgroundColor: theme.backgroundColor }}>
<AppNav screens={appNavInit} />
<AppNav screens={appNavInit} dispatch={this.props.dispatch} />
</View>
<DropdownAlert ref={(ref: any) => this.dropdownAlert_ = ref} tapToCloseEnabled={true} />
<Animated.View pointerEvents='none' style={{ position: 'absolute', backgroundColor: 'black', opacity: this.state.sideMenuContentOpacity, width: '100%', height: '120%' }}/>
@@ -945,6 +972,27 @@ class AppComponent extends React.Component {
</SideMenu>
</View>
);
const paperTheme = theme.appearance === ThemeAppearance.Dark ? MD3DarkTheme : MD3LightTheme;
// Wrap everything in a PaperProvider -- this allows using components from react-native-paper
return (
<PaperProvider theme={{
...paperTheme,
version: 3,
colors: {
...paperTheme.colors,
onPrimaryContainer: theme.color5,
primaryContainer: theme.backgroundColor5,
surfaceVariant: theme.backgroundColor,
onSurfaceVariant: theme.color,
primary: theme.color,
},
}}>
{mainContent}
</PaperProvider>
);
}
}

View File

@@ -0,0 +1,51 @@
// Helper functions to reduce the boiler plate of loading and saving profiles on
// mobile
const RNExitApp = require('react-native-exit-app').default;
import { Profile, ProfileConfig } from '@joplin/lib/services/profileConfig/types';
import { loadProfileConfig as libLoadProfileConfig, saveProfileConfig as libSaveProfileConfig } from '@joplin/lib/services/profileConfig/index';
import RNFetchBlob from 'rn-fetch-blob';
let dispatch_: Function = null;
export const setDispatch = (dispatch: Function) => {
dispatch_ = dispatch;
};
export const getProfilesRootDir = () => {
return RNFetchBlob.fs.dirs.DocumentDir;
};
export const getProfilesConfigPath = () => {
return `${getProfilesRootDir()}/profiles.json`;
};
export const getResourceDir = (profile: Profile, isSubProfile: boolean) => {
if (!isSubProfile) return getProfilesRootDir();
return `${getProfilesRootDir()}/resources-${profile.id}`;
};
export const getDatabaseName = (profile: Profile, isSubProfile: boolean) => {
if (!isSubProfile) return 'joplin.sqlite';
return `joplin-${profile.id}.sqlite`;
};
export const loadProfileConfig = async () => {
return libLoadProfileConfig(getProfilesConfigPath());
};
export const saveProfileConfig = async (profileConfig: ProfileConfig) => {
await libSaveProfileConfig(getProfilesConfigPath(), profileConfig);
dispatch_({
type: 'PROFILE_CONFIG_SET',
value: profileConfig,
});
};
export const switchProfile = async (profileId: string) => {
const config = await loadProfileConfig();
if (config.currentProfileId === profileId) throw new Error('This profile is already active');
config.currentProfileId = profileId;
await saveProfileConfig(config);
RNExitApp.exitApp();
};

View File

@@ -0,0 +1,11 @@
const { themeStyle } = require('../components/global-style');
export default (themeId: number) => {
const theme = themeStyle(themeId);
return {
root: {
flex: 1,
backgroundColor: theme.backgroundColor,
},
};
};

View File

@@ -7,6 +7,7 @@ import BaseModel from './BaseModel';
import { Store } from 'redux';
import { ProfileConfig } from './services/profileConfig/types';
import * as ArrayUtils from './ArrayUtils';
import { FolderEntity } from './services/database/types';
const fastDeepEqual = require('fast-deep-equal');
const { ALL_NOTES_FILTER_ID } = require('./reserved-ids');
const { createSelectorCreator, defaultMemoize } = require('reselect');
@@ -55,7 +56,7 @@ export interface State {
noteSelectionEnabled?: boolean;
notesSource: string;
notesParentType: string;
folders: any[];
folders: FolderEntity[];
tags: any[];
masterKeys: any[];
notLoadedMasterKeys: string[];

View File

@@ -2,6 +2,7 @@ import { rtrimSlashes } from '../../path-utils';
import shim from '../../shim';
import { CurrentProfileVersion, defaultProfile, defaultProfileConfig, DefaultProfileId, Profile, ProfileConfig } from './types';
import { customAlphabet } from 'nanoid/non-secure';
import { _ } from '../../locale';
export const migrateProfileConfig = (profileConfig: any, toVersion: number): ProfileConfig => {
let version = 2;
@@ -99,6 +100,17 @@ export const createNewProfile = (config: ProfileConfig, profileName: string) =>
};
};
export const deleteProfileById = (config: ProfileConfig, profileId: string): ProfileConfig => {
if (profileId === DefaultProfileId) throw new Error(_('The default profile cannot be deleted'));
if (profileId === config.currentProfileId) throw new Error(_('The active profile cannot be deleted. Switch to a different profile and try again.'));
const newProfiles = config.profiles.filter(p => p.id !== profileId);
return {
...config,
profiles: newProfiles,
};
};
export const profileIdByIndex = (config: ProfileConfig, index: number): string => {
return config.profiles[index].id;
};

View File

@@ -41,6 +41,9 @@ export interface Theme {
backgroundColor4: string;
color4: string;
backgroundColor5?: string;
color5?: string;
raisedBackgroundColor: string;
raisedColor: string;
searchMarkerBackgroundColor: string;

View File

@@ -112,7 +112,7 @@
"matchUpdateTypes": ["minor", "patch"],
"automerge": true,
"labels": ["automerge"],
"schedule": "on the first day of the week",
"extends": ["schedule:monthly"],
"matchPackageNames": [
// AWS packages are updated too frequently and we can assume minor
// updates are stable.

View File

@@ -3150,6 +3150,18 @@ __metadata:
languageName: node
linkType: hard
"@callstack/react-theme-provider@npm:^3.0.8":
version: 3.0.8
resolution: "@callstack/react-theme-provider@npm:3.0.8"
dependencies:
deepmerge: ^3.2.0
hoist-non-react-statics: ^3.3.0
peerDependencies:
react: ">=16.3.0"
checksum: 6077a4795aea4eb06a2a2ffe5cf299c3fdcba56530aa68eba5c3ac0728ce02be2f6e9e71278be303065cb7fbe252c35639538477d5d05ee14f6325d550c8c696
languageName: node
linkType: hard
"@cloudcmd/create-element@npm:^2.0.0":
version: 2.0.2
resolution: "@cloudcmd/create-element@npm:2.0.2"
@@ -4725,6 +4737,7 @@ __metadata:
react-native-dialogbox: 0.6.10
react-native-document-picker: 8.1.3
react-native-dropdownalert: 4.5.1
react-native-exit-app: 1.1.0
react-native-file-viewer: 2.1.5
react-native-fingerprint-scanner: 6.0.0
react-native-fs: 2.20.0
@@ -4732,9 +4745,11 @@ __metadata:
react-native-image-picker: 4.10.3
react-native-image-resizer: 1.4.5
react-native-modal-datetime-picker: 14.0.1
react-native-paper: 5.0.2
react-native-popup-menu: 0.16.1
react-native-quick-actions: 0.3.13
react-native-rsa-native: 2.0.5
react-native-safe-area-context: 4.4.1
react-native-securerandom: 1.0.1
react-native-share: 8.1.0
react-native-side-menu-updated: 1.3.2
@@ -11994,7 +12009,7 @@ __metadata:
languageName: node
linkType: hard
"color@npm:3.2.1":
"color@npm:3.2.1, color@npm:^3.1.2":
version: 3.2.1
resolution: "color@npm:3.2.1"
dependencies:
@@ -27604,6 +27619,13 @@ __metadata:
languageName: node
linkType: hard
"react-native-exit-app@npm:1.1.0":
version: 1.1.0
resolution: "react-native-exit-app@npm:1.1.0"
checksum: 383e28e03759ebf21ae54cb914e06462fb3ef43ed78895e83395386d615c9a56faacba599edf17677e44a4ebf641102b295545964b787cc09b01b16c9384e8d9
languageName: node
linkType: hard
"react-native-file-viewer@npm:2.1.5":
version: 2.1.5
resolution: "react-native-file-viewer@npm:2.1.5"
@@ -27687,6 +27709,22 @@ __metadata:
languageName: node
linkType: hard
"react-native-paper@npm:5.0.2":
version: 5.0.2
resolution: "react-native-paper@npm:5.0.2"
dependencies:
"@callstack/react-theme-provider": ^3.0.8
color: ^3.1.2
use-event-callback: ^0.1.0
peerDependencies:
react: "*"
react-native: "*"
react-native-safe-area-context: "*"
react-native-vector-icons: "*"
checksum: 46481e3db2b297f2f02d1d05710d7ab329f901acc5a663e4c81fb7db83534e5d63067e9287bb396703e50d955ddfb8c41d7546cbd1ea2825a6888fd2e0fa80de
languageName: node
linkType: hard
"react-native-popup-menu@npm:0.16.1":
version: 0.16.1
resolution: "react-native-popup-menu@npm:0.16.1"
@@ -27708,6 +27746,16 @@ __metadata:
languageName: node
linkType: hard
"react-native-safe-area-context@npm:4.4.1":
version: 4.4.1
resolution: "react-native-safe-area-context@npm:4.4.1"
peerDependencies:
react: "*"
react-native: "*"
checksum: ef7c41ea59a34b114c6481fb130e66ef85e8d5b88acb46279131367761ca9fbf22cd310fe613f49b6c9b56dbd83e044be640f0532eda1d3856bf708e96335a35
languageName: node
linkType: hard
"react-native-securerandom@npm:1.0.1":
version: 1.0.1
resolution: "react-native-securerandom@npm:1.0.1"
@@ -33141,6 +33189,15 @@ __metadata:
languageName: node
linkType: hard
"use-event-callback@npm:^0.1.0":
version: 0.1.0
resolution: "use-event-callback@npm:0.1.0"
peerDependencies:
react: ">=16.8"
checksum: 1e15fb21306c74f877e9d57686546c363165429412dcb9260254d2dd8f56692cb01ba2162f9169e6fc15b01cf3921b9cd8a9c60cf777d0143afcee92c1a7976a
languageName: node
linkType: hard
"use-isomorphic-layout-effect@npm:^1.1.2":
version: 1.1.2
resolution: "use-isomorphic-layout-effect@npm:1.1.2"