Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions e2e/slashtags.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ d('Profile and Contacts', () => {
// - receive money and attach contact to the transaction
// - backup and restore wallet from the seed
// - check that everything is in place
// - delete profile

it('Can manage Slashtags Profile', async () => {
if (checkComplete('slash-1')) {
Expand Down Expand Up @@ -281,6 +282,19 @@ d('Profile and Contacts', () => {
element(by.text(satoshi.name).withAncestor(by.id('ContactSmall'))),
).toBeVisible();

// DELETE PROFILE
await element(by.id('NavigationClose')).tap();
await element(by.id('Header')).tap();
await element(by.id('EditButton')).tap();
await element(by.id('ProfileDeleteButton')).tap();
await waitFor(element(by.id('DeleteDialog')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('DialogConfirm')).tap();
await expect(element(by.id('EmptyProfileHeader'))).toBeVisible();
await element(by.id('Header')).tap();
await expect(element(by.id('OnboardingContinue'))).toBeVisible();

markComplete('slash-1');
});
});
Expand Down
65 changes: 58 additions & 7 deletions src/screens/Profile/ProfileEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useTranslation } from 'react-i18next';
import { ScrollView, View as ThemedView } from '../../styles/components';
import { BodyS } from '../../styles/text';
import { PlusIcon } from '../../styles/icons';
import Dialog from '../../components/Dialog';
import NavigationHeader from '../../components/NavigationHeader';
import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
import Button from '../../components/buttons/Button';
Expand All @@ -31,9 +32,10 @@ import { BasicProfile } from '../../store/types/slashtags';
import { slashtagsLinksSelector } from '../../store/reselect/slashtags';
import { onboardingProfileStepSelector } from '../../store/reselect/slashtags';
import { arraysMatch } from '../../utils/helpers';
import { deleteProfile, saveProfile } from '../../utils/slashtags';
import { showToast } from '../../utils/notifications';
import ProfileLinkNavigation from '../../navigation/bottom-sheet/ProfileLinkNavigation';
import type { RootStackScreenProps } from '../../navigation/types';
import { saveProfile } from '../../utils/slashtags';

const ProfileEdit = ({
navigation,
Expand All @@ -42,7 +44,8 @@ const ProfileEdit = ({
const { url, profile: slashtagsProfile } = useSlashtags();
const { profile: savedProfile } = useProfile(url);
const [hasEdited, setHasEdited] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const [fields, setFields] = useState<Omit<BasicProfile, 'links'>>({});
const dispatch = useAppDispatch();
const links = useAppSelector(slashtagsLinksSelector);
Expand Down Expand Up @@ -95,9 +98,9 @@ const ProfileEdit = ({
};

const onSave = async (): Promise<void> => {
setIsSaving(true);
setIsLoading(true);
const res = await saveProfile(url, profile, slashtagsProfile);
setIsSaving(false);
setIsLoading(false);
if (res.isErr()) {
return;
}
Expand All @@ -110,6 +113,23 @@ const ProfileEdit = ({
}
};

const onDelete = async (): Promise<void> => {
setIsLoading(true);
const res = await deleteProfile(url, slashtagsProfile);
setIsLoading(false);
setShowDeleteDialog(false);
if (res.isErr()) {
return;
}
await Keyboard.dismiss();
navigation.popToTop();
showToast({
type: 'success',
title: t('profile_delete_success_title'),
description: t('profile_delete_success_msg'),
});
};

const isValid = useCallback(() => {
const isAnyLinkEmpty = links.some((link) => link.url === '');

Expand Down Expand Up @@ -160,12 +180,28 @@ const ProfileEdit = ({
<BodyS color="secondary">{t('profile_public_warn')}</BodyS>

{/* leave button visible over keyboard for onboarding */}
<View style={onboardedProfile && styles.bottom}>
<View
style={[
styles.buttonsContainer,
onboardedProfile && styles.bottom,
]}>
{onboardedProfile && (
<Button
style={styles.button}
text={t('profile_delete')}
size="large"
variant="secondary"
loading={isLoading}
onPress={() => setShowDeleteDialog(true)}
testID="ProfileDeleteButton"
/>
)}

<Button
style={styles.button}
text={t(onboardedProfile ? 'profile_save' : 'continue')}
size="large"
loading={isSaving}
loading={isLoading}
disabled={!hasEdited || !isValid()}
onPress={onSave}
testID="ProfileSaveButton"
Expand All @@ -176,6 +212,17 @@ const ProfileEdit = ({
</KeyboardAvoidingView>

<ProfileLinkNavigation />

<Dialog
visible={showDeleteDialog}
title={t('profile_delete_dialogue_title')}
description={t('profile_delete_dialogue_msg')}
confirmText={t('profile_delete_dialogue_yes')}
visibleTestID="DeleteDialog"
onHide={(): void => setShowDeleteDialog(false)}
onConfirm={onDelete}
onCancel={(): void => setShowDeleteDialog(false)}
/>
</ThemedView>
);
};
Expand All @@ -198,9 +245,13 @@ const styles = StyleSheet.create({
bottom: {
marginTop: 'auto',
},
buttonsContainer: {
flexDirection: 'row',
marginTop: 16,
gap: 16,
},
button: {
flex: 1,
marginTop: 16,
},
});

Expand Down
2 changes: 1 addition & 1 deletion src/screens/Wallets/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const Header = (): ReactElement => {
{profile.name ? (
<Title>{truncate(profile?.name, 20)}</Title>
) : (
<Title>{t('your_name_capital')}</Title>
<Title testID="EmptyProfileHeader">{t('your_name_capital')}</Title>
)}
</Pressable>
<View style={styles.middleColumn} />
Expand Down
9 changes: 9 additions & 0 deletions src/store/slices/slashtags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export const slashtagsSlice = createSlice({
deleteLink: (state, action: PayloadAction<string>) => {
state.links = state.links.filter((link) => link.id !== action.payload);
},
deleteAllLinks: (state) => {
state.links = [];
},
addContact: (
state,
action: PayloadAction<{ url: string; name: string }>,
Expand Down Expand Up @@ -79,6 +82,10 @@ export const slashtagsSlice = createSlice({
const { id } = parse(action.payload.url);
state.profilesCache[id] = action.payload.profile;
},
deleteProfileCache: (state, action: PayloadAction<{ url: string }>) => {
const { id } = parse(action.payload.url);
delete state.profilesCache[id];
},
resetSlashtagsState: () => initialSlashtagsState,
},
});
Expand All @@ -92,11 +99,13 @@ export const {
addLink,
editLink,
deleteLink,
deleteAllLinks,
addContact,
addContacts,
deleteContact,
updateLastPaidContacts,
cacheProfile,
deleteProfileCache,
resetSlashtagsState,
} = actions;

Expand Down
26 changes: 25 additions & 1 deletion src/utils/i18n/locales/en/slashtags.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
"string": "Profile"
},
"profile_save": {
"string": "Save Profile"
"string": "Save"
},
"profile_pay_contacts": {
"string": "Pay Your Contacts"
Expand Down Expand Up @@ -158,6 +158,24 @@
"profile_edit": {
"string": "Edit Profile"
},
"profile_delete": {
"string": "Delete"
},
"profile_delete_dialogue_title": {
"string": "Delete Profile Information?"
},
"profile_delete_dialogue_msg": {
"string": "Are you sure you want to delete all of your Bitkit profile information?"
},
"profile_delete_dialogue_yes": {
"string": "Yes, Delete"
},
"profile_delete_success_title": {
"string": "Profile Deleted"
},
"profile_delete_success_msg": {
"string": "Your Bitkit profile information has been deleted."
},
"offline_enable": {
"string": "Enable payments with contacts*"
},
Expand Down Expand Up @@ -236,6 +254,12 @@
"error_saving_profile": {
"string": "Unable To Save Profile"
},
"error_saving_profile": {
"string": "Unable To Save Profile"
},
"error_deleting_profile": {
"string": "Unable To Delete Profile"
},
"error_pay_title": {
"string": "Unable To Pay Contact"
},
Expand Down
48 changes: 46 additions & 2 deletions src/utils/slashtags/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { format, parse } from '@synonymdev/slashtags-url';
import i18n from '../i18n';
import { showToast } from '../notifications';
import { BasicProfile, SlashPayConfig } from '../../store/types/slashtags';
import { cacheProfile } from '../../store/slices/slashtags';
import {
cacheProfile,
deleteAllLinks,
deleteProfileCache,
setOnboardingProfileStep,
} from '../../store/slices/slashtags';
import { TWalletName } from '../../store/types/wallet';
import { dispatch, getSettingsStore } from '../../store/helpers';
import { createLightningInvoice } from '../../store/utils/lightning';
Expand All @@ -26,6 +31,7 @@ import {
getSelectedWallet,
} from '../wallet';
import SlashpayConfig from './slashpay';
import { updateSettings } from '../../store/slices/settings';

/**
* Handles pasting or scanning a slash:// url
Expand Down Expand Up @@ -61,7 +67,7 @@ export const saveProfile = async (
console.log('profile saving error', e);
showToast({
type: 'warning',
title: i18n.t('slashtags:error_saving_contact'),
title: i18n.t('slashtags:error_saving_profile'),
description: i18n.t('other:try_again'),
});
return err(e);
Expand All @@ -72,6 +78,31 @@ export const saveProfile = async (
return ok('Profile saved');
};

export const deleteProfile = async (
url: string,
slashtagsProfile: SlashtagsProfile,
): Promise<Result<string>> => {
try {
await slashtagsProfile.del({ awaitRelaySync: true });
await deleteSlashPayConfig();
} catch (e) {
console.log('profile delete error', e);
showToast({
type: 'warning',
title: i18n.t('slashtags:error_deleting_profile'),
description: i18n.t('other:try_again'),
});
return err(e);
}

dispatch(deleteProfileCache({ url }));
dispatch(deleteAllLinks());
dispatch(setOnboardingProfileStep('Intro'));
dispatch(updateSettings({ enableOfflinePayments: false }));

return ok('Profile deleted');
};

const INVOICE_EXPIRY_DELTA = 60 * 60 * 24 * 7; // one week

export const getNewProfileUrl = (url: string, webRelayUrl: string): string => {
Expand All @@ -82,6 +113,19 @@ export const getNewProfileUrl = (url: string, webRelayUrl: string): string => {
return res;
};

const deleteSlashPayConfig = async (): Promise<Result<string>> => {
try {
if (!webRelayClient) {
throw new Error('webRelayClient not ready yet');
}
const slashpay = new SlashpayConfig(webRelayClient);
await slashpay.del();
return ok('Deleted slashpay.json');
} catch (e) {
return err(e);
}
};

export const updateSlashPayConfig = debounce(
async ({
forceUpdate = false,
Expand Down
Loading