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

feat: improve push notification opt-in modal #462

Open
wants to merge 45 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
448ab56
feat: add nano contract history effect, actions and reducer
alexruzenhack Feb 21, 2024
5a29294
test: add history effect tests and extract fixtures
alexruzenhack Feb 21, 2024
1371eca
refactor: persistence to use store interface from lib
alexruzenhack Feb 28, 2024
4aaa4c9
refactor: getNanoContractHistory
alexruzenhack Mar 30, 2024
60a7d03
refactor: extract TxHistory construction to the model
alexruzenhack Feb 23, 2024
b9b50f5
feat: add nano contract information on tx details view
alexruzenhack Feb 23, 2024
7b3e271
feat: add version info to tranaction list item
alexruzenhack Mar 6, 2024
c05b1fc
feat: add getShortContent and adjust TxDetailsModal
alexruzenhack Mar 6, 2024
406ef80
feat: adjust FlatList style
alexruzenhack Mar 27, 2024
3327610
feat: add HathorFlatList component
alexruzenhack Mar 27, 2024
47e1612
refactor: TokenSelect to use HathorFlatList
alexruzenhack Mar 27, 2024
4eb2288
refactor: TxHistoryView to use HathorFlatList
alexruzenhack Mar 27, 2024
96e54fe
refactor: align MainScreen content to flex-start
alexruzenhack Mar 27, 2024
dabaec0
feat: extend SimpleButton to support children element
alexruzenhack Mar 27, 2024
f8678c0
refactor: replace info icon to ActionDot action
alexruzenhack Mar 27, 2024
d6d3ebd
feat: make HathorFlatList to shrink
alexruzenhack Mar 27, 2024
dcdfb49
feat: add ActionDot icon
alexruzenhack Mar 28, 2024
37145c7
chore: add instrumentation to sagas/token
alexruzenhack Mar 28, 2024
ca457b1
chore: add instrumentation on sagas/tokens
alexruzenhack Mar 28, 2024
05f81d4
chore: add instrumentation to sagas/helpers
alexruzenhack Mar 28, 2024
dc2bf7d
chore: add instrumentation to sagas/networkSettings
alexruzenhack Mar 28, 2024
66b9327
feat: improve TxDetailsModal
alexruzenhack Mar 28, 2024
aad5705
feat: extends PublicExplorerListButton
alexruzenhack Mar 28, 2024
ddbb58a
feat: add CircleCheck.icon
alexruzenhack Mar 29, 2024
8e41ab4
feat: add CircleClock icon
alexruzenhack Mar 29, 2024
6e15b3d
feat: add CircleError icon
alexruzenhack Mar 29, 2024
b51bf02
feat: add TransactionStatusLabel component
alexruzenhack Mar 29, 2024
01b3053
feat: add colors to styles/themes
alexruzenhack Mar 29, 2024
0d2a74a
feat: add processingStatus property to TxHistory model
alexruzenhack Mar 29, 2024
49084b3
feat: add transaction status nc context on TxDetailsModel component
alexruzenhack Mar 29, 2024
774646d
feat: add icons and components
alexruzenhack Mar 31, 2024
bf5ce64
lint: comply with rules
alexruzenhack Mar 31, 2024
f47c5b1
feat: add TwoOptionsToggle and refactor Dashboard screen
alexruzenhack Mar 31, 2024
6a4612b
feat: add nano contract list
alexruzenhack Mar 31, 2024
243c126
refactor: replace FlatList to HathorFlatList
alexruzenhack Mar 31, 2024
16c09ed
feat(nc): add icons
alexruzenhack Apr 1, 2024
432ff49
feat(nc): add components
alexruzenhack Apr 1, 2024
674669c
feat(nc): implement unregister nano contract saga effect
alexruzenhack Apr 1, 2024
7be7904
feat: add nano contract screen
alexruzenhack Apr 1, 2024
593332c
feat: add nano contract components
alexruzenhack Apr 1, 2024
a0cb0b0
feat(nc): add icons
alexruzenhack Apr 1, 2024
662051e
feat: extends TextValue component
alexruzenhack Apr 1, 2024
ee0be8a
feat: add nano contract transaction screen
alexruzenhack Apr 1, 2024
a0caced
feat: add nano contract transaction components
alexruzenhack Apr 1, 2024
872185e
feat: improve aprearance of push notification opt-in modal
alexruzenhack Apr 9, 2024
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
144 changes: 144 additions & 0 deletions __tests__/sagas/nanoContracts/fixtures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { jest } from '@jest/globals';

