mirror of
https://github.com/selfxyz/self.git
synced 2026-01-09 14:48:06 -05:00
* Revert "fix yarn web (#814)" This reverts commitbf158fd977. * Revert "add hermes ios engine" This reverts commitf6defcfb12. * Revert "codex feedback" This reverts commitdf35a47f72. * Revert "merge with dev" This reverts commit47a8d9d2f1. * Revert "update Gemfile.lock and remove lock when reinstalling" This reverts commit5f25752f77. * Revert "test package update" This reverts commit19dcba0e42. * Revert "revert install cmd changes" This reverts commit5427bd12a1. * Revert "Fix CI build by excluding nokogiri in GitHub Actions/Act environments" This reverts commitdbff69a13e. * Revert "prettier" This reverts commit87f421456f. * Revert "fix building" This reverts commitfbbefd2a3d. * Revert "optimize path resolving" This reverts commit23b1118b8e. * Revert "fix loading" This reverts commitf0c884b43b. * Revert "clean up dupe bundle install" This reverts commit5428567bbe. * Revert "fix metro loading" This reverts commit3a766001dc. * Revert "remove passport provider lazy loading" This reverts commit5f793a54b3. * Revert "allow cursor to edit Gemfile and update lock file" This reverts commitb6f7158e8e. * Revert "Update Gemfile.lock to exclude nokogiri in CI environments" This reverts commit243640152d. * Revert "fix install commands" This reverts commit2dc7d7c1c9. * Revert "fix imports and test" This reverts commit83d6308029. * Revert "fix import" This reverts commitfa42b07ce4. * Revert "update build checks" This reverts commitb281f15a16. * Revert "save updated imports" This reverts commit215bca4bee. * Revert "fix build command" This reverts commit37f95bc8fb. * Revert "build dependencies before linting" This reverts commit9e57e017ca. * Revert "lint suggestions" This reverts commitff9b9d2c7c. * Revert "fix type. more opportunities" This reverts commit7ad82d5817. * Revert "add typing to crypto loader" This reverts commitd55eec8f44. * Revert "yarn nice" This reverts commitdf1c2dbd9b. * Revert "update lock" This reverts commit04692ba3b5. * Revert "cm feedback" This reverts commit848071f315. * fix file name * fix web loading * fix border width styling * fix package commands * fix import and update lock * fixes from reverted commits * more fixes for web building * fix yarn web:build * add yarn web:build workflow * cm feedback * final fixes * fix for and vitge * fix loading
385 lines
13 KiB
TypeScript
385 lines
13 KiB
TypeScript
// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11
|
|
|
|
import { useIsFocused, useNavigation } from '@react-navigation/native';
|
|
import type { SelfAppDisclosureConfig } from '@selfxyz/common';
|
|
import { formatEndpoint } from '@selfxyz/common';
|
|
import { Eye, EyeOff } from '@tamagui/lucide-icons';
|
|
import LottieView from 'lottie-react-native';
|
|
import React, {
|
|
useCallback,
|
|
useEffect,
|
|
useMemo,
|
|
useRef,
|
|
useState,
|
|
} from 'react';
|
|
import {
|
|
LayoutChangeEvent,
|
|
NativeScrollEvent,
|
|
NativeSyntheticEvent,
|
|
ScrollView,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
} from 'react-native';
|
|
import { Image, Text, View, XStack, YStack } from 'tamagui';
|
|
|
|
import miscAnimation from '../../assets/animations/loading/misc.json';
|
|
import { HeldPrimaryButtonProveScreen } from '../../components/buttons/HeldPrimaryButtonProveScreen';
|
|
import Disclosures from '../../components/Disclosures';
|
|
import { BodyText } from '../../components/typography/BodyText';
|
|
import { Caption } from '../../components/typography/Caption';
|
|
import { ProofEvents } from '../../consts/analytics';
|
|
import { ExpandableBottomLayout } from '../../layouts/ExpandableBottomLayout';
|
|
import { setDefaultDocumentTypeIfNeeded } from '../../providers/passportDataProvider';
|
|
import { ProofStatus } from '../../stores/proof-types';
|
|
import { useProofHistoryStore } from '../../stores/proofHistoryStore';
|
|
import { useSelfAppStore } from '../../stores/selfAppStore';
|
|
import analytics from '../../utils/analytics';
|
|
import { black, slate300, white } from '../../utils/colors';
|
|
import { formatUserId } from '../../utils/formatUserId';
|
|
import { buttonTap } from '../../utils/haptic';
|
|
import { useProvingStore } from '../../utils/proving/provingMachine';
|
|
|
|
const { trackEvent } = analytics();
|
|
|
|
const ProveScreen: React.FC = () => {
|
|
const { navigate } = useNavigation();
|
|
const isFocused = useIsFocused();
|
|
const selectedApp = useSelfAppStore(state => state.selfApp);
|
|
const selectedAppRef = useRef<typeof selectedApp>(null);
|
|
|
|
const [hasScrolledToBottom, setHasScrolledToBottom] = useState(false);
|
|
const [scrollViewContentHeight, setScrollViewContentHeight] = useState(0);
|
|
const [scrollViewHeight, setScrollViewHeight] = useState(0);
|
|
const [showFullAddress, setShowFullAddress] = useState(false);
|
|
const scrollViewRef = useRef<ScrollView>(null);
|
|
|
|
const isContentShorterThanScrollView = useMemo(
|
|
() => scrollViewContentHeight <= scrollViewHeight,
|
|
[scrollViewContentHeight, scrollViewHeight],
|
|
);
|
|
|
|
const provingStore = useProvingStore();
|
|
const currentState = useProvingStore(state => state.currentState);
|
|
const isReadyToProve = currentState === 'ready_to_prove';
|
|
|
|
const { addProofHistory } = useProofHistoryStore();
|
|
|
|
useEffect(() => {
|
|
if (provingStore.uuid && selectedApp) {
|
|
addProofHistory({
|
|
appName: selectedApp.appName,
|
|
sessionId: provingStore.uuid!,
|
|
userId: selectedApp.userId,
|
|
userIdType: selectedApp.userIdType,
|
|
endpointType: selectedApp.endpointType,
|
|
status: ProofStatus.PENDING,
|
|
logoBase64: selectedApp.logoBase64,
|
|
disclosures: JSON.stringify(selectedApp.disclosures),
|
|
});
|
|
}
|
|
}, [addProofHistory, provingStore.uuid, selectedApp]);
|
|
|
|
useEffect(() => {
|
|
if (isContentShorterThanScrollView) {
|
|
setHasScrolledToBottom(true);
|
|
} else {
|
|
setHasScrolledToBottom(false);
|
|
}
|
|
}, [isContentShorterThanScrollView]);
|
|
|
|
useEffect(() => {
|
|
if (!isFocused || !selectedApp) {
|
|
return;
|
|
}
|
|
|
|
setDefaultDocumentTypeIfNeeded();
|
|
|
|
if (selectedAppRef.current?.sessionId !== selectedApp.sessionId) {
|
|
console.log('[ProveScreen] Selected app updated:', selectedApp);
|
|
provingStore.init('disclose');
|
|
}
|
|
selectedAppRef.current = selectedApp;
|
|
}, [selectedApp, isFocused, provingStore]);
|
|
|
|
const disclosureOptions = useMemo(() => {
|
|
return (selectedApp?.disclosures as SelfAppDisclosureConfig) || [];
|
|
}, [selectedApp?.disclosures]);
|
|
|
|
// Format the logo source based on whether it's a URL or base64 string
|
|
const logoSource = useMemo(() => {
|
|
if (!selectedApp?.logoBase64) {
|
|
return null;
|
|
}
|
|
|
|
// Check if the logo is already a URL
|
|
if (
|
|
selectedApp.logoBase64.startsWith('http://') ||
|
|
selectedApp.logoBase64.startsWith('https://')
|
|
) {
|
|
return { uri: selectedApp.logoBase64 };
|
|
}
|
|
|
|
// Otherwise handle as base64 as before
|
|
const base64String = selectedApp.logoBase64.startsWith('data:image')
|
|
? selectedApp.logoBase64
|
|
: `data:image/png;base64,${selectedApp.logoBase64}`;
|
|
return { uri: base64String };
|
|
}, [selectedApp?.logoBase64]);
|
|
|
|
const url = useMemo(() => {
|
|
if (!selectedApp?.endpoint) {
|
|
return null;
|
|
}
|
|
return formatEndpoint(selectedApp.endpoint);
|
|
}, [selectedApp?.endpoint]);
|
|
|
|
const formattedUserId = useMemo(
|
|
() => formatUserId(selectedApp?.userId, selectedApp?.userIdType),
|
|
[selectedApp?.userId, selectedApp?.userIdType],
|
|
);
|
|
|
|
function onVerify() {
|
|
provingStore.setUserConfirmed();
|
|
buttonTap();
|
|
trackEvent(ProofEvents.PROOF_VERIFICATION_STARTED, {
|
|
appName: selectedApp?.appName,
|
|
sessionId: provingStore.uuid,
|
|
endpointType: selectedApp?.endpointType,
|
|
userIdType: selectedApp?.userIdType,
|
|
});
|
|
setTimeout(() => {
|
|
navigate('ProofRequestStatusScreen');
|
|
}, 100);
|
|
}
|
|
|
|
const handleScroll = useCallback(
|
|
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
if (hasScrolledToBottom || isContentShorterThanScrollView) {
|
|
return;
|
|
}
|
|
const { layoutMeasurement, contentOffset, contentSize } =
|
|
event.nativeEvent;
|
|
const paddingToBottom = 10;
|
|
const isCloseToBottom =
|
|
layoutMeasurement.height + contentOffset.y >=
|
|
contentSize.height - paddingToBottom;
|
|
if (isCloseToBottom && !hasScrolledToBottom) {
|
|
setHasScrolledToBottom(true);
|
|
buttonTap();
|
|
trackEvent(ProofEvents.PROOF_DISCLOSURES_SCROLLED, {
|
|
appName: selectedApp?.appName,
|
|
sessionId: provingStore.uuid,
|
|
});
|
|
}
|
|
},
|
|
[
|
|
hasScrolledToBottom,
|
|
isContentShorterThanScrollView,
|
|
selectedApp,
|
|
provingStore.uuid,
|
|
],
|
|
);
|
|
|
|
const handleContentSizeChange = useCallback(
|
|
(contentWidth: number, contentHeight: number) => {
|
|
setScrollViewContentHeight(contentHeight);
|
|
},
|
|
[],
|
|
);
|
|
|
|
const handleScrollViewLayout = useCallback((event: LayoutChangeEvent) => {
|
|
setScrollViewHeight(event.nativeEvent.layout.height);
|
|
}, []);
|
|
|
|
const handleAddressToggle = useCallback(() => {
|
|
if (selectedApp?.userIdType === 'hex') {
|
|
setShowFullAddress(!showFullAddress);
|
|
buttonTap();
|
|
}
|
|
}, [selectedApp?.userIdType, showFullAddress]);
|
|
|
|
return (
|
|
<ExpandableBottomLayout.Layout flex={1} backgroundColor={black}>
|
|
<ExpandableBottomLayout.TopSection backgroundColor={black}>
|
|
<YStack alignItems="center">
|
|
{!selectedApp?.sessionId ? (
|
|
<LottieView
|
|
source={miscAnimation}
|
|
autoPlay
|
|
loop
|
|
resizeMode="cover"
|
|
cacheComposition={true}
|
|
renderMode="HARDWARE"
|
|
style={styles.animation}
|
|
speed={1}
|
|
progress={0}
|
|
/>
|
|
) : (
|
|
<YStack alignItems="center" justifyContent="center">
|
|
{logoSource && (
|
|
<Image
|
|
marginBottom={20}
|
|
source={logoSource}
|
|
width={100}
|
|
height={100}
|
|
objectFit="contain"
|
|
/>
|
|
)}
|
|
<BodyText fontSize={12} color={slate300} marginBottom={20}>
|
|
{url}
|
|
</BodyText>
|
|
<BodyText fontSize={24} color={slate300} textAlign="center">
|
|
<Text color={white}>{selectedApp.appName}</Text> is requesting
|
|
that you prove the following information:
|
|
</BodyText>
|
|
</YStack>
|
|
)}
|
|
</YStack>
|
|
</ExpandableBottomLayout.TopSection>
|
|
<ExpandableBottomLayout.BottomSection
|
|
paddingBottom={20}
|
|
backgroundColor={white}
|
|
maxHeight={'55%'}
|
|
>
|
|
<ScrollView
|
|
ref={scrollViewRef}
|
|
onScroll={handleScroll}
|
|
scrollEventThrottle={16}
|
|
onContentSizeChange={handleContentSizeChange}
|
|
onLayout={handleScrollViewLayout}
|
|
>
|
|
<Disclosures disclosures={disclosureOptions} />
|
|
|
|
{/* Display connected wallet or UUID */}
|
|
{formattedUserId && (
|
|
<View marginTop={20} paddingHorizontal={20}>
|
|
<BodyText
|
|
fontSize={16}
|
|
color={black}
|
|
fontWeight="600"
|
|
marginBottom={10}
|
|
>
|
|
{selectedApp?.userIdType === 'hex'
|
|
? 'Connected Wallet'
|
|
: 'Connected ID'}
|
|
:
|
|
</BodyText>
|
|
<TouchableOpacity
|
|
onPress={handleAddressToggle}
|
|
activeOpacity={selectedApp?.userIdType === 'hex' ? 0.7 : 1}
|
|
style={{ minHeight: 44 }}
|
|
>
|
|
<View
|
|
backgroundColor={slate300}
|
|
padding={15}
|
|
borderRadius={8}
|
|
marginBottom={10}
|
|
>
|
|
<XStack alignItems="center" justifyContent="space-between">
|
|
<View
|
|
flex={1}
|
|
marginRight={selectedApp?.userIdType === 'hex' ? 12 : 0}
|
|
>
|
|
<BodyText
|
|
fontSize={14}
|
|
color={black}
|
|
lineHeight={20}
|
|
fontFamily={
|
|
showFullAddress && selectedApp?.userIdType === 'hex'
|
|
? 'monospace'
|
|
: 'normal'
|
|
}
|
|
flexWrap={showFullAddress ? 'wrap' : 'nowrap'}
|
|
>
|
|
{selectedApp?.userIdType === 'hex' && showFullAddress
|
|
? selectedApp.userId
|
|
: formattedUserId}
|
|
</BodyText>
|
|
</View>
|
|
{selectedApp?.userIdType === 'hex' && (
|
|
<View alignItems="center" justifyContent="center">
|
|
{showFullAddress ? (
|
|
<EyeOff size={16} color={black} />
|
|
) : (
|
|
<Eye size={16} color={black} />
|
|
)}
|
|
</View>
|
|
)}
|
|
</XStack>
|
|
{selectedApp?.userIdType === 'hex' && (
|
|
<BodyText
|
|
fontSize={12}
|
|
color={black}
|
|
opacity={0.6}
|
|
marginTop={4}
|
|
>
|
|
{showFullAddress
|
|
? 'Tap to hide address'
|
|
: 'Tap to show full address'}
|
|
</BodyText>
|
|
)}
|
|
</View>
|
|
</TouchableOpacity>
|
|
</View>
|
|
)}
|
|
|
|
{/* Display userDefinedData if it exists */}
|
|
{selectedApp?.userDefinedData && (
|
|
<View marginTop={20} paddingHorizontal={20}>
|
|
<BodyText
|
|
fontSize={16}
|
|
color={black}
|
|
fontWeight="600"
|
|
marginBottom={10}
|
|
>
|
|
Additional Information:
|
|
</BodyText>
|
|
<View
|
|
backgroundColor={slate300}
|
|
padding={15}
|
|
borderRadius={8}
|
|
marginBottom={10}
|
|
>
|
|
<BodyText fontSize={14} color={black} lineHeight={20}>
|
|
{selectedApp.userDefinedData}
|
|
</BodyText>
|
|
</View>
|
|
</View>
|
|
)}
|
|
|
|
<View marginTop={20}>
|
|
<Caption
|
|
textAlign="center"
|
|
size="small"
|
|
marginBottom={20}
|
|
marginTop={10}
|
|
borderRadius={4}
|
|
paddingBottom={20}
|
|
>
|
|
Self will confirm that these details are accurate and none of your
|
|
confidential info will be revealed to {selectedApp?.appName}
|
|
</Caption>
|
|
</View>
|
|
</ScrollView>
|
|
<HeldPrimaryButtonProveScreen
|
|
onVerify={onVerify}
|
|
selectedAppSessionId={selectedApp?.sessionId}
|
|
hasScrolledToBottom={hasScrolledToBottom}
|
|
isReadyToProve={isReadyToProve}
|
|
/>
|
|
</ExpandableBottomLayout.BottomSection>
|
|
</ExpandableBottomLayout.Layout>
|
|
);
|
|
};
|
|
|
|
export default ProveScreen;
|
|
|
|
const styles = StyleSheet.create({
|
|
animation: {
|
|
top: 0,
|
|
width: 200,
|
|
height: 200,
|
|
transform: [{ scale: 2 }, { translateY: -20 }],
|
|
},
|
|
});
|