Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contacts: Saving banner and photos locally #1953

Merged
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
10 changes: 8 additions & 2 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,8 @@ PODS:
- React
- RNCPicker (2.4.8):
- React-Core
- RNFS (2.20.0):
- React-Core
- RNGestureHandler (2.12.1):
- React-Core
- RNKeychain (8.1.1):
Expand Down Expand Up @@ -589,6 +591,7 @@ DEPENDENCIES:
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
- "RNCMaskedView (from `../node_modules/@react-native-community/masked-view`)"
- "RNCPicker (from `../node_modules/@react-native-picker/picker`)"
- RNFS (from `../node_modules/react-native-fs`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNKeychain (from `../node_modules/react-native-keychain`)
- RNPermissions (from `../node_modules/react-native-permissions`)
Expand Down Expand Up @@ -734,6 +737,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-community/masked-view"
RNCPicker:
:path: "../node_modules/@react-native-picker/picker"
RNFS:
:path: "../node_modules/react-native-fs"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNKeychain:
Expand All @@ -756,7 +761,7 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"

SPEC CHECKSUMS:
boost: 57d2868c099736d80fcd648bf211b4431e51a558
boost: a7c83b31436843459a1961bfd74b96033dc77234
BVLinearGradient: 34a999fda29036898a09c6a6b728b0b4189e1a44
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
Expand All @@ -768,7 +773,7 @@ SPEC CHECKSUMS:
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
lottie-ios: 8f97d3271e155c2d688875c29cd3c74908aef5f8
lottie-react-native: 8f9d4be452e23f6e5ca0fdc11669dc99ab52be81
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda
RCTRequired: df81ab637d35fac9e6eb94611cfd20f0feb05455
RCTTypeSafety: 4636e4a36c7c2df332bda6d59b19b41c443d4287
React: e0cc5197a804031a6c53fb38483c3485fcb9d6f3
Expand Down Expand Up @@ -821,6 +826,7 @@ SPEC CHECKSUMS:
RNCClipboard: 99fc8ad669a376b756fbc8098ae2fd05c0ed0668
RNCMaskedView: 0e1bc4bfa8365eba5fbbb71e07fbdc0555249489
RNCPicker: 0bf8ef8f7800524f32d2bb2a8bcadd53eda0ecd1
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: c0d04458598fcb26052494ae23dda8f8f5162b13
RNKeychain: ff836453cba46938e0e9e4c22e43d43fa2c90333
RNPermissions: 06845210af313594dc3fe5e8ae6afc820d791409
Expand Down
2 changes: 2 additions & 0 deletions ios/zeus.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2134,6 +2134,7 @@
"-ld_classic",
"-Wl",
"-ld_classic",
"-Wl -ld_classic ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
Expand Down Expand Up @@ -2211,6 +2212,7 @@
"-ld_classic",
"-Wl",
"-ld_classic",
"-Wl -ld_classic ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
"react-native-draglist": "3.5.1",
"react-native-elements": "3.4.3",
"react-native-encrypted-storage": "4.0.3",
"react-native-fs": "2.20.0",
"react-native-gesture-handler": "2.12.1",
"react-native-get-random-values": "1.9.0",
"react-native-hce": "0.1.2",
Expand Down
101 changes: 101 additions & 0 deletions utils/ContactUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { v4 as uuidv4 } from 'uuid';
import ContactUtils from './ContactUtils';

// Mocking the dependencies
jest.mock('react-native-fs', () => ({
DocumentDirectoryPath: '/mocked/document/directory',
downloadFile: jest.fn(() => ({ promise: Promise.resolve() }))
}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this required if we're not processing any images?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is not required for now, but unknowingly, we get issues in the test when this mock is removed, so I decided to keep it as it will be helpful in our future implementations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good


jest.mock('uuid', () => ({
v4: jest.fn(() => 'mocked-uuid')
}));

describe('ContactUtils', () => {
describe('transformContactData', () => {
it('transforms contact data properly', async () => {
// Test Case 1
const mockContact1 = {
display_name: 'Shubham',
name: 'Shubham',
about: 'Developer',
lud16: 'shubham@zeuspay.com',
nip05: 'nip05Address',
npub: 'npub1a3vuemsaex2rmshv3h5sr8xlxayehzkan3ucw3sxf8l2rtjyuhpshexnw4'
};

const transformedContact1 = await ContactUtils.transformContactData(
mockContact1
);

// Assertions
expect(uuidv4).toHaveBeenCalled();
expect(transformedContact1).toEqual({
photo: '',
name: 'Shubham',
description: 'Developer',
lnAddress: ['shubham@zeuspay.com'],
onchainAddress: [''],
pubkey: [''],
nip05: ['nip05Address'],
nostrNpub: [
'npub1a3vuemsaex2rmshv3h5sr8xlxayehzkan3ucw3sxf8l2rtjyuhpshexnw4'
],
contactId: 'mocked-uuid',
isFavourite: false,
banner: '',
isSelected: false
});

// Test Case 2
const mockContact2 = {
display_name: 'Evan',
name: 'Evan',
about: 'Maintainer at Zeus',
lud16: 'evan@zeuspay.com',
nip05: 'nip05Address',
npub: 'npub1xnf02f60r9v0e5kty33a404dm79zr7z2eepyrk5gsq3m7pwvsz2sazlpr5'
};

const transformedContact2 = await ContactUtils.transformContactData(
mockContact2
);

// Assertions
expect(uuidv4).toHaveBeenCalled();
expect(transformedContact2).toEqual({
photo: '',
name: 'Evan',
description: 'Maintainer at Zeus',
lnAddress: ['evan@zeuspay.com'],
onchainAddress: [''],
pubkey: [''],
nip05: ['nip05Address'],
nostrNpub: [
'npub1xnf02f60r9v0e5kty33a404dm79zr7z2eepyrk5gsq3m7pwvsz2sazlpr5'
],
contactId: 'mocked-uuid',
isFavourite: false,
banner: '',
isSelected: false
});
});

it('handles contact transformation ensuring result is not null and is an object.', async () => {
const mockContact = {
display_name: 'Shubham',
about: 'Software Developer',
lud16: 'shubham@zeuspay.com',
nip05: 'nip05Address',
npub: 'npub1a3vuemsaex2rmshv3h5sr8xlxayehzkan3ucw3sxf8l2rtjyuhpshexnw4'
};
const transformedContact = await ContactUtils.transformContactData(
mockContact
);

// Expect that the transformation did not result in null but is an object
expect(transformedContact).toBeDefined();
expect(transformedContact).toBeInstanceOf(Object);
});
});
});
83 changes: 83 additions & 0 deletions utils/ContactUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { v4 as uuidv4 } from 'uuid';
import { nip19 } from 'nostr-tools';
import RNFS from 'react-native-fs';

const transformContactData = async (contact: any) => {
try {
const name = contact?.display_name || contact?.name || '';
const transformedContact = {
photo: '',
name,
description: contact?.about || '',
lnAddress: contact?.lud16
? [contact?.lud16]
: contact?.lud06
? [contact?.lud06]
: contact?.lud16 && contact?.lud06
? [contact?.lud06, contact?.lud16]
: [],
onchainAddress: [''],
pubkey: [''],
nip05: contact?.nip05 ? [contact?.nip05] : [],
nostrNpub: contact?.npub
? [contact?.npub]
: contact?.pubkey
? [nip19.npubEncode(contact.pubkey)]
: [],
contactId: uuidv4(),
isFavourite: false,
banner: '',
isSelected: false
};

if (contact?.banner) {
console.log('Downloading banner...');
const bannerFileName =
'nostrContactBanner_' + transformedContact.name + '.png';
const bannerFilePath =
RNFS.DocumentDirectoryPath + '/' + bannerFileName;

try {
// Download the banner and save it locally
await RNFS.downloadFile({
fromUrl: contact?.banner,
toFile: bannerFilePath
}).promise;

console.log('Banner download successful!');
transformedContact.banner = 'file://' + bannerFilePath;
} catch (bannerError) {
console.error('Error downloading banner:', bannerError);
}
}

console.log('Transformed contact:', transformedContact);

if (contact?.picture) {
console.log('Downloading image...');
const fileName =
'nostrContactPhoto_' + transformedContact.contactId + '.png';
const filePath = RNFS.DocumentDirectoryPath + '/' + fileName;

try {
// Download the image and save it locally
await RNFS.downloadFile({
fromUrl: contact?.picture,
toFile: filePath
}).promise;

console.log('Download successful!');
transformedContact.photo = 'file://' + filePath;
} catch (photoError) {
console.error('Error downloading photo:', photoError);
}
}

return transformedContact;
} catch (error) {
console.error('Error transforming contact:', error);
return null;
}
};

export default { transformContactData };
2 changes: 1 addition & 1 deletion views/ContactDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export default class ContactDetails extends React.Component<
);

console.log('Contact imported successfully!');
this.props.navigation.navigate('Contacts');
this.props.navigation.navigate('Contacts', { loading: true });
};

toggleFavorite = () => {
Expand Down
Loading