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 @@ @@ -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 @@ + + + + + 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'); - }); -});