From da8efcfa59d570c94dee4abe056738b1ea36cc31 Mon Sep 17 00:00:00 2001 From: Taylor Dawson Date: Fri, 24 Jan 2020 20:34:05 -0800 Subject: [PATCH] Code refactor + Default additions - Lint code - Removes 'vertical' property--card defaults to vertical now - Adds 'horizontal' property to render card horizontally - Update example & README.md to reflect new functionality --- .eslintignore | 4 +- .prettierrc | 1 - README.md | 11 +- example/index.html | 48 ++++---- package-lock.json | 44 ++++++-- package.json | 41 ++----- rollup.config.js | 48 -------- src/nft-card-back.ts | 257 ++++++++++++++++++++---------------------- src/nft-card-front.ts | 133 +++++++++++----------- src/nft-card.ts | 222 ++++++++++++++++++------------------ src/pill.ts | 32 +++--- tslint.json | 90 +++++++++++++++ 12 files changed, 480 insertions(+), 451 deletions(-) delete mode 100644 rollup.config.js create mode 100644 tslint.json diff --git a/.eslintignore b/.eslintignore index b968eb3..ca30da7 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,4 @@ .gitignore -/dist +dist .git -/example +example diff --git a/.prettierrc b/.prettierrc index 480d45d..607eab8 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,2 @@ -# .prettierrc semi: false useTabs: false \ No newline at end of file diff --git a/README.md b/README.md index 86ec12e..633e7d7 100644 --- a/README.md +++ b/README.md @@ -3,24 +3,23 @@ Easily embed OpenSea listings in your website! ### Component inputs -`vertical` - If this is present the card will be rendered vertically. +`horizontal` - If this is present, the card will be rendered horizontally. -`width` - The width of the embeddable. Ex. values `100%` `250px` +`width` - The width of the embeddable. Ex. values `100%` `250px`. Default: `388px` -`height` - The height of the embeddable. Ex. values `40vh` `300px` +`height` - The height of the embeddable. Ex. values `40vh` `300px`. Default: `560px` -`contractAddress`\*- The token Id of the asset. +`contractAddress`\*- The token's contract address. `tokenId`\* - The token Id of the asset. -`network` - The name of the network the asset is on `mainnet` or `rinkeby`. Defaults to `mainnet`. +`network` - The name of the network the asset is on `mainnet` or `rinkeby`. Default: `mainnet`. \*Required inputs Example: ``` - - - - - - + + + + + + - OpenSea Embeddable NFT example - + + -

OpenSea's Embeddable Nft!

-
+

OpenSea's Embeddable Nft!