export const fixtures = {
address: 'HTeZeYTCv7cZ8u7pBGHkWsPwhZAuoq5j3V',
ncId: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595',
ncApi: {
getNanoContractState: {
successResponse: {
success: true,
nc_id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595',
blueprint_name: 'Bet',
fields: {
token_uid: { value: '00' },
total: { value: 300 },
final_result: { value: '1x0' },
oracle_script: { value: '76a91441c431ff7ad5d6ce5565991e3dcd5d9106cfd1e288ac' },
'withdrawals.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: 300 },
'address_details.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: { '1x0': 100 } },
},
},
},
getNanoContractHistory: {
failureResponse: {
success: false,
},
errorResponse: {
error: new Error('API call error'),
},
successResponse: {
success: true,
history: [
{
hash: '000000203e87e8575f121de16d0eb347bd1473eedd9f46cc76c1bc8d4e5a5fce',
nonce: 2104638,
timestamp: 1708356261,
version: 4,
weight: 21.89480540500889,
signal_bits: 0,
parents: [
'0000008fbebdf8d78be50c88aceebf3c6b9e92f4affd7dfc96d48a7a49f23e69',
'00000121c46366b19de5efa8e6c23f62895322486395a0e31e987f9073025989'
],
inputs: [
{
tx_id: '0000008fbebdf8d78be50c88aceebf3c6b9e92f4affd7dfc96d48a7a49f23e69',
index: 0,
data: 'RjBEAiBtWa0q8uzMvBfkh83Y+t4Tv5OeyJSD8NazaGp19Hc2UwIgbV2m5unBEHlTAcLJsZLsCBlnfpua8LrUkVORiW/t4OQhAolqAR4yFaeCBeu/kOG1SwWnRj2X62zT9mU+Deutnbqq'
}
],
outputs: [
{
value: 78500,
token_data: 1,
script: 'dqkU5W5CR9734WcxaZMJkuToO8XlD3mIrA=='
},
{
value: 300,
token_data: 2,
script: 'dqkU5W5CR9734WcxaZMJkuToO8XlD3mIrA=='
}
],
tokens: [
'00000117b0502e9eef9ccbe987af65f153aa899d6eba88d50a6c89e78644713d',
'0000038c49253f86e6792006dd9124e2c50e6487fde3296b7bd637e3e1a497e7'
],
nc_id: '000001342d3c5b858a4d4835baea93fcc683fa615ff5892bd044459621a0340a',
nc_method: 'swap',
nc_args: '',
nc_pubkey: '020b120c8ad037ceb2e5b51b3edda7cd15a44f843b56e49880f6647fa9aadadffa'
},
],
},
},
},
ncSaga: {
getNanoContractState: {
errorResponse: {
error: new Error('API call error'),
},
successResponse: {
ncState: {
success: true,
nc_id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595',
blueprint_name: 'Bet',
fields: {
token_uid: { value: '00' },
total: { value: 300 },
final_result: { value: '1x0' },
oracle_script: { value: '76a91441c431ff7ad5d6ce5565991e3dcd5d9106cfd1e288ac' },
'withdrawals.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: 300 },
'address_details.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: { '1x0': 100 } },
},
},
},
},
fetchHistory: {
successResponse: {
history: [
{
txId: '000000203e87e8575f121de16d0eb347bd1473eedd9f46cc76c1bc8d4e5a5fce',
timestamp: 1708356261,
tokens: [
'00000117b0502e9eef9ccbe987af65f153aa899d6eba88d50a6c89e78644713d',
'0000038c49253f86e6792006dd9124e2c50e6487fde3296b7bd637e3e1a497e7'
],
isVoided: false, // review
ncId: '000001342d3c5b858a4d4835baea93fcc683fa615ff5892bd044459621a0340a',
ncMethod: 'swap',
},
],
next: null,
},
},
},
wallet: {
notReady: {
isReady: () => false,
},
addressNotMine: {
isReady: () => true,
isAddressMine: jest.fn().mockReturnValue(false),
storage: {
isNanoContractRegistered: jest.fn(),
registerNanoContract: jest.fn(),
},
},
readyAndMine: {
isReady: () => true,
isAddressMine: jest.fn().mockReturnValue(true),
storage: {
isNanoContractRegistered: jest.fn(),
registerNanoContract: jest.fn(),
getNanoContract: jest.fn(),
},
},
},
store: {
nanoContractAddressAlreadyRegistered: {
ncId: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595',
blueprintName: 'Bet',
addresses: new Set(['HTeZeYTCv7cZ8u7pBGHkWsPwhZAuoq5j3V']),
},
},
};
150 changes: 150 additions & 0 deletions __tests__/sagas/nanoContracts/historyNanoContract.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { put } from 'redux-saga/effects';
import { ncApi, addressUtils, transactionUtils } from '@hathor/wallet-lib';
import { jest, test, expect, beforeEach, describe } from '@jest/globals';
import {
failureMessage,
requestHistoryNanoContract,
fetchHistory
} from '../../../src/sagas/nanoContract';
import {
nanoContractHistoryFailure,
nanoContractHistoryRequest,
nanoContractHistorySuccess,
onExceptionCaptured
} from '../../../src/actions';
import { STORE } from '../../../src/store';
import { fixtures } from './fixtures';

