refactor(nwc): polish connection QR screen layout and status UI
This commit is contained in:
@@ -173,9 +173,7 @@ export default class FeeEstimate extends React.PureComponent<
|
||||
{ color: themeColor('secondaryText') }
|
||||
]}
|
||||
>
|
||||
{localeString(
|
||||
'components.NWCPendingPayInvoiceModal.totalAmount'
|
||||
)}
|
||||
{localeString('general.totalAmount')}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
+4
-3
@@ -66,6 +66,7 @@
|
||||
"general.frozen": "Frozen",
|
||||
"general.optional": "Optional",
|
||||
"general.sats": "sats",
|
||||
"general.totalAmount": "Total Amount",
|
||||
"general.blocks": "blocks",
|
||||
"general.search": "Search",
|
||||
"general.help": "Help",
|
||||
@@ -1985,11 +1986,13 @@
|
||||
"views.Settings.NostrWalletConnect.connectionId": "Connection ID",
|
||||
"views.Settings.NostrWalletConnect.publicKey": "Public Key",
|
||||
"views.Settings.NostrWalletConnect.connectionSecret": "Connection Secret",
|
||||
"views.Settings.NostrWalletConnect.connectionSecretDescription": "Scan this QR code or copy the connection secret in your Nostr app to link this connection.",
|
||||
"views.Settings.NostrWalletConnect.waitingForAppToConnect": "Waiting for app to connect...",
|
||||
"views.Settings.NostrWalletConnect.noEventReceivedTimeoutDescription": "We didn't receive any event from your Nostr client, but you can still close this screen and use your connection. The connection will work once your Nostr client sends a request.",
|
||||
"views.Settings.NostrWalletConnect.connectionTimeout": "Connection Timeout",
|
||||
"views.Settings.NostrWalletConnect.keepWaiting": "Keep Waiting",
|
||||
"views.Settings.NostrWalletConnect.connectedSuccessfully": "✓ Connected Successfully!",
|
||||
"views.Settings.NostrWalletConnect.connectedSuccessfully": "Connected successfully!",
|
||||
"views.Settings.NostrWalletConnect.returningToConnections": "Returning to connections…",
|
||||
"views.Settings.NostrWalletConnect.noConnectionsFound": "No connections found",
|
||||
"views.Settings.NostrWalletConnect.leftInBudget": "Left in budget",
|
||||
"views.Settings.NostrWalletConnect.switchToCashuWallet": "Switch to Cashu wallet",
|
||||
@@ -2029,7 +2032,6 @@
|
||||
"views.Settings.NostrWalletConnect.error.failedToUpdateNwcSetting": "Failed to update NWC setting",
|
||||
"views.Settings.NostrWalletConnect.error.invalidAmount": "Invalid amount.",
|
||||
"views.Settings.NostrWalletConnect.error.paymentExceedsBudget": "This payment ({{amount}} sats) exceeds the remaining budget for this connection ({{remaining}} sats).",
|
||||
"views.Settings.NostrWalletConnect.confirmDeletePendingPayments": "Are you sure you want to delete all pending payments?",
|
||||
"views.Settings.NostrWalletConnect.totalSpent": "Total Spent",
|
||||
"views.Settings.NostrWalletConnect.totalReceived": "Total Received",
|
||||
"views.Settings.NostrWalletConnect.remainingBudget": "Remaining Budget",
|
||||
@@ -2037,7 +2039,6 @@
|
||||
"views.Settings.NostrWalletConnect.warning.budgetLimitReached": "The assigned budget for this connection has reached its limit. Please review and update it if you wish to continue using this connection.",
|
||||
"views.Settings.NostrWalletConnect.nwcSettings": "NWC Settings",
|
||||
"views.Settings.NostrWalletConnect.persistentNWCService": "Persistent Mode",
|
||||
"views.Settings.NostrWalletConnect.persistentNWCServiceDescription": "Continue to run Nostr Wallet Connect service in the background even when the app is closed.",
|
||||
"views.Settings.NostrWalletConnect.nwcIncludeLud16InUrl": "Include Lightning Address",
|
||||
"views.Settings.NostrWalletConnect.nwcIncludeLud16InUrlDescription": "When enabled, your Lightning address (LUD-16) is added to new and regenerated NWC connection URLs. Some Nostr clients will set this as your Lightning address in your profile. Turn off if you prefer not to share it with the connecting app.",
|
||||
"views.Settings.NostrWalletConnect.persistentNWCServiceDescriptionAndroid": "Keeps Nostr Wallet Connect active while Zeus is in the background. Android shows a persistent foreground notification so the service is not stopped. Restart Zeus after changing this setting.",
|
||||
|
||||
@@ -15,7 +15,6 @@ export default class ModalStore {
|
||||
@observable public showAlertModal: boolean = false;
|
||||
@observable public showShareModal: boolean = false;
|
||||
@observable public showNewChannelModal: boolean = false;
|
||||
@observable public showNWCPendingPaymentsModal: boolean = false;
|
||||
@observable public showRatingModal: boolean = false;
|
||||
@observable public showRestoreChannelModal: boolean = false;
|
||||
@observable public modalUrl: string;
|
||||
|
||||
@@ -27,6 +27,12 @@ jest.mock('./BackendUtils', () => ({
|
||||
default: {}
|
||||
}));
|
||||
|
||||
jest.mock('react-native-notifications', () => ({
|
||||
Notifications: {
|
||||
postLocalNotification: jest.fn()
|
||||
}
|
||||
}));
|
||||
|
||||
import * as nostrTools from 'nostr-tools';
|
||||
|
||||
import NostrConnectUtils from './NostrConnectUtils';
|
||||
|
||||
@@ -22,8 +22,9 @@ import ModalBox from '../../../components/ModalBox';
|
||||
import NostrWalletConnectStore from '../../../stores/NostrWalletConnectStore';
|
||||
import { themeColor } from '../../../utils/ThemeUtils';
|
||||
import { localeString } from '../../../utils/LocaleUtils';
|
||||
import AlertIcon from '../../../assets/images/SVG/Alert.svg';
|
||||
import { font } from '../../../utils/FontUtils';
|
||||
import AlertIcon from '../../../assets/images/SVG/Alert.svg';
|
||||
import SuccessAnimation from '../../../components/SuccessAnimation';
|
||||
|
||||
interface NWCConnectionQRProps {
|
||||
navigation: NativeStackNavigationProp<any, any>;
|
||||
@@ -37,6 +38,8 @@ interface NWCConnectionQRState {
|
||||
showTimeoutMessage: boolean;
|
||||
}
|
||||
const CONNECTION_TIMEOUT_MS = 20000;
|
||||
const CONNECTED_REDIRECT_MS = 2000;
|
||||
|
||||
@inject('NostrWalletConnectStore')
|
||||
@observer
|
||||
export default class NWCConnectionQR extends React.Component<
|
||||
@@ -45,6 +48,7 @@ export default class NWCConnectionQR extends React.Component<
|
||||
> {
|
||||
appStateSubscription: NativeEventSubscription | null = null;
|
||||
connectionTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
navigateBackTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
constructor(props: NWCConnectionQRProps) {
|
||||
super(props);
|
||||
@@ -84,9 +88,9 @@ export default class NWCConnectionQR extends React.Component<
|
||||
this.connectionTimeout = null;
|
||||
}
|
||||
this.setState({ isConnected: true, showTimeoutMessage: false });
|
||||
setTimeout(() => {
|
||||
this.navigateBackTimeout = setTimeout(() => {
|
||||
navigation.popTo('NostrWalletConnect');
|
||||
}, 2000);
|
||||
}, CONNECTED_REDIRECT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +104,10 @@ export default class NWCConnectionQR extends React.Component<
|
||||
clearTimeout(this.connectionTimeout);
|
||||
this.connectionTimeout = null;
|
||||
}
|
||||
if (this.navigateBackTimeout) {
|
||||
clearTimeout(this.navigateBackTimeout);
|
||||
this.navigateBackTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
handleAppStateChange = (nextAppState: AppStateStatus) => {
|
||||
@@ -130,9 +138,80 @@ export default class NWCConnectionQR extends React.Component<
|
||||
navigation.popTo('NostrWalletConnect');
|
||||
};
|
||||
|
||||
renderConnectedSuccess = () => (
|
||||
<View style={styles.connectedContainer}>
|
||||
<View style={styles.connectedAnimationWrap}>
|
||||
<SuccessAnimation />
|
||||
</View>
|
||||
<Text
|
||||
style={[styles.connectedTitle, { color: themeColor('text') }]}
|
||||
>
|
||||
{localeString(
|
||||
'views.Settings.NostrWalletConnect.connectedSuccessfully'
|
||||
)}
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
styles.connectedSubtitle,
|
||||
{ color: themeColor('secondaryText') }
|
||||
]}
|
||||
>
|
||||
{localeString(
|
||||
'views.Settings.NostrWalletConnect.returningToConnections'
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
renderWaitingStatus = () => {
|
||||
const { NostrWalletConnectStore } = this.props;
|
||||
|
||||
if (!NostrWalletConnectStore.waitingForConnection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.statusContainer}>
|
||||
<LoadingIndicator size={36} />
|
||||
<Text
|
||||
style={[
|
||||
styles.statusText,
|
||||
{ color: themeColor('secondaryText') }
|
||||
]}
|
||||
>
|
||||
{localeString(
|
||||
'views.Settings.NostrWalletConnect.waitingForAppToConnect'
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
renderConnectionContent = (nostrUrl: string) => (
|
||||
<>
|
||||
<Text
|
||||
style={[
|
||||
styles.description,
|
||||
{ color: themeColor('secondaryText') }
|
||||
]}
|
||||
>
|
||||
{localeString(
|
||||
'views.Settings.NostrWalletConnect.connectionSecretDescription'
|
||||
)}
|
||||
</Text>
|
||||
|
||||
<View style={styles.qrCard}>
|
||||
<CollapsedQR value={nostrUrl} hideText={true} expanded />
|
||||
</View>
|
||||
|
||||
{this.renderWaitingStatus()}
|
||||
</>
|
||||
);
|
||||
|
||||
render() {
|
||||
const { navigation, NostrWalletConnectStore, route } = this.props;
|
||||
const { nostrUrl } = route.params;
|
||||
const { isConnected } = this.state;
|
||||
|
||||
return (
|
||||
<Screen>
|
||||
@@ -150,77 +229,53 @@ export default class NWCConnectionQR extends React.Component<
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
style={{
|
||||
paddingHorizontal: 15
|
||||
}}
|
||||
>
|
||||
<CollapsedQR value={nostrUrl} hideText={true} expanded />
|
||||
<View style={styles.main}>
|
||||
<ScrollView
|
||||
style={styles.scroll}
|
||||
contentContainerStyle={[
|
||||
styles.scrollContent,
|
||||
isConnected && styles.scrollContentConnected
|
||||
]}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
{isConnected
|
||||
? this.renderConnectedSuccess()
|
||||
: this.renderConnectionContent(nostrUrl)}
|
||||
</ScrollView>
|
||||
|
||||
{NostrWalletConnectStore.waitingForConnection && (
|
||||
<View style={[styles.loadingContainer]}>
|
||||
<LoadingIndicator />
|
||||
<Text
|
||||
style={[
|
||||
styles.loadingText,
|
||||
{ color: themeColor('secondaryText') }
|
||||
]}
|
||||
>
|
||||
{localeString(
|
||||
'views.Settings.NostrWalletConnect.waitingForAppToConnect'
|
||||
)}
|
||||
</Text>
|
||||
{!isConnected && (
|
||||
<View
|
||||
style={[
|
||||
styles.footer,
|
||||
{ backgroundColor: themeColor('background') }
|
||||
]}
|
||||
>
|
||||
<Button
|
||||
title={localeString('general.close')}
|
||||
onPress={() => {
|
||||
navigation.popTo('NostrWalletConnect');
|
||||
}}
|
||||
secondary
|
||||
noUppercase
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{this.state.isConnected && (
|
||||
<View style={[styles.connectedContainer]}>
|
||||
<Text
|
||||
style={[
|
||||
styles.connectedText,
|
||||
{ color: themeColor('success') }
|
||||
]}
|
||||
>
|
||||
{localeString(
|
||||
'views.Settings.NostrWalletConnect.connectedSuccessfully'
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.buttonContainer}>
|
||||
<Button
|
||||
title={localeString('general.close')}
|
||||
onPress={() => {
|
||||
navigation.popTo('NostrWalletConnect');
|
||||
}}
|
||||
secondary
|
||||
noUppercase
|
||||
/>
|
||||
</View>
|
||||
</ScrollView>
|
||||
<ModalBox
|
||||
isOpen={
|
||||
this.state.showTimeoutMessage &&
|
||||
NostrWalletConnectStore.waitingForConnection
|
||||
}
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
minHeight: 200,
|
||||
zIndex: 9999
|
||||
}}
|
||||
style={styles.modalBackdrop}
|
||||
onClosed={() =>
|
||||
this.setState({ showTimeoutMessage: false })
|
||||
}
|
||||
backdropPressToClose={false}
|
||||
swipeToClose={false}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
<View
|
||||
style={[
|
||||
styles.modalContainer,
|
||||
@@ -283,60 +338,95 @@ export default class NWCConnectionQR extends React.Component<
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
loadingContainer: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 10
|
||||
main: {
|
||||
flex: 1
|
||||
},
|
||||
loadingText: {
|
||||
fontSize: 16,
|
||||
fontFamily: 'PPNeueMontreal-Book',
|
||||
textAlign: 'center',
|
||||
marginTop: 12,
|
||||
fontWeight: '500'
|
||||
scroll: {
|
||||
flex: 1
|
||||
},
|
||||
buttonContainer: {
|
||||
width: '100%',
|
||||
scrollContent: {
|
||||
flexGrow: 1,
|
||||
paddingHorizontal: 20,
|
||||
paddingTop: 16,
|
||||
paddingBottom: 24,
|
||||
alignItems: 'center'
|
||||
},
|
||||
connectedContainer: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: 40,
|
||||
scrollContentConnected: {
|
||||
justifyContent: 'center'
|
||||
},
|
||||
description: {
|
||||
fontSize: 15,
|
||||
fontFamily: 'PPNeueMontreal-Book',
|
||||
textAlign: 'center',
|
||||
lineHeight: 22,
|
||||
maxWidth: 340
|
||||
},
|
||||
qrCard: {
|
||||
width: '100%',
|
||||
maxWidth: 340,
|
||||
borderRadius: 20,
|
||||
paddingVertical: 20,
|
||||
paddingHorizontal: 24
|
||||
paddingHorizontal: 12,
|
||||
alignItems: 'center'
|
||||
},
|
||||
connectedText: {
|
||||
fontSize: 16,
|
||||
fontFamily: 'PPNeueMontreal-Book',
|
||||
textAlign: 'center',
|
||||
fontWeight: '600'
|
||||
},
|
||||
iosTimerContainer: {
|
||||
statusContainer: {
|
||||
marginTop: 28,
|
||||
alignItems: 'center',
|
||||
marginVertical: 16,
|
||||
paddingVertical: 16
|
||||
justifyContent: 'center',
|
||||
gap: 12,
|
||||
paddingHorizontal: 16,
|
||||
maxWidth: 340
|
||||
},
|
||||
iosTimerTitle: {
|
||||
fontSize: 18,
|
||||
fontFamily: 'PPNeueMontreal-Book',
|
||||
fontWeight: '600',
|
||||
textAlign: 'center',
|
||||
marginBottom: 16
|
||||
},
|
||||
iosTimerSubtitle: {
|
||||
fontSize: 14,
|
||||
statusText: {
|
||||
fontSize: 15,
|
||||
fontFamily: 'PPNeueMontreal-Book',
|
||||
textAlign: 'center',
|
||||
marginTop: 12,
|
||||
lineHeight: 20
|
||||
lineHeight: 22
|
||||
},
|
||||
connectedContainer: {
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
maxWidth: 340,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 40
|
||||
},
|
||||
connectedAnimationWrap: {
|
||||
alignItems: 'center',
|
||||
marginBottom: 15
|
||||
},
|
||||
connectedTitle: {
|
||||
fontSize: 28,
|
||||
fontFamily: font('marlideBold'),
|
||||
textAlign: 'center',
|
||||
marginBottom: 8
|
||||
},
|
||||
connectedSubtitle: {
|
||||
fontSize: 15,
|
||||
fontFamily: 'PPNeueMontreal-Book',
|
||||
textAlign: 'center',
|
||||
lineHeight: 22
|
||||
},
|
||||
footer: {
|
||||
paddingHorizontal: 20,
|
||||
paddingTop: 12,
|
||||
paddingBottom: 16
|
||||
},
|
||||
modalBackdrop: {
|
||||
backgroundColor: 'transparent',
|
||||
minHeight: 200,
|
||||
zIndex: 9999
|
||||
},
|
||||
modalOverlay: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
modalContainer: {
|
||||
borderRadius: 30,
|
||||
padding: 30,
|
||||
paddingHorizontal: 28,
|
||||
paddingTop: 32,
|
||||
paddingBottom: 28,
|
||||
width: '90%',
|
||||
maxWidth: 400,
|
||||
alignItems: 'center',
|
||||
@@ -350,23 +440,22 @@ const styles = StyleSheet.create({
|
||||
elevation: 8
|
||||
},
|
||||
modalIconContainer: {
|
||||
marginBottom: 20,
|
||||
marginBottom: 16,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: 28,
|
||||
fontSize: 26,
|
||||
fontFamily: font('marlideBold'),
|
||||
fontWeight: '600',
|
||||
textAlign: 'center',
|
||||
marginBottom: 12
|
||||
},
|
||||
modalMessage: {
|
||||
fontSize: 16,
|
||||
fontSize: 15,
|
||||
fontFamily: 'PPNeueMontreal-Book',
|
||||
textAlign: 'center',
|
||||
lineHeight: 22,
|
||||
marginBottom: 30
|
||||
marginBottom: 28
|
||||
},
|
||||
modalButtonsContainer: {
|
||||
width: '100%',
|
||||
|
||||
@@ -426,10 +426,6 @@ export default class NWCConnectionsList extends React.Component<
|
||||
const { NostrWalletConnectStore, navigation, SettingsStore } =
|
||||
this.props;
|
||||
const { connections, loading } = NostrWalletConnectStore;
|
||||
const { settings } = SettingsStore;
|
||||
const isNwcSettingsAvailable =
|
||||
Platform.OS === 'android' ||
|
||||
(BackendUtils.supportsCashuWallet() && settings.ecash.enableCashu);
|
||||
const { connectionsLoading, error } = this.state;
|
||||
const { settings } = SettingsStore;
|
||||
const isNwcSettingsAvailable =
|
||||
|
||||
Reference in New Issue
Block a user