You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-23 22:36:32 +02:00
@@ -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
3
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -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;
|
||||
138
packages/app-mobile/components/SyncWizard/SyncWizard.tsx
Normal file
138
packages/app-mobile/components/SyncWizard/SyncWizard.tsx
Normal 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);
|
||||
72
packages/app-mobile/components/buttons/CardButton.tsx
Normal file
72
packages/app-mobile/components/buttons/CardButton.tsx
Normal 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;
|
||||
@@ -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()}
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)}`;
|
||||
|
||||
@@ -11,4 +11,5 @@ export interface AppState extends State {
|
||||
noteSideMenuOptions: any;
|
||||
disableSideMenuGestures: boolean;
|
||||
noteEditorVisible: boolean;
|
||||
syncWizardVisible: boolean;
|
||||
}
|
||||
|
||||
22
yarn.lock
22
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user