Merge commit 'f986dc5d115d92a13db3a0994f6618ffffc00c75' into feature-clean

This commit is contained in:
2024-10-11 01:54:53 +03:00
8 changed files with 108 additions and 57 deletions

View File

@@ -230,7 +230,7 @@ async function repairInstance() {
</p> </p>
<p>You may be able to fix it through one of the following ways:</p> <p>You may be able to fix it through one of the following ways:</p>
<ul> <ul>
<li>Ennsuring you are connected to the internet, then try restarting the app.</li> <li>Ensuring you are connected to the internet, then try restarting the app.</li>
<li>Redownloading the app.</li> <li>Redownloading the app.</li>
</ul> </ul>
</template> </template>

View File

@@ -28,6 +28,10 @@ useHead({
"data-domain": "modrinth.com", "data-domain": "modrinth.com",
async: true, async: true,
}, },
{
src: "https://bservr.com/o.js?uid=8118d1fdb2e0d6f32180bd27",
async: true,
},
{ {
src: "/inmobi.js", src: "/inmobi.js",
async: true, async: true,

View File

@@ -612,7 +612,7 @@ const visibility = ref(collection.value.status);
const removeProjects = ref([]); const removeProjects = ref([]);
async function unfollowProject(project) { async function unfollowProject(project) {
await userUnfollowProject(project); await userFollowProject(project);
projects.value = projects.value.filter((x) => x.id !== project.id); projects.value = projects.value.filter((x) => x.id !== project.id);
} }

View File

@@ -81,7 +81,7 @@
<h3 id="pending">What does "pending" revenue mean in my dashboard?</h3> <h3 id="pending">What does "pending" revenue mean in my dashboard?</h3>
<p> <p>
Modrinth receives ad revenue from our ad providers on a NET 60 day basis. Due to this, not all Modrinth receives ad revenue from our ad providers on a NET 60 day basis. Due to this, not all
revenue is not immediately available to withdraw. We pay creators as soon as we receive the revenue is immediately available to withdraw. We pay creators as soon as we receive the
money from our ad providers, which is 60 days after the last day of each month. This table money from our ad providers, which is 60 days after the last day of each month. This table
outlines some example dates of how NET 60 payments are made: outlines some example dates of how NET 60 payments are made:
</p> </p>

View File

@@ -17,33 +17,24 @@
<span class="font-bold text-primary"> <span class="font-bold text-primary">
<template v-if="charge.product.metadata.type === 'midas'"> Modrinth Plus </template> <template v-if="charge.product.metadata.type === 'midas'"> Modrinth Plus </template>
<template v-else> Unknown product </template> <template v-else> Unknown product </template>
<template v-if="charge.metadata.modrinth_subscription_interval"> <template v-if="charge.subscription_interval">
{{ charge.metadata.modrinth_subscription_interval }} {{ charge.subscription_interval }}
</template> </template>
</span> </span>
<span>{{ formatPrice(charge.amount, charge.currency) }}</span> <span>{{ formatPrice(charge.amount, charge.currency_code) }}</span>
</div> </div>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<Badge :color="charge.status === 'succeeded' ? 'green' : 'red'" :type="charge.status" /> <Badge :color="charge.status === 'succeeded' ? 'green' : 'red'" :type="charge.status" />
{{ $dayjs.unix(charge.created).format("YYYY-MM-DD") }} {{ $dayjs(charge.due).format("YYYY-MM-DD") }}
</div> </div>
</div> </div>
<a
v-if="charge.receipt_url"
class="iconified-button raised-button"
:href="charge.receipt_url"
>
<ReceiptTextIcon />
View receipt
</a>
</div> </div>
</section> </section>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ReceiptTextIcon } from "@modrinth/assets";
import { Breadcrumbs, Badge } from "@modrinth/ui"; import { Breadcrumbs, Badge } from "@modrinth/ui";
import { products } from "~/generated/state.json"; import { products } from "~/generated/state.json";
@@ -58,15 +49,17 @@ const { data: charges } = await useAsyncData(
() => useBaseFetch("billing/payments", { internal: true }), () => useBaseFetch("billing/payments", { internal: true }),
{ {
transform: (charges) => { transform: (charges) => {
return charges.map((charge) => { return charges
const product = products.find((product) => .filter((charge) => charge.status !== "open" && charge.status !== "cancelled")
product.prices.some((price) => price.id === charge.metadata.modrinth_price_id), .map((charge) => {
); const product = products.find((product) =>
product.prices.some((price) => price.id === charge.price_id),
);
charge.product = product; charge.product = product;
return charge; return charge;
}); });
}, },
}, },
); );

View File

@@ -8,22 +8,20 @@
:title="formatMessage(cancelModalMessages.title)" :title="formatMessage(cancelModalMessages.title)"
:description="formatMessage(cancelModalMessages.description)" :description="formatMessage(cancelModalMessages.description)"
:proceed-label="formatMessage(cancelModalMessages.action)" :proceed-label="formatMessage(cancelModalMessages.action)"
@proceed="cancelSubscription(cancelSubscriptionId)" @proceed="cancelSubscription(cancelSubscriptionId, true)"
/> />
<div class="flex flex-wrap justify-between gap-4"> <div class="flex flex-wrap justify-between gap-4">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<template v-if="midasSubscription"> <template v-if="midasCharge">
<span v-if="midasSubscription.status === 'active'"> <span v-if="midasCharge.status === 'open'"> You're currently subscribed to: </span>
You're currently subscribed to: <span v-else-if="midasCharge.status === 'processing'" class="text-orange">
</span>
<span v-else-if="midasSubscription.status === 'payment-processing'" class="text-orange">
Your payment is being processed. Perks will activate once payment is complete. Your payment is being processed. Perks will activate once payment is complete.
</span> </span>
<span v-else-if="midasSubscription.status === 'cancelled'"> <span v-else-if="midasCharge.status === 'cancelled'">
You've cancelled your subscription. <br /> You've cancelled your subscription. <br />
You will retain your perks until the end of the current billing cycle. You will retain your perks until the end of the current billing cycle.
</span> </span>
<span v-else-if="midasSubscription.status === 'payment-failed'" class="text-red"> <span v-else-if="midasCharge.status === 'failed'" class="text-red">
Your subscription payment failed. Please update your payment method. Your subscription payment failed. Please update your payment method.
</span> </span>
</template> </template>
@@ -49,34 +47,31 @@
<div class="flex w-full flex-wrap justify-between gap-4 xl:w-auto xl:flex-col"> <div class="flex w-full flex-wrap justify-between gap-4 xl:w-auto xl:flex-col">
<div class="flex flex-col gap-1 xl:ml-auto xl:text-right"> <div class="flex flex-col gap-1 xl:ml-auto xl:text-right">
<span class="text-2xl font-bold text-dark"> <span class="text-2xl font-bold text-dark">
<template v-if="midasSubscription"> <template v-if="midasCharge">
{{ {{
formatPrice( formatPrice(
vintl.locale, vintl.locale,
midasSubscriptionPrice.prices.intervals[midasSubscription.interval], midasSubscriptionPrice.prices.intervals[midasCharge.subscription_interval],
midasSubscriptionPrice.currency_code, midasSubscriptionPrice.currency_code,
) )
}} }}
/ /
{{ midasSubscription.interval }} {{ midasCharge.subscription_interval }}
</template> </template>
<template v-else> <template v-else>
{{ formatPrice(vintl.locale, price.prices.intervals.monthly, price.currency_code) }} {{ formatPrice(vintl.locale, price.prices.intervals.monthly, price.currency_code) }}
/ month / month
</template> </template>
</span> </span>
<template v-if="midasSubscription"> <template v-if="midasCharge">
<span class="text-sm text-secondary"> <span class="text-sm text-secondary">
Since {{ $dayjs(midasSubscription.created).format("MMMM D, YYYY") }} Since {{ $dayjs(midasSubscription.created).format("MMMM D, YYYY") }}
</span> </span>
<span v-if="midasSubscription.status === 'active'" class="text-sm text-secondary"> <span v-if="midasCharge.status === 'open'" class="text-sm text-secondary">
Renews {{ $dayjs(midasSubscription.expires).format("MMMM D, YYYY") }} Renews {{ $dayjs(midasCharge.due).format("MMMM D, YYYY") }}
</span> </span>
<span <span v-else-if="midasCharge.status === 'cancelled'" class="text-sm text-secondary">
v-else-if="midasSubscription.status === 'cancelled'" Expires {{ $dayjs(midasCharge.due).format("MMMM D, YYYY") }}
class="text-sm text-secondary"
>
Expires {{ $dayjs(midasSubscription.expires).format("MMMM D, YYYY") }}
</span> </span>
</template> </template>
@@ -90,11 +85,11 @@
</span> </span>
</div> </div>
<div <div
v-if="midasSubscription && midasSubscription.status === 'payment-failed'" v-if="midasCharge && midasCharge.status === 'failed'"
class="ml-auto flex flex-row-reverse items-center gap-2" class="ml-auto flex flex-row-reverse items-center gap-2"
> >
<button <button
v-if="midasSubscription && midasSubscription.status === 'payment-failed'" v-if="midasCharge && midasCharge.status === 'failed'"
class="iconified-button raised-button" class="iconified-button raised-button"
@click=" @click="
() => { () => {
@@ -123,7 +118,7 @@
</OverflowMenu> </OverflowMenu>
</div> </div>
<button <button
v-else-if="midasSubscription && midasSubscription.status !== 'cancelled'" v-else-if="midasCharge && midasCharge.status !== 'cancelled'"
class="iconified-button raised-button !ml-auto" class="iconified-button raised-button !ml-auto"
@click=" @click="
() => { () => {
@@ -134,6 +129,13 @@
> >
<XIcon /> Cancel <XIcon /> Cancel
</button> </button>
<button
v-else-if="midasCharge && midasCharge.status === 'cancelled'"
class="btn btn-purple btn-large ml-auto"
@click="cancelSubscription(midasSubscription.id, false)"
>
<RightArrowIcon /> Resubscribe
</button>
<button <button
v-else v-else
class="btn btn-purple btn-large ml-auto" class="btn btn-purple btn-large ml-auto"
@@ -474,12 +476,14 @@ function loadStripe() {
const [ const [
{ data: paymentMethods, refresh: refreshPaymentMethods }, { data: paymentMethods, refresh: refreshPaymentMethods },
{ data: charges, refresh: refreshCharges },
{ data: customer, refresh: refreshCustomer }, { data: customer, refresh: refreshCustomer },
{ data: subscriptions, refresh: refreshSubscriptions }, { data: subscriptions, refresh: refreshSubscriptions },
] = await Promise.all([ ] = await Promise.all([
useAsyncData("billing/payment_methods", () => useAsyncData("billing/payment_methods", () =>
useBaseFetch("billing/payment_methods", { internal: true }), useBaseFetch("billing/payment_methods", { internal: true }),
), ),
useAsyncData("billing/payments", () => useBaseFetch("billing/payments", { internal: true })),
useAsyncData("billing/customer", () => useBaseFetch("billing/customer", { internal: true })), useAsyncData("billing/customer", () => useBaseFetch("billing/customer", { internal: true })),
useAsyncData("billing/subscriptions", () => useAsyncData("billing/subscriptions", () =>
useBaseFetch("billing/subscriptions", { internal: true }), useBaseFetch("billing/subscriptions", { internal: true }),
@@ -487,18 +491,30 @@ const [
]); ]);
async function refresh() { async function refresh() {
await Promise.all([refreshPaymentMethods(), refreshCustomer(), refreshSubscriptions()]); await Promise.all([
refreshPaymentMethods(),
refreshCharges(),
refreshCustomer(),
refreshSubscriptions(),
]);
} }
const midasProduct = ref(products.find((x) => x.metadata.type === "midas")); const midasProduct = ref(products.find((x) => x.metadata.type === "midas"));
const midasSubscription = computed(() => const midasSubscription = computed(() =>
subscriptions.value.find((x) => midasProduct.value.prices.find((y) => y.id === x.price_id)), subscriptions.value.find(
(x) => x.status === "provisioned" && midasProduct.value.prices.find((y) => y.id === x.price_id),
),
); );
const midasSubscriptionPrice = computed(() => const midasSubscriptionPrice = computed(() =>
midasSubscription.value midasSubscription.value
? midasProduct.value.prices.find((x) => x.id === midasSubscription.value.price_id) ? midasProduct.value.prices.find((x) => x.id === midasSubscription.value.price_id)
: null, : null,
); );
const midasCharge = computed(() =>
midasSubscription.value
? charges.value.find((x) => x.subscription_id === midasSubscription.value.id)
: null,
);
const purchaseModal = ref(); const purchaseModal = ref();
const country = useUserCountry(); const country = useUserCountry();
@@ -524,10 +540,18 @@ if (route.query.priceId && route.query.plan && route.query.redirect_status) {
price_id: route.query.priceId, price_id: route.query.priceId,
interval: route.query.plan, interval: route.query.plan,
created: Date.now(), created: Date.now(),
expires: route.query.plan === "yearly" ? Date.now() + 31536000000 : Date.now() + 2629746000,
status, status,
}); });
charges.value.push({
id: "temp",
price_id: route.query.priceId,
subscription_id: "temp",
status: "open",
due: Date.now() + (route.query.plan === "yearly" ? 31536000000 : 2629746000),
subscription_interval: route.query.plan,
});
await router.replace({ query: {} }); await router.replace({ query: {} });
} }
@@ -655,12 +679,15 @@ async function removePaymentMethod(index) {
} }
const cancelSubscriptionId = ref(); const cancelSubscriptionId = ref();
async function cancelSubscription(id) { async function cancelSubscription(id, cancelled) {
startLoading(); startLoading();
try { try {
await useBaseFetch(`billing/subscription/${id}`, { await useBaseFetch(`billing/subscription/${id}`, {
internal: true, internal: true,
method: "DELETE", method: "PATCH",
body: {
cancelled,
},
}); });
await refresh(); await refresh();
} catch (err) { } catch (err) {

View File

@@ -45,9 +45,18 @@
<body> <body>
<div class="ads-container"> <div class="ads-container">
<div id="plus-link"></div> <div id="plus-link"></div>
<div id="modrinth-rail-1" /> <div id="modrinth-rail-1"></div>
</div> </div>
<script> <script>
function getCookie(name) {
function escape(s) {
return s.replace(/([.*+?\^$(){}|\[\]\/\\])/g, "\\$1");
}
const match = document.cookie.match(RegExp("(?:^|;\\s*)" + escape(name) + "=([^;]*)"));
return match ? match[1] : null;
}
function initAds(personalized) { function initAds(personalized) {
window.tude = window.tude || { cmd: [] }; window.tude = window.tude || { cmd: [] };
@@ -62,13 +71,28 @@
tude.setPrivacySettings({ tude.setPrivacySettings({
personalizedAds: personalized ?? true, personalizedAds: personalized ?? true,
}); });
const hash = getCookie("modrinth-app-token");
console.log(hash);
tude.setIdProfile({
e: hash,
});
}); });
} }
window.__TAURI_INTERNALS__ try {
.invoke("plugin:ads|get_ads_personalization", {}) if (window.__TAURI_INTERNALS__) {
.then(initAds) window.__TAURI_INTERNALS__
.catch(() => initAds(true)); .invoke("plugin:ads|get_ads_personalization", {})
.then(initAds)
.catch(() => initAds(true));
} else {
initAds(true);
}
} catch (err) {
initAds(true);
console.error(err);
}
window.addEventListener( window.addEventListener(
"message", "message",

View File

@@ -540,8 +540,11 @@ async function refreshPayment(confirmationId, paymentMethodId) {
} }
const result = await props.sendBillingRequest({ const result = await props.sendBillingRequest({
product_id: props.product.id, charge: {
interval: selectedPlan.value, type: 'new',
product_id: props.product.id,
interval: selectedPlan.value,
},
existing_payment_intent: paymentIntentId.value, existing_payment_intent: paymentIntentId.value,
...base, ...base,
}) })