2024-03-11 17:02:15 +02:00
|
|
|
import * as React from 'react';
|
2024-06-04 10:57:52 +02:00
|
|
|
import { Card, Text, TouchableRipple } from 'react-native-paper';
|
2024-03-11 17:02:15 +02:00
|
|
|
import { _ } from '@joplin/lib/locale';
|
2024-04-08 13:36:40 +02:00
|
|
|
import { PluginItem } from '@joplin/lib/components/shared/config/plugins/types';
|
2024-06-04 10:57:52 +02:00
|
|
|
import ActionButton from '../buttons/ActionButton';
|
|
|
|
import { ButtonType } from '../../../../buttons/TextButton';
|
|
|
|
import PluginChips from './PluginChips';
|
|
|
|
import { UpdateState } from '../utils/useUpdateState';
|
|
|
|
import { PluginCallback } from '../utils/usePluginCallbacks';
|
|
|
|
import { useCallback, useMemo } from 'react';
|
2024-06-15 11:00:21 +02:00
|
|
|
import { StyleSheet, View } from 'react-native';
|
2024-06-04 10:57:52 +02:00
|
|
|
import InstallButton from '../buttons/InstallButton';
|
|
|
|
import PluginTitle from './PluginTitle';
|
2024-06-15 11:00:21 +02:00
|
|
|
import RecommendedBadge from './RecommendedBadge';
|
2024-03-11 17:02:15 +02:00
|
|
|
|
|
|
|
export enum InstallState {
|
|
|
|
NotInstalled,
|
|
|
|
Installing,
|
|
|
|
Installed,
|
|
|
|
}
|
|
|
|
|
|
|
|
interface Props {
|
2024-04-25 15:02:10 +02:00
|
|
|
themeId: number;
|
2024-03-11 17:02:15 +02:00
|
|
|
item: PluginItem;
|
|
|
|
isCompatible: boolean;
|
|
|
|
|
2024-06-04 10:57:52 +02:00
|
|
|
// In some cases, showing an "installed" chip is redundant (e.g. in the "installed plugins"
|
|
|
|
// tab). In other places (e.g. search), an "installed" chip is important.
|
|
|
|
showInstalledChip: boolean;
|
|
|
|
|
2024-03-11 17:02:15 +02:00
|
|
|
hasErrors?: boolean;
|
|
|
|
installState?: InstallState;
|
|
|
|
updateState?: UpdateState;
|
|
|
|
|
2024-04-25 15:02:10 +02:00
|
|
|
onAboutPress?: PluginCallback;
|
2024-03-11 17:02:15 +02:00
|
|
|
onInstall?: PluginCallback;
|
|
|
|
onShowPluginLog?: PluginCallback;
|
2024-06-04 10:57:52 +02:00
|
|
|
onShowPluginInfo?: PluginCallback;
|
2024-03-11 17:02:15 +02:00
|
|
|
}
|
|
|
|
|
2024-06-04 10:57:52 +02:00
|
|
|
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,
|
2024-04-08 15:52:07 +02:00
|
|
|
},
|
2024-06-04 10:57:52 +02:00
|
|
|
card: !compatible ? {
|
|
|
|
...baseCard,
|
|
|
|
opacity: 0.7,
|
|
|
|
} : baseCard,
|
|
|
|
content: {
|
|
|
|
gap: 5,
|
2024-04-08 15:52:07 +02:00
|
|
|
},
|
2024-06-04 10:57:52 +02:00
|
|
|
});
|
|
|
|
}, [compatible]);
|
2024-04-08 15:52:07 +02:00
|
|
|
};
|
|
|
|
|
2024-04-10 12:39:18 +02:00
|
|
|
|
2024-03-11 17:02:15 +02:00
|
|
|
const PluginBox: React.FC<Props> = props => {
|
|
|
|
const manifest = props.item.manifest;
|
|
|
|
const item = props.item;
|
|
|
|
|
2024-06-04 10:57:52 +02:00
|
|
|
const installButton = <InstallButton
|
|
|
|
item={item}
|
|
|
|
onInstall={props.onInstall}
|
|
|
|
installState={props.installState}
|
|
|
|
isCompatible={props.isCompatible}
|
|
|
|
/>;
|
2024-03-11 17:02:15 +02:00
|
|
|
|
2024-06-04 10:57:52 +02:00
|
|
|
const aboutButton = <ActionButton type={ButtonType.Link} item={item} onPress={props.onAboutPress} title={_('About')}/>;
|
2024-03-11 17:02:15 +02:00
|
|
|
|
2024-06-04 10:57:52 +02:00
|
|
|
const onPress = useCallback(() => {
|
|
|
|
props.onShowPluginInfo?.({ item: props.item });
|
|
|
|
}, [props.onShowPluginInfo, props.item]);
|
2024-03-11 17:02:15 +02:00
|
|
|
|
2024-06-04 10:57:52 +02:00
|
|
|
const styles = useStyles(props.isCompatible);
|
2024-03-11 17:02:15 +02:00
|
|
|
|
2024-06-04 10:57:52 +02:00
|
|
|
return (
|
|
|
|
<TouchableRipple
|
|
|
|
accessibilityRole='button'
|
|
|
|
accessible={true}
|
|
|
|
onPress={props.onShowPluginInfo ? onPress : null}
|
|
|
|
style={styles.cardContainer}
|
2024-04-08 15:52:07 +02:00
|
|
|
>
|
2024-06-04 10:57:52 +02:00
|
|
|
<Card
|
2024-04-03 19:51:09 +02:00
|
|
|
mode='outlined'
|
2024-06-04 10:57:52 +02:00
|
|
|
style={styles.card}
|
|
|
|
testID='plugin-card'
|
|
|
|
>
|
|
|
|
<Card.Content style={styles.content}>
|
2024-06-15 11:00:21 +02:00
|
|
|
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
|
|
|
<View style={{ flexShrink: 1 }}>
|
|
|
|
<PluginTitle manifest={item.manifest} />
|
|
|
|
<Text numberOfLines={2}>{manifest.description}</Text>
|
|
|
|
</View>
|
|
|
|
<RecommendedBadge manifest={item.manifest} isCompatible={props.isCompatible} themeId={props.themeId} />
|
|
|
|
</View>
|
2024-06-04 10:57:52 +02:00
|
|
|
<PluginChips
|
|
|
|
themeId={props.themeId}
|
|
|
|
item={props.item}
|
|
|
|
showInstalledChip={props.showInstalledChip}
|
|
|
|
hasErrors={props.hasErrors}
|
|
|
|
canUpdate={props.updateState === UpdateState.CanUpdate}
|
|
|
|
onShowPluginLog={props.onShowPluginLog}
|
|
|
|
isCompatible={props.isCompatible}
|
|
|
|
/>
|
|
|
|
</Card.Content>
|
|
|
|
<Card.Actions>
|
|
|
|
{props.onAboutPress ? aboutButton : null}
|
|
|
|
{props.onInstall ? installButton : null}
|
|
|
|
</Card.Actions>
|
|
|
|
</Card>
|
|
|
|
</TouchableRipple>
|
2024-03-11 17:02:15 +02:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default PluginBox;
|