UNPKG

7.63 kBPlain TextView Raw
1import { css, customElement, html, LitElement, property } from 'lit-element'
2import { styleMap } from 'lit-html/directives/style-map'
3import { provider as Web3Provider } from 'web3-core'
4
5import { Network, OpenSeaPort } from 'opensea-js'
6import { OpenSeaAsset } from 'opensea-js/lib/types'
7
8/* lit-element classes */
9import './pill.ts'
10import './loader.ts'
11import './nft-card-front.ts'
12import './nft-card-back.ts'
13import { ButtonEvent } from './types'
14import { getProvider, networkFromString } from './utils'
15
16const HORIZONTAL_MIN_CARD_HEIGHT = '200px'
17const VERT_MIN_CARD_HEIGHT = '670px'
18
19const VERT_CARD_HEIGHT = '560px'
20const VERT_CARD_WIDTH = '380px'
21
22const VERT_CARD_WIDTH_MOBILE = '80vw'
23
24const HORIZONTAL_CARD_HEIGHT = '210px'
25const HORIZONTAL_CARD_WIDTH = '80vw'
26const HORIZONTAL_CARD_MAX_WIDTH = '670px'
27
28enum OrientationMode {
29 Auto = 'auto',
30 Manual = 'manual',
31}
32
33const MOBILE_BREAK_POINT = 600
34
35/**
36 * Nft-card element that manages front & back of card.
37 * Facilitates acquisition and distribution data between
38 * components.
39 * Registers <nft-card> as an HTML tag.
40 */
41@customElement('nft-card')
42export class NftCard extends LitElement {
43 /* User configurable properties */
44 @property({ type: Boolean }) public horizontal?: boolean
45 @property({ type: Boolean }) public vertical?: boolean
46 @property({ type: String }) public orientationMode?: OrientationMode
47 @property({ type: String }) public tokenAddress: string = ''
48 @property({ type: String }) public contractAddress: string = ''
49 @property({ type: String }) public tokenId: string = ''
50 @property({ type: String }) public width: string = ''
51 @property({ type: String }) public height: string = ''
52 @property({ type: String }) public minHeight: string = ''
53 @property({ type: String }) public maxWidth: string = ''
54 @property({ type: String }) public network: Network = Network.Main
55 @property({ type: String }) public referrerAddress: string = ''
56
57 @property({ type: Object }) private asset!: OpenSeaAsset
58 @property({ type: Object }) private traitData: object = {}
59 @property({ type: String }) public flippedCard: boolean = false
60 @property({ type: Object }) private provider: Web3Provider = null
61 @property({ type: Object }) private seaport!: OpenSeaPort
62
63 // Card state variables
64 @property({ type: Boolean }) private loading = true
65 @property({ type: Boolean }) private error = false
66
67 static get styles() {
68 return css`
69 :host {
70 all: initial;
71 }
72 p {
73 margin: 0;
74 -webkit-font-smoothing: antialiased;
75 }
76 .card {
77 background-color: white;
78 font-family: 'Roboto', sans-serif;
79 -webkit-font-smoothing: antialiased;
80 font-style: normal;
81 font-weight: normal;
82 line-height: normal;
83 border-radius: 5px;
84 perspective: 1000px;
85 margin: auto;
86 }
87 .card-inner {
88 position: relative;
89 width: 100%;
90 height: 100%;
91 text-align: center;
92 transition: transform 0.6s;
93 transform-style: preserve-3d;
94 box-shadow: 0px 1px 6px rgba(0, 0, 0, 0.25);
95 border-radius: 5px;
96 }
97 .flipped-card .card-inner {
98 transform: rotateY(180deg);
99 }
100 .card .error {
101 height: 100%;
102 display: flex;
103 flex-flow: column;
104 justify-content: center;
105 }
106 .card .error-moji {
107 font-size: 50px;
108 }
109 .card .error-message {
110 font-size: 16px;
111 }
112 `
113 }
114
115 /**
116 * ConnectedCallback - Invoked when a component is added to the document’s DOM.
117 * Grabs data from the OpenSea SDK and populates data objects to be passed to
118 * child components.
119 */
120 public async connectedCallback() {
121 super.connectedCallback()
122 this.tokenAddress = this.contractAddress
123 ? this.contractAddress
124 : this.tokenAddress
125
126 /* If user sets any style overrides assume manual mode unless user has defined the mode */
127 if (!this.orientationMode) {
128 this.orientationMode =
129 this.width || this.height || this.horizontal || this.vertical
130 ? OrientationMode.Manual
131 : OrientationMode.Auto
132 }
133
134 this.horizontal = this.horizontal || !this.vertical
135
136 let vertCardWidth = VERT_CARD_WIDTH
137 if (
138 this.orientationMode === OrientationMode.Auto &&
139 window.innerWidth < MOBILE_BREAK_POINT
140 ) {
141 vertCardWidth = VERT_CARD_WIDTH_MOBILE
142 this.horizontal = false
143 }
144
145 // Set default dimensions
146 this.width = this.width
147 ? this.width
148 : this.horizontal
149 ? HORIZONTAL_CARD_WIDTH
150 : vertCardWidth
151 this.height = this.height
152 ? this.height
153 : this.horizontal
154 ? HORIZONTAL_CARD_HEIGHT
155 : VERT_CARD_HEIGHT
156 this.minHeight = this.horizontal
157 ? HORIZONTAL_MIN_CARD_HEIGHT
158 : VERT_MIN_CARD_HEIGHT
159 this.maxWidth = this.horizontal ? HORIZONTAL_CARD_MAX_WIDTH : ''
160
161 this.provider = getProvider()
162 const networkName = networkFromString(this.network)
163 this.seaport = new OpenSeaPort(this.provider, { networkName })
164
165 try {
166 this.asset = await this.seaport.api.getAsset({
167 tokenAddress: this.tokenAddress,
168 tokenId: this.tokenId,
169 })
170
171 this.traitData = {
172 traits: this.asset.traits,
173 collectionTraits: this.asset.collection.traitStats,
174 }
175 } catch (e) {
176 this.error = true
177 // Probably could not find the asset
178 console.error(e)
179 }
180
181 this.loading = false
182
183 // Tell the component to update with new state
184 await this.requestUpdate()
185
186 }
187
188 public renderErrorTemplate() {
189 return html`
190 <div class="error">
191 <div class="error-moji">¯\\_(ツ)_/¯</div>
192 <div class="error-message">Problem loading asset.</div>
193 </div>
194 `
195 }
196
197 public renderLoaderTemplate() {
198 return html` <loader-element></loader-element> `
199 }
200
201 public renderInnerCardTemplate() {
202 return html`
203 <nft-card-front
204 .horizontal=${this.horizontal}
205 @button-event="${this.eventHandler}"
206 .asset=${this.asset}
207 .state=${{
208 network: this.network,
209 }}
210 .flippedCard="${this.flippedCard}"
211 ></nft-card-front>
212 <nft-card-back
213 .horizontal=${this.horizontal}
214 .traitData=${this.traitData}
215 .openseaLink="${this.asset.openseaLink}"
216 @flip-event="${this.eventHandler}"
217 .flippedCard="${this.flippedCard}"
218 ></nft-card-back>
219 `
220 }
221
222 public render() {
223 return html`
224 <style>
225 @import url('https://fonts.googleapis.com/css?family=Roboto:100,300,400,500&display=swap');
226 </style>
227 <div
228 class="card ${this.flippedCard ? 'flipped-card' : ''}"
229 style=${styleMap({
230 width: this.width,
231 height: this.height,
232 minHeight: this.minHeight,
233 maxWidth: this.maxWidth,
234 })}
235 >
236 <div class="card-inner">
237 ${this.loading
238 ? this.renderLoaderTemplate()
239 : this.error
240 ? this.renderErrorTemplate()
241 : this.renderInnerCardTemplate()}
242 </div>
243 </div>
244 `
245 }
246
247 private flipCard() {
248 this.flippedCard = !this.flippedCard
249 }
250
251 private async eventHandler(event: ButtonEvent) {
252 const { detail } = event
253
254 switch (detail.type) {
255 case 'view':
256 this.goToOpenSea()
257 break
258 case 'flip':
259 this.flipCard()
260 break
261 }
262 }
263
264 private goToOpenSea() {
265 const url = this.referrerAddress
266 ? `${this.asset.openseaLink}?ref=${this.referrerAddress}`
267 : this.asset.openseaLink
268 window.open(url, '_blank')
269 }
270}