Skip to content

Commit c2ee053

Browse files
committed
wip
1 parent 70972a7 commit c2ee053

File tree

7 files changed

+1220
-21
lines changed

7 files changed

+1220
-21
lines changed

src/components/modals/BuyOptionsModal.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@
204204
<script lang="ts">
205205
import { computed, defineComponent, onMounted, ref } from '@vue/composition-api';
206206
import { PageBody, FiatAmount, CircleSpinner } from '@nimiq/vue-components';
207+
import { useDemoStore } from '@/stores/Demo';
207208
import Modal from './Modal.vue';
208209
import CountrySelector from '../CountrySelector.vue';
209210
import CountryFlag from '../CountryFlag.vue';
@@ -234,13 +235,16 @@ export default defineComponent({
234235
235236
const country = ref<Country>(null);
236237
238+
const { isDemoEnabled } = useDemoStore();
237239
const isMoonpayAvailable = computed(() => { // eslint-disable-line arrow-body-style
240+
if (isDemoEnabled.value) return true;
238241
if (!config.moonpay.enabled) return false;
239242
if (!country.value) return true;
240243
return MOONPAY_COUNTRY_CODES.includes(country.value.code);
241244
});
242245
243246
const isSimplexAvailable = computed(() => { // eslint-disable-line arrow-body-style
247+
if (isDemoEnabled.value) return true;
244248
if (!config.simplex.enabled) return false;
245249
if (!country.value) return true;
246250
return SIMPLEX_COUNTRY_CODES.includes(country.value.code);
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<template>
2+
<Modal :showOverlay="showOverlay">
3+
<PageHeader class="flex-column">
4+
<h1 class="nq-h1">{{ $t('Buy NIM') }}</h1>
5+
<div class="demo-warning nq-label">
6+
{{ $t('DEMO') }}
7+
</div>
8+
</PageHeader>
9+
<PageBody>
10+
<div class="flex-row">
11+
12+
<AmountInput v-model="amount" :decimals="5">
13+
<AmountMenu slot="suffix" class="ticker" currency="nim" :open="amountMenuOpened"
14+
:activeCurrency="activeCurrency" :fiatCurrency="fiatCurrency" :feeOption="false"
15+
:otherFiatCurrencies="otherFiatCurrencies"
16+
@click.native.stop="amountMenuOpened = !amountMenuOpened"
17+
/>
18+
</AmountInput>
19+
</div>
20+
</PageBody>
21+
<PageFooter>
22+
<button class="nq-button light-blue" @click="buyDummyNim" :disabled="!amount">
23+
{{ $t('Buy NIM') }}
24+
</button>
25+
</PageFooter>
26+
27+
<PageBody slot="overlay" class="overlay-content">
28+
<HighFiveIcon />
29+
<h2 class="nq-h2">
30+
{{ $t('Your NIM is under its way!') }}
31+
</h2>
32+
<p>
33+
{{ $t('This transaction is instant and secure.') }}
34+
</p>
35+
</PageBody>
36+
</Modal>
37+
</template>
38+
39+
<script lang="ts">
40+
import { computed, defineComponent, ref } from '@vue/composition-api';
41+
import { PageBody, PageHeader, PageFooter } from '@nimiq/vue-components';
42+
import AmountInput from '@/components/AmountInput.vue';
43+
import AmountMenu from '@/components/AmountMenu.vue';
44+
import Modal from '@/components/modals/Modal.vue';
45+
import { useAccountStore } from '@/stores/Account';
46+
import { useFiatStore } from '@/stores/Fiat';
47+
import { FIAT_CURRENCIES_OFFERED } from '@/lib/Constants';
48+
// import { useTransactionsStore } from '@/stores/Transactions';
49+
import { useDemoStore } from '@/stores/Demo';
50+
import { useRouter } from '@/router';
51+
import HighFiveIcon from '@/components/icons/HighFiveIcon.vue';
52+
// import { useAddressStore } from '@/stores/Address';
53+
54+
export default defineComponent({
55+
setup() {
56+
const { activeCurrency } = useAccountStore();
57+
const { currency: fiatCurrency } = useFiatStore();
58+
const otherFiatCurrencies = computed(() =>
59+
FIAT_CURRENCIES_OFFERED.filter((fiat) => fiat !== fiatCurrency.value));
60+
const amount = ref(0);
61+
const amountMenuOpened = ref(false);
62+
const showOverlay = ref(false);
63+
const router = useRouter();
64+
65+
// const { activeAddressInfo } = useAddressStore();
66+
// const maxSendableAmount = computed(() => Math.max((activeAddressInfo.value!.balance || 0), 0));
67+
// const sendMax = () => amount.value = maxSendableAmount.value;
68+
69+
function buyDummyNim() {
70+
useDemoStore().buyDemoNim(amount.value);
71+
showOverlay.value = true;
72+
setTimeout(() => {
73+
showOverlay.value = false;
74+
router.push('/');
75+
}, 4000);
76+
}
77+
78+
return {
79+
amount,
80+
activeCurrency,
81+
fiatCurrency,
82+
otherFiatCurrencies,
83+
amountMenuOpened,
84+
buyDummyNim,
85+
showOverlay,
86+
// sendMax,
87+
};
88+
},
89+
components: {
90+
Modal,
91+
AmountInput,
92+
AmountMenu,
93+
PageHeader,
94+
PageBody,
95+
PageFooter,
96+
HighFiveIcon,
97+
},
98+
});
99+
</script>
100+
101+
<style scoped lang="scss">
102+
.small-page {
103+
> .page-header {
104+
overflow: hidden;
105+
106+
.demo-warning {
107+
margin: 0;
108+
text-align: center;
109+
position: absolute;
110+
top: 0;
111+
left: 0;
112+
right: 0;
113+
background: var(--nimiq-orange-bg);
114+
color: white;
115+
padding: 0.5rem 0;
116+
}
117+
}
118+
}
119+
120+
::v-deep .nq-card.overlay {
121+
background: var(--nimiq-green);
122+
color: white;
123+
124+
.overlay-content {
125+
display: flex;
126+
flex-direction: column;
127+
justify-content: center;
128+
align-items: center;
129+
text-align: center;
130+
131+
svg {
132+
width: 128px;
133+
height: 128px;
134+
}
135+
136+
p {
137+
margin-top: 0;
138+
text-wrap: pretty;
139+
}
140+
}
141+
142+
.close-button {
143+
display: none;
144+
}
145+
}
146+
</style>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<template>
2+
<Modal>
3+
<PageHeader class="flex-column">
4+
<h1 class="nq-h1">{{ $t('Nimiq Demo') }}</h1>
5+
</PageHeader>
6+
<PageBody>
7+
<p class="nq-p">
8+
{{ $t('This is not a real Nimiq Wallet. It is just a demo so it is limited in functionality.') }}
9+
</p>
10+
<p>
11+
{{ $t('You can open a free NIM account in less than a minute.') }}
12+
</p>
13+
</PageBody>
14+
<PageFooter>
15+
<a href="https://wallet.nimiq.com" target="_blank" class="nq-button light-blue">
16+
{{ $t('Open Nimiq Wallet') }}
17+
</a>
18+
</PageFooter>
19+
</Modal>
20+
</template>
21+
22+
<script lang="ts">
23+
import { defineComponent } from '@vue/composition-api';
24+
import { PageBody, PageHeader, PageFooter } from '@nimiq/vue-components';
25+
import Modal from '../Modal.vue';
26+
27+
export default defineComponent({
28+
components: {
29+
Modal,
30+
PageHeader,
31+
PageBody,
32+
PageFooter,
33+
},
34+
});
35+
</script>

src/hub.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { WELCOME_MODAL_LOCALSTORAGE_KEY, WELCOME_STAKING_MODAL_LOCALSTORAGE_KEY
3838
import { usePwaInstallPrompt } from './composables/usePwaInstallPrompt';
3939
import type { SetupSwapWithKycResult, SWAP_KYC_HANDLER_STORAGE_KEY } from './swap-kyc-handler'; // avoid bundling
4040
import type { RelayServerInfo } from './lib/usdc/OpenGSN';
41-
import { HubApiMock, isPlaygroundEnabled } from './stores/Playground';
41+
import { DemoHubApi, checkIfDemoIsActive } from './stores/Demo';
4242

4343
export function shouldUseRedirects(ignoreSettings = false): boolean {
4444
if (!ignoreSettings) {
@@ -115,7 +115,7 @@ function getBehavior({
115115

116116
// We can't use the reactive config via useConfig() here because that one can only be used after the composition-api
117117
// plugin has been registered in Vue 2.
118-
const hubApi = new HubApi(Config.hubEndpoint);
118+
const hubApi = checkIfDemoIsActive() ? DemoHubApi.create() : new HubApi(Config.hubEndpoint);
119119

120120
hubApi.on(HubApi.RequestType.ONBOARD, async (accounts) => {
121121
const { config } = useConfig();

src/main.ts

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { launchElectrum } from './electrum';
1717
import { launchPolygon } from './ethers';
1818
import { useAccountStore } from './stores/Account';
1919
import { useFiatStore } from './stores/Fiat';
20+
import { useDemoStore } from './stores/Demo';
2021
import { useSettingsStore } from './stores/Settings';
2122
import router from './router';
2223
import { i18n, loadLanguage } from './i18n/i18n-setup';
@@ -48,14 +49,21 @@ Vue.use(VuePortal, { name: 'Portal' });
4849

4950
async function start() {
5051
initPwa(); // Must be called as soon as possible to catch early browser events related to PWA
51-
await initStorage(); // Must be awaited before starting Vue
52-
initTrials(); // Must be called after storage was initialized, can affect Config
53-
// Must run after VueCompositionApi has been enabled and after storage was initialized. Could potentially run in
54-
// background and in parallel to syncFromHub, but RedirectRpcClient.init does not actually run async code anyways.
55-
await initHubApi();
56-
syncFromHub(); // Can run parallel to Vue initialization; must be called after storage was initialized.
57-
58-
serviceWorkerHasUpdate.then((hasUpdate) => useSettingsStore().state.updateAvailable = hasUpdate);
52+
const { isDemoEnabled } = useDemoStore();
53+
54+
if (!isDemoEnabled.value) {
55+
await initStorage(); // Must be awaited before starting Vue
56+
initTrials(); // Must be called after storage was initialized, can affect Config
57+
// Must run after VueCompositionApi has been enabled and after storage was initialized. Could potentially run in
58+
// background and in parallel to syncFromHub, but RedirectRpcClient.init does not actually run async code
59+
// anyways.
60+
await initHubApi();
61+
syncFromHub(); // Can run parallel to Vue initialization; must be called after storage was initialized.
62+
63+
serviceWorkerHasUpdate.then((hasUpdate) => useSettingsStore().state.updateAvailable = hasUpdate);
64+
} else {
65+
useDemoStore().initialize(router);
66+
}
5967

6068
// Update exchange rates every 2 minutes or every 10 minutes, depending on whether the Wallet is currently actively
6169
// used. If an update takes longer than that time due to a provider's rate limit, wait until the update succeeds
@@ -94,11 +102,15 @@ async function start() {
94102
const { language } = useSettingsStore();
95103
loadLanguage(language.value);
96104

97-
startSentry();
105+
if (!isDemoEnabled.value) {
106+
startSentry();
107+
}
98108

99109
const { config } = useConfig();
100110

101-
if (config.environment !== ENV_MAIN) {
111+
if (isDemoEnabled.value) {
112+
document.title = 'Nimiq Wallet Demo';
113+
} else if (config.environment !== ENV_MAIN) {
102114
document.title = 'Nimiq Testnet Wallet';
103115
}
104116

@@ -107,15 +119,17 @@ async function start() {
107119
initFastspotApi(config.fastspot.apiEndpoint, config.fastspot.apiKey);
108120
});
109121

110-
watch(() => {
111-
if (!config.oasis.apiEndpoint) return;
112-
initOasisApi(config.oasis.apiEndpoint);
113-
});
122+
if (!isDemoEnabled.value) {
123+
watch(() => {
124+
if (!config.oasis.apiEndpoint) return;
125+
initOasisApi(config.oasis.apiEndpoint);
126+
});
114127

115-
watch(() => {
116-
if (!config.ten31Pass.enabled) return;
117-
initKycConnection();
118-
});
128+
watch(() => {
129+
if (!config.ten31Pass.enabled) return;
130+
initKycConnection();
131+
});
132+
}
119133

120134
// Make reactive config accessible in components
121135
Vue.prototype.$config = config;

src/network.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { AddStakeEvent, ApiValidator, RawValidator, useStakingStore } from './st
1212
import { ENV_MAIN, STAKING_CONTRACT_ADDRESS } from './lib/Constants';
1313
import { reportToSentry } from './lib/Sentry';
1414
import { useAccountStore } from './stores/Account';
15+
import { useDemoStore } from './stores/Demo';
1516

1617
let isLaunched = false;
1718
let clientPromise: Promise<Client>;
@@ -102,6 +103,7 @@ export async function launchNetwork() {
102103
const transactionsStore = useTransactionsStore();
103104
const addressStore = useAddressStore();
104105
const stakingStore = useStakingStore();
106+
const demoStore = useDemoStore();
105107

106108
const subscribedAddresses = new Set<string>();
107109

@@ -118,7 +120,7 @@ export async function launchNetwork() {
118120
network$.fetchingTxHistory--;
119121

120122
async function updateBalances(addresses: string[] = [...balances.keys()]) {
121-
if (!addresses.length) return;
123+
if (!addresses.length || demoStore.isDemoEnabled) return;
122124
await client.waitForConsensusEstablished();
123125
const accounts = await retry(() => client.getAccounts(addresses)).catch(reportFor('getAccounts'));
124126
if (!accounts) return;
@@ -303,6 +305,7 @@ export async function launchNetwork() {
303305
})();
304306

305307
function transactionListener(plain: PlainTransactionDetails) {
308+
if (demoStore.isDemoEnabled) return;
306309
if (plain.recipient === STAKING_CONTRACT_ADDRESS) {
307310
if (plain.data.type === 'add-stake') {
308311
if (!balances.has(plain.sender) && 'staker' in plain.data) {
@@ -340,6 +343,7 @@ export async function launchNetwork() {
340343
}
341344

342345
function subscribe(addresses: string[]) {
346+
if (demoStore.isDemoEnabled) return false;
343347
client.addTransactionListener(transactionListener, addresses);
344348
updateBalances(addresses);
345349
updateStakes(addresses);
@@ -349,6 +353,7 @@ export async function launchNetwork() {
349353
// Subscribe to new addresses (for balance updates and transactions)
350354
// Also remove logged out addresses from fetched (so that they get fetched on next login)
351355
watch(addressStore.addressInfos, () => {
356+
if (demoStore.isDemoEnabled) return;
352357
const newAddresses: string[] = [];
353358
const removedAddresses = new Set(subscribedAddresses);
354359

@@ -380,6 +385,7 @@ export async function launchNetwork() {
380385

381386
// Fetch transactions for active address
382387
watch([addressStore.activeAddress, txFetchTrigger], ([activeAddress, trigger]) => {
388+
if (demoStore.isDemoEnabled) return;
383389
const address = activeAddress as string | null;
384390
if (!address || fetchedAddresses.value.includes(address)) return;
385391
addFetchedAddress(address);
@@ -428,6 +434,7 @@ export async function launchNetwork() {
428434
// Fetch transactions for proxies
429435
const proxyStore = useProxyStore();
430436
watch(proxyStore.networkTrigger, () => {
437+
if (demoStore.isDemoEnabled) return;
431438
const newProxies: string[] = [];
432439
const addressesToSubscribe: string[] = [];
433440
for (const proxyAddress of proxyStore.allProxies.value) {

0 commit comments

Comments
 (0)