kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Merge branch 'cashu' into 'main'
Draft: checkpoint: call '/api/v1/ditto/wallet/create' endpoint See merge request soapbox-pub/soapbox!3329merge-requests/3329/merge
commit
d33c8f6a32
|
@ -0,0 +1,69 @@
|
||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { useApi } from 'soapbox/hooks/useApi.ts';
|
||||||
|
import { queryClient } from 'soapbox/queries/client.ts';
|
||||||
|
|
||||||
|
function useCashu() {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
const { mutate: createWallet } = useMutation({
|
||||||
|
mutationFn: (data: {mints: string[]}) => api.post('/api/v1/ditto/wallet/create', data),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries({ queryKey: ['cashu', 'create', 'wallet'] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutate: createNutzapInfo } = useMutation({
|
||||||
|
mutationFn: (data: {mints: string[]; relays: string[]}) => api.post('/api/v1/ditto/nutzap_information/create', data),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries({ queryKey: ['cashu', 'nutzap', 'info'] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutate: swapCashuToWallet } = useMutation({
|
||||||
|
mutationFn: () => api.post('/api/v1/ditto/nutzap/swap_to_wallet'),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries({ queryKey: ['cashu', 'swap', 'wallet'] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutate: createQuote } = useMutation({
|
||||||
|
mutationFn: (data: {mint: string; amount: number}) => api.post('/api/v1/ditto/cashu/quote', data),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries({ queryKey: ['cashu', 'nutzap', 'info'] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutate: getQuoteState } = useMutation({
|
||||||
|
mutationFn: (quote_id: string) => api.get(`/api/v1/ditto/cashu/quote/${quote_id}`),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries({ queryKey: ['cashu', 'nutzap', 'info'] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutate: getWallet } = useMutation({
|
||||||
|
mutationFn: () => api.get('/api/v1/ditto/cashu/wallet'),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries({ queryKey: ['cashu', 'nutzap', 'info'] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutate: mintTheMint } = useMutation({
|
||||||
|
mutationFn: (quote_id: string) => api.post(`/api/v1/ditto/cashu/mint/${quote_id}`),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries({ queryKey: ['cashu', 'nutzap', 'info'] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
getWallet,
|
||||||
|
mintTheMint,
|
||||||
|
getQuoteState,
|
||||||
|
createQuote,
|
||||||
|
createWallet,
|
||||||
|
createNutzapInfo,
|
||||||
|
swapCashuToWallet,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useCashu };
|
|
@ -0,0 +1,403 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { HTTPError } from 'soapbox/api/HTTPError.ts';
|
||||||
|
import Button from 'soapbox/components/ui/button.tsx';
|
||||||
|
import { Column } from 'soapbox/components/ui/column.tsx';
|
||||||
|
import FormActions from 'soapbox/components/ui/form-actions.tsx';
|
||||||
|
import FormGroup from 'soapbox/components/ui/form-group.tsx';
|
||||||
|
import Form from 'soapbox/components/ui/form.tsx';
|
||||||
|
import Input from 'soapbox/components/ui/input.tsx';
|
||||||
|
import Stack from 'soapbox/components/ui/stack.tsx';
|
||||||
|
import Streamfield, { StreamfieldComponent } from 'soapbox/components/ui/streamfield.tsx';
|
||||||
|
import { useCashu } from 'soapbox/features/cashu/hooks/useCashu.ts';
|
||||||
|
import toast from 'soapbox/toast.tsx';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'cashu.wallet.create', defaultMessage: 'Create Cashu Wallet' },
|
||||||
|
mints: { id: 'cashu.wallet.mints', defaultMessage: 'Your mints' },
|
||||||
|
nutzap_info: { id: 'cashu.nutzap.info', defaultMessage: 'Your nutzap info' },
|
||||||
|
swap_cashu: { id: 'cashu.nutzap.swap', defaultMessage: 'Swap your Cashu' },
|
||||||
|
mint_placeholder: { id: 'cashu.wallet.mint_placeholder', defaultMessage: 'https://<mint-url>' },
|
||||||
|
submit_success: { id: 'generic.saved', defaultMessage: 'Saved!' },
|
||||||
|
create_cashu_quote: { id: 'cashu.quote', defaultMessage: 'Create Cashu Quote' },
|
||||||
|
get_cashu_quote_state: { id: 'cashu.quote.state', defaultMessage: 'Create Cashu Quote' },
|
||||||
|
mint_the_mint: { id: 'cashu.quote.mint', defaultMessage: 'Mint the Mint' },
|
||||||
|
get_wallet: { id: 'cashu.get_wallet', defaultMessage: 'Get wallet' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const Cashu = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const { createWallet, createNutzapInfo, swapCashuToWallet, createQuote, getQuoteState, mintTheMint, getWallet } = useCashu();
|
||||||
|
|
||||||
|
const [mints, setMints] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const updateData = (values: string[]) => {
|
||||||
|
setMints(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStreamItemChange = () => {
|
||||||
|
return (values: string[]) => {
|
||||||
|
updateData(values);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteStreamItem = () => {
|
||||||
|
return (i: number) => {
|
||||||
|
setMints(prevData => {
|
||||||
|
return [...prevData ].toSpliced(i, 1);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddMint = (): void => {
|
||||||
|
setMints(prevData => [...prevData, '']);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreateWalletSubmit: React.FormEventHandler = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
createWallet({ mints }, {
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success(messages.submit_success);
|
||||||
|
},
|
||||||
|
onError: async (err) => {
|
||||||
|
if (err instanceof HTTPError) {
|
||||||
|
try {
|
||||||
|
const { error } = await err.response.json();
|
||||||
|
if (typeof error === 'string') {
|
||||||
|
toast.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch { /* empty */ }
|
||||||
|
}
|
||||||
|
toast.error(err.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreateNutzapInfoSubmit: React.FormEventHandler = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
createNutzapInfo({ mints, relays: [] }, {
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success(messages.submit_success);
|
||||||
|
},
|
||||||
|
onError: async (err) => {
|
||||||
|
if (err instanceof HTTPError) {
|
||||||
|
try {
|
||||||
|
const { error } = await err.response.json();
|
||||||
|
if (typeof error === 'string') {
|
||||||
|
toast.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch { /* empty */ }
|
||||||
|
}
|
||||||
|
toast.error(err.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSwapWalletSubmit: React.FormEventHandler = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
swapCashuToWallet(undefined, {
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success(messages.submit_success);
|
||||||
|
},
|
||||||
|
onError: async (err) => {
|
||||||
|
if (err instanceof HTTPError) {
|
||||||
|
try {
|
||||||
|
const { error } = await err.response.json();
|
||||||
|
if (typeof error === 'string') {
|
||||||
|
toast.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch { /* empty */ }
|
||||||
|
}
|
||||||
|
toast.error(err.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMintQuote: React.FormEventHandler = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
createQuote({ mint: mints[0], amount: 20 }, {
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success(messages.submit_success);
|
||||||
|
},
|
||||||
|
onError: async (err) => {
|
||||||
|
if (err instanceof HTTPError) {
|
||||||
|
try {
|
||||||
|
const { error } = await err.response.json();
|
||||||
|
if (typeof error === 'string') {
|
||||||
|
toast.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch { /* empty */ }
|
||||||
|
}
|
||||||
|
toast.error(err.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGetMintQuoteState: React.FormEventHandler = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
getQuoteState(mints[0], {
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success(messages.submit_success);
|
||||||
|
},
|
||||||
|
onError: async (err) => {
|
||||||
|
if (err instanceof HTTPError) {
|
||||||
|
try {
|
||||||
|
const { error } = await err.response.json();
|
||||||
|
if (typeof error === 'string') {
|
||||||
|
toast.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch { /* empty */ }
|
||||||
|
}
|
||||||
|
toast.error(err.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMintTheMintQuoteState: React.FormEventHandler = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
mintTheMint(mints[0], {
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success(messages.submit_success);
|
||||||
|
},
|
||||||
|
onError: async (err) => {
|
||||||
|
if (err instanceof HTTPError) {
|
||||||
|
try {
|
||||||
|
const { error } = await err.response.json();
|
||||||
|
if (typeof error === 'string') {
|
||||||
|
toast.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch { /* empty */ }
|
||||||
|
}
|
||||||
|
toast.error(err.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGetWallet: React.FormEventHandler = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
getWallet(undefined, {
|
||||||
|
onSuccess: async () => {
|
||||||
|
toast.success(messages.submit_success);
|
||||||
|
},
|
||||||
|
onError: async (err) => {
|
||||||
|
if (err instanceof HTTPError) {
|
||||||
|
try {
|
||||||
|
const { error } = await err.response.json();
|
||||||
|
if (typeof error === 'string') {
|
||||||
|
toast.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch { /* empty */ }
|
||||||
|
}
|
||||||
|
toast.error(err.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column label={intl.formatMessage(messages.title)}>
|
||||||
|
<Form onSubmit={handleCreateWalletSubmit}>
|
||||||
|
<Stack space={4}>
|
||||||
|
|
||||||
|
<Streamfield
|
||||||
|
label={intl.formatMessage(messages.mints)}
|
||||||
|
component={CashuInput}
|
||||||
|
values={mints}
|
||||||
|
onChange={handleStreamItemChange()}
|
||||||
|
onAddItem={handleAddMint}
|
||||||
|
onRemoveItem={deleteStreamItem()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormActions>
|
||||||
|
<Button to='/settings' theme='tertiary'>
|
||||||
|
<FormattedMessage id='common.cancel' defaultMessage='Cancel' />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button theme='primary' type='submit'>
|
||||||
|
<FormattedMessage id='edit_profile.save' defaultMessage='Save' />
|
||||||
|
</Button>
|
||||||
|
</FormActions>
|
||||||
|
</Stack>
|
||||||
|
</Form>
|
||||||
|
<Form onSubmit={handleCreateNutzapInfoSubmit}>
|
||||||
|
<Stack space={4}>
|
||||||
|
|
||||||
|
<Streamfield
|
||||||
|
label={intl.formatMessage(messages.nutzap_info)}
|
||||||
|
component={CashuInput}
|
||||||
|
values={mints}
|
||||||
|
onChange={handleStreamItemChange()}
|
||||||
|
onAddItem={handleAddMint}
|
||||||
|
onRemoveItem={deleteStreamItem()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormActions>
|
||||||
|
<Button to='/settings' theme='tertiary'>
|
||||||
|
<FormattedMessage id='common.cancel' defaultMessage='Cancel' />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button theme='primary' type='submit'>
|
||||||
|
<FormattedMessage id='edit_profile.save' defaultMessage='Save' />
|
||||||
|
</Button>
|
||||||
|
</FormActions>
|
||||||
|
</Stack>
|
||||||
|
</Form>
|
||||||
|
<Form onSubmit={handleSwapWalletSubmit}>
|
||||||
|
<Stack space={4}>
|
||||||
|
|
||||||
|
<Streamfield
|
||||||
|
label={intl.formatMessage(messages.swap_cashu)}
|
||||||
|
component={CashuInput}
|
||||||
|
values={mints}
|
||||||
|
onChange={handleStreamItemChange()}
|
||||||
|
onAddItem={handleAddMint}
|
||||||
|
onRemoveItem={deleteStreamItem()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormActions>
|
||||||
|
<Button to='/settings' theme='tertiary'>
|
||||||
|
<FormattedMessage id='common.cancel' defaultMessage='Cancel' />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button theme='primary' type='submit'>
|
||||||
|
<FormattedMessage id='edit_profile.save' defaultMessage='Save' />
|
||||||
|
</Button>
|
||||||
|
</FormActions>
|
||||||
|
</Stack>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
<Form onSubmit={handleMintQuote}>
|
||||||
|
<Stack space={4}>
|
||||||
|
|
||||||
|
<Streamfield
|
||||||
|
label={intl.formatMessage(messages.create_cashu_quote)}
|
||||||
|
component={CashuInput}
|
||||||
|
values={mints}
|
||||||
|
onChange={handleStreamItemChange()}
|
||||||
|
onAddItem={handleAddMint}
|
||||||
|
onRemoveItem={deleteStreamItem()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormActions>
|
||||||
|
<Button to='/settings' theme='tertiary'>
|
||||||
|
<FormattedMessage id='common.cancel' defaultMessage='Cancel' />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button theme='primary' type='submit'>
|
||||||
|
<FormattedMessage id='edit_profile.save' defaultMessage='Save' />
|
||||||
|
</Button>
|
||||||
|
</FormActions>
|
||||||
|
</Stack>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
<Form onSubmit={handleGetMintQuoteState}>
|
||||||
|
<Stack space={4}>
|
||||||
|
|
||||||
|
<Streamfield
|
||||||
|
label={intl.formatMessage(messages.get_cashu_quote_state)}
|
||||||
|
component={CashuInput}
|
||||||
|
values={mints}
|
||||||
|
onChange={handleStreamItemChange()}
|
||||||
|
onAddItem={handleAddMint}
|
||||||
|
onRemoveItem={deleteStreamItem()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormActions>
|
||||||
|
<Button to='/settings' theme='tertiary'>
|
||||||
|
<FormattedMessage id='common.cancel' defaultMessage='Cancel' />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button theme='primary' type='submit'>
|
||||||
|
<FormattedMessage id='edit_profile.save' defaultMessage='Save' />
|
||||||
|
</Button>
|
||||||
|
</FormActions>
|
||||||
|
</Stack>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
<Form onSubmit={handleMintTheMintQuoteState}>
|
||||||
|
<Stack space={4}>
|
||||||
|
|
||||||
|
<Streamfield
|
||||||
|
label={intl.formatMessage(messages.mint_the_mint)}
|
||||||
|
component={CashuInput}
|
||||||
|
values={mints}
|
||||||
|
onChange={handleStreamItemChange()}
|
||||||
|
onAddItem={handleAddMint}
|
||||||
|
onRemoveItem={deleteStreamItem()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormActions>
|
||||||
|
<Button to='/settings' theme='tertiary'>
|
||||||
|
<FormattedMessage id='common.cancel' defaultMessage='Cancel' />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button theme='primary' type='submit'>
|
||||||
|
<FormattedMessage id='edit_profile.save' defaultMessage='Save' />
|
||||||
|
</Button>
|
||||||
|
</FormActions>
|
||||||
|
</Stack>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
<Form onSubmit={handleGetWallet}>
|
||||||
|
<Stack space={4}>
|
||||||
|
|
||||||
|
<Streamfield
|
||||||
|
label={intl.formatMessage(messages.get_wallet)}
|
||||||
|
component={CashuInput}
|
||||||
|
values={mints}
|
||||||
|
onChange={handleStreamItemChange()}
|
||||||
|
onAddItem={handleAddMint}
|
||||||
|
onRemoveItem={deleteStreamItem()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormActions>
|
||||||
|
<Button to='/settings' theme='tertiary'>
|
||||||
|
<FormattedMessage id='common.cancel' defaultMessage='Cancel' />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button theme='primary' type='submit'>
|
||||||
|
<FormattedMessage id='edit_profile.save' defaultMessage='Save' />
|
||||||
|
</Button>
|
||||||
|
</FormActions>
|
||||||
|
</Stack>
|
||||||
|
</Form>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type Mint = string
|
||||||
|
|
||||||
|
const CashuInput: StreamfieldComponent<Mint> = ({ value, onChange }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const handleChange = (): React.ChangeEventHandler<HTMLInputElement> => {
|
||||||
|
return e => {
|
||||||
|
onChange(e.currentTarget.value);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack space={3} grow className='my-2'>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<Input
|
||||||
|
type='text'
|
||||||
|
outerClassName='grow'
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange()}
|
||||||
|
placeholder={intl.formatMessage(messages.mint_placeholder)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Cashu;
|
|
@ -32,6 +32,7 @@ const messages = defineMessages({
|
||||||
editProfile: { id: 'settings.edit_profile', defaultMessage: 'Edit Profile' },
|
editProfile: { id: 'settings.edit_profile', defaultMessage: 'Edit Profile' },
|
||||||
editIdentity: { id: 'settings.edit_identity', defaultMessage: 'Identity' },
|
editIdentity: { id: 'settings.edit_identity', defaultMessage: 'Identity' },
|
||||||
editRelays: { id: 'nostr_relays.title', defaultMessage: 'Relays' },
|
editRelays: { id: 'nostr_relays.title', defaultMessage: 'Relays' },
|
||||||
|
manageCashu: { id: 'settings.nostr.cashu', defaultMessage: 'Cashu' },
|
||||||
exportData: { id: 'column.export_data', defaultMessage: 'Export data' },
|
exportData: { id: 'column.export_data', defaultMessage: 'Export data' },
|
||||||
importData: { id: 'navigation_bar.import_data', defaultMessage: 'Import data' },
|
importData: { id: 'navigation_bar.import_data', defaultMessage: 'Import data' },
|
||||||
mfaDisabled: { id: 'mfa.disabled', defaultMessage: 'Disabled' },
|
mfaDisabled: { id: 'mfa.disabled', defaultMessage: 'Disabled' },
|
||||||
|
@ -88,6 +89,7 @@ const Settings = () => {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
{features.nostr && <ListItem label={intl.formatMessage(messages.editRelays)} to='/settings/relays' />}
|
{features.nostr && <ListItem label={intl.formatMessage(messages.editRelays)} to='/settings/relays' />}
|
||||||
|
{features.nostr && <ListItem label={intl.formatMessage(messages.manageCashu)} to='/settings/cashu' />}
|
||||||
</List>
|
</List>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ import {
|
||||||
LandingTimeline,
|
LandingTimeline,
|
||||||
EditIdentity,
|
EditIdentity,
|
||||||
Domains,
|
Domains,
|
||||||
NostrRelays,
|
Cashu,
|
||||||
Bech32Redirect,
|
Bech32Redirect,
|
||||||
Relays,
|
Relays,
|
||||||
ManageZapSplit,
|
ManageZapSplit,
|
||||||
|
@ -153,6 +153,7 @@ import {
|
||||||
AdminNostrRelays,
|
AdminNostrRelays,
|
||||||
NostrBunkerLogin,
|
NostrBunkerLogin,
|
||||||
ManageDittoServer,
|
ManageDittoServer,
|
||||||
|
NostrRelays,
|
||||||
} from './util/async-components.ts';
|
} from './util/async-components.ts';
|
||||||
import GlobalHotkeys from './util/global-hotkeys.tsx';
|
import GlobalHotkeys from './util/global-hotkeys.tsx';
|
||||||
import { WrappedRoute } from './util/react-router-helpers.tsx';
|
import { WrappedRoute } from './util/react-router-helpers.tsx';
|
||||||
|
@ -328,6 +329,7 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = ({ children }) =>
|
||||||
{features.accountMoving && <WrappedRoute path='/settings/migration' page={DefaultPage} component={Migration} content={children} />}
|
{features.accountMoving && <WrappedRoute path='/settings/migration' page={DefaultPage} component={Migration} content={children} />}
|
||||||
{features.backups && <WrappedRoute path='/settings/backups' page={DefaultPage} component={Backups} content={children} />}
|
{features.backups && <WrappedRoute path='/settings/backups' page={DefaultPage} component={Backups} content={children} />}
|
||||||
<WrappedRoute path='/settings/relays' page={DefaultPage} component={NostrRelays} content={children} />
|
<WrappedRoute path='/settings/relays' page={DefaultPage} component={NostrRelays} content={children} />
|
||||||
|
<WrappedRoute path='/settings/cashu' page={DefaultPage} component={Cashu} content={children} />
|
||||||
<WrappedRoute path='/settings/email' page={DefaultPage} component={EditEmail} content={children} />
|
<WrappedRoute path='/settings/email' page={DefaultPage} component={EditEmail} content={children} />
|
||||||
<WrappedRoute path='/settings/password' page={DefaultPage} component={EditPassword} content={children} />
|
<WrappedRoute path='/settings/password' page={DefaultPage} component={EditPassword} content={children} />
|
||||||
<WrappedRoute path='/settings/account' page={DefaultPage} component={DeleteAccount} content={children} />
|
<WrappedRoute path='/settings/account' page={DefaultPage} component={DeleteAccount} content={children} />
|
||||||
|
|
|
@ -167,6 +167,7 @@ export const EditIdentity = lazy(() => import('soapbox/features/edit-identity/in
|
||||||
export const Domains = lazy(() => import('soapbox/features/admin/domains.tsx'));
|
export const Domains = lazy(() => import('soapbox/features/admin/domains.tsx'));
|
||||||
export const EditDomainModal = lazy(() => import('soapbox/features/ui/components/modals/edit-domain-modal.tsx'));
|
export const EditDomainModal = lazy(() => import('soapbox/features/ui/components/modals/edit-domain-modal.tsx'));
|
||||||
export const NostrRelays = lazy(() => import('soapbox/features/nostr-relays/index.tsx'));
|
export const NostrRelays = lazy(() => import('soapbox/features/nostr-relays/index.tsx'));
|
||||||
|
export const Cashu = lazy(() => import('soapbox/features/cashu/index.tsx'));
|
||||||
export const Bech32Redirect = lazy(() => import('soapbox/features/nostr/Bech32Redirect.tsx'));
|
export const Bech32Redirect = lazy(() => import('soapbox/features/nostr/Bech32Redirect.tsx'));
|
||||||
export const ManageZapSplit = lazy(() => import('soapbox/features/admin/manage-zap-split.tsx'));
|
export const ManageZapSplit = lazy(() => import('soapbox/features/admin/manage-zap-split.tsx'));
|
||||||
export const ManageDittoServer = lazy(() => import('soapbox/features/admin/manage-ditto-server.tsx'));
|
export const ManageDittoServer = lazy(() => import('soapbox/features/admin/manage-ditto-server.tsx'));
|
||||||
|
|
|
@ -1190,6 +1190,16 @@
|
||||||
"new_group_panel.action": "Create Group",
|
"new_group_panel.action": "Create Group",
|
||||||
"new_group_panel.subtitle": "Can't find what you're looking for? Start your own private or public group.",
|
"new_group_panel.subtitle": "Can't find what you're looking for? Start your own private or public group.",
|
||||||
"new_group_panel.title": "Create Group",
|
"new_group_panel.title": "Create Group",
|
||||||
|
"settings.nostr.cashu": "Cashu",
|
||||||
|
"cashu.title": "Cashu",
|
||||||
|
"cashu.wallet.create": "Create Cashu Wallet",
|
||||||
|
"cashu.nutzap.info": "Your nutzap info",
|
||||||
|
"cashu.nutzap.swap": "Swap your Cashu",
|
||||||
|
"cashu.quote": "Create Cashu Quote",
|
||||||
|
"cashu.quote.state": "Get Cashu Quote State",
|
||||||
|
"cashu.wallet.mints": "Your mints",
|
||||||
|
"cashu.quote.mint": "Mint the Mint",
|
||||||
|
"cashu.wallet.mint_placeholder": "https://<mint-url>",
|
||||||
"nostr_extension.found": "<link>Sign in</link> with browser extension.",
|
"nostr_extension.found": "<link>Sign in</link> with browser extension.",
|
||||||
"nostr_extension.not_found": "Browser extension not found.",
|
"nostr_extension.not_found": "Browser extension not found.",
|
||||||
"nostr_extension.not_supported": "Browser extension not supported. Please upgrade to the latest version.",
|
"nostr_extension.not_supported": "Browser extension not supported. Please upgrade to the latest version.",
|
||||||
|
|
Ładowanie…
Reference in New Issue