Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TAS-623] ✨ Add ISCN card print page #406

Merged
merged 2 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions components/IscnCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
<div
ref="canvas"
:class="canvasWrapperClasses"
:style="rootStyle"
@resize="handleResize"
/>
<svg
Expand Down Expand Up @@ -65,6 +64,18 @@
/>
</foreignObject>
</g>
<rect
key="border_radius_hack_for_printing"
class="hidden print:block"
x="-9"
y="-9"
:width="svgSizeProps.width + 18"
:height="svgSizeProps.height + 18"
rx="30"
fill="none"
stroke="#fff"
stroke-width="18"
/>
</svg>
</div>
</div>
Expand Down Expand Up @@ -107,7 +118,6 @@ export default class IscnCard extends Vue {
static baseWidth = 560
static baseHeight = 280
static baseBorderWidth = 18
static baseBorderRadius = 24
static baseAnimationDuration = 500
static baseShapeMorphingMagnitude = 4
static baseColorMultiplier = 10
Expand Down Expand Up @@ -210,10 +220,6 @@ export default class IscnCard extends Vue {
return this.orientation === IscnCardOrientation.portrait
}

get borderRadius() {
return IscnCard.baseBorderRadius * this.width / this.svgSizeProps.width
}

get viewBox() {
return this.isPortrait
? `0 0 ${IscnCard.baseHeight} ${IscnCard.baseWidth}`
Expand All @@ -224,12 +230,6 @@ export default class IscnCard extends Vue {
return !this.isAnimated && this.isQRCodeRendering
}

get rootStyle() {
return {
borderRadius: `${this.borderRadius}px`,
}
}

get rootProps() {
return {
class: [
Expand All @@ -239,7 +239,10 @@ export default class IscnCard extends Vue {
'animate-pulse': this.isShowLoadingIndicator,
},
],
style: this.rootStyle,
style: {
maskSize: 'contain',
maskImage: `url(/images/iscn-card/mask-${this.isPortrait ? 'portrait' : 'landscape'}.png)`,
},
}
}

Expand Down Expand Up @@ -505,8 +508,7 @@ export default class IscnCard extends Vue {

// eslint-disable-next-line no-param-reassign
s.setup = () => {
const canvas = s.createCanvas(this.width, this.height)
canvas.style('border-radius', `${this.borderRadius}px`)
s.createCanvas(this.width, this.height)
if (!this.isAnimated) {
s.noLoop()
}
Expand Down
4 changes: 4 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@
"iscn.meta.usage.info": "Usage Info",
"iscn.meta.version.placeholder": "Version",
"iscn.meta.version": "Version",
"IscnCardPage.button.back": "Back",
"IscnCardPage.button.landscape": "Landscape",
"IscnCardPage.button.portrait": "Portrait",
"IscnCardPage.button.print": "Print",
"IscnRegisterForm.arweave.link": "ar://{arweaveId}",
"IscnRegisterForm.button.back": "Back",
"IscnRegisterForm.button.confirm": "Confirm",
Expand Down
128 changes: 128 additions & 0 deletions pages/view/_iscnId/card.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<template>
<div class="fixed inset-0 flex justify-center items-center">
<header
v-if="shouldShowControls"
class="print:hidden absolute inset-x-0 top-0 flex justify-between p-4 bg-white shadow-sm z-10"
>
<Button
preset="tertiary"
:text="$t('IscnCardPage.button.back')"
:to="localeLocation({
name: 'view-iscnId',
params: { iscnId },
})"
>
<template #prepend>
<IconArrowLeft />
</template>
</Button>

<div class="flex gap-2">
<Button
preset="tertiary"
:text="$t(isLandscape ? 'IscnCardPage.button.portrait' : 'IscnCardPage.button.landscape')"
:to="localeLocation({
name: 'view-iscnId-card',
params: { iscnId },
query: { orientation: isLandscape ? 'portrait' : 'landscape' },
})"
@click.native="handleClickOrientationChange"
/>
<Button
preset="secondary"
:text="$t('IscnCardPage.button.print')"
@click="handleClickPrint"
/>
</div>
</header>

<IscnCard
:key="orientation"
:record="record"
:is-animated="true"
:orientation="orientation"
:style="`width: ${width}px`"
/>

</div>
</template>

<script lang="ts">
import { Vue, Component, Watch } from 'vue-property-decorator'
import { namespace } from 'vuex-class'

import { ISCN_PREFIX } from '~/constant'

