一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
124 行
2.6 KiB
TypeScript
124 行
2.6 KiB
TypeScript
import * as React from 'react';
|
|
import { useMemo, useCallback, useRef } from 'react';
|
|
import { TouchableOpacity, StyleSheet, Text, Linking, View } from 'react-native';
|
|
import { themeStyle } from '../global-style';
|
|
import NavService from '@joplin/lib/services/NavService';
|
|
|
|
interface UrlTarget {
|
|
url: string;
|
|
|
|
screen?: undefined;
|
|
}
|
|
|
|
interface ScreenTarget {
|
|
screen: string;
|
|
screenProps?: Record<string, unknown>;
|
|
|
|
url?: undefined;
|
|
}
|
|
|
|
interface CallbackTarget {
|
|
onPress: ()=> void;
|
|
|
|
url?: undefined;
|
|
screen?: undefined;
|
|
}
|
|
|
|
export type WarningBoxTarget = UrlTarget|ScreenTarget|CallbackTarget;
|
|
|
|
export interface WarningBoxAction {
|
|
title: string;
|
|
onPress: ()=> void;
|
|
}
|
|
|
|
interface Props {
|
|
themeId: number;
|
|
target: WarningBoxTarget;
|
|
message: string;
|
|
testID?: string;
|
|
actions?: WarningBoxAction[];
|
|
}
|
|
|
|
const useStyles = (themeId: number) => {
|
|
return useMemo(() => {
|
|
const theme = themeStyle(themeId);
|
|
return StyleSheet.create({
|
|
container: {
|
|
backgroundColor: '#ff9900',
|
|
flexDirection: 'row',
|
|
padding: theme.marginLeft,
|
|
},
|
|
text: {
|
|
flex: 1,
|
|
color: 'black',
|
|
},
|
|
actionText: {
|
|
color: 'black',
|
|
fontWeight: 'bold',
|
|
textDecorationLine: 'underline',
|
|
marginLeft: theme.marginLeft,
|
|
},
|
|
});
|
|
}, [themeId]);
|
|
};
|
|
|
|
const WarningBox: React.FC<Props> = props => {
|
|
const styles = useStyles(props.themeId);
|
|
|
|
const propsRef = useRef(props);
|
|
propsRef.current = props;
|
|
|
|
const onPress = useCallback(() => {
|
|
const target = propsRef.current.target;
|
|
if (!target) return;
|
|
|
|
const isUrlTarget = (target: WarningBoxTarget): target is UrlTarget => !!target.url;
|
|
if (isUrlTarget(target)) {
|
|
void Linking.openURL(target.url);
|
|
} else if ('onPress' in target) {
|
|
target.onPress();
|
|
} else {
|
|
void NavService.go(target.screen, target.screenProps);
|
|
}
|
|
}, []);
|
|
|
|
const hasTarget = !!propsRef.current.target;
|
|
const bannerContent = <Text style={styles.text}>{props.message}</Text>;
|
|
if (props.actions) {
|
|
return (
|
|
<View style={styles.container} testID={props.testID}>
|
|
{bannerContent}
|
|
{props.actions.map(action => (
|
|
<TouchableOpacity key={action.title} onPress={action.onPress} accessibilityRole='button'>
|
|
<Text style={styles.actionText}>{action.title}</Text>
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
);
|
|
}
|
|
if (hasTarget) {
|
|
return (
|
|
<TouchableOpacity
|
|
style={styles.container}
|
|
onPress={onPress}
|
|
activeOpacity={0.8}
|
|
accessibilityRole={'button'}
|
|
testID={props.testID}
|
|
>
|
|
{bannerContent}
|
|
</TouchableOpacity>
|
|
);
|
|
} else {
|
|
return (
|
|
<View
|
|
style={styles.container}
|
|
testID={props.testID}
|
|
>
|
|
{bannerContent}
|
|
</View>
|
|
);
|
|
}
|
|
};
|
|
|
|
export default WarningBox;
|