jest.mock('@hathor/wallet-lib');

beforeEach(() => {
jest.clearAllMocks();
STORE.clearItems();
});

describe('sagas/nanoContract/fetchHistory', () => {
test('success', async () => {
// arrange wallet mock
const mockedWallet = {
getNetworkObject: jest.fn(),
storage: {
isAddressMine: jest.fn(),
},
};
// arrange ncApi mock
const mockedNcApi = jest.mocked(ncApi);
mockedNcApi.getNanoContractHistory
.mockReturnValue(fixtures.ncApi.getNanoContractHistory.successResponse);
// arrange addressUtils mock
const mockedAddressUtils = jest.mocked(addressUtils);
mockedAddressUtils.getAddressFromPubkey
.mockResolvedValue('123');
// arrange transactionUtils
const mockedTransactionUtils = jest.mocked(transactionUtils);
mockedTransactionUtils.getTxBalance
.mockResolvedValue({});

// call fetchHistory
const count = 1;
const after = null;
const result = await fetchHistory(fixtures.ncId, count, after, mockedWallet);

// assert result is defined
expect(result.history).toBeDefined();
expect(result.next).toBeDefined();
// assert next value is a txId from the last element of history
expect(result.next).toBe(fixtures.ncSaga.fetchHistory.successResponse.history[0].txId);
// assert call count to API
expect(mockedNcApi.getNanoContractHistory).toBeCalledTimes(1);
});

test('failure', async () => {
// arrange ncApi mock
const mockedNcApi = jest.mocked(ncApi);
mockedNcApi.getNanoContractHistory
.mockReturnValue(fixtures.ncApi.getNanoContractHistory.failureResponse);

// call fetchHistory and assert exception
const count = 1;
const after = null;
await expect(fetchHistory(fixtures.ncId, count, after)).rejects.toThrow('Failed to fetch nano contract history');
});
});

describe('sagas/nanoContract/requestHistoryNanoContract', () => {
test('history without registered contract', () => {
// arrange Nano Contract registration inputs
const { address, ncId } = fixtures;

// call effect to request history
const gen = requestHistoryNanoContract(nanoContractHistoryRequest({ address, ncId }));
// select wallet
gen.next();
// feed back wallet
gen.next(fixtures.wallet.readyAndMine);

// expect failure
// feed back isNanoContractRegistered
expect(gen.next(false).value)
.toStrictEqual(put(nanoContractHistoryFailure(failureMessage.notRegistered)));
});

test('fetch history fails', () => {
// arrange Nano Contract registration inputs
const { address, ncId } = fixtures;
const storage = STORE.getStorage();
storage.registerNanoContract(ncId, { ncId });

// call effect to request history
const gen = requestHistoryNanoContract(nanoContractHistoryRequest({ address, ncId }));
// select wallet
gen.next();
// feed back wallet
gen.next(fixtures.wallet.readyAndMine);
// feed back isNanoContractRegistered
const fetchHistoryCall = gen.next(true).value;

// throws on fetchHistory call
const failureCall = gen.throw(new Error('history')).value;
const onErrorCall = gen.next().value;

// assert failure
expect(fetchHistoryCall.payload.fn).toBe(fetchHistory);
expect(failureCall).toStrictEqual(put(nanoContractHistoryFailure(failureMessage.nanoContractHistoryFailure, new Error('history'))));
expect(onErrorCall).toStrictEqual(put(onExceptionCaptured(new Error('history'), false)));
});

test('history with success', () => {
// arrange Nano Contract registration inputs
const { address, ncId } = fixtures;

// call effect to request history
const gen = requestHistoryNanoContract(nanoContractHistoryRequest({ address, ncId }));
// select wallet
gen.next();
// feed back wallet
gen.next(fixtures.wallet.readyAndMine);
// feed back isNanoContractRegistered
const fetchHistoryCall = gen.next(true).value;
// feed back fetchHistory
gen.next(fixtures.ncSaga.fetchHistory.successResponse);
// feed back getNanoContract
gen.next({ ncId });
// call registerNanoContract and yield put nanoContractHistoryLoad
const historyLoadCall = gen.next().value;

const sucessCall = gen.next().value;

// assert success
expect(fetchHistoryCall.payload.fn).toBe(fetchHistory);
expect(historyLoadCall.payload).toHaveProperty('action.payload.ncId');
expect(historyLoadCall.payload).toHaveProperty('action.payload.history');
expect(historyLoadCall.payload.action.payload.ncId).toStrictEqual(ncId);
expect(historyLoadCall.payload.action.payload.history).toStrictEqual(
fixtures.ncSaga.fetchHistory.successResponse.history
);
expect(sucessCall).toStrictEqual(put(nanoContractHistorySuccess()));
// assert termination
expect(gen.next().value).toBeUndefined();
});
});
62 changes: 1 addition & 61 deletions __tests__/sagas/nanoContracts/registerNanoContract.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,7 @@ import { jest, test, expect, beforeEach, describe } from '@jest/globals';
import { registerNanoContract, failureMessage } from '../../../src/sagas/nanoContract';
import { nanoContractRegisterFailure, nanoContractRegisterRequest, onExceptionCaptured, types } from '../../../src/actions';
import { STORE } from '../../../src/store';

