Show "Subscribe" button on profile page if subscriptions are enabled
This commit is contained in:
parent
c28e812de9
commit
31f328b0ce
5 changed files with 90 additions and 44 deletions
|
@ -7,6 +7,7 @@ import { ethereumAddressMatch, EthereumSignature } from "@/utils/ethereum"
|
||||||
import { floatToBigNumber, roundBigNumber } from "@/utils/numbers"
|
import { floatToBigNumber, roundBigNumber } from "@/utils/numbers"
|
||||||
import { http } from "./common"
|
import { http } from "./common"
|
||||||
import { Contracts, getContract } from "./contracts"
|
import { Contracts, getContract } from "./contracts"
|
||||||
|
import { User } from "./users"
|
||||||
|
|
||||||
const SECONDS_IN_DAY = 3600 * 24
|
const SECONDS_IN_DAY = 3600 * 24
|
||||||
const SECONDS_IN_MONTH = SECONDS_IN_DAY * 30
|
const SECONDS_IN_MONTH = SECONDS_IN_DAY * 30
|
||||||
|
@ -57,7 +58,7 @@ export async function getSubscriptionAuthorization(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function configureSubscription(
|
export async function configureSubscriptions(
|
||||||
contractAddress: string,
|
contractAddress: string,
|
||||||
signer: Signer,
|
signer: Signer,
|
||||||
recipientAddress: string,
|
recipientAddress: string,
|
||||||
|
@ -75,6 +76,22 @@ export async function configureSubscription(
|
||||||
return transaction
|
return transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function onSubscriptionsEnabled(
|
||||||
|
authToken: string,
|
||||||
|
): Promise<User> {
|
||||||
|
const url = `${BACKEND_URL}/api/v1/accounts/subscriptions_enabled`
|
||||||
|
const response = await http(url, {
|
||||||
|
method: "POST",
|
||||||
|
authToken,
|
||||||
|
})
|
||||||
|
const data = await response.json()
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error(data.message)
|
||||||
|
} else {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Subscription {
|
export class Subscription {
|
||||||
|
|
||||||
recipientAddress: string;
|
recipientAddress: string;
|
||||||
|
|
|
@ -27,6 +27,8 @@ export interface Profile {
|
||||||
followers_count: number;
|
followers_count: number;
|
||||||
following_count: number;
|
following_count: number;
|
||||||
statuses_count: number;
|
statuses_count: number;
|
||||||
|
|
||||||
|
subscription_page_url: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getVerifiedEthereumAddress(profile: Profile): string | null {
|
export function getVerifiedEthereumAddress(profile: Profile): string | null {
|
||||||
|
@ -180,7 +182,7 @@ export async function getIdentityClaim(authToken: string): Promise<string> {
|
||||||
export async function createIdentityProof(
|
export async function createIdentityProof(
|
||||||
authToken: string,
|
authToken: string,
|
||||||
signature: string,
|
signature: string,
|
||||||
): Promise<Profile> {
|
): Promise<User> {
|
||||||
const url = `${BACKEND_URL}/api/v1/accounts/identity_proof`
|
const url = `${BACKEND_URL}/api/v1/accounts/identity_proof`
|
||||||
const response = await http(url, {
|
const response = await http(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<div class="wallet-error" v-if="walletError">
|
<div class="wallet-error" v-if="walletError">
|
||||||
{{ walletError }}
|
{{ walletError }}
|
||||||
</div>
|
</div>
|
||||||
<div class="info" v-if="subscriptionConfigured !== null">
|
<div class="info" v-if="subscriptionsEnabled !== null">
|
||||||
<template v-if="subscription">
|
<template v-if="subscription">
|
||||||
<div class="price">
|
<div class="price">
|
||||||
{{ subscription.pricePerMonth }} {{ subscription.tokenSymbol }}
|
{{ subscription.pricePerMonth }} {{ subscription.tokenSymbol }}
|
||||||
|
@ -137,6 +137,7 @@ const guest: Profile = {
|
||||||
followers_count: 0,
|
followers_count: 0,
|
||||||
following_count: 0,
|
following_count: 0,
|
||||||
statuses_count: 0,
|
statuses_count: 0,
|
||||||
|
subscription_page_url: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
const { currentUser } = $(useCurrentUser())
|
const { currentUser } = $(useCurrentUser())
|
||||||
|
@ -147,7 +148,7 @@ const recipientEthereumAddress = recipient.getVerifiedEthereumAddress()
|
||||||
const sender = $ref<ProfileWrapper>(new ProfileWrapper(currentUser || guest))
|
const sender = $ref<ProfileWrapper>(new ProfileWrapper(currentUser || guest))
|
||||||
let senderEthereumAddress = $ref<string | null>(sender.getVerifiedEthereumAddress())
|
let senderEthereumAddress = $ref<string | null>(sender.getVerifiedEthereumAddress())
|
||||||
let { walletAddress, walletError, getSigner } = $(useWallet())
|
let { walletAddress, walletError, getSigner } = $(useWallet())
|
||||||
let subscriptionConfigured = $ref<boolean | null>(null)
|
let subscriptionsEnabled = $ref<boolean | null>(null)
|
||||||
let subscription = $ref<Subscription | null>(null)
|
let subscription = $ref<Subscription | null>(null)
|
||||||
let subscriptionState = $ref<SubscriptionState | null>(null)
|
let subscriptionState = $ref<SubscriptionState | null>(null)
|
||||||
let tokenBalance = $ref<BigNumber | null>(null)
|
let tokenBalance = $ref<BigNumber | null>(null)
|
||||||
|
@ -175,7 +176,7 @@ function canConnectWallet(): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
subscriptionConfigured = null
|
subscriptionsEnabled = null
|
||||||
subscription = null
|
subscription = null
|
||||||
subscriptionState = null
|
subscriptionState = null
|
||||||
}
|
}
|
||||||
|
@ -210,9 +211,9 @@ async function checkSubscription() {
|
||||||
recipientEthereumAddress,
|
recipientEthereumAddress,
|
||||||
)
|
)
|
||||||
if (subscription !== null) {
|
if (subscription !== null) {
|
||||||
subscriptionConfigured = true
|
subscriptionsEnabled = true
|
||||||
} else {
|
} else {
|
||||||
subscriptionConfigured = false
|
subscriptionsEnabled = false
|
||||||
isLoading = false
|
isLoading = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -227,7 +228,7 @@ async function checkSubscription() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function canSubscribe(): boolean {
|
function canSubscribe(): boolean {
|
||||||
return subscriptionConfigured === true
|
return subscriptionsEnabled === true
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPaymentAmount(): FixedNumber {
|
function getPaymentAmount(): FixedNumber {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<h1>Configure subscription</h1>
|
<h1>Manage subscriptions</h1>
|
||||||
<div class="subscription">
|
<div class="subscription">
|
||||||
<div class="connect-wallet" v-if="canConnectWallet()">
|
<div class="connect-wallet" v-if="canConnectWallet()">
|
||||||
<button class="btn" @click="connectWallet()">Connect wallet</button>
|
<button class="btn" @click="connectWallet()">Connect wallet</button>
|
||||||
|
@ -7,19 +7,19 @@
|
||||||
<div class="wallet-error" v-if="walletError">
|
<div class="wallet-error" v-if="walletError">
|
||||||
{{ walletError }}
|
{{ walletError }}
|
||||||
</div>
|
</div>
|
||||||
<div class="info" v-if="subscriptionConfigured !== null">
|
<div class="info" v-if="subscriptionsEnabled !== null">
|
||||||
<template v-if="subscription">
|
<template v-if="subscription">
|
||||||
<span>Subscription is enabled</span>
|
<span>Subscriptions are enabled</span>
|
||||||
<div class="price">
|
<div class="price">
|
||||||
{{ subscription.pricePerMonth }} {{ subscription.tokenSymbol }}
|
{{ subscription.pricePerMonth }} {{ subscription.tokenSymbol }}
|
||||||
<span class="price-subtext">per month</span>
|
<span class="price-subtext">per month</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
Subscription is not configured
|
Subscriptions are not enabled
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<form class="setup" v-if="canConfigureSubscription()">
|
<form class="setup" v-if="canEnableSubscriptions()">
|
||||||
<div class="price">
|
<div class="price">
|
||||||
<label for="price">Price</label>
|
<label for="price">Price</label>
|
||||||
<input type="number" id="price" v-model="subscriptionPrice" min="0.00">
|
<input type="number" id="price" v-model="subscriptionPrice" min="0.00">
|
||||||
|
@ -28,9 +28,9 @@
|
||||||
<button
|
<button
|
||||||
class="btn primary"
|
class="btn primary"
|
||||||
:disabled="subscriptionPrice <= 0"
|
:disabled="subscriptionPrice <= 0"
|
||||||
@click.prevent="onConfigureSubscription()"
|
@click.prevent="onEnableSubscriptions()"
|
||||||
>
|
>
|
||||||
Set up subscription
|
Enable subscriptions
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<form class="withdraw" v-if="subscription !== null">
|
<form class="withdraw" v-if="subscription !== null">
|
||||||
|
@ -57,14 +57,15 @@
|
||||||
import { onMounted, watch } from "vue"
|
import { onMounted, watch } from "vue"
|
||||||
import { $, $$, $ref } from "vue/macros"
|
import { $, $$, $ref } from "vue/macros"
|
||||||
|
|
||||||
import { getVerifiedEthereumAddress, Profile } from "@/api/users"
|
import { Profile, ProfileWrapper } from "@/api/users"
|
||||||
import {
|
import {
|
||||||
configureSubscription,
|
configureSubscriptions,
|
||||||
getPricePerSec,
|
getPricePerSec,
|
||||||
getSubscriptionAuthorization,
|
getSubscriptionAuthorization,
|
||||||
getSubscriptionInfo,
|
getSubscriptionInfo,
|
||||||
getSubscriptionState,
|
getSubscriptionState,
|
||||||
getSubscriptionToken,
|
getSubscriptionToken,
|
||||||
|
onSubscriptionsEnabled,
|
||||||
withdrawReceived,
|
withdrawReceived,
|
||||||
Subscription,
|
Subscription,
|
||||||
SubscriptionState,
|
SubscriptionState,
|
||||||
|
@ -81,15 +82,16 @@ const props = defineProps<{
|
||||||
profile: Profile,
|
profile: Profile,
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { ensureAuthToken } = $(useCurrentUser())
|
const { ensureAuthToken, setCurrentUser } = $(useCurrentUser())
|
||||||
const { instance } = $(useInstanceInfo())
|
const { instance } = $(useInstanceInfo())
|
||||||
const { connectWallet: connectEthereumWallet, getSigner } = useWallet()
|
const { connectWallet: connectEthereumWallet, getSigner } = useWallet()
|
||||||
const profileEthereumAddress = getVerifiedEthereumAddress(props.profile)
|
const profile = new ProfileWrapper(props.profile)
|
||||||
|
const profileEthereumAddress = profile.getVerifiedEthereumAddress()
|
||||||
const subscriptionPrice = $ref<number>(1)
|
const subscriptionPrice = $ref<number>(1)
|
||||||
let { walletAddress, walletError } = $(useWallet())
|
let { walletAddress, walletError } = $(useWallet())
|
||||||
let isLoading = $ref(false)
|
let isLoading = $ref(false)
|
||||||
let subscriptionToken = $ref<SubscriptionToken | null>(null)
|
let subscriptionToken = $ref<SubscriptionToken | null>(null)
|
||||||
let subscriptionConfigured = $ref<boolean | null>(null)
|
let subscriptionsEnabled = $ref<boolean | null>(null)
|
||||||
let subscription = $ref<Subscription | null>(null)
|
let subscription = $ref<Subscription | null>(null)
|
||||||
let subscriptionState = $ref<SubscriptionState | null>(null)
|
let subscriptionState = $ref<SubscriptionState | null>(null)
|
||||||
let subscriberAddress = $ref<string | null>(null)
|
let subscriberAddress = $ref<string | null>(null)
|
||||||
|
@ -113,7 +115,7 @@ function canConnectWallet(): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
subscriptionConfigured = null
|
subscriptionsEnabled = null
|
||||||
subscription = null
|
subscription = null
|
||||||
subscriptionState = null
|
subscriptionState = null
|
||||||
subscriberAddress = null
|
subscriberAddress = null
|
||||||
|
@ -149,9 +151,11 @@ async function checkSubscription() {
|
||||||
profileEthereumAddress,
|
profileEthereumAddress,
|
||||||
)
|
)
|
||||||
if (subscription !== null) {
|
if (subscription !== null) {
|
||||||
subscriptionConfigured = true
|
subscriptionsEnabled = true
|
||||||
|
// Ensure server is aware of subscription configuration
|
||||||
|
await onSubscriptionsEnabled(ensureAuthToken())
|
||||||
} else {
|
} else {
|
||||||
subscriptionConfigured = false
|
subscriptionsEnabled = false
|
||||||
subscriptionToken = await getSubscriptionToken(
|
subscriptionToken = await getSubscriptionToken(
|
||||||
instance.blockchain_contract_address,
|
instance.blockchain_contract_address,
|
||||||
signer,
|
signer,
|
||||||
|
@ -160,15 +164,16 @@ async function checkSubscription() {
|
||||||
isLoading = false
|
isLoading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function canConfigureSubscription(): boolean {
|
function canEnableSubscriptions(): boolean {
|
||||||
return (
|
return (
|
||||||
profileEthereumAddress !== null &&
|
profileEthereumAddress !== null &&
|
||||||
subscriptionConfigured === false &&
|
subscriptionsEnabled === false &&
|
||||||
subscriptionToken !== null
|
subscriptionToken !== null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onConfigureSubscription() {
|
// enableSubscriptions
|
||||||
|
async function onEnableSubscriptions() {
|
||||||
if (
|
if (
|
||||||
profileEthereumAddress === null ||
|
profileEthereumAddress === null ||
|
||||||
!instance ||
|
!instance ||
|
||||||
|
@ -187,7 +192,7 @@ async function onConfigureSubscription() {
|
||||||
const signature = await getSubscriptionAuthorization(authToken, pricePerSec)
|
const signature = await getSubscriptionAuthorization(authToken, pricePerSec)
|
||||||
let transaction
|
let transaction
|
||||||
try {
|
try {
|
||||||
transaction = await configureSubscription(
|
transaction = await configureSubscriptions(
|
||||||
instance.blockchain_contract_address,
|
instance.blockchain_contract_address,
|
||||||
signer,
|
signer,
|
||||||
profileEthereumAddress,
|
profileEthereumAddress,
|
||||||
|
@ -200,12 +205,15 @@ async function onConfigureSubscription() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await transaction.wait()
|
await transaction.wait()
|
||||||
subscriptionConfigured = true
|
subscriptionsEnabled = true
|
||||||
subscription = await getSubscriptionInfo(
|
subscription = await getSubscriptionInfo(
|
||||||
instance.blockchain_contract_address,
|
instance.blockchain_contract_address,
|
||||||
signer,
|
signer,
|
||||||
profileEthereumAddress,
|
profileEthereumAddress,
|
||||||
)
|
)
|
||||||
|
const user = await onSubscriptionsEnabled(authToken)
|
||||||
|
setCurrentUser(user)
|
||||||
|
profile.subscription_page_url = user.subscription_page_url
|
||||||
isLoading = false
|
isLoading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,20 +49,12 @@
|
||||||
Verify ethereum address
|
Verify ethereum address
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="canConfigureSubscription()">
|
<li v-if="canManageSubscriptions()">
|
||||||
<router-link
|
<router-link
|
||||||
title="Set up subscription"
|
title="Manage subscriptions"
|
||||||
:to="{ name: 'profile-subscription', params: { profileId: profile.id }}"
|
:to="{ name: 'profile-subscription', params: { profileId: profile.id }}"
|
||||||
>
|
>
|
||||||
Set up subscription
|
Manage subscriptions
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<li v-if="canSubscribe()">
|
|
||||||
<router-link
|
|
||||||
title="Pay for subscription"
|
|
||||||
:to="{ name: 'profile-subscription', params: { profileId: profile.id }}"
|
|
||||||
>
|
|
||||||
Pay for subscription
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="canHideReposts()">
|
<li v-if="canHideReposts()">
|
||||||
|
@ -92,6 +84,26 @@
|
||||||
<template v-if="isFollowRequestPending()">Cancel follow request</template>
|
<template v-if="isFollowRequestPending()">Cancel follow request</template>
|
||||||
<template v-else>Unfollow</template>
|
<template v-else>Unfollow</template>
|
||||||
</button>
|
</button>
|
||||||
|
<template v-if="canSubscribe()">
|
||||||
|
<router-link
|
||||||
|
v-if="isLocalUser()"
|
||||||
|
class="btn"
|
||||||
|
title="Pay for subscription"
|
||||||
|
:to="{ name: 'profile-subscription', params: { profileId: profile.id } }"
|
||||||
|
>
|
||||||
|
Subscribe
|
||||||
|
</router-link>
|
||||||
|
<a
|
||||||
|
v-else-if="profile.subscription_page_url"
|
||||||
|
class="btn"
|
||||||
|
title="Pay for subscription"
|
||||||
|
:href="profile.subscription_page_url"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Subscribe
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bio" v-html="profile.note"></div>
|
<div class="bio" v-html="profile.note"></div>
|
||||||
|
@ -205,7 +217,12 @@ import { useCurrentUser } from "@/store/user"
|
||||||
import { getWallet, getWalletSignature } from "@/utils/ethereum"
|
import { getWallet, getWalletSignature } from "@/utils/ethereum"
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { currentUser, authToken, ensureAuthToken } = $(useCurrentUser())
|
const {
|
||||||
|
authToken,
|
||||||
|
currentUser,
|
||||||
|
ensureAuthToken,
|
||||||
|
setCurrentUser,
|
||||||
|
} = $(useCurrentUser())
|
||||||
const { instance, getActorAddress } = $(useInstanceInfo())
|
const { instance, getActorAddress } = $(useInstanceInfo())
|
||||||
|
|
||||||
let profile = $ref<Profile | null>(null)
|
let profile = $ref<Profile | null>(null)
|
||||||
|
@ -403,11 +420,12 @@ async function verifyEthereumAddress(): Promise<void> {
|
||||||
const authToken = ensureAuthToken()
|
const authToken = ensureAuthToken()
|
||||||
const message = await getIdentityClaim(authToken)
|
const message = await getIdentityClaim(authToken)
|
||||||
const signature = await getWalletSignature(signer, message)
|
const signature = await getWalletSignature(signer, message)
|
||||||
const { identity_proofs } = await createIdentityProof(authToken, signature)
|
const user = await createIdentityProof(authToken, signature)
|
||||||
profile.identity_proofs = identity_proofs
|
setCurrentUser(user)
|
||||||
|
profile.identity_proofs = user.identity_proofs
|
||||||
}
|
}
|
||||||
|
|
||||||
function canConfigureSubscription(): boolean {
|
function canManageSubscriptions(): boolean {
|
||||||
// Only users with verified address can configure subscription
|
// Only users with verified address can configure subscription
|
||||||
return (
|
return (
|
||||||
Boolean(instance?.blockchain_contract_address) &&
|
Boolean(instance?.blockchain_contract_address) &&
|
||||||
|
@ -423,7 +441,7 @@ function canSubscribe(): boolean {
|
||||||
Boolean(instance?.blockchain_features?.subscription) &&
|
Boolean(instance?.blockchain_features?.subscription) &&
|
||||||
profile !== null &&
|
profile !== null &&
|
||||||
getVerifiedEthereumAddress(profile) !== null &&
|
getVerifiedEthereumAddress(profile) !== null &&
|
||||||
isLocalUser() &&
|
profile.subscription_page_url !== null &&
|
||||||
!isCurrentUser()
|
!isCurrentUser()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue