Redesign subscription page

This commit is contained in:
silverpill 2022-06-07 21:14:04 +00:00
parent b865eaff10
commit 4110df590d
3 changed files with 192 additions and 28 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-right"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>

After

Width:  |  Height:  |  Size: 316 B

View file

@ -19,6 +19,8 @@ export default class Avatar extends Vue {
get avatarUrl(): string {
if (this.profile.avatar) {
return this.profile.avatar
} else if (this.profile.acct === "") {
return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAC8ElEQVR4nO3Y3UtTcRzHcf+nPdwccQ4GmeBymZDXYUgP2BTbdOIfURmFMibOdJPZ00WRBq1wq3RBBlkIS106N92m9gd8vWrwW5B69fuccz4X78svfH/ntXMOO01/jivCcGrSvQAjCHQEAYsgYBEELIKARRCwCAIWQcAiCFgEAYsgYBEELIKARRCwCAIWQcAiCFgEAYsgYBEELIKARRCwCAIWQcAiCFgEAYsgYBEELIKARRCwCAKWKUGKu9sSn47J4GBQ/B0d4vG0iNvtFm9rqwQCnRK6OySJxBPZL+9o39XSIIe1skxMPJLm5mZxOByn5vG0yEx8SvvelgSpVvbk1s0bZ4JoLDISluOjA+1nsBRIZCSsXGSn0ynBYL8spJLyJfdJ1r+vyepKVubmZqS399o/KA/H72s/g2VA0u+WlIvr9XpleTn935mFVFJcLld9xjAM2drc0H4WS4A0Pqoyp2D8bfzBPWXODO8TeJC94rbyS+/vv33m2eLutrjd7vrscDik/TymB3mffqv8ypPJ2XPNX/L767N9fde1n8f0IEeH+1Io5OXH+jdZ+ZyR3Z2tc813dV0mCEq1akkMw6iDjI2Nat/J1iDz87PK4+75s5T2nWwLUijkxefz1THa2i5IrVrSvpctQbY2N6S7+4pydyy+eaV9L1uCZLMfpL39ooIxFYtq38uWIJOTj5X/HYZhyMsXT7XvZTuQw1pZQqEh5a4IBDpl7WtO+262BGn88DgwcEfKpd/a97IlyNLiawVjNDJsmk/tlgTp6bmqPKYqB0XtO9kW5Ff+p3J3xKdj2neyNUhmOa2A5FY/at/J1iBWjCBgEQQsU4NEoxPKOySVSmjfiSAEwYkgYBGEEcRuEQQsgoBFELAIAhZBwCIIWAQBiyBgEQQsgoBFELAIAhZBwCIIWAQBiyBgEQQsgoBFELAIAhZBwCIIWAQBiyBgEQQsgoBFELAIAhZBwCIIWAQBiyBgEQQsgoBFELAIAhZBwDoBOz/dnwXMOO8AAAAASUVORK5CYII="
} else {
return makeBlockie(this.profile.acct)
}

View file

@ -1,38 +1,69 @@
<template>
<h1>Subscription</h1>
<div class="subscription">
<h1>Subscription</h1>
<div class="participants">
<component
class="profile-card"
:is="sender.id ? 'router-link' : 'div'"
:to="{ name: 'profile', params: { profileId: sender.id }}"
>
<avatar :profile="sender"></avatar>
<div class="display-name">{{ sender.getDisplayName() }}</div>
<div class="wallet-address">{{ senderEthereumAddress || '?' }}</div>
</component>
<div class="separator">
<img :src="require('@/assets/feather/arrow-right.svg')">
</div>
<router-link class="profile-card" :to="{ name: 'profile', params: { profileId: recipient.id }}">
<avatar :profile="recipient"></avatar>
<div class="display-name">{{ recipient.getDisplayName() }}</div>
<div class="wallet-address">{{ recipientEthereumAddress }}</div>
</router-link>
</div>
<div class="connect-wallet" v-if="canConnectWallet()">
<button class="btn" @click="connectWallet()">Connect wallet</button>
<button class="btn primary" @click="connectWallet()">Connect wallet</button>
</div>
<div class="wallet-error" v-if="walletError">
{{ walletError }}
</div>
<div class="info" v-if="subscriptionConfigured !== null">
<template v-if="subscription">
<div>Recipient address: {{ subscription.recipientAddress }}</div>
<div>Token address: {{ subscription.tokenAddress }}</div>
<div>Token symbol: {{ subscription.tokenSymbol }}</div>
<div>Price of one month: {{ subscription.pricePerMonth.round(2) }}</div>
<template v-if="subscriptionState">
<div>Your address: {{ subscriptionState.senderAddress }}</div>
<div>Your balance: {{ subscriptionState.senderBalance }}</div>
<div>Expires at: {{ subscription.getExpirationDate(subscriptionState.senderBalance).toLocaleString() }}</div>
</template>
<div class="price">
{{ subscription.pricePerMonth }} {{ subscription.tokenSymbol }}
<span class="price-subtext">per month</span>
</div>
<div class="status">
<template v-if="subscriptionState && !subscriptionState.senderBalance.isZero()">
<div>Your balance {{ subscriptionState.senderBalance }} {{ subscription.tokenSymbol }}</div>
<div>Subscription expires {{ subscription.getExpirationDate(subscriptionState.senderBalance).toLocaleString() }}</div>
</template>
<template v-else>You are not subscribed yet</template>
</div>
</template>
<template v-else>
Subscription is not available.
</template>
</div>
<div class="payment" v-if="canSubscribe()">
<button class="btn" @click="onMakeSubscriptionPayment()">
Pay for subscription
</button>
</div>
<div class="cancel" v-if="canCancel()">
<button class="btn" @click="onCancelSubscription()">
Cancel subscription
</button>
</div>
<form class="payment" v-if="canSubscribe()">
<div class="input-group">
<label for="duration">Duration</label>
<input type="number" id="duration" v-model="paymentDuration" min="1">
<span>months</span>
</div>
<div class="input-group">
<label>Amount</label>
<span>{{ getPaymentAmount() }} {{ subscription.tokenSymbol }}</span>
</div>
<div class="button-row">
<button type="submit" class="btn primary" @click.prevent="onMakeSubscriptionPayment()">
<template v-if="!subscriptionState || subscriptionState.senderBalance.isZero()">Pay</template>
<template v-else>Extend</template>
</button>
<button v-if="canCancel()" class="btn secondary" @click.prevent="onCancelSubscription()">
Cancel
</button>
</div>
</form>
</div>
</template>
@ -49,8 +80,10 @@ import {
Subscription,
SubscriptionState,
} from "@/api/subscriptions"
import Avatar from "@/components/Avatar.vue"
import { useWallet } from "@/composables/wallet"
import { useInstanceInfo } from "@/store/instance"
import { useCurrentUser } from "@/store/user"
import { ethereumAddressMatch, getWeb3Provider } from "@/utils/ethereum"
/* eslint-disable-next-line no-undef */
@ -58,14 +91,34 @@ const props = defineProps<{
profile: Profile,
}>()
const guest: Profile = {
id: "",
username: "",
acct: "",
url: "",
display_name: "You",
note: null,
avatar: null,
header: null,
identity_proofs: [],
fields: [],
followers_count: 0,
following_count: 0,
statuses_count: 0,
}
const { currentUser } = $(useCurrentUser())
const { instance } = $(useInstanceInfo())
const { connectWallet: connectEthereumWallet } = useWallet()
const recipient = new ProfileWrapper(props.profile)
const recipientEthereumAddress = recipient.getVerifiedEthereumAddress()
const sender = $ref<ProfileWrapper>(new ProfileWrapper(currentUser || guest))
let senderEthereumAddress = $ref<string | null>(sender.getVerifiedEthereumAddress())
let { walletAddress, walletError } = $(useWallet())
let subscriptionConfigured = $ref<boolean | null>(null)
let subscription = $ref<Subscription | null>(null)
let subscriptionState = $ref<SubscriptionState | null>(null)
const paymentDuration = $ref<number>(1)
onMounted(() => {
if (walletAddress && !walletError) {
@ -111,6 +164,7 @@ async function checkSubscription() {
walletError = "incorrect wallet address"
return
}
senderEthereumAddress = walletAddress.toLowerCase()
const signer = getWeb3Provider().getSigner()
subscription = await getSubscriptionInfo(
instance.blockchain_contract_address,
@ -130,6 +184,13 @@ async function checkSubscription() {
)
}
function getPaymentAmount(): number {
if (!subscription) {
return 0
}
return subscription.pricePerMonth.toUnsafeFloat() * paymentDuration
}
function canSubscribe(): boolean {
return subscriptionConfigured === true
}
@ -144,7 +205,7 @@ async function onMakeSubscriptionPayment() {
return
}
const signer = getWeb3Provider().getSigner()
const amount = subscription.pricePerMonthInt
const amount = subscription.pricePerMonthInt.mul(paymentDuration)
const transaction = await makeSubscriptionPayment(
instance.blockchain_contract_address,
signer,
@ -196,20 +257,120 @@ async function onCancelSubscription() {
@import "../styles/mixins";
@import "../styles/theme";
.subscription {
@include block-btn;
.btn.primary {
background-color: $btn-background-hover-color;
color: $btn-text-hover-color;
&:hover {
background-color: $block-background-color;
color: $btn-text-color;
}
}
.btn.secondary {
&:hover {
background-color: $block-background-color;
color: $btn-text-color;
}
}
.subscription {
display: flex;
flex-direction: column;
gap: $block-outer-padding;
text-align: center;
}
.participants {
$avatar-size: 60px;
align-items: center;
display: flex;
gap: $block-inner-padding;
.profile-card {
background-color: $block-background-color;
border-radius: $block-border-radius;
display: flex;
flex-basis: 50%;
flex-direction: column;
gap: $block-inner-padding / 2;
min-width: 0;
padding: $block-inner-padding;
}
.separator img {
height: $icon-size;
min-width: $icon-size;
object-fit: contain;
width: $icon-size;
}
.avatar {
height: $avatar-size;
margin: 0 auto;
width: $avatar-size;
}
.display-name {
font-size: 16px;
}
.wallet-address {
font-family: monospace;
overflow: hidden;
text-align: center;
text-overflow: ellipsis;
}
}
.info {
background-color: $block-background-color;
border-radius: $block-border-radius;
display: flex;
flex-direction: column;
gap: $block-inner-padding / 2;
margin-bottom: $block-outer-padding;
padding: $block-inner-padding;
h1 {
font-size: 20px;
margin: 0;
.price {
font-size: 16px;
font-weight: bold;
}
.price-subtext {
font-size: 14px;
}
.status {
color: $secondary-text-color;
}
}
.payment {
align-items: center;
display: flex;
flex-direction: column;
gap: $block-inner-padding;
.input-group {
align-items: center;
display: flex;
font-size: 16px;
gap: $input-padding;
justify-content: center;
label {
font-weight: bold;
}
input {
width: 100px;
}
}
.button-row {
display: flex;
gap: $block-inner-padding;
}
}
</style>