import { ISCNRecordWithID } from '~/utils/cosmos/iscn/iscn.type'
import { logTrackerEvent } from '~/utils/logger'

const iscnModule = namespace('iscn')

@Component({
layout: 'blank',
components: { IscnCard: () => import('~/components/IscnCard.vue') },
async asyncData({ params, store, error }) {
try {
const { iscnId } = params
if (iscnId && iscnId.startsWith(ISCN_PREFIX)) {
const res = await store.dispatch('iscn/fetchISCNById', iscnId)
if (res) {
return {
iscnId: res.records[0].id,
};
}
}
} catch (err) {
error(err as Error)
}
return {}
},
})
export default class ISCNCardPrintPage extends Vue {
iscnId = ''
width = 0

@iscnModule.Getter getISCNById!: (arg0: string) => ISCNRecordWithID

get isLandscape() {
return this.$route.query.orientation === 'landscape'
}

get shouldShowControls() {
return this.$route.query.control !== '0'
}

get orientation() {
return this.isLandscape ? 'landscape' : 'portrait'
}

get record() {
return this.getISCNById(this.iscnId)
}

mounted() {
this.handleResize()
window.addEventListener('resize', this.handleResize)
}

beforeDestroy() {
window.removeEventListener('resize', this.handleResize)
}

@Watch('isLandscape')
handleResize() {
window.requestAnimationFrame(() => {
this.width = this.isLandscape ? Math.min(window.innerHeight * 2, 560) : Math.min(window.innerHeight / 2, 280);
})
}

handleClickOrientationChange() {
logTrackerEvent(this, 'ISCNView', `ISCNCardPageSet${this.isLandscape ? 'Landscape' : 'Portrait' }`, this.iscnId, 1)
}

handleClickPrint() {
logTrackerEvent(this, 'ISCNView', 'ISCNCardPagePrint', this.iscnId, 1)
window.print()
}
}
</script>
55 changes: 31 additions & 24 deletions pages/view/_iscnId.vue → pages/view/_iscnId/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -128,30 +128,37 @@
'lg:max-w-[280px]',
]"
>
<IscnCard
:key="`${record.id}-portrait`"
:class="[
'hidden',
'lg:block',
'flex-shrink-0',
'w-[280px]',
]"
:record="record"
orientation="portrait"
:is-animated="true"
/>
<IscnCard
:key="`${record.id}-landscape`"
:class="[
'w-full',
'lg:absolute',
'lg:opacity-0',
'lg:pointer-events-none',
]"
:record="record"
:is-animated="true"
orientation="landscape"
/>
<NuxtLink
:to="localeLocation({
name: 'view-iscnId-card',
params: { iscnId: record.id },
})"
>
<IscnCard
:key="`${record.id}-portrait`"
:class="[
'hidden',
'lg:block',
'flex-shrink-0',
'w-[280px]',
]"
:record="record"
orientation="portrait"
:is-animated="true"
/>
<IscnCard
:key="`${record.id}-landscape`"
:class="[
'w-full',
'lg:absolute',
'lg:opacity-0',
'lg:pointer-events-none',
]"
:record="record"
:is-animated="true"
orientation="landscape"
/>
</NuxtLink>
<Button
v-if="viewContentURL"
class="mx-auto mt-[16px]"
Expand Down
Binary file added static/images/iscn-card/mask-landscape.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/images/iscn-card/mask-portrait.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 8 additions & 4 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ module.exports = {
'airdrop-gold':'#D1AB79',
'twitter-blue':'#4696F1',
},
boxShadow: {
popup: '2px 4px 8px rgba(0, 0, 0, 0.25)',
},
fontFamily: {
'mono': [
'PT Mono',
Expand All @@ -38,6 +35,13 @@ module.exports = {
'48px': '48px',
'64px': '64px',
},
screens: {
// Should remove this once we upgrade TailwindCSS to v3
print: { raw: 'print' },
},
boxShadow: {
popup: '2px 4px 8px rgba(0, 0, 0, 0.25)',
},
},
screens: {
'sm': '640px',
Expand All @@ -59,7 +63,7 @@ module.exports = {
'.scrollbar-hidden': {
'-ms-overflow-style': 'none', /* IE and Edge */
'scrollbar-width': 'none', /* Firefox */
'&::-webkit-scrollbar': {
'&::-webkit-scrollbar': {
display: 'none', /* Chrome */
},
},
Expand Down
Loading