+
- + contractAddress="0x7dca125b1e805dc88814aed7ccc810f677d3e1db" + network="rinkeby" + tokenId="18" + >
+ contractAddress="0x5caebd3b32e210e85ce3e9d51638b9c445481567" + height="250px" + horizontal + network="mainnet" + tokenId="2242579050293992223" + width="100%" + >
-
+
diff --git a/package-lock.json b/package-lock.json index 4bcc9b8..757322b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13622,19 +13622,30 @@ } }, "tslint-eslint-rules": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-4.1.1.tgz", - "integrity": "sha1-fDDniC8mvCdr/5HSOEl1xp2viLo=", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz", + "integrity": "sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==", + "dev": true, "requires": { - "doctrine": "^0.7.2", - "tslib": "^1.0.0", - "tsutils": "^1.4.0" + "doctrine": "0.7.2", + "tslib": "1.9.0", + "tsutils": "^3.0.0" }, "dependencies": { + "tslib": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", + "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", + "dev": true + }, "tsutils": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-1.9.1.tgz", - "integrity": "sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=" + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } } } }, @@ -15591,6 +15602,21 @@ "tapable": "^0.2.7" } }, + "tslint-eslint-rules": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-4.1.1.tgz", + "integrity": "sha1-fDDniC8mvCdr/5HSOEl1xp2viLo=", + "requires": { + "doctrine": "^0.7.2", + "tslib": "^1.0.0", + "tsutils": "^1.4.0" + } + }, + "tsutils": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-1.9.1.tgz", + "integrity": "sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=" + }, "typescript": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", diff --git a/package.json b/package.json index 18b9ac2..8890c63 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev:demo": "webpack-dev-server --config ./webpack.dev.config.js", - "lint": "xo", - "lint:fix": "xo --fix" + "lint": "tslint --project . 'src/**/*.ts'", + "lint:fix": "tslint --project . 'src/**/*.ts' --fix" }, "repository": { "type": "git", @@ -19,10 +19,10 @@ }, "homepage": "https://github.com/ProjectOpenSea/embeddable_nfts#readme", "dependencies": { - "@types/node": "^13.1.8", - "ky": "^0.16.1", "opensea-js": "github:ProjectOpenSea/opensea-js", - "web3": "^1.2.5-rc.0" + "web3": "^1.2.5-rc.0", + "lit-element": "^2.2.1", + "lit-html": "^2.2.1" }, "devDependencies": { "@babel/cli": "^7.8.3", @@ -31,41 +31,20 @@ "@babel/plugin-proposal-decorators": "^7.8.3", "@babel/preset-env": "^7.8.3", "@babel/preset-typescript": "^7.8.3", - "@rollup/plugin-commonjs": "^11.0.1", - "@rollup/plugin-json": "^4.0.1", - "@rollup/plugin-node-resolve": "^7.0.0", "@typescript-eslint/eslint-plugin": "^2.16.0", "@typescript-eslint/parser": "^2.16.0", "@webcomponents/webcomponentsjs": "^2.4.1", - "eslint-config-xo-typescript": "^0.24.1", + "@types/node": "^13.1.8", "html-webpack-plugin": "^3.2.0", - "lit-element": "^2.2.1", + "prettier": "^1.19.1", - "rollup": "^1.29.0", - "rollup-plugin-babel": "^4.3.3", - "rollup-plugin-filesize": "^6.2.1", - "rollup-plugin-livereload": "^1.0.4", - "rollup-plugin-node-builtins": "^2.1.2", - "rollup-plugin-node-globals": "^1.4.0", - "rollup-plugin-node-resolve": "^5.2.0", - "rollup-plugin-serve": "^1.0.1", - "rollup-plugin-terser": "^5.2.0", "style-loader": "^1.1.3", "ts-loader": "^6.2.1", + "tslint": "^5.20.1", + "tslint-eslint-rules": "^5.4.0", "typescript": "^3.7.5", - "url-loader": "^3.0.0", "webpack": "^4.41.5", "webpack-cli": "^3.3.10", - "webpack-dev-server": "^3.10.1", - "xo": "^0.25.3" - }, - "xo": { - "semicolon": false, - "space": true, - "prettier": true, - "extends": "xo-typescript", - "extensions": [ - "ts" - ] + "webpack-dev-server": "^3.10.1" } } diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index 6df7b07..0000000 --- a/rollup.config.js +++ /dev/null @@ -1,48 +0,0 @@ -import resolve from '@rollup/plugin-node-resolve' -import babel from 'rollup-plugin-babel' -import livereload from 'rollup-plugin-livereload' -import serve from 'rollup-plugin-serve' -import commonjs from '@rollup/plugin-commonjs' -import json from '@rollup/plugin-json' -import builtins from 'rollup-plugin-node-builtins' -import globals from 'rollup-plugin-node-globals' -import filesize from 'rollup-plugin-filesize' -import {terser} from 'rollup-plugin-terser' -const extensions = ['.ts', '.js'] - -export default { - input: 'src/nft-card.ts', - output: { - name: 'nftcard', - file: 'dist/nft-card.js', - compact: true, - format: 'iife', - sourcemap: true, - globals: { - crypto: 'crypto' - // Sha3: 'SHA3Hash' - } - }, - // External: [ 'sha3' ], - plugins: [ - resolve({ - preferBuiltins: true, - extensions - }), - commonjs(), - json(), - builtins(), - globals(), - babel({ - exclude: 'node_modules/**', - extensions - }), - livereload(), - serve({ - openPage: '/example/index.html', - contentBase: '.' - }), - filesize(), - terser() - ] -} diff --git a/src/nft-card-back.ts b/src/nft-card-back.ts index 53ff6a1..5d8ccd0 100644 --- a/src/nft-card-back.ts +++ b/src/nft-card-back.ts @@ -5,108 +5,17 @@ import {classMap} from 'lit-html/directives/class-map' enum TraitType { Property = 'prop', Stat = 'stat', - Ranking = 'ranking', - Boost = 'boost', + Ranking = 'ranking', + Boost = 'boost', } -const TRAIT_HEIGHT = 40; +const TRAIT_HEIGHT = 40 @customElement('nft-card-back') export class NftCardBackTemplate extends LitElement { - /** - * Create an observed property. Triggers update on change. - */ - @property({type: Object}) traitData = {} - @property({type: Boolean}) loading = true - @property({type: Boolean}) vertical - - @property({type: Number}) cardHeight - - private buildTraits(traitData) { - this.traits = { - props: [], - stats: [], - rankings: [], - boosts: [] - }; - const {traits, collectionTraits} = this.traitData; - - for(const trait of traits) { - const type = this.getTraitType(trait, collectionTraits); - - const name = trait.trait_type; - this.traits[type + 's'].push({ - name, - value: trait.value, - ...(type === TraitType.Ranking ? { max: collectionTraits[name].max } : {}) - }); - } - } - - private getTraitType(trait: object, collectionTraits: object) { - if (this.isProperty(trait, collectionTraits)) return TraitType.Property; - if (this.isStat(trait)) return TraitType.Stat; - if (this.isRanking(trait, collectionTraits)) return TraitType.Ranking; - if (this.isBoost(trait)) return TraitType.Boost; - } - - private isBoost(trait: object) { - return trait.display_type.includes('boost'); - } - - private isRanking(trait: object, collectionTraits: object) { - return ( - trait.display_type === null && 'max' in collectionTraits[trait.trait_type] - } - - - /** - * IsStat - Checks to see if the given trait is a 'Stat' - * A 'Stat' is defined as any trait that has a `display_type` of 'number' - * - * @param {type} trait - The object containing an asset's trait - * @return {boolean} true if the trait is a 'Stat' and false otherwise - */ - private isStat(trait: object) { - return trait.display_type === 'number'; - } - /** - * IsProperty - Checks to see if the given trait is a 'Property'. - * A 'Property' is defined as any trait that has a `display_type` of null - * and does not have a min/max value - * - * @param {object} trait - The object containing an asset's trait - * @return {boolean} true if the trait is a 'Property' and false otherwise - */ - private isProperty(trait: object, collectionTraits: object) { - return ( - trait.display_type === null && - !('max' in collectionTraits[trait.trait_type]) - ); - } - - updated(changedProperties: array) { - // Assumption: If the traitData gets updated we should rebuild the - // traits object that populates UI - // Assumption: This will ONLY get called once per refresh - changedProperties.forEach((oldValue, propName) => { - if (propName === 'traitData') { - this.buildTraits(this.traitData); - - // We got the data so we are done loading - this.loading = false - - // Tell the component to update with new state - this.requestUpdate() - } - }) - // Console.log(this.shadowRoot.firstElementChild.offsetHeight) - } - - - static get styles() { - return css` + static get styles() { + return css` .card-back { position: absolute; backface-visibility: hidden; @@ -257,11 +166,35 @@ export class NftCardBackTemplate extends LitElement { color: #ffffff; } ` - } + } + + @property({type: Object}) public traitData = {} + @property({type: Boolean}) public loading = true + @property({type: Boolean}) public horizontal + + @property({type: Number}) public cardHeight + + public updated(changedProperties: array) { + // Assumption: If the traitData gets updated we should rebuild the + // traits object that populates UI + // Assumption: This will ONLY get called once per refresh + changedProperties.forEach(async (oldValue, propName) => { + if (propName === 'traitData') { + this.buildTraits(this.traitData) - getBoostsTemplate(boosts) { - if(boosts.length <= 0) return // Don't render if empty array - return html` + // We got the data so we are done loading + this.loading = false + + // Tell the component to update with new state + await this.requestUpdate() + } + }) + // Console.log(this.shadowRoot.firstElementChild.offsetHeight) + } + + public getBoostsTemplate(boosts) { + if (boosts.length <= 0) { return } // Don't render if empty array + return html`
${stat.value}
- ${stat.name.replace(/_/g, ' ')} + ${this.formatTrait(stat.name)}
` } ` - } + } - getRankingsTemplate(rankings) { - return html` + public getRankingsTemplate(rankings) { + return html`

${this.formatTrait(props[i].name)}

${props[i].value}

- `); - } - - return propsTemplate; - } + `) + } - formatTrait(trait) { - return trait.replace(/_/g, ' '); - } + return propsTemplate + } - /** - * Implement `render` to define a template for your element. - */ - render() { - // TODO: Add loading templates - return html` + public render() { + // TODO: Add loading templates + return html`
@@ -414,11 +340,11 @@ export class NftCardBackTemplate extends LitElement {
${this.loading ? '' : this.getPropsTemplate(this.traits.props)}
- +
${this.loading ? 'loadingTemplate()' : - this.traits.rankings.length > 0 ? this.getRankingsTemplate(this.traits.rankings) - : this.getStatsTemplate(this.traits.stats) + this.traits.rankings.length > 0 ? this.getRankingsTemplate(this.traits.rankings) + : this.getStatsTemplate(this.traits.stats) }
@@ -430,5 +356,72 @@ export class NftCardBackTemplate extends LitElement {
` - } + } + + private formatTrait(trait) { + return trait.replace(/_/g, ' ') + } + + private buildTraits(traitData) { + this.traits = { + props: [], + stats: [], + rankings: [], + boosts: [] + } + const {traits, collectionTraits} = this.traitData + + for (const trait of traits) { + const type = this.getTraitType(trait, collectionTraits) + + const name = trait.trait_type + this.traits[type + 's'].push({ + name, + value: trait.value, + ...(type === TraitType.Ranking ? { max: collectionTraits[name].max } : {}) + }) + } + } + + private getTraitType(trait: object, collectionTraits: object) { + if (this.isProperty(trait, collectionTraits)) { return TraitType.Property } + if (this.isStat(trait)) { return TraitType.Stat } + if (this.isRanking(trait, collectionTraits)) { return TraitType.Ranking } + if (this.isBoost(trait)) { return TraitType.Boost } + } + + private isBoost(trait: object) { + return trait.display_type.includes('boost') + } + + private isRanking(trait: object, collectionTraits: object) { + return ( + trait.display_type === null && 'max' in collectionTraits[trait.trait_type] + } + + /** + * IsStat - Checks to see if the given trait is a 'Stat' + * A 'Stat' is defined as any trait that has a `display_type` of 'number' + * + * @param trait - The object containing an asset's trait + * @return true if the trait is a 'Stat' and false otherwise + */ + private isStat(trait: object) { + return trait.display_type === 'number' + } + + /** + * IsProperty - Checks to see if the given trait is a 'Property'. + * A 'Property' is defined as any trait that has a `display_type` of null + * and does not have a min/max value + * + * @param trait - The object containing an asset's trait + * @return true if the trait is a 'Property' and false otherwise + */ + private isProperty(trait: object, collectionTraits: object) { + return ( + trait.display_type === null && + !('max' in collectionTraits[trait.trait_type]) + ) + } } diff --git a/src/nft-card-front.ts b/src/nft-card-front.ts index d60799f..c5a1f07 100644 --- a/src/nft-card-front.ts +++ b/src/nft-card-front.ts @@ -1,7 +1,7 @@ /** * Import LitElement base class, html helper function, * and TypeScript decorators - **/ + */ import {LitElement, html, customElement, property, css} from 'lit-element' import {classMap} from 'lit-html/directives/class-map' @@ -15,17 +15,13 @@ const BTN_TEXT = { @customElement('nft-card-front') export class NftCardFrontTemplate extends LitElement { - @property({type: Object}) asset = {} - @property({type: String}) acount; - @property({type: Object}) loading = true - @property({type: Boolean}) vertical + @property({type: Object}) public asset = {} + @property({type: String}) public account + @property({type: Object}) public loading = true + @property({type: Boolean}) public horizontal - constructor() { - super() - } - - static get styles() { - return css` + static get styles() { + return css` .card-front { position: absolute; backface-visibility: hidden; @@ -49,6 +45,12 @@ export class NftCardFrontTemplate extends LitElement { .asset-image-container { border-right: 1px solid #e2e6ef; + padding: 26px; + background-size: cover; + box-sizing: border-box; + } + + .asset-image { background-size: contain; background-position: 50%; background-repeat: no-repeat; @@ -58,19 +60,9 @@ export class NftCardFrontTemplate extends LitElement { .is-vertical .asset-image-container { border-bottom: 1px solid #e2e6ef; border-right: none; - height: min-content; - margin-top: 0; - height: 100%; - width: 100%; - } - .asset-image-container img { width: 100%; } - .is-vertical .asset-image-container img { - width: 100%; - height: 100%; - /* width: auto; */ - } + .asset-details-container { display: grid; grid-template-rows: auto; @@ -143,16 +135,16 @@ export class NftCardFrontTemplate extends LitElement { opacity: 1; } ` - } + } - /** + /** * EventHandler - Dispatch event allowing parent to handle click event * - * @param {Object} e the event context - * @param {string} type the event context - * @param {Object} data the event context + * @param e the event context + * @param type the event context + * @param data the event context */ - eventHandler(e, type, data = {}) { + public eventHandler(e, type, data = {}) { const event = new CustomEvent('new-event', { detail: { type, @@ -160,25 +152,25 @@ export class NftCardFrontTemplate extends LitElement { } }) this.dispatchEvent(event) - } + } - connectedCallback() { - super.connectedCallback(); - // setTimeout(() => this.eventHandler('','flip'), 10); - // console.warn('I flip card for testing remove me later') - } + public connectedCallback() { + super.connectedCallback() + // setTimeout(() => this.eventHandler('','flip'), 10); + // console.warn('I flip card for testing remove me later') + } - updated(changedProperties: array) { - // Assumption: If the traitData gets updated we should rebuild the - // traits object that populates UI - // Assumption: This will ONLY get called once per refresh - changedProperties.forEach((oldValue, propName) => { - if (propName === 'asset') { - // We got the data so we are done loading - this.loading = false + public updated(changedProperties: array) { + // Assumption: If the traitData gets updated we should rebuild the + // traits object that populates UI + // Assumption: This will ONLY get called once per refresh + changedProperties.forEach((oldValue, propName) => { + if (propName === 'asset') { + // We got the data so we are done loading + this.loading = false // Check for a sell order to populate the UI with the sell information - if(this.asset.sellOrders.length > 0) { + if (this.asset.sellOrders.length > 0) { this.canBuy = true const order = this.asset.sellOrders[0] const token = order.paymentTokenContract @@ -188,39 +180,37 @@ export class NftCardFrontTemplate extends LitElement { this.sellOrder = { token, currentPrice, - ...this.prevPrice, expires } } - // Tell the component to update with new state - this.requestUpdate() - } - }) - } - - + // Tell the component to update with new state + this.requestUpdate() + } + console.log(oldValue, propName) + }) + } - getAssetImageTemplate(imageUrl) { - return (html` + public getAssetImageTemplate(imageUrl) { + return (html`
`) } - getAssetPriceTemplate() { + public getAssetPriceTemplate() { // TODO: Needs to account for tokens with images not symbols - // Maybe change ethereum to an image instead of symbol? + // If payment_token.image_url then use token image instead of symbol let prevPriceTemplate = '' let currentPriceTemplate = '' - if(this.sellOrder) { + if (this.sellOrder) { const currentPriceSymbol = this.sellOrder.token.symbol === 'ETH' ? 'Ξ ' : '' currentPriceTemplate = html`
${currentPriceSymbol} ${this.sellOrder.currentPrice}
` } - if(this.asset.lastSale) { + if (this.asset.lastSale) { const formattedPrevPrice = this.asset.lastSale.total_price / Math.pow(10, this.asset.lastSale.payment_token.decimals) const prevPriceSymbol = this.asset.lastSale.payment_token.symbol === 'ETH' ? 'Ξ ' : '' prevPriceTemplate = html`
Prev. ${prevPriceSymbol} ${formattedPrevPrice}
` @@ -234,9 +224,9 @@ export class NftCardFrontTemplate extends LitElement { `) } - getButtonTemplate() { - let btnType; - if(this.asset.isOwnedByAccount) { + public getButtonTemplate() { + let btnType + if (this.asset.isOwnedByAccount) { btnType = 'manage' } else if (this.canBuy) { btnType = 'buy' @@ -253,13 +243,13 @@ export class NftCardFrontTemplate extends LitElement { ` } - /** + /** * Implement `render` to define a template for your element. */ - render() { - if (this.isLoading) return html``; - return html` -
+ public render() { + if (this.isLoading) { return html`` } + return html` +
-
+
+
+
+
@@ -312,5 +305,5 @@ export class NftCardFrontTemplate extends LitElement {
` - } + } } diff --git a/src/nft-card.ts b/src/nft-card.ts index 5cc898e..252d5fa 100644 --- a/src/nft-card.ts +++ b/src/nft-card.ts @@ -2,8 +2,8 @@ import {LitElement, html, customElement, property, css} from 'lit-element' import {styleMap} from 'lit-html/directives/style-map' import {classMap} from 'lit-html/directives/class-map' -import Web3 from 'web3'; -import {OpenSeaPort, Network, OpenSeaAsset} from 'opensea-js'; +import Web3 from 'web3' +import {OpenSeaPort, Network, OpenSeaAsset} from 'opensea-js' import { OrderSide } from 'opensea-js/lib/types' /* lit-element clases */ @@ -11,7 +11,6 @@ import './pill.ts' import './nft-card-front.ts' import './nft-card-back.ts' - /** * Nft-card element that manages front & back of card. * Facilitates aquisition and distribution data between @@ -21,93 +20,8 @@ import './nft-card-back.ts' @customElement('nft-card') export class NftCard extends LitElement { - /* User configurable propties */ - @property({type: Boolean}) vertical - @property({type: String}) contractAddress - @property({type: String}) tokenId - @property({type: String}) width = '388px' - @property({type: String}) height = '560px' - @property({type: String}) network = 'mainnet' - - @property({type: String}) flippedCard = false - @property({type: Boolean}) loading = true - - - @property({type: Object}) asset; - @property({type: Object}) traitData = {} - @property({type: String}) account; - - - getNetwork() { - switch (this.network) { - case 'mainnet': return Network.Main; - case 'rinkeby': return Network.Rinkeby; - } - } - - constructor() { - super(); - } - - /** - * ConnectedCallback - Invoked when a component is added to the document’s DOM. - * Grabs data from the OpenSea SDK and populates data objects to be passed to - * child components. - */ - async connectedCallback() { - super.connectedCallback(); - this.provider = - typeof web3 !== 'undefined' - ? window.web3.currentProvider - : Web3.providers.HttpProvider('https://mainnet.infura.io'); - - this.web3 = new Web3(this.provider) - this.seaport = new OpenSeaPort(this.provider, {networkName: this.getNetwork()}) - await this.connectWallet(); - this.asset = await this.seaport.api.getAsset(this.contractAddress, this.tokenId); - - [this.account] = await this.web3.eth.getAccounts() - - // TODO: Check if account exists - this.asset.isOwnedByAccount = this.asset.owner.address.toLowerCase() === this.account.toLowerCase() - - this.traitData = { - traits: this.asset.traits, - collectionTraits: this.asset.collection.traitStats - }; - - // We got the data so we are done loading - this.loading = false - - // Tell the component to update with new state - await this.requestUpdate(); - - // Watch for the account to change then update state of component - window.ethereum.on('accountsChanged', function (accounts) { - [this.account] = accounts - }) - } - - - - /** - * async connectWallet - Initializes connection to the injected web3 account - */ - private async connectWallet() { - if (!window.web3) { - // - } else if (window.ethereum) { - window.ethereum.enable(); - } else { - const errorMessage = - 'You need an Ethereum wallet to interact with this marketplace. Unlock your wallet, get MetaMask.io or Portis on desktop, or get Trust Wallet or Coinbase Wallet on mobile.'; - alert(errorMessage); - throw new Error(errorMessage); - } - } - - static get styles() { - return css` + static get styles() { + return css` p { margin: 0; -webkit-font-smoothing: antialiased; @@ -136,9 +50,77 @@ export class NftCard extends LitElement { transform: rotateY(180deg); } ` - } + } + + /* User configurable properties */ + @property({type: Boolean}) public horizontal = false + @property({type: String}) public contractAddress + @property({type: String}) public tokenId + @property({type: String}) public width = this.horizontal ? '670px' : '388px' + @property({type: String}) public height = this.horizontal ? '250px' : '560px' + @property({type: String}) public network = 'mainnet' + + @property({type: String}) private flippedCard = false + @property({type: Boolean}) private loading = true + + @property({type: Object}) public asset + @property({type: Object}) public traitData = {} + @property({type: String}) public account + + private getNetwork() { + switch (this.network) { + case 'mainnet': return Network.Main + case 'rinkeby': return Network.Rinkeby + } + } + + constructor() { + super() + } + + /** + * ConnectedCallback - Invoked when a component is added to the document’s DOM. + * Grabs data from the OpenSea SDK and populates data objects to be passed to + * child components. + */ + public async connectedCallback() { + super.connectedCallback() + this.provider = + typeof web3 !== 'undefined' + ? window.web3.currentProvider + : Web3.providers.HttpProvider('https://mainnet.infura.io') + + this.web3 = new Web3(this.provider) + this.seaport = new OpenSeaPort(this.provider, {networkName: this.getNetwork()}) + await this.connectWallet() + this.asset = await this.seaport.api.getAsset(this.contractAddress, this.tokenId); + + [this.account] = await this.web3.eth.getAccounts() - async buyEvent(data) { + // TODO: Check if account exists + if(this.account) { + this.asset.isOwnedByAccount = this.asset.owner.address.toLowerCase() === this.account.toLowerCase() + } + + this.traitData = { + traits: this.asset.traits, + collectionTraits: this.asset.collection.traitStats + } + + // We got the data so we are done loading + this.loading = false + + // Tell the component to update with new state + await this.requestUpdate() + + // Watch for the account to change then update state of component + window.ethereum.on('accountsChanged', function(accounts) { + [this.account] = accounts + console.log("accountsChanged", accounts) + }) + } + + public async buyEvent(data) { const order = await this.seaport.api.getOrder({ side: OrderSide.Sell, @@ -147,39 +129,39 @@ export class NftCard extends LitElement { }) // The buyer's wallet address, also the taker - const accountAddress = this.account - await this.seaport.fulfillOrder({ order, accountAddress }) - } + const accountAddress = this.account + await this.seaport.fulfillOrder({ order, accountAddress }) + } - flipCard(data) { - this.flippedCard = !this.flippedCard - } + public flipCard(data) { + this.flippedCard = !this.flippedCard + } - eventHandler(event) { - const {detail} = event; - switch (detail.type) { + public eventHandler(event) { + const {detail} = event + switch (detail.type) { case 'view': case 'manage': this.goToOpenSea() break - case 'buy': + case 'buy': this.buyEvent(detail.data) break - case 'flip': + case 'flip': this.flipCard() break - } - } + } + } - goToOpenSea() { - window.open(this.asset.openseaLink, '_blank'); + public goToOpenSea() { + window.open(this.asset.openseaLink, '_blank') } - /** + /** * Implement `render` to define a template for your element. */ - render() { - /** + public render() { + /** * Use JavaScript expressions to include property values in * the element template. */ @@ -198,13 +180,13 @@ export class NftCard extends LitElement { : html` ` @@ -212,4 +194,20 @@ export class NftCard extends LitElement {
` } + + /** + * async connectWallet - Initializes connection to the injected web3 account + */ + private async connectWallet() { + if (!window.web3) { + // + } else if (window.ethereum) { + window.ethereum.enable() + } else { + const errorMessage = + 'You need an Ethereum wallet to interact with this marketplace. Unlock your wallet, get MetaMask.io or Portis on desktop, or get Trust Wallet or Coinbase Wallet on mobile.' + alert(errorMessage) + throw new Error(errorMessage) + } + } } diff --git a/src/pill.ts b/src/pill.ts index 3946421..a30c643 100644 --- a/src/pill.ts +++ b/src/pill.ts @@ -2,9 +2,9 @@ * Import LitElement base class, html helper function, * and TypeScript decorators **/ -import {LitElement, html, customElement, property, css} from 'lit-element'; +import {LitElement, html, customElement, property, css} from 'lit-element' -import {styleMap} from 'lit-html/directives/style-map'; +import {styleMap} from 'lit-html/directives/style-map' /** * Use the customElement decorator to define your class as @@ -12,15 +12,15 @@ import {styleMap} from 'lit-html/directives/style-map'; */ @customElement('pill-element') export class pillTemplate extends LitElement { - /** + /** * Create an observed property. Triggers update on change. */ - @property({type: String}) imageUrl; - @property({type: String}) label; - @property({type: String}) backgroundColor; - @property({type: String}) textColor; - @property({type: String}) border = 'none'; - @property({type: Object}) customStyles = { + @property({type: String}) public imageUrl + @property({type: String}) public label + @property({type: String}) public backgroundColor + @property({type: String}) public textColor + @property({type: String}) public border = 'none' + @property({type: Object}) public customStyles = { backgroundColor: this.backgroundColor, color: this.textColor, border: this.border @@ -63,13 +63,13 @@ export class pillTemplate extends LitElement { /* Reverts transform (see above) */ /* transform: none; */ } - `; + ` } /** * Implement `render` to define a template for your element. */ - render() { + public render() { /** * Use JavaScript expressions to include property values in * the element template. @@ -78,14 +78,14 @@ export class pillTemplate extends LitElement {
${this.imageUrl ? html`` : ''}

${this.label}

- `; + ` } } diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..635a5c9 --- /dev/null +++ b/tslint.json @@ -0,0 +1,90 @@ +{ + "extends": ["tslint:latest", "tslint-eslint-rules"], + "rules": { + "adjacent-overload-signatures": true, + "arrow-parens": [true, "ban-single-arg-parens"], + "arrow-return-shorthand": true, + "await-promise": true, + "binary-expression-operand-order": true, + "callable-types": true, + "class-name": true, + "completed-docs": [ + true, + { + "functions": { "visibilities": ["exported"] }, + "methods": { "locations": "instance", "privacies": ["public", "protected"] } + } + ], + "curly": true, + "eofline": true, + "encoding": true, + "import-spacing": true, + "indent": [true, "spaces", 2], + "interface-name": false, + "interface-over-type-literal": true, + "linebreak-style": [true, "LF"], + "max-classes-per-file": [true, 1], + "max-line-length": false, + "member-access": true, + "member-ordering": [true, "public-before-private", "static-before-instance", "variables-before-functions"], + "newline-before-return": false, + "new-parens": true, + "no-angle-bracket-type-assertion": true, + "no-boolean-literal-compare": true, + "no-console": [true, "log"], + "no-default-export": true, + "no-empty-interface": false, + "no-floating-promises": true, + "no-non-null-assertion": true, + "no-parameter-reassignment": false, + "no-redundant-jsdoc": true, + "no-return-await": false, + "no-string-throw": true, + "no-string-literal": false, + "no-submodule-imports": false, + "no-unnecessary-type-assertion": true, + "no-implicit-dependencies": [true, "dev"], + "number-literal-format": true, + "object-literal-sort-keys": false, + "object-literal-key-quotes": false, + "ordered-imports": [ + false, + { + "grouped-imports": true + } + ], + "trailing-comma": false, + "prefer-const": true, + "prefer-for-of": true, + "prefer-function-over-method": false, + "promise-function-async": true, + "quotemark": false, + "radix": false, + "semicolon": [true, "never"], + "space-before-function-paren": [ + true, + { + "anonymous": "never", + "named": "never", + "method": "never", + "constructor": "never", + "asyncArrow": "always" + } + ], + "space-within-parens": false, + "triple-equals": false, + "type-literal-delimiter": false, + "variable-name": [true, "ban-keywords", "allow-pascal-case"], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-rest-spread", + "check-type", + "check-typecast", + "check-preblock" + ] + } +} \ No newline at end of file