Make it easier to switch between subscription plans

This commit is contained in:
2024-02-22 17:20:41 +01:00
parent 5c9c66746a
commit 20610204e5
5 changed files with 227 additions and 111 deletions

View File

@@ -140,7 +140,7 @@ onMount(() => {
// Parse the results saved in the URL, if any // Parse the results saved in the URL, if any
if (window.location.hash[0] === "#") { if (window.location.hash[0] === "#") {
const hash = window.location.hash.replace("#", ""); const hash = window.location.hash.replace("#", "");
const result = hash.replace("#", "").split('&').reduce((res, item) => { const result = hash.split('&').reduce((res, item) => {
const parts = item.split('=') const parts = item.split('=')
const n = Number(parts[1]) const n = Number(parts[1])
if (n !== NaN) { if (n !== NaN) {

View File

@@ -1,5 +1,5 @@
<script> <script>
import { onDestroy, onMount } from "svelte"; import { onMount } from "svelte";
import { formatDataVolume, formatThousands } from "../util/Formatting.svelte"; import { formatDataVolume, formatThousands } from "../util/Formatting.svelte";
import Chart from "../util/Chart.svelte"; import Chart from "../util/Chart.svelte";
import StorageProgressBar from "./StorageProgressBar.svelte"; import StorageProgressBar from "./StorageProgressBar.svelte";
@@ -126,14 +126,7 @@ let load_direct_bw = () => {
}) })
} }
let patreon_response = ""
onMount(() => { onMount(() => {
if (window.location.hash.startsWith("#patreon_")) {
patreon_response = window.location.hash.replace("#patreon_", "")
window.location.hash = ""
}
if (window.user.monthly_transfer_cap > 0) { if (window.user.monthly_transfer_cap > 0) {
transfer_cap = window.user.monthly_transfer_cap transfer_cap = window.user.monthly_transfer_cap
} else if (window.user.subscription.monthly_transfer_cap > 0) { } else if (window.user.subscription.monthly_transfer_cap > 0) {
@@ -176,58 +169,33 @@ onMount(() => {
]; ];
update_graphs(43200, 1440, true); update_graphs(43200, 1440, true);
})
onDestroy(() => { return () => {
if (graph_timeout !== null) { if (graph_timeout !== null) {
clearTimeout(graph_timeout) clearTimeout(graph_timeout)
}
} }
}) })
</script> </script>
<section> <section>
{#if patreon_response !== ""}
{#if patreon_response === "error"}
<div class="highlight_red">
An error occurred while linking Patreon subscription. Please try
again later.
</div>
{:else if patreon_response === "pledge_not_found"}
<div class="highlight_yellow">
<p>
We were not able to find your payment on Patreon. Please
wait until the payment is confirmed and try again. Even if
the payment says confirmed on Patreon itself it takes a
while before it's communicated to pixeldrain. Please wait at
least 10 minutes and try again.
</p>
<p>
If it has been more than 30 minutes, your payment is
complete and your account is still not upgraded please
contact me at support@pixeldrain.com, or send me a message
on Patreon.
<p/>
</div>
{:else if patreon_response === "success"}
<div class="highlight_green">
Success! Your Patreon pledge has been linked to your pixeldrain
account. You are now able to use Pro features.
</div>
{/if}
<br/>
{/if}
<h2>Account information</h2> <h2>Account information</h2>
<ul> <ul>
<li>Username: {window.user.username}</li> <li>Username: {window.user.username}</li>
<li>E-mail address: {window.user.email}</li> <li>
{#if window.user.email === ""}
No e-mail address configured. You will not be able to recover
your account if you lose your password. Set an e-mail address on
the <a href="/user/settings">settings page</a>.
{:else}
E-mail address: {window.user.email}
(<a href="/user/settings">configure</a>)
{/if}
</li>
<li> <li>
Supporter level: {window.user.subscription.name} Supporter level: {window.user.subscription.name}
{#if window.user.subscription.type === "patreon"} (<a href="/user/subscription">manage subscriptions</a> /
(<a href="https://www.patreon.com/join/pixeldrain/checkout?edit=1">Manage subscription</a>) <a href="/api/patreon_auth/start">link patreon subscription</a>)
{:else if window.user.subscription.id === ""}
(<a href="/api/patreon_auth/start">Link Patreon subscription</a> /
<a href="/user/prepaid/deposit">deposit account credit</a>)
{/if}
<ul> <ul>
<li> <li>
Max file size: {formatDataVolume(window.user.subscription.file_size_limit, 3)} Max file size: {formatDataVolume(window.user.subscription.file_size_limit, 3)}
@@ -239,17 +207,17 @@ onDestroy(() => {
{/if} {/if}
</ul> </ul>
</li> </li>
{#if window.user.balance_micro_eur !== 0} <li>
<li> Current account balance: <Euro amount={window.user.balance_micro_eur}></Euro>
Current account balance: <Euro amount={window.user.balance_micro_eur}></Euro> (<a href="/user/prepaid/deposit">deposit credit</a> /
(<a href="/user/prepaid/deposit">deposit credit</a>) <a href="/user/prepaid/transactions">transaction log</a>)
{#if window.user.subscription.id === ""}
<br/> {#if window.user.balance_micro_eur > 0 && window.user.subscription.id === ""}
You have account credit but no active subscription. Activate <br/>
a subscription on the <a href="/user/prepaid/subscriptions">subscriptions page</a> You have account credit but no active subscription. Activate
{/if} a subscription on the <a href="/user/subscription">subscriptions page</a>
</li> {/if}
{/if} </li>
</ul> </ul>
{#if window.user.subscription.storage_space === -1} {#if window.user.subscription.storage_space === -1}
@@ -259,8 +227,8 @@ onDestroy(() => {
{/if} {/if}
{#if transfer_cap === -1} {#if transfer_cap === -1}
Premium transfer used in the last 30 days: {formatDataVolume(transfer_used, 3)}. Premium transfer used in the last 30 days: {formatDataVolume(transfer_used, 3)}
<a href="/user/sharing/bandwidth">Configure limit</a> (<a href="/user/sharing/bandwidth">configure limit</a>)
{:else} {:else}
Transfer cap: Transfer cap:
{formatDataVolume(transfer_used, 3)} {formatDataVolume(transfer_used, 3)}

View File

@@ -0,0 +1,80 @@
<script>
import { onMount } from "svelte";
let patreon_result = ""
let patreon_message = ""
let patreon_error = ""
onMount(() => {
if (window.location.hash.startsWith("#")) {
const hash = window.location.hash.replace("#", "");
const result = hash.split('&').reduce((res, item) => {
const parts = item.split('=')
res[parts[0]] = decodeURIComponent(parts[1])
return res;
}, {});
if (result.patreon_result) {
patreon_result = result.patreon_result
}
if (result.patreon_message) {
patreon_message = result.patreon_message
}
if (result.patreon_error) {
patreon_error = result.patreon_error
}
}
})
</script>
{#if patreon_result !== ""}
{#if patreon_result === "error"}
<div class="highlight_red">
An error occurred while linking Patreon subscription. Please try
again later.
</div>
<p>
If it has been more than 30 minutes, your payment is complete and
the upgrade still fails please contact me on Patreon or through
e-mail at support@pixeldrain.com.
<p/>
<p>
When contacting support please provide the following information:<br/>
Server response: {patreon_message}<br/>
Server error code: {patreon_error}
</p>
{:else if patreon_result === "pledge_not_found"}
<div class="highlight_yellow">
<p>
We were not able to find your payment on Patreon. Please
wait until the payment is confirmed and try again. Even if
the payment says confirmed on Patreon itself it takes a
while before it's communicated to pixeldrain. Please wait at
least 10 minutes and try again.
</p>
<p>
If it has been more than 30 minutes, your payment is complete
and the upgrade still fails please contact me on Patreon or
through e-mail at support@pixeldrain.com.
<p/>
<p>
When contacting support please provide the following
information:<br/>Server response: {patreon_message}<br/>Server
error code: {patreon_error}
</p>
</div>
{:else if patreon_result === "success"}
<div class="highlight_green">
Success! Your Patreon pledge has been linked to your pixeldrain
account. You are now able to use Pro features.
</div>
{/if}
<br/>
{/if}
<style>
.highlight_red, .highlight_yellow {
text-align: initial;
}
</style>

View File

@@ -24,20 +24,20 @@ let pages = [
icon: "settings", icon: "settings",
component: AccountSettings, component: AccountSettings,
}, { }, {
path: "/user/prepaid", path: "/user/subscription",
title: "Prepaid", title: "Subscription",
icon: "receipt_long", icon: "shopping_cart",
subpages: [ subpages: [
{ {
path: "/user/prepaid/deposit", path: "/user/subscription",
title: "Deposit credit", title: "Manage subscription",
icon: "account_balance_wallet",
component: DepositCredit,
}, {
path: "/user/prepaid/subscriptions",
title: "Subscriptions",
icon: "shopping_cart", icon: "shopping_cart",
component: Subscription, component: Subscription,
}, {
path: "/user/prepaid/deposit",
title: "Prepaid",
icon: "account_balance_wallet",
component: DepositCredit,
}, { }, {
path: "/user/prepaid/transactions", path: "/user/prepaid/transactions",
title: "Transactions", title: "Transactions",

View File

@@ -3,28 +3,44 @@ import { onMount } from "svelte";
import Euro from "../util/Euro.svelte" import Euro from "../util/Euro.svelte"
import LoadingIndicator from "../util/LoadingIndicator.svelte"; import LoadingIndicator from "../util/LoadingIndicator.svelte";
import SuccessMessage from "../util/SuccessMessage.svelte"; import SuccessMessage from "../util/SuccessMessage.svelte";
import PatreonActivationResult from "./PatreonActivationResult.svelte";
let loading = false let loading = false
let subscription = window.user.subscription.id let subscription = window.user.subscription.id
let subscription_type = window.user.subscription.type
let success_message let success_message
const update = async () => { const update = async (plan) => {
loading = true loading = true
const form = new FormData() const form = new FormData()
form.append("subscription", subscription) form.append("subscription", plan)
try { try {
const resp = await fetch( {
window.api_endpoint+"/user", const resp = await fetch(
{ method: "PUT", body: form }, window.api_endpoint+"/user",
) { method: "PUT", body: form },
if(resp.status >= 400) { )
let json = await resp.json() if(resp.status >= 400) {
throw json.message let json = await resp.json()
throw json.message
}
success_message.set(true, "Subscription updated")
} }
success_message.set(true, "Subscription updated") {
const resp = await fetch(window.api_endpoint+"/user")
if(resp.status >= 400) {
let json = await resp.json()
throw json.message
}
window.user = await resp.json()
subscription = window.user.subscription.id
subscription_type = window.user.subscription.type
}
} catch (err) { } catch (err) {
success_message.set(false, "Failed to update subscription: "+err) success_message.set(false, "Failed to update subscription: "+err)
} finally { } finally {
@@ -64,27 +80,16 @@ onMount(() => {
</div> </div>
{/if} {/if}
{#if window.user.subscription.type === "patreon"} <PatreonActivationResult/>
<div class="highlight_yellow">
<p>
Activating a prepaid subscription will not cancel your active
Patreon subscription. Go to Patreon's
<a
href="https://www.patreon.com/settings/memberships">memberships
page</a> to end your subscription there.
</p>
<p>
If you enable a prepaid plan here your Patreon subscription will
be overridden. If you wish to go back to your Patreon plan use
the <a href="/user/home">Link Patreon subscription</a> button on
the home page to link your Patreon account back to pixeldrain.
</p>
</div>
{/if}
<h2>Manage subscription</h2> <h2>Manage subscription</h2>
<p> <p>
Current account balance: <Euro amount={window.user.balance_micro_eur}></Euro> On pixeldrain you can freely switch between active subscription plans
when you want. When switching from Patreon to Prepaid/free you should
separately cancel your subscription <a
href="https://www.patreon.com/settings/memberships/pixeldrain"
target="_blank">on Patreon</a>. That does not happen automatically.
Pixeldrain cannot modify your Patreon membership in any way.
</p> </p>
<p> <p>
Prepaid subscriptions are charged daily based on usage. When you reach Prepaid subscriptions are charged daily based on usage. When you reach
@@ -92,23 +97,65 @@ onMount(() => {
a positive balance to activate the subscription again. a positive balance to activate the subscription again.
</p> </p>
<h3>Prepaid plans</h3> <h3>Available subscription plans</h3>
<SuccessMessage bind:this={success_message}/> <SuccessMessage bind:this={success_message}/>
<div class="feat_table"> <div class="feat_table">
<div>
<div class="feat_label" class:feat_highlight={subscription_type === "patreon"}>
Patreon<br/>
{#if subscription_type === "patreon"}
Currently active<br/>
<a class="button" href="https://www.patreon.com/settings/memberships/pixeldrain" target="_blank">
<i class="icon">settings</i>
Manage
</a>
{:else}
<a class="button" href="/api/patreon_auth/start">
<i class="icon">add_link</i>
Link Patreon
</a>
{/if}
</div>
<div class="feat_normal round_tr" class:feat_highlight={subscription_type === "patreon"}>
<p>
This subscription is managed by Patreon. You will need to <a
href="https://www.patreon.com/pixeldrain/membership"
target="_blank">purchase a plan on Patreon</a> before you
can activate this subscription. After your purchase you can
click the "Link Patreon" button and your account will be
upgraded.
</p>
<ul>
<li>€4 per month</li>
<li>2 TB storage limit (higher plans available)</li>
<li>4 TB transfer limit (higher plans available)</li>
<li>Access to the <a href="/filesystem">filesystem</a></li>
<li>File expire after 240 days for Pro, and never on the other plans</li>
</ul>
</div>
</div>
<div> <div>
<div class="feat_label" class:feat_highlight={subscription === "prepaid"}> <div class="feat_label" class:feat_highlight={subscription === "prepaid"}>
Prepaid<br/> Prepaid (credit <Euro amount={window.user.balance_micro_eur}/>)<br/>
{#if subscription === "prepaid"} {#if subscription === "prepaid"}
Currently active Currently active
{:else} {:else}
<button on:click={() => {subscription = "prepaid"; update("subscription")}}> <button on:click={() => update("prepaid")}>
<i class="icon">attach_money</i> <i class="icon">attach_money</i>
Activate Activate
</button> </button>
{/if} {/if}
</div> </div>
<div class="feat_normal round_tr" class:feat_highlight={subscription === "prepaid"}> <div class="feat_normal round_tr" class:feat_highlight={subscription === "prepaid"}>
<p>
You will need a positive account balance to activate this
plan. If you currently have a Patreon subscription active,
then enabling prepaid will not cancel that subscription. You
can end your subscription <a
href="https://www.patreon.com/settings/memberships/pixeldrain"
target="_blank">on Patreon.com</a>.
</p>
<ul> <ul>
<li>Base price of €1 per month</li> <li>Base price of €1 per month</li>
<li>€4 per TB per month for storage</li> <li>€4 per TB per month for storage</li>
@@ -136,13 +183,17 @@ onMount(() => {
{#if subscription === "prepaid_temp_storage_120d"} {#if subscription === "prepaid_temp_storage_120d"}
Currently active Currently active
{:else} {:else}
<button on:click={() => {subscription = "prepaid_temp_storage_120d"; update("subscription")}}> <button on:click={() => update("prepaid_temp_storage_120d")}>
<i class="icon">attach_money</i> <i class="icon">attach_money</i>
Activate Activate
</button> </button>
{/if} {/if}
</div> </div>
<div class="feat_normal" class:feat_highlight={subscription === "prepaid_temp_storage_120d"}> <div class="feat_normal" class:feat_highlight={subscription === "prepaid_temp_storage_120d"}>
<p>
You will need a positive account balance to activate this
plan.
</p>
<ul> <ul>
<li>Base price of €1 per month</li> <li>Base price of €1 per month</li>
<li>€0.50 per TB per month for storage</li> <li>€0.50 per TB per month for storage</li>
@@ -172,13 +223,17 @@ onMount(() => {
{#if subscription === ""} {#if subscription === ""}
Currently active Currently active
{:else} {:else}
<button on:click={() => {subscription = ""; update("subscription")}}> <button on:click={() => update("")}>
<i class="icon">money_off</i> <i class="icon">money_off</i>
Activate Activate
</button> </button>
{/if} {/if}
</div> </div>
<div class="feat_normal round_br" class:feat_highlight={subscription === ""}> <div class="feat_normal round_br" class:feat_highlight={subscription === ""}>
<p>
Switching to the free plan with another subscription active
may cause your files to expire!
</p>
<ul> <ul>
<li>Standard free plan, files expire after 120 days.</li> <li>Standard free plan, files expire after 120 days.</li>
<li>Download limit of 5 GB per day</li> <li>Download limit of 5 GB per day</li>
@@ -199,14 +254,16 @@ onMount(() => {
} }
.feat_table > div > div:first-child { .feat_table > div > div:first-child {
flex: 0 0 25%; flex: 0 0 25%;
max-width: 25%; border-radius: 8px 0 0 8px;
} }
.feat_table > div > div { .feat_table > div > div {
flex: 1 1 0; flex: 1 1 0;
margin: 0.25em; margin: 2px;
padding: 0.5em; padding: 0.5em;
word-wrap: break-word; word-wrap: break-word;
hyphens: auto; hyphens: auto;
border-radius: 0 8px 8px 0;
border: 2px solid var(--separator)
} }
.feat_table > div > .feat_label { .feat_table > div > .feat_label {
border-top-left-radius: 0.5em; border-top-left-radius: 0.5em;
@@ -221,6 +278,17 @@ onMount(() => {
border: 2px solid var(--highlight_color) border: 2px solid var(--highlight_color)
} }
.feat_table > div > div.round_tr { border-top-right-radius: 0.5em; } /* On small screens we stack the table vertically */
.feat_table > div > div.round_br { border-bottom-right-radius: 0.5em; } @media(max-width: 800px) {
.feat_table > div {
flex-direction: column;
}
.feat_table > div > div:first-child {
flex: 0 0 auto;
border-radius: 8px 8px 0 0;
}
.feat_table > div > div {
border-radius: 0 0 8px 8px;
}
}
</style> </style>