@react-navigation/native#useIsFocused TypeScript Examples
The following examples show how to use
@react-navigation/native#useIsFocused.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: a11y-element.tsx From protect-scotland with Apache License 2.0 | 6 votes |
export function useA11yElement() {
const {
accessibility: {screenReaderEnabled}
} = useApplication();
const focusRef = useRef(null);
const isFocused = useIsFocused();
const focusA11yElement = useCallback(
(timeout = 250) => {
if (screenReaderEnabled && focusRef.current && isFocused) {
const tag = findNodeHandle(focusRef.current);
if (tag) {
const id = setTimeout(
() => AccessibilityInfo.setAccessibilityFocus(tag),
timeout
);
return () => clearTimeout(id);
}
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[screenReaderEnabled, focusRef.current, isFocused]
);
const a11yProps = useMemo(
() => ({
ref: focusRef
}),
[]
);
return {a11yProps, focusRef, focusA11yElement};
}
Example #2
Source File: common.ts From iotc-cpm-sample with MIT License | 6 votes |
export function useHeaderTitle(title: string): void {
const isFocused = useIsFocused();
const navigation = useNavigation<NavigationProperty>();
const counter = useRef(0);
useEffect(() => {
if (isFocused) {
counter.current++;
navigation.setParams({title});
}
}, [navigation, title, isFocused]);
}
Example #3
Source File: IterableInbox.tsx From react-native-sdk with MIT License | 4 votes |
IterableInbox = ({
returnToInboxTrigger = true,
messageListItemLayout = () => { return null },
customizations = {} as IterableInboxCustomizations,
tabBarHeight = 80,
tabBarPadding = 20,
safeAreaMode = true
}: inboxProps) => {
const defaultInboxTitle = "Inbox"
const inboxDataModel = new IterableInboxDataModel()
let { height, width, isPortrait } = useDeviceOrientation()
const appState = useAppStateListener()
const isFocused = useIsFocused()
const [selectedRowViewModelIdx, setSelectedRowViewModelIdx] = useState<number>(0)
const [rowViewModels, setRowViewModels] = useState<InboxRowViewModel[]>([])
const [loading, setLoading] = useState<boolean>(true)
const [animatedValue] = useState<any>(new Animated.Value(0))
const [isMessageDisplay, setIsMessageDisplay] = useState<boolean>(false)
const [visibleMessageImpressions, setVisibleMessageImpressions] = useState<InboxImpressionRowInfo[]>([])
const styles = StyleSheet.create({
loadingScreen: {
height: '100%',
backgroundColor: 'whitesmoke'
},
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
height: '100%',
width: 2 * width,
paddingBottom: 0,
paddingLeft: 0,
paddingRight: 0
},
messageListContainer: {
height: '100%',
width: width,
flexDirection: 'column',
justifyContent: 'flex-start',
},
headline: {
fontWeight: 'bold',
fontSize: 40,
width: '100%',
height: 60,
marginTop: 0,
paddingTop: 10,
paddingBottom: 10,
paddingLeft: 30,
backgroundColor: 'whitesmoke'
}
})
let {
loadingScreen,
container,
headline,
messageListContainer
} = styles
const navTitleHeight = headline.height + headline.paddingTop + headline.paddingBottom
headline = { ...headline, height: Platform.OS === "android" ? 70 : 60 }
headline = (!isPortrait) ? { ...headline, paddingLeft: 70 } : headline
//fetches inbox messages and adds listener for inbox changes on mount
useEffect(() => {
fetchInboxMessages()
addInboxChangedListener()
//removes listener for inbox changes on unmount and ends inbox session
return () => {
removeInboxChangedListener()
inboxDataModel.endSession(visibleMessageImpressions)
}
}, [])
//starts session when user is on inbox and app is active
//ends session when app is in background or app is closed
useEffect(() => {
if(isFocused) {
if(appState === 'active') {
inboxDataModel.startSession(visibleMessageImpressions)
} else if(appState === 'background' && Platform.OS === 'android' || appState === 'inactive') {
inboxDataModel.endSession(visibleMessageImpressions)
}
}
}, [appState])
//starts session when user is on inbox
//ends session when user navigates away from inbox
useEffect(() => {
if(appState === 'active') {
if(isFocused) {
inboxDataModel.startSession(visibleMessageImpressions)
} else {
inboxDataModel.endSession(visibleMessageImpressions)
}
}
}, [isFocused])
//updates the visible rows when visible messages changes
useEffect(() => {
inboxDataModel.updateVisibleRows(visibleMessageImpressions)
}, [visibleMessageImpressions])
//if return to inbox trigger is provided, runs the return to inbox animation whenever the trigger is toggled
useEffect(() => {
if(isMessageDisplay) {
returnToInbox()
}
}, [returnToInboxTrigger])
function addInboxChangedListener() {
RNEventEmitter.addListener(
"receivedIterableInboxChanged",
() => {
fetchInboxMessages()
}
)
}
function removeInboxChangedListener() {
RNEventEmitter.removeAllListeners("receivedIterableInboxChanged")
}
async function fetchInboxMessages() {
let newMessages = await inboxDataModel.refresh()
newMessages = newMessages.map((message, index) => {
return { ...message, last: index === newMessages.length - 1 }
})
setRowViewModels(newMessages)
setLoading(false)
}
function getHtmlContentForRow(id: string) {
return inboxDataModel.getHtmlContentForMessageId(id)
}
function handleMessageSelect(id: string, index: number, rowViewModels: InboxRowViewModel[]) {
let newRowViewModels = rowViewModels.map((rowViewModel) => {
return (rowViewModel.inAppMessage.messageId === id) ?
{ ...rowViewModel, read: true } : rowViewModel
})
setRowViewModels(newRowViewModels)
inboxDataModel.setMessageAsRead(id)
setSelectedRowViewModelIdx(index)
Iterable.trackInAppOpen(rowViewModels[index].inAppMessage, IterableInAppLocation.inbox)
slideLeft()
}
function deleteRow(messageId: string) {
inboxDataModel.deleteItemById(messageId, IterableInAppDeleteSource.inboxSwipe)
fetchInboxMessages()
}
function returnToInbox(callback?: Function) {
Animated.timing(animatedValue, {
toValue: 0,
duration: 300,
useNativeDriver: false
}).start(() => typeof callback === 'function' && callback())
setIsMessageDisplay(false)
}
function updateVisibleMessageImpressions(messageImpressions: InboxImpressionRowInfo[]) {
setVisibleMessageImpressions(messageImpressions)
}
function showMessageDisplay(rowViewModelList: InboxRowViewModel[], index: number) {
const selectedRowViewModel = rowViewModelList[index]
return (
selectedRowViewModel ?
<IterableInboxMessageDisplay
rowViewModel={selectedRowViewModel}
inAppContentPromise={getHtmlContentForRow(selectedRowViewModel.inAppMessage.messageId)}
returnToInbox={(callback: Function) => returnToInbox(callback)}
deleteRow={(messageId: string) => deleteRow(messageId)}
contentWidth={width}
isPortrait={isPortrait}
/> : null
)
}
function showMessageList(loading: boolean) {
return (
<View style={messageListContainer}>
<Text style={headline}>
{customizations?.navTitle ? customizations?.navTitle : defaultInboxTitle}
</Text>
{rowViewModels.length ?
<IterableInboxMessageList
dataModel={inboxDataModel}
rowViewModels={rowViewModels}
customizations={customizations}
messageListItemLayout={messageListItemLayout}
deleteRow={(messageId: string) => deleteRow(messageId)}
handleMessageSelect={(messageId: string, index: number) => handleMessageSelect(messageId, index, rowViewModels)}
updateVisibleMessageImpressions={(messageImpressions: InboxImpressionRowInfo[]) => updateVisibleMessageImpressions(messageImpressions)}
contentWidth={width}
isPortrait={isPortrait}
/> :
renderEmptyState()
}
</View>)
}
function renderEmptyState() {
return loading ?
<View style={loadingScreen} /> :
<IterableInboxEmptyState
customizations={customizations}
tabBarHeight={tabBarHeight}
tabBarPadding={tabBarPadding}
navTitleHeight={navTitleHeight}
contentWidth={width}
height={height}
isPortrait={isPortrait}
/>
}
function slideLeft() {
Animated.timing(animatedValue, {
toValue: 1,
duration: 300,
useNativeDriver: false
}).start()
setIsMessageDisplay(true)
}
const inboxAnimatedView =
<Animated.View
style={{
transform: [
{
translateX: animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, -width]
})
}
],
height: '100%',
flexDirection: 'row',
width: 2 * width,
justifyContent: 'flex-start'
}}
>
{showMessageList(loading)}
{showMessageDisplay(rowViewModels, selectedRowViewModelIdx)}
</Animated.View>
return(
(safeAreaMode) ?
<SafeAreaView style={container}>{inboxAnimatedView}</SafeAreaView> :
<View style={container}>{inboxAnimatedView}</View>
)
}
Example #4
Source File: close-contact.tsx From protect-scotland with Apache License 2.0 | 4 votes |
CloseContact: FC<CloseContactProps> = () => {
const {t} = useTranslation();
const {getTranslation} = useAgeGroupTranslation();
const navigation = useNavigation();
const {contacts, getCloseContacts} = useExposure();
const isFocused = useIsFocused();
const {
isolationDuration,
testingInstructions,
helplineNumber,
showCertUnderage
} = useSettings();
const [showSendNotice, setShowSendNotice] = useState(false);
PushNotification.setApplicationIconBadgeNumber(0);
const exposureDate = getExposureDate(contacts);
const remainingDays = getSelfIsolationRemainingDays(
isolationDuration,
contacts
);
const {
accessibility: {screenReaderEnabled},
user: {ageGroup = UserAgeGroup.ageGroup1} = {}
} = useApplication();
useEffect(() => {
getCloseContacts();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const steps = [
{
title: t('closeContact:nextSteps:step1:title'),
text: getTranslation('closeContact:nextSteps:step1:text'),
link: t('links:d'),
linkText: getTranslation('closeContact:nextSteps:step1:link')
},
{
title: t('closeContact:nextSteps:step2:title'),
text: t('closeContact:nextSteps:step2:text'),
link: t('links:s'),
linkText: t('closeContact:nextSteps:step2:link')
},
{
title: t('closeContact:nextSteps:step3:title'),
text: t('closeContact:nextSteps:step3:text'),
link: t('links:t'),
linkText: t('closeContact:nextSteps:step3:link')
},
{
title: t('closeContact:nextSteps:step4:title'),
text: t('closeContact:nextSteps:step4:text')
},
{
title: t('closeContact:nextSteps:step5:title'),
text: (
<Markdown
markdownStyles={markdownStylesBlock}
accessibleLink={`tel:${helplineNumber.split(' ').join('')}`}>
{getTranslation('closeContact:nextSteps:step5:text', {
helplineNumber,
helplineNumberTrimmed: helplineNumber.split(' ').join('')
})}
</Markdown>
)
}
];
useFocusEffect(
useCallback(() => {
const getCertificateKey = async () => {
try {
const selfIsolationEndDate = getIsolationEndDate(
isolationDuration,
contacts,
'yyyy-MM-dd'
);
const data = await SecureStore.getItemAsync('createNoticeCertKey');
if (data) {
const {key, selfIsolationDate} = JSON.parse(data);
if (key && selfIsolationDate) {
if (selfIsolationDate !== selfIsolationEndDate?.formatted) {
return setShowSendNotice(true);
}
const isValidKey = await validateNoticeKey(key);
return setShowSendNotice(Boolean(isValidKey));
}
}
setShowSendNotice(true);
} catch (err) {
console.log('Error retrieving createNoticeCertKey', err);
setShowSendNotice(false);
}
};
if (!isFocused) {
return;
}
getCertificateKey();
}, [isFocused, contacts, isolationDuration])
);
return (
<ScrollView style={styles.container}>
<View style={styles.content}>
<ModalHeader type="inline" heading={t('closeContact:title')} />
<Text variant="h1" color="errorRed">
{getTranslation('closeContact:warning', {
days: remainingDays
})}
</Text>
<Spacing s={32} />
<Markdown>
{getTranslation('closeContact:body1', {
days: remainingDays,
date: exposureDate ? format(exposureDate, DATE_FORMAT) : undefined
})}
</Markdown>
</View>
{ageGroup !== UserAgeGroup.ageGroup1 && <Spacing s={24} />}
<Divider color="white" />
<View style={styles.content}>
<Spacing s={24} />
<ArrowLink
externalLink={t('links:c')}
accessibilityHint={t('closeContact:link1Hint')}>
<Text variant="h4" color="primaryPurple">
{t('closeContact:link1')}
</Text>
</ArrowLink>
<Spacing s={24} />
</View>
<Divider color="white" />
<View style={styles.content}>
<Spacing s={50} />
<Text variant="h3" color="errorRed">
{t('closeContact:nextSteps:title')}
</Text>
<Spacing s={23} />
{ageGroup !== UserAgeGroup.ageGroup1 && (
<Text>{t('closeContact:nextSteps:intro')}</Text>
)}
<Spacing s={32} />
{steps.map(({...step}, i) => (
<Fragment key={i}>
<CloseContactStep number={i + 1} {...step} />
<Spacing s={i === steps.length - 1 ? 24 : 40} />
</Fragment>
))}
{showSendNotice &&
(ageGroup === UserAgeGroup.ageGroup1 ? true : showCertUnderage) && (
<>
<Spacing s={20} />
<RoundedBox style={styles.sendNoticeBox}>
<Spacing s={10} />
<Markdown markdownStyles={markdownStyles}>
{t('closeContact:sendNotice:text')}
</Markdown>
<Button
variant="dark"
onPress={() => navigation.navigate(ScreenNames.sendNotice)}
label={t('closeContact:sendNotice:button')}
hint={t('closeContact:sendNotice:button')}>
{t('closeContact:sendNotice:button')}
</Button>
<Spacing s={10} />
</RoundedBox>
<Spacing s={50} />
</>
)}
{ageGroup === UserAgeGroup.ageGroup1 && (
<>
<ArrowLink externalLink={t('links:k')}>
<Text variant="h4" color="primaryPurple">
{t('closeContact:link3')}
</Text>
</ArrowLink>
<Spacing s={40} />
</>
)}
<Markdown>{testingInstructions}</Markdown>
</View>
<Spacing s={8} />
<View style={styles.content}>
<RoundedBox style={styles.notWellBox}>
<Spacing s={10} />
<Markdown markdownStyles={markdownStyles}>
{t('closeContact:notWell')}
</Markdown>
<Spacing s={20} />
<Button
variant="dark"
onPress={() => openBrowserAsync(t('links:e'))}
label={t('closeContact:button1')}
hint={t('closeContact:button1')}>
{t('closeContact:button1')}
</Button>
<Spacing s={20} />
<Markdown markdownStyles={markdownStyles}>
{t('closeContact:notWell1', {
phone: screenReaderEnabled
? t('closeContact:accessiblePhoneLink')
: t('closeContact:phoneLink')
})}
</Markdown>
<Spacing s={20} />
<Button
variant="dark"
icon={TelIcon}
onPress={() => Linking.openURL('tel:111')}
label={t('closeContact:button2')}
hint={t('closeContact:button2')}>
{t('closeContact:button2')}
</Button>
<Spacing s={10} />
</RoundedBox>
</View>
</ScrollView>
);
}
Example #5
Source File: dashboard.tsx From protect-scotland with Apache License 2.0 | 4 votes |
Dashboard: FC = () => {
const {t} = useTranslation();
const {
initialised,
enabled,
status,
contacts,
getCloseContacts,
permissions,
readPermissions
} = useExposure();
const [appState] = useAppState();
const {checked, paused} = useReminder();
const navigation = useNavigation();
const {onboarded, setContext, loadAppData} = useApplication();
const {
isolationDuration,
isolationCompleteDuration,
latestVersion: appLatestVersion
} = useSettings();
const [refreshing, setRefreshing] = useState(false);
const {
focusRef: tourFocus,
focusA11yElement: focusTourElem
} = useA11yElement();
const {
focusRef: dashboardFocus,
focusA11yElement: focusDashboardElem
} = useA11yElement();
const isFocused = useIsFocused();
const messageOpacity = useRef(new Animated.Value(0)).current;
const contentOpacity = useRef(new Animated.Value(0)).current;
const gridOpacity = useRef(new Animated.Value(0)).current;
const exposureEnabled = useRef(enabled);
const bluetoothDisabled = useRef(
status.state === 'disabled' && status.type?.includes(StatusType.bluetooth)
);
const pushNotificationsDisabled = useRef(
permissions.notifications.status === 'not_allowed'
);
const [state, setState] = useState<{
stage: number;
exposurePrompt: boolean;
bluetoothPrompt: boolean;
pushNotificationsPrompt: boolean;
disabled: boolean;
current: string;
isolationMessage: string | null;
isolationComplete: boolean;
default: string;
messages: string[];
}>({
stage: onboarded ? -1 : 0,
exposurePrompt: false,
bluetoothPrompt: false,
pushNotificationsPrompt: false,
disabled: false,
current: t(
getMessage({
onboarded,
enabled,
status,
messages: t('dashboard:tour', {returnObjects: true}),
stage: onboarded ? -1 : 0,
paused
})
),
isolationMessage: null,
isolationComplete: false,
messages: t('dashboard:tour', {returnObjects: true}),
default: t('dashboard:message:standard')
});
const version = useVersion();
const resetToNormal = () =>
setState((s) => ({
...s,
isolationComplete: false,
isolationMessage: null
}));
const setExposed = () =>
setState((s) => ({
...s,
isolationComplete: false,
isolationMessage: t('dashboard:exposed')
}));
const setIsolationComplete = () =>
setState((s) => ({
...s,
isolationComplete: true,
isolationMessage: t('dashboard:isolationComplete')
}));
const processContactsForMessaging = async () => {
let currentStatus = null;
try {
currentStatus = await SecureStore.getItemAsync('niexposuredate');
} catch (err) {
await SecureStore.deleteItemAsync('niexposuredate');
console.log('processContactsForMessaging', err);
}
if (currentStatus) {
const daysDiff = differenceInCalendarDays(
new Date(),
new Date(Number(currentStatus))
);
const withIsolation = isolationDuration + isolationCompleteDuration;
if (daysDiff >= withIsolation) {
await SecureStore.deleteItemAsync('niexposuredate');
return resetToNormal();
}
if (daysDiff >= isolationDuration && daysDiff < withIsolation) {
return setIsolationComplete();
}
if (contacts && contacts.length > 0) {
return setExposed();
}
}
return resetToNormal();
};
const checkLatestExposure = async () => {
const latestExposure = getExposureDate(contacts);
if (latestExposure) {
await SecureStore.setItemAsync(
'niexposuredate',
String(latestExposure.getTime())
);
}
processContactsForMessaging();
};
const onRefresh = () => {
setRefreshing(true);
loadAppData().then(() => setRefreshing(false));
};
useEffect(() => {
onRefresh();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
getCloseContacts();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [status]);
useEffect(() => {
checkLatestExposure();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [contacts, status]);
useFocusEffect(
React.useCallback(() => {
if (!isFocused || appState !== 'active') {
return;
}
readPermissions();
}, [isFocused, appState, readPermissions])
);
useEffect(() => {
setState((s) => ({
...s,
current: t(
getMessage({
onboarded,
enabled,
status,
messages: state.messages,
stage: state.stage,
paused
})
)
}));
exposureEnabled.current = enabled;
bluetoothDisabled.current =
status.state === 'disabled' &&
status.type?.includes(StatusType.bluetooth);
pushNotificationsDisabled.current =
permissions.notifications.status === 'not_allowed';
if (!exposureEnabled.current && onboarded) {
setTimeout(() => {
if (!exposureEnabled.current) {
setState((s) => ({
...s,
exposurePrompt: true
}));
}
}, PROMPT_OFFSET);
} else if (bluetoothDisabled.current && onboarded) {
setTimeout(() => {
if (bluetoothDisabled.current) {
setState((s) => ({
...s,
bluetoothPrompt: true
}));
}
}, PROMPT_OFFSET);
} else if (pushNotificationsDisabled.current && onboarded) {
setTimeout(() => {
if (
pushNotificationsDisabled.current &&
exposureEnabled.current &&
!bluetoothDisabled.current
) {
setState((s) => ({
...s,
pushNotificationsPrompt: true
}));
}
}, PROMPT_OFFSET);
} else if (onboarded && exposureEnabled.current) {
setState((s) => ({
...s,
exposurePrompt: false
}));
}
setTimeout(() => checkLatestExposure(), 100);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [enabled, onboarded, status, permissions]);
const animateTourIn = () => {
setState((s) => ({...s, disabled: true}));
Animated.parallel([
Animated.timing(messageOpacity, {
toValue: 1,
duration: ANIMATION_DURATION,
useNativeDriver: true
}),
Animated.timing(gridOpacity, {
toValue: 1,
duration: ANIMATION_DURATION,
useNativeDriver: true
})
]).start(() => {
setState((s) => ({...s, disabled: false}));
});
};
const animateTourOut = () => {
setState((s) => ({...s, disabled: true}));
Animated.parallel([
Animated.timing(messageOpacity, {
toValue: 0,
duration: ANIMATION_DURATION,
useNativeDriver: true
}),
Animated.timing(gridOpacity, {
toValue: 0,
duration: ANIMATION_DURATION,
useNativeDriver: true
})
]).start(() => {
if (state.stage < state.messages.length - 1) {
setState((s) => ({
...s,
stage: state.stage + 1,
current: getMessage({
onboarded,
enabled,
status,
messages: state.messages,
stage: state.stage + 1
})
}));
animateTourIn();
} else {
setState((s) => ({
...s,
stage: -1,
current: s.default
}));
setContext({onboarded: true});
animateDashboard();
}
});
};
const animateDashboard = () => {
setState((s) => ({...s, disabled: true}));
Animated.parallel([
Animated.timing(messageOpacity, {
toValue: 1,
duration: ANIMATION_DURATION,
useNativeDriver: true
}),
Animated.timing(contentOpacity, {
toValue: 1,
duration: ANIMATION_DURATION,
useNativeDriver: true
}),
Animated.timing(gridOpacity, {
toValue: 1,
duration: ANIMATION_DURATION,
useNativeDriver: true
})
]).start(() => {
AsyncStorage.setItem('scot.onboarded', 'true');
setState((s) => ({...s, disabled: false}));
});
};
useEffect(() => {
if (onboarded) {
setTimeout(() => animateDashboard(), 200);
} else {
setTimeout(() => animateTourIn(), 200);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (!state.disabled) {
focusTourElem();
}
}, [focusTourElem, state.disabled]);
useEffect(() => {
if (onboarded && !state.disabled) {
focusDashboardElem();
}
}, [focusDashboardElem, onboarded, state.disabled]);
const handleTour = () => {
animateTourOut();
};
if (!initialised || !checked) {
return (
<>
<Container center="both">
<ActivityIndicator color={colors.darkGrey} size="large" />
</Container>
</>
);
}
return (
<>
<Header />
<ScrollView
refreshControl={
<RefreshControl
refreshing={onboarded && refreshing}
onRefresh={onRefresh}
/>
}>
{onboarded &&
appLatestVersion &&
version &&
appLatestVersion !== version?.display && (
<View style={blockStyles.block}>
<NewVersionCard />
<Spacing s={16} />
</View>
)}
<Spacing s={onboarded ? 15 : 65} />
{!onboarded && state.stage > -1 && (
<>
<View style={styles.dots}>
{[-1, 0, 1, 2].map((x) => (
<Animated.View
key={`step-${x}`}
style={[
styles.dot,
{
backgroundColor:
state.stage > x
? colors.primaryPurple
: colors.lighterPurple
}
]}
/>
))}
</View>
<A11yView
ref={tourFocus}
accessible
accessibilityHint={
t('dashboard:tourA11y', {returnObjects: true})[state.stage]
}
/>
<Animated.View style={{opacity: messageOpacity}}>
<TouchableWithoutFeedback onPress={() => handleTour()}>
<Markdown markdownStyles={markDownStyles}>
{state.current}
</Markdown>
</TouchableWithoutFeedback>
<Spacing s={30} />
<ArrowLink
onPress={() => {
if (!state.disabled) {
handleTour();
}
}}
accessibilityHint={t('dashboard:tourActionHint')}
invert>
<Text variant="h3" color="pink" style={styles.nextLink}>
{t('dashboard:tourAction')}
</Text>
</ArrowLink>
</Animated.View>
</>
)}
{onboarded && state.isolationMessage && (
<>
<Animated.View
style={{
opacity: messageOpacity
}}>
<View accessible ref={dashboardFocus}>
<Markdown
markdownStyles={
state.isolationComplete
? markDownStyles
: markDownStylesExposed
}>
{state.isolationMessage}
</Markdown>
</View>
{!state.isolationComplete && (
<>
<Spacing s={30} />
<ArrowLink
onPress={() =>
navigation.navigate(ScreenNames.closeContact)
}
accessibilityHint={t('dashboard:exposedAction')}
invert>
<Text variant="h3" color="pink" style={styles.nextLink}>
{t('dashboard:tourAction')}
</Text>
</ArrowLink>
</>
)}
{state.isolationComplete && (
<>
<Spacing s={20} />
<Text style={blockStyles.block} inline color="darkGrey">
{t('dashboard:isolationCompleteSupplemental')}
</Text>
</>
)}
</Animated.View>
<Spacing s={30} />
<Animated.View
style={[{opacity: contentOpacity}, blockStyles.block]}>
<Message />
</Animated.View>
</>
)}
{onboarded && !state.isolationMessage && (
<Animated.View style={{opacity: messageOpacity}}>
<View accessible ref={dashboardFocus}>
<Markdown markdownStyles={markDownStyles}>
{state.current}
</Markdown>
</View>
{state.stage === -1 && !paused && (
<>
<Spacing s={20} />
<Text style={blockStyles.block} inline color="darkGrey">
{t(`dashboard:message:bluetooth:${Platform.OS}`)}
</Text>
</>
)}
{state.stage === -1 && paused && (
<>
<Spacing s={20} />
<Text style={blockStyles.block} inline color="darkGrey">
{t('dashboard:message:pausedSupplemental')}
</Text>
</>
)}
</Animated.View>
)}
<Spacing s={30} />
<Grid
onboarded={onboarded}
stage={state.stage}
opacity={gridOpacity}
onboardingCallback={() => handleTour()}
/>
{state.isolationMessage && <Spacing s={34} />}
{onboarded && !state.isolationMessage && (
<>
<Animated.View
style={[{opacity: contentOpacity}, blockStyles.block]}>
<Spacing s={30} />
<Message />
<Spacing s={16} />
<Message
image={RestrictionsImage}
markdown={t('restrictions:message')}
accessibilityLabel={t('restrictions:a11y:label')}
accessibilityHint={t('restrictions:a11y:hint')}
link={t('links:r')}
/>
<Spacing s={45} />
</Animated.View>
</>
)}
{onboarded && (
<Text variant="h4" color="primaryPurple" align="center">
{t('dashboard:thanks')}
</Text>
)}
<Spacing s={60} />
</ScrollView>
{checked && !paused && state.exposurePrompt && (
<ExposureNotificationsModal
isVisible={state.exposurePrompt}
onBackdropPress={() =>
setState((s) => ({...s, exposurePrompt: false}))
}
onClose={() => setState((s) => ({...s, exposurePrompt: false}))}
/>
)}
{checked && !paused && state.bluetoothPrompt && (
<BluetoothNotificationsModal
isVisible={state.bluetoothPrompt}
onBackdropPress={() =>
setState((s) => ({...s, bluetoothPrompt: false}))
}
onClose={() => setState((s) => ({...s, bluetoothPrompt: false}))}
/>
)}
{checked && !paused && state.pushNotificationsPrompt && (
<PushNotificationsModal
isVisible={state.pushNotificationsPrompt}
onBackdropPress={() =>
setState((s) => ({...s, pushNotificationsPrompt: false}))
}
onClose={() =>
setState((s) => ({...s, pushNotificationsPrompt: false}))
}
/>
)}
</>
);
}
Example #6
Source File: about.tsx From beancount-mobile with MIT License | 4 votes |
About = connect( (state: AppState) => ({ authToken: state.base.authToken, locale: state.base.locale, currentTheme: state.base.currentTheme, userId: state.base.userId, }), (dispatch) => ({ logout(authToken: string): void { dispatch(actionLogout(authToken)); }, updateReduxState(payload: { base: { locale: string } }): void { dispatch(actionUpdateReduxState(payload)); }, }) )( ({ authToken, locale, logout, updateReduxState, currentTheme, userId, navigation, }: Props) => { const theme = useTheme().colorTheme; const { setLocale } = React.useContext(LocalizationContext); const pickerSource = [ { value: ReportStatus.WEEKLY, label: i18n.t("weekly") }, { value: ReportStatus.MONTHLY, label: i18n.t("monthly") }, { value: ReportStatus.OFF, label: i18n.t("off") }, ]; useEffect(() => { async function init() { await registerForPushNotificationAsync(); } init(); }, []); const [reportAnimateCount, setReportAnimateCount] = useState(0); const [subscriptionFlash, setSubscriptionFlash] = useState(false); const isFocused = useIsFocused(); React.useEffect(() => { async function init() { try { const value = await AsyncStorage.getItem("@SubscriptionFlash:key"); if (value !== null) { setSubscriptionFlash(value === "true"); } else { setSubscriptionFlash(false); } await AsyncStorage.setItem("@SubscriptionFlash:key", "false"); } catch (error) { console.error(`failed to get subscription flash value: ${error}`); } } init(); }, [isFocused]); useEffect(() => { if (subscriptionFlash) { const interval = setInterval(() => { if (reportAnimateCount < 5) { setReportAnimateCount(reportAnimateCount + 1); } }, 300); return () => clearInterval(interval); } setReportAnimateCount(0); return undefined; }, [subscriptionFlash, reportAnimateCount]); const { emailReportStatus } = useUserProfile(userId); const [reportStatus, setReportStatue] = useState<string>( emailReportStatus ? emailReportStatus.toString() : "" ); useEffect(() => { setReportStatue(emailReportStatus ? emailReportStatus.toString() : ""); }, [emailReportStatus]); const { error, mutate } = useUpdateReportSubscribeToRemote(); const getReportStatusLabel = (status: string) => { switch (status) { case ReportStatus.OFF: return i18n.t("off"); case ReportStatus.WEEKLY: return i18n.t("weekly"); case ReportStatus.MONTHLY: return i18n.t("monthly"); default: return i18n.t("off"); } }; const getReportStatusEnum = (status: string) => { switch (status) { case ReportStatus.OFF: return ReportStatus.OFF; case ReportStatus.WEEKLY: return ReportStatus.WEEKLY; case ReportStatus.MONTHLY: return ReportStatus.MONTHLY; default: return ReportStatus.OFF; } }; const renderAppSection = () => { const backgroundColor = { backgroundColor: theme.white, color: theme.text01, }; const { spendingReportSubscription } = useFeatureFlags(userId); return ( // @ts-ignore <List style={backgroundColor} renderHeader={<ListHeader>{i18n.t("about")}</ListHeader>} > <Item disabled extra={Platform.OS === "ios" ? "Apple Store" : "Google Play"} arrow="horizontal" style={backgroundColor} onPress={async () => { const storeUrl = Platform.OS === "ios" ? "https://apps.apple.com/us/app/id1527950512" : "https://play.google.com/store/apps/details?id=io.beancount.android"; if (storeUrl) { await WebBrowser.openBrowserAsync(storeUrl); await analytics.track("tap_review_app", { storeUrl }); } }} > {i18n.t("reviewApp")} </Item> {spendingReportSubscription && ( <Picker data={pickerSource} cols={1} extra={getReportStatusLabel(reportStatus)} onChange={async (value) => { const newValue = value ? String(value[0]) : ""; if (newValue === reportStatus) { return; } setReportStatue(newValue); const loadingKey = Toast.loading(i18n.t("updating")); await mutate({ variables: { userId, status: getReportStatusEnum(newValue) }, }); Portal.remove(loadingKey); if (!error) { Toast.success(i18n.t("updateSuccess")); } else { console.error("failed to update report status", error); Toast.fail(i18n.t("updateFailed")); } }} > <Item style={[ backgroundColor, { backgroundColor: reportAnimateCount % 2 === 1 ? theme.warning : theme.white, }, ]} arrow="horizontal" > {i18n.t("subscribe")} </Item> </Picker> )} <Item disabled style={backgroundColor} extra={ <Switch value={String(locale).startsWith("en")} onValueChange={async (value) => { const changeTo = value ? "en" : "zh"; updateReduxState({ base: { locale: changeTo }, }); i18n.locale = changeTo; setLocale(changeTo); await analytics.track("tap_switch_language", { changeTo }); }} /> } > {i18n.t("currentLanguage")} <Brief> {String(locale).startsWith("en") ? i18n.t("english") : i18n.t("chinese")} </Brief> </Item> <Item style={backgroundColor} disabled extra={ <Switch value={currentTheme === "dark"} onValueChange={async (value) => { const mode = value ? "dark" : "light"; updateReduxState({ base: { currentTheme: mode }, }); await analytics.track("tap_switch_theme", { mode }); }} /> } > {i18n.t("theme")} <Brief>{currentTheme === "dark" ? "Dark" : "Light"}</Brief> </Item> <Item style={backgroundColor} disabled extra={Constants.nativeAppVersion} > {i18n.t("currentVersion")} </Item> {authToken ? ( <Item style={backgroundColor} disabled onPress={() => { Alert.alert( "", i18n.t("logoutAlertMsg"), [ { text: i18n.t("logoutAlertCancel"), style: "cancel" }, { text: i18n.t("logoutAlertConfirm"), onPress: () => { logout(authToken); }, }, ], { cancelable: false } ); }} > {i18n.t("logout")} </Item> ) : ( <View /> )} </List> ); }; return ( <ScrollView style={{ backgroundColor: theme.white }}> <AccountHeader /> <InviteSection navigation={navigation} /> {renderAppSection()} </ScrollView> ); } )