Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
onmax committed Jan 22, 2025
1 parent 45c035f commit 1489d07
Show file tree
Hide file tree
Showing 9 changed files with 488 additions and 34 deletions.
18 changes: 11 additions & 7 deletions src/components/layouts/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,7 @@

<div class="trade-actions" v-if="!isLegacyAccount">
<Tooltip
v-if="(
$config.fastspot.enabled
&& fastspotEnabledCryptoSwapAssets.length
&& fastspotEnabledFiatSwapAssets.length
)
|| $config.moonpay.enabled
|| $config.simplex.enabled"
v-if="canBuy"
preferredPosition="top right"
:container="$parent"
theme="inverse"
Expand Down Expand Up @@ -203,6 +197,7 @@ import AttentionDot from '../AttentionDot.vue';
import { useAddressStore } from '../../stores/Address';
import { useBtcAddressStore } from '../../stores/BtcAddress';
import { usePolygonAddressStore } from '../../stores/PolygonAddress';
import { usePlaygroundStore } from '../../stores/Playground';
import { useSettingsStore } from '../../stores/Settings';
import { useAccountStore, AccountType } from '../../stores/Account';
import { useAccountSettingsStore } from '../../stores/AccountSettings';
Expand Down Expand Up @@ -328,6 +323,14 @@ export default defineComponent({
: null;
});
const { isEnabled: isPlaygroundEnabled } = usePlaygroundStore();
const canBuy = computed(() => (
config.fastspot.enabled
&& fastspotEnabledCryptoSwapAssets.value.length && fastspotEnabledFiatSwapAssets.value.length)
|| config.moonpay.enabled || config.simplex.enabled || isPlaygroundEnabled,
);
return {
CryptoCurrency,
navigateTo,
Expand All @@ -338,6 +341,7 @@ export default defineComponent({
priceChartTimeRange,
switchPriceChartTimeRange,
isLegacyAccount,
canBuy,
walletActivatedCurrencies,
fastspotEnabledFiatSwapAssets,
fastspotEnabledCryptoSwapAssets,
Expand Down
4 changes: 4 additions & 0 deletions src/components/modals/BuyOptionsModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@
<script lang="ts">
import { computed, defineComponent, onMounted, ref } from '@vue/composition-api';
import { PageBody, FiatAmount, CircleSpinner } from '@nimiq/vue-components';
import { usePlaygroundStore } from '@/stores/Playground';
import Modal from './Modal.vue';
import CountrySelector from '../CountrySelector.vue';
import CountryFlag from '../CountryFlag.vue';
Expand Down Expand Up @@ -234,13 +235,16 @@ export default defineComponent({
const country = ref<Country>(null);
const { isEnabled: isPlaygroundEnabled } = usePlaygroundStore();
const isMoonpayAvailable = computed(() => { // eslint-disable-line arrow-body-style
if (isPlaygroundEnabled.value) return true;
if (!config.moonpay.enabled) return false;
if (!country.value) return true;
return MOONPAY_COUNTRY_CODES.includes(country.value.code);
});
const isSimplexAvailable = computed(() => { // eslint-disable-line arrow-body-style
if (isPlaygroundEnabled.value) return true;
if (!config.simplex.enabled) return false;
if (!country.value) return true;
return SIMPLEX_COUNTRY_CODES.includes(country.value.code);
Expand Down
3 changes: 2 additions & 1 deletion src/components/modals/Modal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ const Modal = defineComponent({
while (context.root.$route.matched.find((routeRecord) => 'modal' in routeRecord.components
|| 'persistent-modal' in routeRecord.components
|| Object.values(routeRecord.components).some((component) => /modal/i.test(component.name || '')))
|| Object.values(routeRecord.components).some((component) => /modal/i.test(
component.name as string || '')))
) {
context.root.$router.back();
Expand Down
96 changes: 96 additions & 0 deletions src/components/modals/playground/PlaygroundBuy.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<template>
<Modal>
<PageHeader class="flex-column">
<h1 class="nq-h1">{{ $t('Buy NIM') }}</h1>
<div class="demo-warning nq-label">
{{ $t('DEMO') }}
</div>
</PageHeader>
<PageBody>
<div class="flex-row">

<AmountInput v-model="amount" :decimals="5">
<AmountMenu slot="suffix" class="ticker" currency="nim" :open="amountMenuOpened"
:activeCurrency="activeCurrency" :fiatCurrency="fiatCurrency" :feeOption="false"
:otherFiatCurrencies="otherFiatCurrencies"
@click.native.stop="amountMenuOpened = !amountMenuOpened" :sendAllOption="false" />
</AmountInput>
</div>
</PageBody>
<PageFooter>
<button class="nq-button light-blue" @click="buyDummyNim" :disabled="!amount">
{{ $t('Buy NIM') }}
</button>
</PageFooter>
</Modal>
</template>

<script lang="ts">
import { computed, defineComponent, ref } from '@vue/composition-api';
import { PageBody, PageHeader, PageFooter } from '@nimiq/vue-components';
import AmountInput from '@/components/AmountInput.vue';
import AmountMenu from '@/components/AmountMenu.vue';
import Modal from '@/components/modals/Modal.vue';
import { useAccountStore } from '@/stores/Account';
import { useFiatStore } from '@/stores/Fiat';
import { FIAT_CURRENCIES_OFFERED } from '@/lib/Constants';
// import { useTransactionsStore } from '@/stores/Transactions';
import { usePlaygroundStore } from '@/stores/Playground';
// import { useAddressStore } from '@/stores/Address';
export default defineComponent({
setup() {
const { activeCurrency } = useAccountStore();
const { currency: fiatCurrency } = useFiatStore();
// const { activeAddressInfo } = useAddressStore();
const otherFiatCurrencies = computed(() =>
FIAT_CURRENCIES_OFFERED.filter((fiat) => fiat !== fiatCurrency.value));
const amount = ref(0);
const amountMenuOpened = ref(false);
// const maxSendableAmount = computed(() => Math.max((activeAddressInfo.value!.balance || 0), 0));
// const sendMax =() => amount.value = maxSendableAmount.value;
function buyDummyNim() {
usePlaygroundStore().buyDummyNim(amount.value);
}
return {
amount,
activeCurrency,
fiatCurrency,
otherFiatCurrencies,
amountMenuOpened,
buyDummyNim,
};
},
components: {
Modal,
AmountInput,
AmountMenu,
PageHeader,
PageBody,
PageFooter,
},
});
</script>

<style scoped lang="scss">
.small-page {
> .page-header {
overflow: hidden;
.demo-warning {
margin: 0;
text-align: center;
position: absolute;
top: 0;
left: 0;
right: 0;
background: var(--nimiq-orange-bg);
color: white;
padding: 0.5rem 0;
}
}
}
</style>
35 changes: 35 additions & 0 deletions src/components/modals/playground/PlaygroundHubMock.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<template>
<Modal>
<PageHeader class="flex-column">
<h1 class="nq-h1">{{ $t('Nimiq Playground') }}</h1>
</PageHeader>
<PageBody>
<p class="nq-p">
{{ $t('This is not a real Nimiq Wallet. It is just a playground so it is limited in functionality.') }}
</p>
<p>
{{ $t('You can open a free NIM account in less than a minute.') }}
</p>
</PageBody>
<PageFooter>
<a href="https://wallet.nimiq.com" target="_blank" class="nq-button light-blue">
{{ $t('Open Nimiq Wallet') }}
</a>
</PageFooter>
</Modal>
</template>

<script lang="ts">
import { defineComponent } from '@vue/composition-api';
import { PageBody, PageHeader, PageFooter } from '@nimiq/vue-components';
import Modal from '../Modal.vue';
export default defineComponent({
components: {
Modal,
PageHeader,
PageBody,
PageFooter,
},
});
</script>
2 changes: 1 addition & 1 deletion src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ function getBehavior({

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

hubApi.on(HubApi.RequestType.ONBOARD, async (accounts) => {
const { config } = useConfig();
Expand Down
62 changes: 38 additions & 24 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { launchElectrum } from './electrum';
import { launchPolygon } from './ethers';
import { useAccountStore } from './stores/Account';
import { useFiatStore } from './stores/Fiat';
import { usePlaygroundStore } from './stores/Playground';
import { useSettingsStore } from './stores/Settings';
import router from './router';
import { i18n, loadLanguage } from './i18n/i18n-setup';
Expand Down Expand Up @@ -48,14 +49,21 @@ Vue.use(VuePortal, { name: 'Portal' });

async function start() {
initPwa(); // Must be called as soon as possible to catch early browser events related to PWA
await initStorage(); // Must be awaited before starting Vue
initTrials(); // Must be called after storage was initialized, can affect Config
// Must run after VueCompositionApi has been enabled and after storage was initialized. Could potentially run in
// background and in parallel to syncFromHub, but RedirectRpcClient.init does not actually run async code anyways.
await initHubApi();
syncFromHub(); // Can run parallel to Vue initialization; must be called after storage was initialized.

serviceWorkerHasUpdate.then((hasUpdate) => useSettingsStore().state.updateAvailable = hasUpdate);
const { isEnabled: isPlaygroundEnabled } = usePlaygroundStore();

if (!isPlaygroundEnabled.value) {
await initStorage(); // Must be awaited before starting Vue
initTrials(); // Must be called after storage was initialized, can affect Config
// Must run after VueCompositionApi has been enabled and after storage was initialized. Could potentially run in
// background and in parallel to syncFromHub, but RedirectRpcClient.init does not actually run async code
// anyways.
await initHubApi();
syncFromHub(); // Can run parallel to Vue initialization; must be called after storage was initialized.

serviceWorkerHasUpdate.then((hasUpdate) => useSettingsStore().state.updateAvailable = hasUpdate);
} else {
usePlaygroundStore().setup(router);
}

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

startSentry();
if (!isPlaygroundEnabled.value) {
startSentry();
}

const { config } = useConfig();

if (config.environment !== ENV_MAIN) {
if (isPlaygroundEnabled.value) {
document.title = 'Nimiq Wallet Playground';
} else if (config.environment !== ENV_MAIN) {
document.title = 'Nimiq Testnet Wallet';
}

watch(() => {
if (!config.fastspot.apiEndpoint || !config.fastspot.apiKey) return;
initFastspotApi(config.fastspot.apiEndpoint, config.fastspot.apiKey);
});

watch(() => {
if (!config.oasis.apiEndpoint) return;
initOasisApi(config.oasis.apiEndpoint);
});

watch(() => {
if (!config.ten31Pass.enabled) return;
initKycConnection();
});
if (!isPlaygroundEnabled.value) {
watch(() => {
if (!config.fastspot.apiEndpoint || !config.fastspot.apiKey) return;
initFastspotApi(config.fastspot.apiEndpoint, config.fastspot.apiKey);
});

watch(() => {
if (!config.oasis.apiEndpoint) return;
initOasisApi(config.oasis.apiEndpoint);
});

watch(() => {
if (!config.ten31Pass.enabled) return;
initKycConnection();
});
}

// Make reactive config accessible in components
Vue.prototype.$config = config;
Expand Down
9 changes: 8 additions & 1 deletion src/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { AddStakeEvent, ApiValidator, RawValidator, useStakingStore } from './st
import { ENV_MAIN, STAKING_CONTRACT_ADDRESS } from './lib/Constants';
import { reportToSentry } from './lib/Sentry';
import { useAccountStore } from './stores/Account';
import { usePlaygroundStore } from './stores/Playground';

let isLaunched = false;
let clientPromise: Promise<Client>;
Expand Down Expand Up @@ -102,6 +103,7 @@ export async function launchNetwork() {
const transactionsStore = useTransactionsStore();
const addressStore = useAddressStore();
const stakingStore = useStakingStore();
const playgroundStore = usePlaygroundStore();

const subscribedAddresses = new Set<string>();

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

async function updateBalances(addresses: string[] = [...balances.keys()]) {
if (!addresses.length) return;
if (!addresses.length || playgroundStore.isEnabled) return;
await client.waitForConsensusEstablished();
const accounts = await retry(() => client.getAccounts(addresses)).catch(reportFor('getAccounts'));
if (!accounts) return;
Expand Down Expand Up @@ -303,6 +305,7 @@ export async function launchNetwork() {
})();

function transactionListener(plain: PlainTransactionDetails) {
if (playgroundStore.isEnabled) return;
if (plain.recipient === STAKING_CONTRACT_ADDRESS) {
if (plain.data.type === 'add-stake') {
if (!balances.has(plain.sender) && 'staker' in plain.data) {
Expand Down Expand Up @@ -340,6 +343,7 @@ export async function launchNetwork() {
}

function subscribe(addresses: string[]) {
if (playgroundStore.isEnabled) return false;
client.addTransactionListener(transactionListener, addresses);
updateBalances(addresses);
updateStakes(addresses);
Expand All @@ -349,6 +353,7 @@ export async function launchNetwork() {
// Subscribe to new addresses (for balance updates and transactions)
// Also remove logged out addresses from fetched (so that they get fetched on next login)
watch(addressStore.addressInfos, () => {
if (playgroundStore.isEnabled) return;
const newAddresses: string[] = [];
const removedAddresses = new Set(subscribedAddresses);

Expand Down Expand Up @@ -380,6 +385,7 @@ export async function launchNetwork() {

// Fetch transactions for active address
watch([addressStore.activeAddress, txFetchTrigger], ([activeAddress, trigger]) => {
if (playgroundStore.isEnabled) return;
const address = activeAddress as string | null;
if (!address || fetchedAddresses.value.includes(address)) return;
addFetchedAddress(address);
Expand Down Expand Up @@ -428,6 +434,7 @@ export async function launchNetwork() {
// Fetch transactions for proxies
const proxyStore = useProxyStore();
watch(proxyStore.networkTrigger, () => {
if (playgroundStore.isEnabled) return;
const newProxies: string[] = [];
const addressesToSubscribe: string[] = [];
for (const proxyAddress of proxyStore.allProxies.value) {
Expand Down
Loading

0 comments on commit 1489d07

Please sign in to comment.