Skip to content

feat: medal promotion on servers page #4117

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

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions apps/frontend/src/assets/images/servers/medal_server_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions apps/frontend/src/assets/styles/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ html {

--landing-raw-bg: #fff;

--medal-promotion-bg: #fff;
--medal-promotion-bg-orange: #c08a3a;
--medal-promotion-text-orange: #a86200;
--medal-promotion-bg-gradient: linear-gradient(90deg, rgba(255, 184, 75, 0.15) 0%, #fff 100%);

--banner-error-bg: #fee2e2;
--banner-error-text: #991b1b;
--banner-error-border: #ef4444;
Expand Down Expand Up @@ -301,6 +306,11 @@ html {

--landing-raw-bg: #000;

--medal-promotion-bg: #000;
--medal-promotion-bg-orange: #ffb84b54;
--medal-promotion-text-orange: #ffb84b;
--medal-promotion-bg-gradient: linear-gradient(90deg, #ffb74b21, transparent 50%, #000 100%);

--hover-filter: brightness(120%);
--active-filter: brightness(140%);

Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/src/components/ui/servers/ServerIcon.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div
class="experimental-styles-within flex size-24 shrink-0 overflow-hidden rounded-xl border-[1px] border-solid border-button-border bg-button-bg shadow-sm"
class="experimental-styles-within flex size-16 shrink-0 overflow-hidden rounded-xl border-[1px] border-solid border-button-border bg-button-bg shadow-sm"
>
<client-only>
<img
Expand Down
28 changes: 10 additions & 18 deletions apps/frontend/src/components/ui/servers/ServerListing.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,19 @@
:to="status === 'suspended' ? '' : `/servers/manage/${props.server_id}`"
>
<div
v-tooltip="
status === 'suspended'
? suspension_reason === 'upgrading'
? 'This server is being transferred to a new node. It will be unavailable until this process finishes.'
: 'This server has been suspended. Please visit your billing settings or contact Modrinth Support for more information.'
: ''
"
class="flex cursor-pointer flex-row items-center overflow-x-hidden rounded-3xl bg-bg-raised p-4 transition-transform duration-100"
:class="status === 'suspended' ? '!rounded-b-none opacity-75' : 'active:scale-95'"
class="flex flex-row items-center overflow-x-hidden rounded-2xl border-[1px] border-solid border-button-bg bg-bg-raised p-4 transition-transform duration-100"
:class="status === 'suspended' ? '!rounded-b-none border-b-0 opacity-75' : 'active:scale-95'"
data-pyro-server-listing
:data-pyro-server-listing-id="server_id"
>
<UiServersServerIcon v-if="status !== 'suspended'" :image="image" />
<div
v-else
class="bg-bg-secondary flex size-24 items-center justify-center rounded-xl border-[1px] border-solid border-button-border bg-button-bg shadow-sm"
class="bg-bg-secondary flex size-16 items-center justify-center rounded-xl border-[1px] border-solid border-button-border bg-button-bg shadow-sm"
>
<LockIcon class="size-20 text-secondary" />
<LockIcon class="size-12 text-secondary" />
</div>
<div class="ml-8 flex flex-col gap-2.5">
<div class="ml-4 flex flex-col gap-2.5">
<div class="flex flex-row items-center gap-2">
<h2 class="m-0 text-xl font-bold text-contrast">{{ name }}</h2>
<ChevronRightIcon />
Expand All @@ -41,7 +34,6 @@
/>
Using {{ projectData?.title || "Unknown" }}
</div>
<div v-else class="min-h-[20px]"></div>

<div
v-if="isConfiguring"
Expand All @@ -61,14 +53,14 @@
</div>
<div
v-if="status === 'suspended' && suspension_reason === 'upgrading'"
class="relative -mt-4 flex w-full flex-row items-center gap-2 rounded-b-3xl bg-bg-blue p-4 text-sm font-bold text-contrast"
class="relative -mt-4 flex w-full flex-row items-center gap-2 rounded-b-2xl border-[1px] border-t-0 border-solid border-bg-blue bg-bg-blue p-4 text-sm font-bold text-contrast"
>
<UiServersPanelSpinner />
Your server's hardware is currently being upgraded and will be back online shortly.
</div>
<div
v-else-if="status === 'suspended' && suspension_reason === 'cancelled'"
class="relative -mt-4 flex w-full flex-col gap-2 rounded-b-3xl bg-bg-red p-4 text-sm font-bold text-contrast"
class="relative -mt-4 flex w-full flex-col gap-2 rounded-b-2xl border-[1px] border-t-0 border-solid border-bg-red bg-bg-red p-4 text-sm font-bold text-contrast"
>
<div class="flex flex-row gap-2">
<UiServersIconsPanelErrorIcon class="!size-5" /> Your server has been cancelled. Please
Expand All @@ -78,7 +70,7 @@
</div>
<div
v-else-if="status === 'suspended' && suspension_reason"
class="relative -mt-4 flex w-full flex-col gap-2 rounded-b-3xl bg-bg-red p-4 text-sm font-bold text-contrast"
class="relative -mt-4 flex w-full flex-col gap-2 rounded-b-2xl border-[1px] border-t-0 border-solid border-bg-red bg-bg-red p-4 text-sm font-bold text-contrast"
>
<div class="flex flex-row gap-2">
<UiServersIconsPanelErrorIcon class="!size-5" /> Your server has been suspended:
Expand All @@ -89,7 +81,7 @@
</div>
<div
v-else-if="status === 'suspended'"
class="relative -mt-4 flex w-full flex-col gap-2 rounded-b-3xl bg-bg-red p-4 text-sm font-bold text-contrast"
class="relative -mt-4 flex w-full flex-col gap-2 rounded-b-2xl border-[1px] border-t-0 border-solid border-bg-red bg-bg-red p-4 text-sm font-bold text-contrast"
>
<div class="flex flex-row gap-2">
<UiServersIconsPanelErrorIcon class="!size-5" /> Your server has been suspended. Please
Expand All @@ -103,8 +95,8 @@
<script setup lang="ts">
import { ChevronRightIcon, LockIcon, SparklesIcon } from "@modrinth/assets";
import type { Project, Server } from "@modrinth/utils";
import { useModrinthServers } from "~/composables/servers/modrinth-servers.ts";
import { Avatar, CopyCode } from "@modrinth/ui";
import { useModrinthServers } from "~/composables/servers/modrinth-servers.ts";

const props = defineProps<Partial<Server>>();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<template>
<div
id="medal"
class="medal-promotion flex w-full flex-row justify-between rounded-xl p-6 shadow-xl"
>
<div class="overlay"></div>
<MedalPromoBackground class="background-pattern shadow-xl" />
<div class="z-10 flex flex-col gap-2">
<div class="flex items-center gap-2 text-2xl font-semibold text-contrast">
<ClockIcon class="clock-glow text-medal-orange size-6" /><span>
Try a free
<span class="text-medal-orange">3GB server</span> for 5 days powered by
<span class="text-medal-orange">Medal</span>
</span>
</div>
<div class="flex items-center">
<span class="text-sm text-secondary"
>Limited-time offer. No credit card required. Available for US servers.</span
>
</div>
</div>
<ButtonStyled color="orange" type="outlined" size="large">
<nuxt-link to="https://medal.tv/" class="z-10 my-auto">Learn more <ExternalIcon /></nuxt-link>
</ButtonStyled>
</div>
</template>

<script lang="ts" setup>
import { ClockIcon, ExternalIcon } from "@modrinth/assets";
import { ButtonStyled } from "@modrinth/ui";
import MedalPromoBackground from "~/assets/images/illustrations/medal_promo_background.svg?component";
</script>

<style scoped lang="scss">
.medal-promotion {
position: relative;
border: 1px solid var(--medal-promotion-bg-orange);
overflow: hidden;
}

.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--medal-promotion-bg-gradient);
z-index: 1;
border-radius: inherit;
}

.background-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
background-color: var(--medal-promotion-bg);
border-radius: inherit;
color: var(--color-orange);
}

.clock-glow {
filter: drop-shadow(0 0 72px var(--color-orange)) drop-shadow(0 0 36px var(--color-orange))
drop-shadow(0 0 18px var(--color-orange));
}

.text-medal-orange {
color: var(--medal-promotion-text-orange);
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<template>
<div
class="medal-promotion flex w-full flex-row items-center justify-between rounded-2xl p-4 shadow-xl"
>
<div class="overlay"></div>
<MedalPromoBackground class="background-pattern scale-[125%]" />

<div class="z-10 flex flex-col gap-1">
<div class="flex items-center gap-2 text-lg font-semibold text-contrast">
<ClockIcon class="clock-glow text-medal-orange size-5" />
<span>
Your <span class="text-medal-orange">Medal</span> powered Modrinth Server will expire in
<span class="text-medal-orange font-bold">{{ timeLeftCountdown.days }}</span> days
<span class="text-medal-orange font-bold">{{ timeLeftCountdown.hours }}</span> hours
<span class="text-medal-orange font-bold">{{ timeLeftCountdown.minutes }}</span> minutes
<span class="text-medal-orange font-bold">{{ timeLeftCountdown.seconds }}</span> seconds.
</span>
</div>
</div>

<ButtonStyled color="orange" type="outlined" size="large">
<button class="z-10 my-auto" @click="handleUpgrade"><RocketIcon /> Upgrade</button>
</ButtonStyled>
</div>
</template>

<script setup lang="ts">
import { ClockIcon, RocketIcon } from "@modrinth/assets";
import { ButtonStyled } from "@modrinth/ui";
import dayjs from "dayjs";
import dayjsDuration from "dayjs/plugin/duration";
import MedalPromoBackground from "~/assets/images/illustrations/medal_promo_background.svg?component";

// eslint-disable-next-line import/no-named-as-default-member
dayjs.extend(dayjsDuration);

const props = defineProps<{
expiryDate?: string | Date;
}>();

const expiryDate = computed(() => {
if (props.expiryDate) {
return dayjs(props.expiryDate);
}
return dayjs().add(5, "day");
});

const timeLeftCountdown = ref({ days: 0, hours: 0, minutes: 0, seconds: 0 });

function handleUpgrade(event: Event) {
event.stopPropagation();
// TODO: Upgrade logic
}

function updateCountdown() {
const now = dayjs();
const diff = expiryDate.value.diff(now);

if (diff <= 0) {
timeLeftCountdown.value = { days: 0, hours: 0, minutes: 0, seconds: 0 };
return;
}

const duration = dayjs.duration(diff);
timeLeftCountdown.value = {
days: duration.days(),
hours: duration.hours(),
minutes: duration.minutes(),
seconds: duration.seconds(),
};
}

updateCountdown();

const intervalId = ref<NodeJS.Timeout | null>(null);
onMounted(() => {
intervalId.value = setInterval(updateCountdown, 1000);
});

onUnmounted(() => {
if (intervalId.value) clearInterval(intervalId.value);
});
</script>

<style scoped lang="scss">
.medal-promotion {
position: relative;
border: 1px solid var(--medal-promotion-bg-orange);
background: inherit; // allows overlay + pattern to take over
overflow: hidden;
}

.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--medal-promotion-bg-gradient);
z-index: 1;
border-radius: inherit;
}

.background-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
background-color: var(--medal-promotion-bg);
border-radius: inherit;
color: var(--medal-promotion-text-orange);
}

.clock-glow {
filter: drop-shadow(0 0 72px var(--color-orange)) drop-shadow(0 0 36px var(--color-orange))
drop-shadow(0 0 18px var(--color-orange));
}

.text-medal-orange {
color: var(--medal-promotion-text-orange);
font-weight: bold;
}
</style>
Loading