const fixtures = {
address: 'HTeZeYTCv7cZ8u7pBGHkWsPwhZAuoq5j3V',
ncId: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595',
ncApi: {
getNanoContractState: {
successResponse: {
success: true,
nc_id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595',
blueprint_name: 'Bet',
fields: {
token_uid: { value: '00' },
total: { value: 300 },
final_result: { value: '1x0' },
oracle_script: { value: '76a91441c431ff7ad5d6ce5565991e3dcd5d9106cfd1e288ac' },
'withdrawals.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: 300 },
'address_details.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: { '1x0': 100 } },
}
}
}
},
ncSaga: {
getNanoContractState: {
errorResponse: new Error('API call error'),
successResponse: {
success: true,
nc_id: '3cb032600bdf7db784800e4ea911b10676fa2f67591f82bb62628c234e771595',
blueprint_name: 'Bet',
fields: {
token_uid: { value: '00' },
total: { value: 300 },
final_result: { value: '1x0' },
oracle_script: { value: '76a91441c431ff7ad5d6ce5565991e3dcd5d9106cfd1e288ac' },
'withdrawals.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: 300 },
'address_details.a\'Wi8zvxdXHjaUVAoCJf52t3WovTZYcU9aX6\'': { value: { '1x0': 100 } },
}
}
},
},
wallet: {
notReady: {
isReady: () => false,
},
addressNotMine: {
isReady: () => true,
isAddressMine: jest.fn().mockReturnValue(false),
storage: {
isNanoContractRegistered: jest.fn(),
registerNanoContract: jest.fn(),
},
},
readyAndMine: {
isReady: () => true,
isAddressMine: jest.fn().mockReturnValue(true),
storage: {
isNanoContractRegistered: jest.fn(),
registerNanoContract: jest.fn(),
},
},
},
};
import { fixtures } from './fixtures';

beforeEach(() => {
jest.clearAllMocks();
Expand Down
8 changes: 8 additions & 0 deletions locale/da/texts.po
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,14 @@ msgstr ""
msgid "Error while trying to get Nano Contract state."
msgstr ""

#: src/sagas/nanoContract.js:35
msgid "Nano Contract not registered."
msgstr ""

#: src/sagas/nanoContract.js:36
msgid "Error while trying to fetch Nano Contract history."
msgstr ""

#: src/sagas/networkSettings.js:88
msgid "Custom Network Settings cannot be empty."
msgstr ""
Expand Down
8 changes: 8 additions & 0 deletions locale/pt-br/texts.po
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,14 @@ msgstr "Nano Contrato não encontrado"
msgid "Error while trying to get Nano Contract state."
msgstr "Erro ao obter o estado do Nano Contrato."

#: src/sagas/nanoContract.js:35
msgid "Nano Contract not registered."
msgstr "Nano Contrato não registrado."

#: src/sagas/nanoContract.js:36
msgid "Error while trying to fetch Nano Contract history."
msgstr "Error ao fazer download do histórico de transações do Nano Contrato."

#: src/sagas/networkSettings.js:88
msgid "Custom Network Settings cannot be empty."
msgstr "As Configurações de Rede não podem estar vazias."
Expand Down
Loading