diff --git a/.env b/.env
index 4c1d68b49..49c8f2c34 100644
--- a/.env
+++ b/.env
@@ -1,3 +1,2 @@
VUE_APP_BACKEND_URL=https://base-backend.prd.aepps.com
VUE_APP_VAPID_PUBLIC_KEY=BHkQhNWW2TKfKfxo7vAgXkZGcVOXGrjhIZJlN1hKp6abIjWJgO8FYPswXJ35XEuKw46O9yZ-8KmsZ4-TXNBePcw
-VUE_APP_HOME_PAGE_URL=https://home.base.aepps.com
diff --git a/src/components/AppShortcut.vue b/src/components/AppShortcut.vue
index a344d7259..a3689e549 100644
--- a/src/components/AppShortcut.vue
+++ b/src/components/AppShortcut.vue
@@ -1,6 +1,6 @@
-
+
{{ name }}
@@ -14,6 +14,7 @@ export default {
props: {
name: { type: String, required: true },
icon: { type: String, default: DEFAULT_ICON },
+ iconNotPadded: Boolean,
},
};
@@ -31,11 +32,17 @@ export default {
overflow-wrap: break-word;
img {
+ box-sizing: border-box;
width: functions.rem(75px);
height: functions.rem(75px);
border-radius: functions.rem(18px);
box-shadow: 0 0 16px rgba(0, 33, 87, 0.15);
margin-bottom: 5px;
+ background: #fff;
+
+ &.iconNotPadded {
+ padding: functions.rem(10px);
+ }
}
}
diff --git a/src/lib/appsRegistry.json b/src/lib/appsRegistry.json
new file mode 100644
index 000000000..541fc4c61
--- /dev/null
+++ b/src/lib/appsRegistry.json
@@ -0,0 +1 @@
+["superhero.com", "governance.aeternity.com", "graffiti.aeternity.com", "faucet.aepps.com"]
diff --git a/src/lib/storeErrorHandler.js b/src/lib/storeErrorHandler.js
index 82d1024fd..971ecaffc 100644
--- a/src/lib/storeErrorHandler.js
+++ b/src/lib/storeErrorHandler.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-window.onerror = async function errorHandler() {
+window.onerror = async function errorHandler(...args) {
if (document.getElementById('app').innerHTML) {
window.onerror = null;
return;
@@ -10,4 +10,7 @@ window.onerror = async function errorHandler() {
new Vue({
render: (h) => h(StoreLoadError),
}).$mount('#app');
+
+ // eslint-disable-next-line no-console
+ console.error('Unknown error', ...args);
};
diff --git a/src/locales/cn.json b/src/locales/cn.json
index 4e8602a15..b2f859fbd 100644
--- a/src/locales/cn.json
+++ b/src/locales/cn.json
@@ -132,6 +132,7 @@
"list": {
"featured-guide": "精选",
+ "browse-guide": "浏览",
"bookmarked-guide": "Bookmarks",
"guide": "æpps 浏览器 (beta)",
"note": "将运行在æternity区块链上的æpps添加到这里",
diff --git a/src/locales/en.json b/src/locales/en.json
index e1eca0f66..0a1a604c2 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -132,6 +132,7 @@
"list": {
"featured-guide": "Featured",
+ "browse-guide": "Browse",
"bookmarked-guide": "Bookmarks",
"guide": "æpps browser (beta)",
"note": "æpps running on the æternity blockchain will be added below.",
diff --git a/src/locales/es.json b/src/locales/es.json
index de236242c..8adbd0ce0 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -132,6 +132,7 @@
"list": {
"featured-guide": "æpps destacados",
+ "browse-guide": "Examinar æpps",
"bookmarked-guide": "Mis æpps",
"guide": "æpps explorador (beta)",
"note": "æpps que se ejecutan en la blockchain de æternity se agregarán a continuación",
diff --git a/src/locales/keysUsedInOtherProjects.js b/src/locales/keysUsedInOtherProjects.js
deleted file mode 100644
index 29ed30bde..000000000
--- a/src/locales/keysUsedInOtherProjects.js
+++ /dev/null
@@ -1,6 +0,0 @@
-const $t = () => {};
-
-$t('app.list.featured-guide');
-$t('app.list.bookmarked-guide');
-$t('app.list.by');
-$t('app.list.launch');
diff --git a/src/locales/ru.json b/src/locales/ru.json
index ce1f8124c..abfe3d05c 100644
--- a/src/locales/ru.json
+++ b/src/locales/ru.json
@@ -132,6 +132,7 @@
"list": {
"featured-guide": "Избранное",
+ "browse-guide": "Просмотр",
"bookmarked-guide": "Закладки",
"guide": "æpps браузер (бета)",
"note": "æpps, работающие в блокчейне æternity, будут добавлены ниже.",
diff --git a/src/pages/desktop/Apps.vue b/src/pages/desktop/Apps.vue
index bbf6f949b..121a36c7a 100644
--- a/src/pages/desktop/Apps.vue
+++ b/src/pages/desktop/Apps.vue
@@ -7,12 +7,7 @@
@@ -23,21 +18,14 @@ import { fetchJson } from '../../store/utils';
import Guide from '../../components/Guide.vue';
import Note from '../../components/Note.vue';
import AppShortcut from '../../components/AppShortcut.vue';
+import appsRegistry from '../../lib/appsRegistry';
export default {
components: { Guide, Note, AppShortcut },
- data: () => ({ aeternityAppsPaths: [] }),
computed: mapState({
- aeternityApps(state, getters) {
- return this.aeternityAppsPaths.map((path) => ({
- ...getters['appsMetadata/get'](path),
- path,
- }));
- },
+ apps: (state, getters) =>
+ appsRegistry.map((host) => ({ ...getters['appsMetadata/get'](host), host })),
}),
- async mounted() {
- this.aeternityAppsPaths = await fetchJson(`${process.env.VUE_APP_HOME_PAGE_URL}/apps.json`);
- },
};
diff --git a/src/pages/mobile/AppBrowser.vue b/src/pages/mobile/AppBrowser.vue
index 27812f075..d7de2e1fb 100644
--- a/src/pages/mobile/AppBrowser.vue
+++ b/src/pages/mobile/AppBrowser.vue
@@ -3,11 +3,12 @@
-
-
-
-
-
+
+
+
+
+
+
@@ -39,11 +40,12 @@
diff --git a/src/pages/mobile/AppDetails.vue b/src/pages/mobile/AppDetails.vue
new file mode 100644
index 000000000..b585509f4
--- /dev/null
+++ b/src/pages/mobile/AppDetails.vue
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+ {{ app.name }}
+
+
+ {{ $t('app.list.by') }}
+
+
+ {{ app.author }} »
+
+ {{ app.author }}
+
+
+
+
+
+
+ {{ app.categories }}
+ {{ app.networks }}
+
+ {{ app.description }}
+
+
+
+ {{ $t('app.list.launch') }}
+
+
+
+
+
+
+
diff --git a/src/pages/mobile/AppList.vue b/src/pages/mobile/AppList.vue
new file mode 100644
index 000000000..93e6d2ab7
--- /dev/null
+++ b/src/pages/mobile/AppList.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/router/routes/mobile.js b/src/router/routes/mobile.js
index 3e7106cf7..8fa89691c 100644
--- a/src/router/routes/mobile.js
+++ b/src/router/routes/mobile.js
@@ -11,6 +11,8 @@ import OnboardingAepps from '../../pages/mobile/OnboardingAepps.vue';
import OnboardingSubaccounts from '../../pages/mobile/OnboardingSubaccounts.vue';
import Login from '../../pages/mobile/Login.vue';
import Recover from '../../pages/mobile/Recover.vue';
+import AppList from '../../pages/mobile/AppList.vue';
+import AppDetails from '../../pages/mobile/AppDetails.vue';
import AppBrowser from '../../pages/mobile/AppBrowser.vue';
import VaultSetupMethod from '../../pages/mobile/VaultSetupMethod.vue';
import VaultSetupAnotherDevice from '../../pages/mobile/VaultSetupAnotherDevice.vue';
@@ -128,9 +130,20 @@ export default [
component: Recover,
},
{
- name: 'app-browser',
+ name: 'app-list',
path: '/browser',
- alias: '/browser/*',
+ component: AppList,
+ beforeEnter: ensureLoggedIn,
+ },
+ {
+ name: 'app-details',
+ path: '/browser/details/:host',
+ component: AppDetails,
+ beforeEnter: ensureLoggedIn,
+ },
+ {
+ name: 'app-browser',
+ path: '/browser/*',
component: AppBrowser,
beforeEnter: ensureLoggedIn,
},
diff --git a/src/store/modules/mobile.js b/src/store/modules/mobile.js
index fb681685c..f37ba7c66 100644
--- a/src/store/modules/mobile.js
+++ b/src/store/modules/mobile.js
@@ -7,7 +7,7 @@ export default {
state: {
followers: {},
stepFraction: null,
- browserPath: { name: 'app-browser' },
+ browserPath: { name: 'app-list' },
readSecurityCourses: [],
skipAddingToHomeScreen: false,
},
diff --git a/src/store/modules/root.js b/src/store/modules/root.js
index 436b8fc56..da994cc6b 100644
--- a/src/store/modules/root.js
+++ b/src/store/modules/root.js
@@ -5,6 +5,10 @@ import { mergeWith } from 'lodash-es';
import networksRegistry from '../../lib/networksRegistry';
import { genRandomBuffer } from '../utils';
+const getAppDefaults = () => ({
+ bookmarked: false,
+ permissions: { accessToAccounts: [] },
+});
const getAppByHost = (apps, appHost) => apps.find(({ host }) => host === appHost);
export default {
@@ -65,10 +69,13 @@ export default {
removeNetwork(state, networkIdx) {
state.customNetworks.splice(networkIdx - networksRegistry.length, 1);
},
+ toggleAppBookmarking({ apps }, appHost) {
+ if (!getAppByHost(apps, appHost)) apps.push({ ...getAppDefaults(), host: appHost });
+ const app = getAppByHost(apps, appHost);
+ app.bookmarked = !app.bookmarked;
+ },
toggleAccessToAccount({ apps }, { appHost, accountAddress }) {
- if (!getAppByHost(apps, appHost)) {
- apps.push({ host: appHost, permissions: { accessToAccounts: [] } });
- }
+ if (!getAppByHost(apps, appHost)) apps.push({ ...getAppDefaults(), host: appHost });
const {
permissions: { accessToAccounts },
} = getAppByHost(apps, appHost);
diff --git a/src/store/plugins/ui/__tests__/appsMetadata.js b/src/store/plugins/ui/__tests__/appsMetadata.js
index 0920d92bb..8c31474d6 100644
--- a/src/store/plugins/ui/__tests__/appsMetadata.js
+++ b/src/store/plugins/ui/__tests__/appsMetadata.js
@@ -16,6 +16,7 @@ describe('appsMetadata', () => {
metadata: {
name: 'HackerWeb',
icon: 'http://example.com/icon/hd_hi.ico',
+ iconNotPadded: false,
},
}, {
name: 'Twitter',
@@ -23,6 +24,7 @@ describe('appsMetadata', () => {
metadata: {
name: 'Twitter',
icon: 'https://abs.twimg.com/responsive-web/web/icon-default.604e2486a34a2f6e.png',
+ iconNotPadded: false,
},
}].forEach(({ name, manifest, metadata }) => it(
`returns app metadata for ${name}`,
diff --git a/src/store/plugins/ui/appsMetadata.js b/src/store/plugins/ui/appsMetadata.js
index 5042caacd..f190efe4e 100644
--- a/src/store/plugins/ui/appsMetadata.js
+++ b/src/store/plugins/ui/appsMetadata.js
@@ -4,6 +4,8 @@ import Vue from 'vue';
import { handleUnknownError } from '../../../lib/utils';
import { PROTOCOL_DEFAULT } from '../../../lib/constants';
+const notPaddedIconAt = ['superhero.com', 'graffiti.aeternity.com', 'faucet.aepps.com'];
+
export default (store) => store.registerModule('appsMetadata', {
namespaced: true,
@@ -25,13 +27,15 @@ export default (store) => store.registerModule('appsMetadata', {
.map(({ sizes = '', ...icon }) => sizes.split(' ').map((size) => ({ ...icon, size })))
.flat()
.map(({ size, ...icon }) => ({ ...icon, side: Math.max(...size.split('x')) }));
+ const optimalIconSide = 75 * window.devicePixelRatio;
const icon = icons.reduce((p, i) => {
- if (!p) return i || p;
- if (p.side < 75) return i.side > p.side ? i : p;
- return i.side > 75 && i.side < p.side ? i : p;
+ if (p == null) return i;
+ if (p.side < optimalIconSide) return i.side > p.side ? i : p;
+ return i.side > optimalIconSide && i.side < p.side ? i : p;
}, null);
if (icon) {
metadata.icon = new URL(icon.src, `${PROTOCOL_DEFAULT}//${host}`).toString();
+ metadata.iconNotPadded = notPaddedIconAt.includes(host);
}
return metadata;
diff --git a/src/store/plugins/ui/pathTracker.js b/src/store/plugins/ui/pathTracker.js
index b34b3558f..a55ee96e6 100644
--- a/src/store/plugins/ui/pathTracker.js
+++ b/src/store/plugins/ui/pathTracker.js
@@ -4,7 +4,7 @@ export default (store) => {
store.watch(
({ route }) => route,
({ name, fullPath, params } = {}) => {
- if (name === 'app-browser') {
+ if (['app-list', 'app-details', 'app-browser'].includes(name)) {
store.commit('setBrowserPath', fullPath);
} else if (NAME_LIST_ROUTE_NAMES.includes(name)) {
store.commit('setNameListRoute', { name, params });
diff --git a/tests/e2e/specs/__image_snapshots__/Browser shows aepp in browser and bookmarks #0.png b/tests/e2e/specs/__image_snapshots__/Browser shows aepp in browser and bookmarks #0.png
new file mode 100644
index 000000000..023f5e3ff
Binary files /dev/null and b/tests/e2e/specs/__image_snapshots__/Browser shows aepp in browser and bookmarks #0.png differ
diff --git a/tests/e2e/specs/__image_snapshots__/Browser shows app details #0.png b/tests/e2e/specs/__image_snapshots__/Browser shows app details #0.png
new file mode 100644
index 000000000..314853abc
Binary files /dev/null and b/tests/e2e/specs/__image_snapshots__/Browser shows app details #0.png differ
diff --git a/tests/e2e/specs/__image_snapshots__/Browser shows app list #0.png b/tests/e2e/specs/__image_snapshots__/Browser shows app list #0.png
new file mode 100644
index 000000000..9f9e31a64
Binary files /dev/null and b/tests/e2e/specs/__image_snapshots__/Browser shows app list #0.png differ
diff --git a/tests/e2e/specs/__image_snapshots__/Browser shows app list on desktop #0.png b/tests/e2e/specs/__image_snapshots__/Browser shows app list on desktop #0.png
new file mode 100644
index 000000000..9fb59bb60
Binary files /dev/null and b/tests/e2e/specs/__image_snapshots__/Browser shows app list on desktop #0.png differ
diff --git a/tests/e2e/specs/browser.cy.js b/tests/e2e/specs/browser.cy.js
new file mode 100644
index 000000000..480efe98b
--- /dev/null
+++ b/tests/e2e/specs/browser.cy.js
@@ -0,0 +1,66 @@
+function ensureImagesLoaded($imgs) {
+ Array.from($imgs).forEach((img) => {
+ expect(img.naturalWidth).to.be.greaterThan(0);
+ });
+}
+
+describe('Browser', () => {
+ it('shows app list', () => {
+ cy.viewport('iphone-se2').visit('/browser', {
+ login: true,
+ state: {
+ apps: ['example.com', 'faucet.aepps.com'].map((host) => ({
+ host,
+ bookmarked: true,
+ permissions: { accessToAccounts: [] },
+ })),
+ },
+ });
+ cy.get('.ae-card img').should('be.visible').and('length', 3).and(ensureImagesLoaded);
+ cy.get('.shortcuts img:not([src^="data:image"])')
+ .should('be.visible')
+ .and('length', 1)
+ .and(ensureImagesLoaded);
+ cy.matchImage();
+
+ cy.get('.ae-card a').contains('faucet.aepps.com').click();
+ cy.location('pathname').should('equal', '/browser/details/faucet.aepps.com');
+ });
+
+ it('shows app details', () => {
+ cy.viewport('iphone-se2').visit('/browser/details/faucet.aepps.com', { login: true });
+ cy.get('img').should('be.visible').and('length', 1).and(ensureImagesLoaded);
+ cy.matchImage();
+
+ cy.get('.ae-button').click();
+ cy.location('pathname').should('equal', '/browser/faucet.aepps.com');
+ });
+
+ it('shows aepp in browser and bookmarks', () => {
+ cy.viewport('iphone-se2').visit('/browser/faucet.aepps.com', { login: true });
+ cy.get('.progress-fake').should('not.exist');
+ cy.getIframeBody().find('button:contains("Wallet")').should('be.visible');
+ cy.matchImage();
+
+ cy.get('.button-plain .icon.bookmark').click().should('have.class', 'bookmark-full');
+ cy.get('.button-plain .icon.home').click();
+ cy.location('pathname').should('equal', '/browser');
+ cy.get('.shortcuts .ae-link').contains('Faucet Aepp').should('be.visible');
+ });
+
+ it('navigates to aepp', () => {
+ cy.viewport('iphone-se2').visit('/browser', { login: true });
+ cy.get('input').type('faucet.aepps.com{enter}');
+ cy.location('pathname').should('equal', '/browser/faucet.aepps.com');
+ });
+
+ it('shows app list on desktop', () => {
+ cy.visit('/', { isDesktop: true });
+ cy.get('.apps img:not([src^="data:image"])')
+ .should('be.visible')
+ .and('length', 3)
+ .and(ensureImagesLoaded);
+ cy.matchImage();
+ cy.get('a').contains('Faucet Aepp').should('have.attr', 'href', 'https://faucet.aepps.com');
+ });
+});
diff --git a/tests/e2e/specs/browser/__image_snapshots__/Browser opens #0.png b/tests/e2e/specs/browser/__image_snapshots__/Browser opens #0.png
deleted file mode 100644
index df70c3c66..000000000
Binary files a/tests/e2e/specs/browser/__image_snapshots__/Browser opens #0.png and /dev/null differ
diff --git a/tests/e2e/specs/browser/index.cy.js b/tests/e2e/specs/browser/index.cy.js
deleted file mode 100644
index ec6380ff7..000000000
--- a/tests/e2e/specs/browser/index.cy.js
+++ /dev/null
@@ -1,16 +0,0 @@
-describe('Browser', () => {
- it('opens', () => {
- cy.viewport('iphone-se2').visit('/browser', { login: true });
- cy.get('.progress-fake').should('not.exist');
- cy.getIframeBody()
- .find('img')
- .should('be.visible')
- .and('length', 3)
- .and(($imgs) =>
- Array.from($imgs).forEach((img) => {
- expect(img.naturalWidth).to.be.greaterThan(0);
- }),
- );
- cy.matchImage();
- });
-});
diff --git a/tests/e2e/specs/names/index.cy.js b/tests/e2e/specs/names/index.cy.js
index 2933d3a1f..fb95c1407 100644
--- a/tests/e2e/specs/names/index.cy.js
+++ b/tests/e2e/specs/names/index.cy.js
@@ -42,7 +42,6 @@ describe('Names', () => {
[
'new',
- 'bid/engine.chain',
'bid/engine.chain/amount',
'personal/entertainment.chain/point',
'personal/entertainment.chain/transfer',
@@ -52,4 +51,10 @@ describe('Names', () => {
cy.matchImage();
});
});
+
+ it('shows bid/engine.chain', () => {
+ cy.viewport('iphone-se2').visit('/names/bid/engine.chain', { login: true });
+ cy.get('.ae-button').should('not.be.disabled');
+ cy.matchImage();
+ });
});
diff --git a/tests/e2e/specs/test.cy.js b/tests/e2e/specs/test.cy.js
deleted file mode 100644
index cd600c156..000000000
--- a/tests/e2e/specs/test.cy.js
+++ /dev/null
@@ -1,6 +0,0 @@
-describe('My First Test', () => {
- it('Visits the app root url', () => {
- cy.visit('/');
- cy.contains('Please add this app to home screen');
- });
-});