1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-11-23 22:36:32 +02:00

Mobile: Resolves #13123: Add sync wizard (#13234)

This commit is contained in:
Henry Heino
2025-10-01 01:34:18 -07:00
committed by GitHub
parent b8db70f707
commit 2d703b6292
23 changed files with 412 additions and 97 deletions

View File

@@ -725,6 +725,8 @@ packages/app-mobile/components/SearchInput.js
packages/app-mobile/components/SelectDateTimeDialog.js
packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/SideMenuContentNote.js
packages/app-mobile/components/SyncWizard/JoplinCloudIcon.js
packages/app-mobile/components/SyncWizard/SyncWizard.js
packages/app-mobile/components/TagEditor.test.js
packages/app-mobile/components/TagEditor.js
packages/app-mobile/components/TextInput.js
@@ -741,6 +743,7 @@ packages/app-mobile/components/base-screen.js
packages/app-mobile/components/biometrics/BiometricPopup.js
packages/app-mobile/components/biometrics/biometricAuthenticate.js
packages/app-mobile/components/biometrics/sensorInfo.js
packages/app-mobile/components/buttons/CardButton.js
packages/app-mobile/components/buttons/FloatingActionButton.js
packages/app-mobile/components/buttons/LabelledIconButton.js
packages/app-mobile/components/buttons/MultiTouchableOpacity.js

3
.gitignore vendored
View File

@@ -698,6 +698,8 @@ packages/app-mobile/components/SearchInput.js
packages/app-mobile/components/SelectDateTimeDialog.js
packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/SideMenuContentNote.js
packages/app-mobile/components/SyncWizard/JoplinCloudIcon.js
packages/app-mobile/components/SyncWizard/SyncWizard.js
packages/app-mobile/components/TagEditor.test.js
packages/app-mobile/components/TagEditor.js
packages/app-mobile/components/TextInput.js
@@ -714,6 +716,7 @@ packages/app-mobile/components/base-screen.js
packages/app-mobile/components/biometrics/BiometricPopup.js
packages/app-mobile/components/biometrics/biometricAuthenticate.js
packages/app-mobile/components/biometrics/sensorInfo.js
packages/app-mobile/components/buttons/CardButton.js
packages/app-mobile/components/buttons/FloatingActionButton.js
packages/app-mobile/components/buttons/LabelledIconButton.js
packages/app-mobile/components/buttons/MultiTouchableOpacity.js

View File

@@ -4,7 +4,7 @@ import { Platform, ScrollView, StyleSheet, View } from 'react-native';
import { BarcodeScanner } from './utils/useBarcodeScanner';
import { LinkButton, PrimaryButton } from '../buttons';
import { _ } from '@joplin/lib/locale';
import DismissibleDialog, { DialogSize } from '../DismissibleDialog';
import DismissibleDialog, { DialogVariant } from '../DismissibleDialog';
import { Chip, Text } from 'react-native-paper';
import { isCallbackUrl, parseCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import CommandService from '@joplin/lib/services/CommandService';
@@ -84,7 +84,7 @@ const ScannedBarcodes: React.FC<Props> = props => {
visible={dialogVisible}
onDismiss={onHideDialog}
themeId={props.themeId}
size={DialogSize.Small}
size={DialogVariant.Small}
>
<ScrollView>
<Text variant='titleMedium' role='heading'>{_('Scanned code')}</Text>

View File

@@ -6,7 +6,10 @@ import { themeStyle } from './global-style';
import Modal from './Modal';
import { _ } from '@joplin/lib/locale';
export enum DialogSize {
export enum DialogVariant {
// Small width, auto-determined height
SmallResize = 'small-resize',
Small = 'small',
// Ideal for panels and dialogs that should be fullscreen even on large devices
@@ -20,34 +23,58 @@ interface Props {
containerStyle?: ViewStyle;
children: React.ReactNode;
heading?: string;
scrollOverflow?: boolean;
size: DialogSize;
size: DialogVariant;
}
const useStyles = (themeId: number, containerStyle: ViewStyle, size: DialogSize) => {
const useStyles = (themeId: number, containerStyle: ViewStyle, size: DialogVariant) => {
const windowSize = useWindowDimensions();
return useMemo(() => {
const theme = themeStyle(themeId);
const maxWidth = size === DialogSize.Large ? windowSize.width : 500;
const maxHeight = size === DialogSize.Large ? windowSize.height : 700;
const maxWidth = size === DialogVariant.Large ? windowSize.width : 500;
const maxHeight = size === DialogVariant.Large ? windowSize.height : 700;
const dialogSizing: ViewStyle = {
width: '100%',
...(size !== DialogVariant.SmallResize ? {
height: '100%',
} : { }),
};
return StyleSheet.create({
closeButtonContainer: {
closeButtonRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignContent: 'center',
marginBottom: 8,
},
closeButtonRowWithHeading: {
marginBottom: 16,
},
closeButton: {
margin: 0,
},
// Ensure that the close button is aligned with the center of the header:
// Make its container smaller and center it.
closeButtonWrapper: {
height: 18,
flexDirection: 'column',
justifyContent: 'center',
alignSelf: 'center',
},
heading: {
alignSelf: 'center',
},
modalBackground: {
justifyContent: 'center',
},
dialogContainer: {
maxHeight,
maxWidth,
width: '100%',
height: '100%',
flexShrink: 1,
...dialogSizing,
// Center
marginLeft: 'auto',
@@ -58,11 +85,11 @@ const useStyles = (themeId: number, containerStyle: ViewStyle, size: DialogSize)
...containerStyle,
},
dialogSurface: {
borderRadius: 12,
borderRadius: 24,
backgroundColor: theme.backgroundColor,
padding: 10,
width: '100%',
height: '100%',
paddingHorizontal: 16,
paddingVertical: 24,
...dialogSizing,
},
});
}, [themeId, windowSize.width, windowSize.height, containerStyle, size]);
@@ -76,14 +103,17 @@ const DismissibleDialog: React.FC<Props> = props => {
<Text variant='headlineSmall' role='heading' style={styles.heading}>{props.heading}</Text>
) : null;
const closeButtonRow = (
<View style={styles.closeButtonContainer}>
<View style={[styles.closeButtonRow, !!props.heading && styles.closeButtonRowWithHeading]}>
{heading ?? <View/>}
<View style={styles.closeButtonWrapper}>
<IconButton
icon='close'
accessibilityLabel={_('Close')}
onPress={props.onDismiss}
style={styles.closeButton}
/>
</View>
</View>
);
return (
@@ -92,9 +122,13 @@ const DismissibleDialog: React.FC<Props> = props => {
onDismiss={props.onDismiss}
onRequestClose={props.onDismiss}
containerStyle={styles.dialogContainer}
modalBackgroundStyle={styles.modalBackground}
animationType='fade'
backgroundColor={theme.backgroundColorTransparent2}
transparent={true}
scrollOverflow={props.scrollOverflow}
// Allows the modal background to extend under the statusbar
statusBarTranslucent
>
<Surface style={styles.dialogSurface} elevation={1}>
{closeButtonRow}

View File

@@ -12,7 +12,7 @@ import { AppState } from '../../utils/types';
import CommandService from '@joplin/lib/services/CommandService';
import allToolbarCommandNamesFromState from './utils/allToolbarCommandNamesFromState';
import Setting from '@joplin/lib/models/Setting';
import DismissibleDialog, { DialogSize } from '../DismissibleDialog';
import DismissibleDialog, { DialogVariant } from '../DismissibleDialog';
import selectedCommandNamesFromState from './utils/selectedCommandNamesFromState';
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext';
import { DeleteButton } from '../buttons';
@@ -158,7 +158,7 @@ const ToolbarEditorScreen: React.FC<EditorDialogProps> = props => {
return (
<DismissibleDialog
size={DialogSize.Small}
size={DialogVariant.Small}
themeId={props.themeId}
visible={props.visible}
onDismiss={props.onDismiss}

View File

@@ -4,8 +4,8 @@ import { Text } from 'react-native-paper';
import IconButton from '../IconButton';
import { _ } from '@joplin/lib/locale';
import { useCallback, useState } from 'react';
import DismissibleDialog, { DialogSize } from '../DismissibleDialog';
import { LinkButton } from '../buttons';
import DismissibleDialog, { DialogVariant } from '../DismissibleDialog';
import { LinkButton, PrimaryButton } from '../buttons';
import makeDiscourseDebugUrl from '@joplin/lib/makeDiscourseDebugUrl';
import getPackageInfo from '../../utils/getPackageInfo';
import PluginService from '@joplin/lib/services/plugins/PluginService';
@@ -30,7 +30,10 @@ const onReportBug = () => {
const styles = StyleSheet.create({
feedbackContainer: {
flexGrow: 1,
flexDirection: 'row',
gap: 16,
justifyContent: 'flex-end',
flexWrap: 'wrap',
},
paragraph: {
paddingBottom: 7,
@@ -65,7 +68,7 @@ const WebBetaButton: React.FC<Props> = props => {
/>
<DismissibleDialog
heading={_('Beta')}
size={DialogSize.Small}
size={DialogVariant.SmallResize}
themeId={props.themeId}
visible={dialogVisible}
onDismiss={onHideDialog}
@@ -76,7 +79,7 @@ const WebBetaButton: React.FC<Props> = props => {
{renderParagraph('Feel free to use it and let us know if have any questions or notice any issues!')}
<View style={styles.feedbackContainer}>
<LinkButton onPress={onReportBug}>{'Report bug'}</LinkButton>
<LinkButton onPress={onLeaveFeedback}>{'Give feedback'}</LinkButton>
<PrimaryButton onPress={onLeaveFeedback}>{'Give feedback'}</PrimaryButton>
</View>
</DismissibleDialog>
</>

View File

@@ -0,0 +1,37 @@
import * as React from 'react';
import Svg, { SvgProps, G, Path, Defs, LinearGradient, Stop, ClipPath, Rect } from 'react-native-svg';
const JoplinCloudIcon: React.FC<SvgProps> = props => {
return <Svg
viewBox='0 0 84 84'
fill='none'
{...props}
>
<G clipPath='url(#a)'>
<Path fill='url(#b)' d='M0 0h84v84H0z'/>
<Path
fill='#fff'
d='M73.706 49.825c0 3.732-1.534 7.148-4.007 9.592a13.714 13.714 0 0 1-9.675 3.973h-8.199v-7.065h8.2c1.818 0 3.436-.723 4.635-1.904 1.19-1.188 1.92-2.784 1.92-4.596 0-1.804-.73-3.408-1.92-4.597a6.539 6.539 0 0 0-4.636-1.903h-6.933l.386-3.882c.042-.4.059-.79.059-1.197 0-3.3-1.342-6.251-3.513-8.412a11.964 11.964 0 0 0-8.484-3.483c-3.328 0-6.304 1.33-8.484 3.483-2.18 2.16-3.513 5.112-3.513 8.412 0 .399.017.798.06 1.197l.385 3.882h-6.154c-1.819 0-3.437.723-4.636 1.903-1.19 1.189-1.92 2.785-1.92 4.597 0 1.803.73 3.408 1.92 4.596a6.539 6.539 0 0 0 4.636 1.904h9.935a8.854 8.854 0 0 0 4.82-2.452 8.705 8.705 0 0 0 2.59-6.201v-7.523h-7.217v-2.726c0-2.32 1.903-4.215 4.25-4.215h9.968v14.464c0 4.14-1.685 8.187-4.636 11.105-2.6 2.585-6.078 4.19-9.733 4.53l-.923.083h-9.062c-3.764 0-7.21-1.52-9.674-3.973a13.483 13.483 0 0 1 0-19.185 13.673 13.673 0 0 1 8.35-3.906c.452-4.464 2.48-8.487 5.507-11.488a19.154 19.154 0 0 1 13.523-5.552 19.16 19.16 0 0 1 13.522 5.552 18.842 18.842 0 0 1 5.5 11.446c3.554.141 6.782 1.613 9.129 3.948a13.428 13.428 0 0 1 4.024 9.593z'
strokeWidth={1.3}
/>
</G>
<Defs>
<LinearGradient
id='b'
x1={3}
x2={78}
y1={4}
y2={79}
gradientUnits='userSpaceOnUse'
>
<Stop offset={0.14} stopColor='#3873DB'/>
<Stop offset={0.974} stopColor='#163467'/>
</LinearGradient>
<ClipPath id='a'>
<Rect width={84} height={84} fill='#fff' rx={20} />
</ClipPath>
</Defs>
</Svg>;
};
export default JoplinCloudIcon;

View File

@@ -0,0 +1,138 @@
import * as React from 'react';
import DismissibleDialog, { DialogVariant } from '../DismissibleDialog';
import { AppState } from '../../utils/types';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { useCallback } from 'react';
import { Icon, Text } from 'react-native-paper';
import { _ } from '@joplin/lib/locale';
import JoplinCloudIcon from './JoplinCloudIcon';
import NavService from '@joplin/lib/services/NavService';
import { StyleSheet, View } from 'react-native';
import CardButton from '../buttons/CardButton';
interface Props {
dispatch: Dispatch;
visible: boolean;
themeId: number;
}
const iconSize = 24;
const styles = StyleSheet.create({
titleContainer: {
flexDirection: 'row',
gap: 8,
paddingBottom: 6,
alignItems: 'center',
},
subheading: {
marginBottom: 24,
},
cardContent: {
padding: 12,
borderRadius: 14,
},
syncProviderList: {
gap: 8,
},
featuresList: {
marginTop: 4,
},
listItem: {
flexDirection: 'row',
gap: 8,
marginVertical: 6,
verticalAlign: 'middle',
},
});
interface SyncProviderProps {
title: string;
icon: ()=> React.ReactNode;
description: string;
onPress: ()=> void;
featuresList: string[];
disabled: boolean;
}
const SyncProvider: React.FC<SyncProviderProps> = props => {
return <CardButton
disabled={props.disabled}
onPress={props.onPress}
testID='sync-provider-card'
>
<View style={styles.cardContent}>
<View style={styles.titleContainer}>
{props.icon()}
<Text variant='titleMedium'>{props.title}{props.disabled ? ' (Not supported)' : ''}</Text>
</View>
{props.description && <Text variant='bodyMedium'>{props.description}</Text>}
<View style={styles.featuresList}>
{props.featuresList.map((feature, index) => (
<View key={`feature-${index}`} style={styles.listItem}>
<Icon size={14} source='check'/><Text>{feature}</Text>
</View>
))}
</View>
</View>
</CardButton>;
};
const SyncWizard: React.FC<Props> = ({ themeId, visible, dispatch }) => {
const onDismiss = useCallback(() => {
dispatch({
type: 'SYNC_WIZARD_VISIBLE_CHANGE',
visible: false,
});
}, [dispatch]);
const onSelectJoplinCloud = useCallback(async () => {
onDismiss();
await NavService.go('JoplinCloudLogin');
}, [onDismiss]);
const onSelectOtherTarget = useCallback(async () => {
onDismiss();
await NavService.go('Config', { sectionName: 'sync' });
}, [onDismiss]);
return <DismissibleDialog
themeId={themeId}
visible={visible}
onDismiss={onDismiss}
size={DialogVariant.SmallResize}
scrollOverflow={true}
heading={_('Sync')}
>
<Text variant='bodyLarge' role='heading' style={styles.subheading}>{
_('Joplin can synchronise your notes using various providers. Select one from the list below.')
}</Text>
<View style={styles.syncProviderList}>
<SyncProvider
title={_('Joplin Cloud')}
description={_('Joplin\'s own sync service. Also gives access to Joplin-specific features such as publishing notes or collaborating on notebooks with others.')}
featuresList={[
_('Sync your notes'),
_('Publish notes to the internet'),
_('Collaborate on notebooks with others'),
]}
icon={() => <JoplinCloudIcon width={iconSize} height={iconSize}/>}
onPress={onSelectJoplinCloud}
disabled={false}
/>
<SyncProvider
title={_('Other')}
description={_('Select one of the other supported sync targets.')}
icon={() => <Icon size={iconSize} source='dots-horizontal-circle'/>}
featuresList={[]}
onPress={onSelectOtherTarget}
disabled={false}
/>
</View>
</DismissibleDialog>;
};
export default connect((state: AppState) => ({
visible: state.syncWizardVisible,
themeId: state.settings.theme,
}))(SyncWizard);

View File

@@ -0,0 +1,72 @@
import * as React from 'react';
import { Card, TouchableRipple } from 'react-native-paper';
import { useMemo } from 'react';
import { StyleSheet, View, ViewStyle } from 'react-native';
export enum InstallState {
NotInstalled,
Installing,
Installed,
}
interface Props {
onPress: ()=> void;
disabled: boolean;
children: React.ReactNode;
style?: ViewStyle;
testID?: string;
}
const useStyles = (disabled: boolean) => {
return useMemo(() => {
// For the TouchableRipple to work on Android, the card needs a transparent background.
const baseCard = { backgroundColor: 'transparent' };
return StyleSheet.create({
cardOuterWrapper: {
margin: 0,
padding: 0,
borderRadius: 12,
overflow: 'hidden',
},
cardInnerWrapper: {
width: '100%',
},
card: disabled ? {
...baseCard,
opacity: 0.7,
} : baseCard,
content: {
gap: 5,
},
});
}, [disabled]);
};
const CardButton: React.FC<Props> = props => {
const containerIsButton = !!props.onPress;
const styles = useStyles(props.disabled);
const CardWrapper = containerIsButton ? TouchableRipple : View;
return (
<View style={[styles.cardOuterWrapper, props.style]}>
<CardWrapper
accessibilityRole={containerIsButton ? 'button' : null}
accessible={containerIsButton}
onPress={props.onPress}
disabled={props.disabled}
style={styles.cardInnerWrapper}
testID={props.testID}
>
<Card
mode='outlined'
style={styles.card}
>
{props.children}
</Card>
</CardWrapper>
</View>
);
};
export default CardButton;

View File

@@ -12,7 +12,7 @@ import PluginUserWebView from './PluginUserWebView';
import { View, StyleSheet, AccessibilityInfo } from 'react-native';
import { _ } from '@joplin/lib/locale';
import Setting from '@joplin/lib/models/Setting';
import DismissibleDialog, { DialogSize } from '../../../components/DismissibleDialog';
import DismissibleDialog, { DialogVariant } from '../../../components/DismissibleDialog';
import CommandService from '@joplin/lib/services/CommandService';
interface Props {
@@ -164,7 +164,7 @@ const PluginPanelViewer: React.FC<Props> = props => {
<DismissibleDialog
themeId={props.themeId}
visible={props.visible}
size={DialogSize.Large}
size={DialogVariant.Large}
onDismiss={onClose}
>
{renderTabContent()}

View File

@@ -9,6 +9,7 @@ import { reg } from '@joplin/lib/registry';
import { State } from '@joplin/lib/reducer';
import BackButtonService from '../../../services/BackButtonService';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import ScreenHeader from '../../ScreenHeader';
import { _ } from '@joplin/lib/locale';
import BaseScreenComponent from '../../base-screen';
@@ -60,6 +61,7 @@ interface ConfigScreenProps {
themeId: number;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
navigation: any;
dispatch: Dispatch;
}
class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, ConfigScreenState> {
@@ -126,6 +128,13 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
void NavService.go('EncryptionConfig');
};
private onShowSyncWizard_ = () => {
this.props.dispatch({
type: 'SYNC_WIZARD_VISIBLE_CHANGE',
visible: true,
});
};
private saveButton_press = async () => {
if (this.state.changedSettingKeys.includes('sync.target') && this.state.settings['sync.target'] === SyncTargetRegistry.nameToId('filesystem')) {
if (Platform.OS === 'android') {
@@ -545,6 +554,7 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
}
if (section.name === 'sync') {
addSettingButton('sync_wizard_button', _('Open Sync Wizard...'), this.onShowSyncWizard_);
addSettingButton('e2ee_config_button', _('Encryption Config'), this.e2eeConfig_);
}

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { Card, Text, TouchableRipple } from 'react-native-paper';
import { Card, Text } from 'react-native-paper';
import { _ } from '@joplin/lib/locale';
import { PluginItem } from '@joplin/lib/components/shared/config/plugins/types';
import ActionButton from '../buttons/ActionButton';
@@ -7,11 +7,12 @@ import { ButtonType } from '../../../../buttons/TextButton';
import PluginChips from './PluginChips';
import { UpdateState } from '../utils/useUpdateState';
import { PluginCallback } from '../utils/usePluginCallbacks';
import { useCallback, useMemo } from 'react';
import { useCallback } from 'react';
import { StyleSheet, View } from 'react-native';
import InstallButton from '../buttons/InstallButton';
import PluginTitle from './PluginTitle';
import RecommendedBadge from './RecommendedBadge';
import CardButton from '../../../../buttons/CardButton';
export enum InstallState {
NotInstalled,
@@ -38,28 +39,14 @@ interface Props {
onShowPluginInfo?: PluginCallback;
}
const useStyles = (compatible: boolean) => {
return useMemo(() => {
// For the TouchableRipple to work on Android, the card needs a transparent background.
const baseCard = { backgroundColor: 'transparent' };
return StyleSheet.create({
cardContainer: {
margin: 0,
marginTop: 8,
padding: 0,
borderRadius: 14,
},
card: !compatible ? {
...baseCard,
opacity: 0.7,
} : baseCard,
const styles = StyleSheet.create({
content: {
gap: 5,
},
cardContainer: {
marginTop: 8,
},
});
}, [compatible]);
};
const PluginBox: React.FC<Props> = props => {
const manifest = props.item.manifest;
@@ -78,21 +65,12 @@ const PluginBox: React.FC<Props> = props => {
props.onShowPluginInfo?.({ item: props.item });
}, [props.onShowPluginInfo, props.item]);
const styles = useStyles(props.isCompatible);
const CardWrapper = props.onShowPluginInfo ? TouchableRipple : View;
const containerIsButton = !!props.onShowPluginInfo;
return (
<CardWrapper
accessibilityRole={containerIsButton ? 'button' : null}
accessible={containerIsButton}
onPress={props.onShowPluginInfo ? onPress : null}
<CardButton
style={styles.cardContainer}
>
<Card
mode='outlined'
style={styles.card}
onPress={props.onShowPluginInfo ? onPress : null}
testID='plugin-card'
disabled={!props.isCompatible}
>
<Card.Content style={styles.content}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
@@ -116,8 +94,7 @@ const PluginBox: React.FC<Props> = props => {
{props.onAboutPress ? aboutButton : null}
{props.onInstall ? installButton : null}
</Card.Actions>
</Card>
</CardWrapper>
</CardButton>
);
};

View File

@@ -5,7 +5,7 @@ import { useCallback, useMemo } from 'react';
import { Card, Divider, List, Portal, Switch, Text } from 'react-native-paper';
import getPluginIssueReportUrl from '@joplin/lib/services/plugins/utils/getPluginIssueReportUrl';
import { Linking, ScrollView, StyleSheet, View, ViewStyle } from 'react-native';
import DismissibleDialog, { DialogSize } from '../../../DismissibleDialog';
import DismissibleDialog, { DialogVariant } from '../../../DismissibleDialog';
import openWebsiteForPlugin from './utils/openWebsiteForPlugin';
import PluginService, { PluginSettings } from '@joplin/lib/services/plugins/PluginService';
import PluginTitle from './PluginBox/PluginTitle';
@@ -253,7 +253,7 @@ const PluginInfoModal: React.FC<Props> = props => {
<DismissibleDialog
themeId={props.themeId}
visible={props.visible}
size={DialogSize.Small}
size={DialogVariant.Small}
onDismiss={props.onModalDismiss}
>
{ props.item ? <PluginInfoModalContent {...props}/> : null }

View File

@@ -15,7 +15,7 @@ import useEncryptionWarningMessage from '@joplin/lib/components/shared/ShareNote
import { SharingStatus } from '@joplin/lib/components/shared/ShareNoteDialog/types';
import { AppState } from '../../utils/types';
import { connect } from 'react-redux';
import DismissibleDialog, { DialogSize } from '../DismissibleDialog';
import DismissibleDialog, { DialogVariant } from '../DismissibleDialog';
import { _, _n } from '@joplin/lib/locale';
import { LinkButton, PrimaryButton } from '../buttons';
import { themeStyle } from '../global-style';
@@ -191,7 +191,7 @@ const ShareNoteDialog: React.FC<Props> = props => {
themeId={props.themeId}
visible={props.visible}
onDismiss={props.onClose}
size={DialogSize.Small}
size={DialogVariant.Small}
heading={_('Publish Note')}
>
{props.visible ? <ShareNoteDialogContent {...props}/> : null}

View File

@@ -502,9 +502,8 @@ const SideMenuContentComponent = (props: Props) => {
});
props.dispatch({
type: 'NAV_GO',
routeName: 'Config',
sectionName: 'sync',
type: 'SYNC_WIZARD_VISIBLE_CHANGE',
visible: true,
});
return 'init';

View File

@@ -341,6 +341,7 @@
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo/RNDeviceInfoPrivacyInfo.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
@@ -372,6 +373,7 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",

View File

@@ -1914,6 +1914,8 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- RNSVG (15.13.0):
- React-Core
- RNVectorIcons (10.2.0):
- DoubleConversion
- glog
@@ -2055,6 +2057,7 @@ DEPENDENCIES:
- RNQuickAction (from `../node_modules/react-native-quick-actions`)
- RNSecureRandom (from `../node_modules/react-native-securerandom`)
- RNShare (from `../node_modules/react-native-share`)
- RNSVG (from `../node_modules/react-native-svg`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
@@ -2278,6 +2281,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-securerandom"
RNShare:
:path: "../node_modules/react-native-share"
RNSVG:
:path: "../node_modules/react-native-svg"
RNVectorIcons:
:path: "../node_modules/react-native-vector-icons"
Yoga:
@@ -2391,6 +2396,7 @@ SPEC CHECKSUMS:
RNQuickAction: c2c8f379e614428be0babe4d53a575739667744d
RNSecureRandom: b64d263529492a6897e236a22a2c4249aa1b53dc
RNShare: 675e8e4a84f0137baf33057cac8f7334b0bb4b98
RNSVG: 295a96bc43f2baa5958d64aeec9847a1d8ca7a3d
RNVectorIcons: d53917643fddb261b22bd6d889776f336893622b
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: c758bfb934100bb4bf9cbaccb52557cee35e8bdf

View File

@@ -70,6 +70,7 @@
"react-native-securerandom": "1.0.1",
"react-native-share": "12.0.11",
"react-native-sqlite-storage": "6.0.1",
"react-native-svg": "15.13.0",
"react-native-url-polyfill": "2.0.0",
"react-native-vector-icons": "10.2.0",
"react-native-version-info": "1.1.1",

View File

@@ -107,6 +107,7 @@ import DocumentScanner from './components/screens/DocumentScanner/DocumentScanne
import buildStartupTasks from './utils/buildStartupTasks';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import appReducer from './utils/appReducer';
import SyncWizard from './components/SyncWizard/SyncWizard';
const logger = Logger.create('root');
const perfLogger = PerformanceLogger.create();
@@ -762,6 +763,7 @@ class AppComponent extends React.Component<AppComponentProps, AppComponentState>
</View>
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied */}
<DropdownAlert alert={(func: any) => (this.dropdownAlert_ = func)} />
<SyncWizard/>
</SafeAreaView>
</View>
</SideMenu>

View File

@@ -17,6 +17,7 @@ const appDefaultState: AppState = {
disableSideMenuGestures: false,
showPanelsDialog: false,
noteEditorVisible: false,
syncWizardVisible: false,
...defaultState,
// On mobile, it's possible to select notes that aren't in the selected folder/tag/etc.

View File

@@ -195,6 +195,10 @@ const appReducer = (state = appDefaultState, action: any) => {
case 'NOTE_EDITOR_VISIBLE_CHANGE':
newState = { ...state, noteEditorVisible: action.visible };
break;
case 'SYNC_WIZARD_VISIBLE_CHANGE':
newState = { ...state, syncWizardVisible: action.visible };
break;
}
} catch (error) {
error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`;

View File

@@ -11,4 +11,5 @@ export interface AppState extends State {
noteSideMenuOptions: any;
disableSideMenuGestures: boolean;
noteEditorVisible: boolean;
syncWizardVisible: boolean;
}

View File

@@ -9615,6 +9615,7 @@ __metadata:
react-native-securerandom: "npm:1.0.1"
react-native-share: "npm:12.0.11"
react-native-sqlite-storage: "npm:6.0.1"
react-native-svg: "npm:15.13.0"
react-native-url-polyfill: "npm:2.0.0"
react-native-vector-icons: "npm:10.2.0"
react-native-version-info: "npm:1.1.1"
@@ -42100,6 +42101,20 @@ __metadata:
languageName: node
linkType: hard
"react-native-svg@npm:15.13.0":
version: 15.13.0
resolution: "react-native-svg@npm:15.13.0"
dependencies:
css-select: "npm:^5.1.0"
css-tree: "npm:^1.1.3"
warn-once: "npm:0.1.1"
peerDependencies:
react: "*"
react-native: "*"
checksum: 10/03db4516aec2dfcdf406283bf6b0e18ea3f56cdf228c97f92d282128b1e39c3336424d46a1afec894ebc1c3681b16d2c35c03b08a8a5ee2314c124bdea7ae6e3
languageName: node
linkType: hard
"react-native-url-polyfill@npm:2.0.0":
version: 2.0.0
resolution: "react-native-url-polyfill@npm:2.0.0"
@@ -50732,6 +50747,13 @@ __metadata:
languageName: node
linkType: hard
"warn-once@npm:0.1.1":
version: 0.1.1
resolution: "warn-once@npm:0.1.1"
checksum: 10/e6a5a1f5a8dba7744399743d3cfb571db4c3947897875d4962a7c5b1bf2195ab4518c838cb4cea652e71729f21bba2e98dc75686f5fccde0fabbd894e2ed0c0d
languageName: node
linkType: hard
"wasm-feature-detect@npm:^1.2.11":
version: 1.5.1
resolution: "wasm-feature-detect@npm:1.5.1"