forked from didirus/AstralRinth
feat: tax form download stage (#4513)
* feat: start on fix * fix: withdraw btn * fix: lint issues * feat: start on download stage for tax form modal * fix: use button rather than span * fix: lint * fix: lint issues * feat: tax form notification email for users who didnt get chance to download * feat: finish download stage for tax modal * fix: lint & i18n * fix: lint + svg cleanup --------- Signed-off-by: Calum H. <contact@cal.engineer> Co-authored-by: --global <--global>
This commit is contained in:
@@ -1,89 +1,161 @@
|
||||
<template>
|
||||
<NewModal ref="taxFormModal" :header="formatMessage(messages.taxFormHeader)">
|
||||
<div class="w-full sm:w-[540px]">
|
||||
<Admonition type="info" :header="formatMessage(messages.securityHeader)">
|
||||
<IntlFormatted :message-id="messages.securityDescription">
|
||||
<template #security-link="{ children }">
|
||||
<a
|
||||
href="https://www.track1099.com/info/security"
|
||||
class="flex w-fit flex-row gap-1 align-middle text-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<component :is="() => normalizeChildren(children)" />
|
||||
<ExternalIcon class="my-auto" />
|
||||
</a>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</Admonition>
|
||||
<div class="mt-4 flex flex-col gap-2">
|
||||
<label>
|
||||
<span class="text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.usCitizenQuestion) }}
|
||||
<span class="text-brand-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<Chips
|
||||
v-model="isUSCitizen"
|
||||
:items="['yes', 'no']"
|
||||
:format-label="
|
||||
(item) => (item === 'yes' ? formatMessage(messages.yes) : formatMessage(messages.no))
|
||||
"
|
||||
:never-empty="false"
|
||||
:capitalize="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-300 ease-in-out"
|
||||
enter-from-class="h-0 overflow-hidden opacity-0"
|
||||
enter-to-class="h-auto overflow-visible opacity-100"
|
||||
leave-active-class="transition-all duration-300 ease-in-out"
|
||||
leave-from-class="h-auto overflow-visible opacity-100"
|
||||
leave-to-class="h-0 overflow-hidden opacity-0"
|
||||
>
|
||||
<div v-if="isUSCitizen === 'no'" class="flex flex-col gap-1">
|
||||
<label class="mt-4">
|
||||
<NewModal
|
||||
ref="taxFormModal"
|
||||
:header="formatMessage(messages.taxFormHeader)"
|
||||
:hide-header="currentStage === 'download-confirmation'"
|
||||
:close-on-click-outside="currentStage !== 'download-confirmation'"
|
||||
:close-on-esc="currentStage !== 'download-confirmation'"
|
||||
>
|
||||
<div
|
||||
class="w-full"
|
||||
:class="[currentStage === 'form-selection' ? 'sm:w-[540px]' : 'sm:w-[400px]']"
|
||||
>
|
||||
<div v-if="currentStage === 'form-selection'">
|
||||
<Admonition type="info" :header="formatMessage(messages.securityHeader)">
|
||||
<IntlFormatted :message-id="messages.securityDescription">
|
||||
<template #security-link="{ children }">
|
||||
<a
|
||||
href="https://www.track1099.com/info/security"
|
||||
class="flex w-fit flex-row gap-1 align-middle text-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<component :is="() => normalizeChildren(children)" />
|
||||
<ExternalIcon class="my-auto" />
|
||||
</a>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</Admonition>
|
||||
<div class="mt-4 flex flex-col gap-2">
|
||||
<label>
|
||||
<span class="text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.entityQuestion) }}
|
||||
{{ formatMessage(messages.usCitizenQuestion) }}
|
||||
<span class="text-brand-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<Chips
|
||||
v-model="entityType"
|
||||
:items="['private-individual', 'foreign-entity']"
|
||||
v-model="isUSCitizen"
|
||||
:items="['yes', 'no']"
|
||||
:format-label="
|
||||
(item) =>
|
||||
item === 'private-individual'
|
||||
? formatMessage(messages.privateIndividual)
|
||||
: formatMessage(messages.foreignEntity)
|
||||
(item) => (item === 'yes' ? formatMessage(messages.yes) : formatMessage(messages.no))
|
||||
"
|
||||
:never-empty="false"
|
||||
:capitalize="false"
|
||||
class="mt-2"
|
||||
:capitalize="true"
|
||||
/>
|
||||
<span class="text-md mt-2 leading-tight">
|
||||
{{ formatMessage(messages.entityDescription) }}
|
||||
</span>
|
||||
</div>
|
||||
</Transition>
|
||||
<div class="mt-4 flex justify-end gap-3">
|
||||
<ButtonStyled @click="handleCancel">
|
||||
<button><XIcon /> {{ formatMessage(messages.cancel) }}</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button :disabled="!canContinue || loading" @click="continueForm">
|
||||
{{ formatMessage(messages.continue) }}
|
||||
<RightArrowIcon v-if="!loading" /> <SpinnerIcon v-else class="animate-spin" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-300 ease-in-out"
|
||||
enter-from-class="h-0 overflow-hidden opacity-0"
|
||||
enter-to-class="h-auto overflow-visible opacity-100"
|
||||
leave-active-class="transition-all duration-300 ease-in-out"
|
||||
leave-from-class="h-auto overflow-visible opacity-100"
|
||||
leave-to-class="h-0 overflow-hidden opacity-0"
|
||||
>
|
||||
<div v-if="isUSCitizen === 'no'" class="flex flex-col gap-1">
|
||||
<label class="mt-4">
|
||||
<span class="text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.entityQuestion) }}
|
||||
<span class="text-brand-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<Chips
|
||||
v-model="entityType"
|
||||
:items="['private-individual', 'foreign-entity']"
|
||||
:format-label="
|
||||
(item) =>
|
||||
item === 'private-individual'
|
||||
? formatMessage(messages.privateIndividual)
|
||||
: formatMessage(messages.foreignEntity)
|
||||
"
|
||||
:never-empty="false"
|
||||
:capitalize="false"
|
||||
class="mt-2"
|
||||
/>
|
||||
<span class="text-md mt-2 leading-tight">
|
||||
{{ formatMessage(messages.entityDescription) }}
|
||||
</span>
|
||||
</div>
|
||||
</Transition>
|
||||
<div class="mt-4 flex justify-end gap-3">
|
||||
<ButtonStyled @click="handleCancel">
|
||||
<button><XIcon /> {{ formatMessage(messages.cancel) }}</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button :disabled="!canContinue || loading" @click="continueForm">
|
||||
{{ formatMessage(messages.continue) }}
|
||||
<RightArrowIcon v-if="!loading" />
|
||||
<SpinnerIcon v-else class="animate-spin" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="currentStage === 'download-confirmation'" class="flex flex-col gap-6">
|
||||
<div class="relative block h-[180px] w-[400px] overflow-hidden rounded-xl rounded-b-none">
|
||||
<div
|
||||
class="absolute inset-0 rounded-xl rounded-b-none bg-gradient-to-r from-brand-green to-brand-blue"
|
||||
></div>
|
||||
<div
|
||||
class="absolute inset-0 rounded-xl rounded-b-none"
|
||||
style="
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(39, 41, 46, 0.15) 0%,
|
||||
var(--color-raised-bg) 100%
|
||||
);
|
||||
"
|
||||
></div>
|
||||
<BrowserWindowSuccessIllustration
|
||||
class="absolute left-[90px] top-[48px] h-[140px] w-[220px]"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="text-2xl font-semibold text-contrast">{{
|
||||
formatMessage(messages.confirmationTitle)
|
||||
}}</span>
|
||||
<span>{{
|
||||
formatMessage(messages.confirmationSuccess, { formType: determinedFormType })
|
||||
}}</span>
|
||||
<IntlFormatted :message-id="messages.confirmationSupportText">
|
||||
<template #support-link="{ children }">
|
||||
<nuxt-link
|
||||
to="https://support.modrinth.com"
|
||||
class="text-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<component :is="() => normalizeChildren(children)" />
|
||||
</nuxt-link>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</div>
|
||||
<div class="flex w-full flex-row justify-stretch gap-2">
|
||||
<ButtonStyled>
|
||||
<button class="w-full text-contrast" @click="handleClose">{{ closeButtonText }}</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="green">
|
||||
<button class="w-full text-contrast" @click="downloadTaxForm">
|
||||
<DownloadIcon />{{
|
||||
formatMessage(messages.downloadButton, { formType: determinedFormType })
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ExternalIcon, RightArrowIcon, SpinnerIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
BrowserWindowSuccessIllustration,
|
||||
DownloadIcon,
|
||||
ExternalIcon,
|
||||
RightArrowIcon,
|
||||
SpinnerIcon,
|
||||
XIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { Admonition, ButtonStyled, Chips, injectNotificationManager, NewModal } from '@modrinth/ui'
|
||||
import { defineMessages, useVIntl } from '@vintl/vintl'
|
||||
import { IntlFormatted } from '@vintl/vintl/components'
|
||||
@@ -91,19 +163,41 @@ import { IntlFormatted } from '@vintl/vintl/components'
|
||||
import { type FormRequestResponse, useAvalara1099 } from '@/composables/avalara1099'
|
||||
import { normalizeChildren } from '@/utils/vue-children.ts'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
closeButtonText?: string
|
||||
emitSuccessOnClose?: boolean
|
||||
}>(),
|
||||
{
|
||||
closeButtonText: 'Close',
|
||||
emitSuccessOnClose: true,
|
||||
},
|
||||
)
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
|
||||
const taxFormModal = ref<InstanceType<typeof NewModal> | null>(null)
|
||||
|
||||
type ModalStage = 'form-selection' | 'download-confirmation'
|
||||
const currentStage = ref<ModalStage>('form-selection')
|
||||
|
||||
async function startTaxForm(e: MouseEvent) {
|
||||
currentStage.value = 'form-selection'
|
||||
taxFormModal.value?.show(e)
|
||||
}
|
||||
|
||||
async function showDownloadConfirmation(e: MouseEvent) {
|
||||
currentStage.value = 'download-confirmation'
|
||||
taxFormModal.value?.show(e)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
startTaxForm,
|
||||
showDownloadConfirmation,
|
||||
})
|
||||
|
||||
const auth = await useAuth()
|
||||
const flags = useFeatureFlags()
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
@@ -146,6 +240,23 @@ const messages = defineMessages({
|
||||
},
|
||||
cancel: { id: 'action.cancel', defaultMessage: 'Cancel' },
|
||||
continue: { id: 'action.continue', defaultMessage: 'Continue' },
|
||||
confirmationTitle: {
|
||||
id: 'dashboard.creator-tax-form-modal.confirmation.title',
|
||||
defaultMessage: "You're all set! 🎉",
|
||||
},
|
||||
confirmationSuccess: {
|
||||
id: 'dashboard.creator-tax-form-modal.confirmation.success',
|
||||
defaultMessage: 'Your {formType} tax form has been submitted successfully!',
|
||||
},
|
||||
confirmationSupportText: {
|
||||
id: 'dashboard.creator-tax-form-modal.confirmation.support-text',
|
||||
defaultMessage:
|
||||
'You can freely withdraw now. If you have questions or need to update your details <support-link>contact support</support-link>.',
|
||||
},
|
||||
downloadButton: {
|
||||
id: 'dashboard.creator-tax-form-modal.confirmation.download-button',
|
||||
defaultMessage: 'Download {formType}',
|
||||
},
|
||||
})
|
||||
|
||||
const isUSCitizen = ref<'yes' | 'no' | null>(null)
|
||||
@@ -159,6 +270,19 @@ function hideModal() {
|
||||
function handleCancel() {
|
||||
emit('cancelled')
|
||||
hideModal()
|
||||
setTimeout(() => {
|
||||
currentStage.value = 'form-selection'
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
if (currentStage.value === 'download-confirmation' && props.emitSuccessOnClose) {
|
||||
emit('success')
|
||||
}
|
||||
hideModal()
|
||||
setTimeout(() => {
|
||||
currentStage.value = 'form-selection'
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const determinedFormType = computed(() => {
|
||||
@@ -186,6 +310,7 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
const avalaraState = ref<ReturnType<typeof useAvalara1099> | null>(null)
|
||||
const formResponse = ref<any>(null)
|
||||
const manualLoading = ref(false)
|
||||
const loading = computed(
|
||||
() =>
|
||||
@@ -199,6 +324,13 @@ async function continueForm() {
|
||||
|
||||
manualLoading.value = true
|
||||
|
||||
// Skip Avalara if testTaxForm flag is enabled
|
||||
if (flags.value.testTaxForm) {
|
||||
currentStage.value = 'download-confirmation'
|
||||
manualLoading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
const response = (await useBaseFetch('payout/compliance', {
|
||||
apiVersion: 3,
|
||||
method: 'POST',
|
||||
@@ -219,15 +351,11 @@ async function continueForm() {
|
||||
|
||||
try {
|
||||
if (avalaraState.value) {
|
||||
await avalaraState.value.start()
|
||||
const response = await avalaraState.value.start()
|
||||
formResponse.value = response
|
||||
if (avalaraState.value.status === 'signed') {
|
||||
addNotification({
|
||||
title: 'Tax form submitted',
|
||||
text: 'You can now withdraw your full balance.',
|
||||
type: 'success',
|
||||
})
|
||||
emit('success')
|
||||
hideModal()
|
||||
currentStage.value = 'download-confirmation'
|
||||
manualLoading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
@@ -245,6 +373,15 @@ async function continueForm() {
|
||||
}
|
||||
}
|
||||
|
||||
function downloadTaxForm() {
|
||||
if (!formResponse.value) return
|
||||
|
||||
const signedPdfUrl = formResponse.value.links?.signed_pdf
|
||||
if (signedPdfUrl) {
|
||||
window.open(signedPdfUrl, '_blank')
|
||||
}
|
||||
}
|
||||
|
||||
watch(isUSCitizen, (newValue) => {
|
||||
if (newValue === 'yes') {
|
||||
entityType.value = null
|
||||
@@ -280,6 +417,7 @@ dialog[open] > iframe[src*='form_embed'] {
|
||||
height: 95vh !important;
|
||||
border-radius: var(--radius-md) !important;
|
||||
}
|
||||
|
||||
dialog[open] > iframe[src*='form_embed'] {
|
||||
border-radius: var(--radius-md) !important;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({
|
||||
showVersionFilesInTable: false,
|
||||
showAdsWithPlus: false,
|
||||
alwaysShowChecklistAsPopup: true,
|
||||
testTaxForm: false,
|
||||
|
||||
// Feature toggles
|
||||
projectTypesPrimaryNav: false,
|
||||
|
||||
@@ -189,7 +189,8 @@
|
||||
|
||||
<CreatorTaxFormModal
|
||||
ref="taxFormModalRef"
|
||||
@success="() => navigateTo('/dashboard/revenue', { external: true })"
|
||||
close-button-text="Close"
|
||||
:emit-success-on-close="false"
|
||||
/>
|
||||
<header
|
||||
class="experimental-styles-within desktop-only relative z-[5] mx-auto grid max-w-[1280px] grid-cols-[1fr_auto] items-center gap-2 px-6 py-4 lg:grid-cols-[auto_1fr_auto]"
|
||||
@@ -911,6 +912,7 @@ const { data: payoutBalance } = await useAsyncData('payout/balance', () =>
|
||||
)
|
||||
|
||||
const showTaxComplianceBanner = computed(() => {
|
||||
if (flags.value.testTaxForm && auth.value.user) return true
|
||||
const bal = payoutBalance.value
|
||||
if (!bal) return false
|
||||
const thresholdMet = (bal.withdrawn_ytd ?? 0) >= 600
|
||||
|
||||
@@ -578,6 +578,18 @@
|
||||
"dashboard.collections.long-title": {
|
||||
"message": "Your collections"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.download-button": {
|
||||
"message": "Download {formType}"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.success": {
|
||||
"message": "Your {formType} tax form has been submitted successfully!"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.support-text": {
|
||||
"message": "You can freely withdraw now. If you have questions or need to update your details <support-link>contact support</support-link>."
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.title": {
|
||||
"message": "You're all set! 🎉"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.entity.description": {
|
||||
"message": "A foreign entity means a business entity organized outside the United States (such as a non-US corporation, partnership, or LLC)."
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<CreatorTaxFormModal
|
||||
ref="taxFormModalRef"
|
||||
close-button-text="Continue"
|
||||
@success="onTaxFormSuccess"
|
||||
@cancelled="onTaxFormCancelled"
|
||||
/>
|
||||
@@ -351,13 +352,17 @@ const agreedTransfer = ref(false)
|
||||
const agreedFees = ref(false)
|
||||
const agreedTerms = ref(false)
|
||||
|
||||
const flags = useFeatureFlags()
|
||||
|
||||
const blockedByTax = computed(() => {
|
||||
if (flags.value.testTaxForm) return true
|
||||
const status = userBalance.value?.form_completion_status ?? 'unknown'
|
||||
const thresholdMet = (userBalance.value?.withdrawn_ytd ?? 0) >= 600
|
||||
return thresholdMet && status !== 'complete'
|
||||
})
|
||||
|
||||
const willTriggerTaxForm = computed(() => {
|
||||
if (flags.value.testTaxForm) return true
|
||||
const status = userBalance.value?.form_completion_status ?? 'unknown'
|
||||
const currentWithdrawn = userBalance.value?.withdrawn_ytd ?? 0
|
||||
const wouldExceedThreshold = currentWithdrawn + parsedAmount.value >= 600
|
||||
@@ -386,15 +391,7 @@ watch(selectedMethod, () => {
|
||||
const taxFormModalRef = ref(null)
|
||||
const taxFormCancelled = ref(false)
|
||||
|
||||
async function withdraw() {
|
||||
if (willTriggerTaxForm.value) {
|
||||
taxFormCancelled.value = false
|
||||
if (taxFormModalRef.value && taxFormModalRef.value.startTaxForm) {
|
||||
await taxFormModalRef.value.startTaxForm(new MouseEvent('click'))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
async function performWithdrawal() {
|
||||
startLoading()
|
||||
try {
|
||||
const auth = await useAuth()
|
||||
@@ -428,13 +425,31 @@ async function withdraw() {
|
||||
stopLoading()
|
||||
}
|
||||
|
||||
async function withdraw() {
|
||||
if (willTriggerTaxForm.value) {
|
||||
taxFormCancelled.value = false
|
||||
if (taxFormModalRef.value && taxFormModalRef.value.startTaxForm) {
|
||||
await taxFormModalRef.value.startTaxForm(new MouseEvent('click'))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
await performWithdrawal()
|
||||
}
|
||||
|
||||
async function onTaxFormSuccess() {
|
||||
// Skip balance check if testTaxForm flag is enabled
|
||||
if (flags.value.testTaxForm) {
|
||||
await performWithdrawal()
|
||||
return
|
||||
}
|
||||
|
||||
// Refresh user balance to get updated form completion status
|
||||
const updatedBalance = await useBaseFetch(`payout/balance`, { apiVersion: 3 })
|
||||
userBalance.value = updatedBalance
|
||||
|
||||
if (updatedBalance?.form_completion_status === 'complete') {
|
||||
await withdraw()
|
||||
await performWithdrawal()
|
||||
} else {
|
||||
addNotification({
|
||||
title: 'Tax form incomplete',
|
||||
|
||||
@@ -31,6 +31,6 @@ export default {
|
||||
'project-invited': () => import('./project/ProjectInvited.vue'),
|
||||
'project-transferred': () => import('./project/ProjectTransferred.vue'),
|
||||
|
||||
// Organization
|
||||
// Organizations
|
||||
'organization-invited': () => import('./organization/OrganizationInvited.vue'),
|
||||
} as Record<string, () => Promise<{ default: Component }>>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="220" height="132" viewBox="0 0 220 132" fill="none">
|
||||
<mask id="path-1-inside-1_687_15946" fill="white">
|
||||
<path
|
||||
d="M204 0C212.837 0 220 7.16344 220 16V140H0V16C0 7.16344 7.16344 0 16 0H204ZM15 12C13.3431 12 12 13.3431 12 15C12 16.6569 13.3431 18 15 18C16.6569 18 18 16.6569 18 15C18 13.3431 16.6569 12 15 12ZM25 12C23.3431 12 22 13.3431 22 15C22 16.6569 23.3431 18 25 18C26.6569 18 28 16.6569 28 15C28 13.3431 26.6569 12 25 12ZM35 12C33.3431 12 32 13.3431 32 15C32 16.6569 33.3431 18 35 18C36.6569 18 38 16.6569 38 15C38 13.3431 36.6569 12 35 12Z" />
|
||||
</mask>
|
||||
<path
|
||||
d="M204 0C212.837 0 220 7.16344 220 16V140H0V16C0 7.16344 7.16344 0 16 0H204ZM15 12C13.3431 12 12 13.3431 12 15C12 16.6569 13.3431 18 15 18C16.6569 18 18 16.6569 18 15C18 13.3431 16.6569 12 15 12ZM25 12C23.3431 12 22 13.3431 22 15C22 16.6569 23.3431 18 25 18C26.6569 18 28 16.6569 28 15C28 13.3431 26.6569 12 25 12ZM35 12C33.3431 12 32 13.3431 32 15C32 16.6569 33.3431 18 35 18C36.6569 18 38 16.6569 38 15C38 13.3431 36.6569 12 35 12Z"
|
||||
fill="white" fill-opacity="0.12" />
|
||||
<path
|
||||
d="M220 16H221H220ZM220 140V141H221V140H220ZM0 140H-1V141H0V140ZM204 0V1C212.284 1 219 7.71573 219 16H220H221C221 6.61116 213.389 -1 204 -1V0ZM220 16H219V140H220H221V16H220ZM220 140V139H0V140V141H220V140ZM0 140H1V16H0H-1V140H0ZM0 16H1C1 7.71573 7.71573 1 16 1V0V-1C6.61116 -1 -1 6.61116 -1 16H0ZM16 0V1H204V0V-1H16V0ZM15 12V11C12.7909 11 11 12.7909 11 15H12H13C13 13.8954 13.8954 13 15 13V12ZM12 15H11C11 17.2091 12.7909 19 15 19V18V17C13.8954 17 13 16.1046 13 15H12ZM15 18V19C17.2091 19 19 17.2091 19 15H18H17C17 16.1046 16.1046 17 15 17V18ZM18 15H19C19 12.7909 17.2091 11 15 11V12V13C16.1046 13 17 13.8954 17 15H18ZM25 12V11C22.7909 11 21 12.7909 21 15H22H23C23 13.8954 23.8954 13 25 13V12ZM22 15H21C21 17.2091 22.7909 19 25 19V18V17C23.8954 17 23 16.1046 23 15H22ZM25 18V19C27.2091 19 29 17.2091 29 15H28H27C27 16.1046 26.1046 17 25 17V18ZM28 15H29C29 12.7909 27.2091 11 25 11V12V13C26.1046 13 27 13.8954 27 15H28ZM35 12V11C32.7909 11 31 12.7909 31 15H32H33C33 13.8954 33.8954 13 35 13V12ZM32 15H31C31 17.2091 32.7909 19 35 19V18V17C33.8954 17 33 16.1046 33 15H32ZM35 18V19C37.2091 19 39 17.2091 39 15H38H37C37 16.1046 36.1046 17 35 17V18ZM38 15H39C39 12.7909 37.2091 11 35 11V12V13C36.1046 13 37 13.8954 37 15H38Z"
|
||||
fill="white" fill-opacity="0.12" mask="url(#path-1-inside-1_687_15946)" />
|
||||
<path
|
||||
d="M220 16H221H220ZM220 140V141H221V140H220ZM0 140H-1V141H0V140ZM204 0V1C212.284 1 219 7.71573 219 16H220H221C221 6.61116 213.389 -1 204 -1V0ZM220 16H219V140H220H221V16H220ZM220 140V139H0V140V141H220V140ZM0 140H1V16H0H-1V140H0ZM0 16H1C1 7.71573 7.71573 1 16 1V0V-1C6.61116 -1 -1 6.61116 -1 16H0ZM16 0V1H204V0V-1H16V0ZM15 12V11C12.7909 11 11 12.7909 11 15H12H13C13 13.8954 13.8954 13 15 13V12ZM12 15H11C11 17.2091 12.7909 19 15 19V18V17C13.8954 17 13 16.1046 13 15H12ZM15 18V19C17.2091 19 19 17.2091 19 15H18H17C17 16.1046 16.1046 17 15 17V18ZM18 15H19C19 12.7909 17.2091 11 15 11V12V13C16.1046 13 17 13.8954 17 15H18ZM25 12V11C22.7909 11 21 12.7909 21 15H22H23C23 13.8954 23.8954 13 25 13V12ZM22 15H21C21 17.2091 22.7909 19 25 19V18V17C23.8954 17 23 16.1046 23 15H22ZM25 18V19C27.2091 19 29 17.2091 29 15H28H27C27 16.1046 26.1046 17 25 17V18ZM28 15H29C29 12.7909 27.2091 11 25 11V12V13C26.1046 13 27 13.8954 27 15H28ZM35 12V11C32.7909 11 31 12.7909 31 15H32H33C33 13.8954 33.8954 13 35 13V12ZM32 15H31C31 17.2091 32.7909 19 35 19V18V17C33.8954 17 33 16.1046 33 15H32ZM35 18V19C37.2091 19 39 17.2091 39 15H38H37C37 16.1046 36.1046 17 35 17V18ZM38 15H39C39 12.7909 37.2091 11 35 11V12V13C36.1046 13 37 13.8954 37 15H38Z"
|
||||
fill="url(#paint0_radial_687_15946)" fill-opacity="0.25" mask="url(#path-1-inside-1_687_15946)" />
|
||||
<path
|
||||
d="M110 42C129.882 42 146 58.1177 146 78C146 97.8823 129.882 114 110 114C90.1177 114 74 97.8823 74 78C74 58.1177 90.1177 42 110 42ZM127.828 63.9219C126.266 62.36 123.734 62.3598 122.172 63.9219L104.375 81.7188L97.8281 75.1719C96.266 73.61 93.7339 73.6098 92.1719 75.1719C90.61 76.7339 90.61 79.2661 92.1719 80.8281L101.547 90.2031C103.109 91.7652 105.641 91.765 107.203 90.2031L127.828 69.5781C129.39 68.016 129.39 65.484 127.828 63.9219Z"
|
||||
fill="white" fill-opacity="0.75" />
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_687_15946" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="rotate(34.0355) scale(184.025 170.739)">
|
||||
<stop stop-color="white" />
|
||||
<stop offset="0.68" stop-color="white" stop-opacity="0" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -8,26 +8,26 @@ import _ArrowBigUpDashIcon from './icons/arrow-big-up-dash.svg?component'
|
||||
import _AsteriskIcon from './icons/asterisk.svg?component'
|
||||
import _BadgeCheckIcon from './icons/badge-check.svg?component'
|
||||
import _BanIcon from './icons/ban.svg?component'
|
||||
import _BellIcon from './icons/bell.svg?component'
|
||||
import _BellRingIcon from './icons/bell-ring.svg?component'
|
||||
import _BellIcon from './icons/bell.svg?component'
|
||||
import _BlocksIcon from './icons/blocks.svg?component'
|
||||
import _BoldIcon from './icons/bold.svg?component'
|
||||
import _BookIcon from './icons/book.svg?component'
|
||||
import _BookOpenIcon from './icons/book-open.svg?component'
|
||||
import _BookTextIcon from './icons/book-text.svg?component'
|
||||
import _BookIcon from './icons/book.svg?component'
|
||||
import _BookmarkIcon from './icons/bookmark.svg?component'
|
||||
import _BotIcon from './icons/bot.svg?component'
|
||||
import _BoxIcon from './icons/box.svg?component'
|
||||
import _BoxImportIcon from './icons/box-import.svg?component'
|
||||
import _BoxIcon from './icons/box.svg?component'
|
||||
import _BracesIcon from './icons/braces.svg?component'
|
||||
import _BrushCleaningIcon from './icons/brush-cleaning.svg?component'
|
||||
import _CalendarIcon from './icons/calendar.svg?component'
|
||||
import _CardIcon from './icons/card.svg?component'
|
||||
import _ChangeSkinIcon from './icons/change-skin.svg?component'
|
||||
import _ChartIcon from './icons/chart.svg?component'
|
||||
import _CheckIcon from './icons/check.svg?component'
|
||||
import _CheckCheckIcon from './icons/check-check.svg?component'
|
||||
import _CheckCircleIcon from './icons/check-circle.svg?component'
|
||||
import _CheckIcon from './icons/check.svg?component'
|
||||
import _ChevronLeftIcon from './icons/chevron-left.svg?component'
|
||||
import _ChevronRightIcon from './icons/chevron-right.svg?component'
|
||||
import _ClearIcon from './icons/clear.svg?component'
|
||||
@@ -57,13 +57,13 @@ import _EditIcon from './icons/edit.svg?component'
|
||||
import _EllipsisVerticalIcon from './icons/ellipsis-vertical.svg?component'
|
||||
import _ExpandIcon from './icons/expand.svg?component'
|
||||
import _ExternalIcon from './icons/external.svg?component'
|
||||
import _EyeIcon from './icons/eye.svg?component'
|
||||
import _EyeOffIcon from './icons/eye-off.svg?component'
|
||||
import _FileIcon from './icons/file.svg?component'
|
||||
import _EyeIcon from './icons/eye.svg?component'
|
||||
import _FileArchiveIcon from './icons/file-archive.svg?component'
|
||||
import _FileTextIcon from './icons/file-text.svg?component'
|
||||
import _FilterIcon from './icons/filter.svg?component'
|
||||
import _FileIcon from './icons/file.svg?component'
|
||||
import _FilterXIcon from './icons/filter-x.svg?component'
|
||||
import _FilterIcon from './icons/filter.svg?component'
|
||||
import _FolderArchiveIcon from './icons/folder-archive.svg?component'
|
||||
import _FolderOpenIcon from './icons/folder-open.svg?component'
|
||||
import _FolderSearchIcon from './icons/folder-search.svg?component'
|
||||
@@ -80,8 +80,8 @@ import _HashIcon from './icons/hash.svg?component'
|
||||
import _Heading1Icon from './icons/heading-1.svg?component'
|
||||
import _Heading2Icon from './icons/heading-2.svg?component'
|
||||
import _Heading3Icon from './icons/heading-3.svg?component'
|
||||
import _HeartIcon from './icons/heart.svg?component'
|
||||
import _HeartHandshakeIcon from './icons/heart-handshake.svg?component'
|
||||
import _HeartIcon from './icons/heart.svg?component'
|
||||
import _HistoryIcon from './icons/history.svg?component'
|
||||
import _HomeIcon from './icons/home.svg?component'
|
||||
import _ImageIcon from './icons/image.svg?component'
|
||||
@@ -97,13 +97,13 @@ import _LeftArrowIcon from './icons/left-arrow.svg?component'
|
||||
import _LibraryIcon from './icons/library.svg?component'
|
||||
import _LightBulbIcon from './icons/light-bulb.svg?component'
|
||||
import _LinkIcon from './icons/link.svg?component'
|
||||
import _ListIcon from './icons/list.svg?component'
|
||||
import _ListBulletedIcon from './icons/list-bulleted.svg?component'
|
||||
import _ListEndIcon from './icons/list-end.svg?component'
|
||||
import _ListOrderedIcon from './icons/list-ordered.svg?component'
|
||||
import _ListIcon from './icons/list.svg?component'
|
||||
import _LoaderIcon from './icons/loader.svg?component'
|
||||
import _LockIcon from './icons/lock.svg?component'
|
||||
import _LockOpenIcon from './icons/lock-open.svg?component'
|
||||
import _LockIcon from './icons/lock.svg?component'
|
||||
import _LogInIcon from './icons/log-in.svg?component'
|
||||
import _LogOutIcon from './icons/log-out.svg?component'
|
||||
import _MailIcon from './icons/mail.svg?component'
|
||||
@@ -114,8 +114,8 @@ import _MessageIcon from './icons/message.svg?component'
|
||||
import _MicrophoneIcon from './icons/microphone.svg?component'
|
||||
import _MinimizeIcon from './icons/minimize.svg?component'
|
||||
import _MinusIcon from './icons/minus.svg?component'
|
||||
import _MonitorIcon from './icons/monitor.svg?component'
|
||||
import _MonitorSmartphoneIcon from './icons/monitor-smartphone.svg?component'
|
||||
import _MonitorIcon from './icons/monitor.svg?component'
|
||||
import _MoonIcon from './icons/moon.svg?component'
|
||||
import _MoreHorizontalIcon from './icons/more-horizontal.svg?component'
|
||||
import _MoreVerticalIcon from './icons/more-vertical.svg?component'
|
||||
@@ -124,16 +124,16 @@ import _NoSignalIcon from './icons/no-signal.svg?component'
|
||||
import _NotepadTextIcon from './icons/notepad-text.svg?component'
|
||||
import _OmorphiaIcon from './icons/omorphia.svg?component'
|
||||
import _OrganizationIcon from './icons/organization.svg?component'
|
||||
import _PackageIcon from './icons/package.svg?component'
|
||||
import _PackageClosedIcon from './icons/package-closed.svg?component'
|
||||
import _PackageOpenIcon from './icons/package-open.svg?component'
|
||||
import _PackageIcon from './icons/package.svg?component'
|
||||
import _PaintbrushIcon from './icons/paintbrush.svg?component'
|
||||
import _PickaxeIcon from './icons/pickaxe.svg?component'
|
||||
import _PlayIcon from './icons/play.svg?component'
|
||||
import _PlugIcon from './icons/plug.svg?component'
|
||||
import _PlusIcon from './icons/plus.svg?component'
|
||||
import _RadioButtonIcon from './icons/radio-button.svg?component'
|
||||
import _RadioButtonCheckedIcon from './icons/radio-button-checked.svg?component'
|
||||
import _RadioButtonIcon from './icons/radio-button.svg?component'
|
||||
import _ReceiptTextIcon from './icons/receipt-text.svg?component'
|
||||
import _RedoIcon from './icons/redo.svg?component'
|
||||
import _RefreshCwIcon from './icons/refresh-cw.svg?component'
|
||||
@@ -150,8 +150,8 @@ import _ScaleIcon from './icons/scale.svg?component'
|
||||
import _ScanEyeIcon from './icons/scan-eye.svg?component'
|
||||
import _SearchIcon from './icons/search.svg?component'
|
||||
import _SendIcon from './icons/send.svg?component'
|
||||
import _ServerIcon from './icons/server.svg?component'
|
||||
import _ServerPlusIcon from './icons/server-plus.svg?component'
|
||||
import _ServerIcon from './icons/server.svg?component'
|
||||
import _SettingsIcon from './icons/settings.svg?component'
|
||||
import _ShareIcon from './icons/share.svg?component'
|
||||
import _ShieldIcon from './icons/shield.svg?component'
|
||||
@@ -181,23 +181,23 @@ import _TrashIcon from './icons/trash.svg?component'
|
||||
import _TriangleAlertIcon from './icons/triangle-alert.svg?component'
|
||||
import _UnderlineIcon from './icons/underline.svg?component'
|
||||
import _UndoIcon from './icons/undo.svg?component'
|
||||
import _UnknownIcon from './icons/unknown.svg?component'
|
||||
import _UnknownDonationIcon from './icons/unknown-donation.svg?component'
|
||||
import _UnknownIcon from './icons/unknown.svg?component'
|
||||
import _UnlinkIcon from './icons/unlink.svg?component'
|
||||
import _UnplugIcon from './icons/unplug.svg?component'
|
||||
import _UpdatedIcon from './icons/updated.svg?component'
|
||||
import _UploadIcon from './icons/upload.svg?component'
|
||||
import _UserIcon from './icons/user.svg?component'
|
||||
import _UserPlusIcon from './icons/user-plus.svg?component'
|
||||
import _UserXIcon from './icons/user-x.svg?component'
|
||||
import _UserIcon from './icons/user.svg?component'
|
||||
import _UsersIcon from './icons/users.svg?component'
|
||||
import _VersionIcon from './icons/version.svg?component'
|
||||
import _WikiIcon from './icons/wiki.svg?component'
|
||||
import _WindowIcon from './icons/window.svg?component'
|
||||
import _WorldIcon from './icons/world.svg?component'
|
||||
import _WrenchIcon from './icons/wrench.svg?component'
|
||||
import _XIcon from './icons/x.svg?component'
|
||||
import _XCircleIcon from './icons/x-circle.svg?component'
|
||||
import _XIcon from './icons/x.svg?component'
|
||||
import _ZoomInIcon from './icons/zoom-in.svg?component'
|
||||
import _ZoomOutIcon from './icons/zoom-out.svg?component'
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import './omorphia.scss'
|
||||
|
||||
import _FourOhFourNotFound from './branding/404.svg?component'
|
||||
// Branding
|
||||
import _BrowserWindowSuccessIllustration from './branding/illustrations/browser-window-success.svg?component'
|
||||
import _ModrinthIcon from './branding/logo.svg?component'
|
||||
import _ModrinthPlusIcon from './branding/modrinth-plus.svg?component'
|
||||
import _AngryRinthbot from './branding/rinthbot/angry.webp'
|
||||
@@ -47,6 +48,7 @@ import _WindowsIcon from './external/windows.svg?component'
|
||||
import _YouTubeIcon from './external/youtube.svg?component'
|
||||
|
||||
export const ModrinthIcon = _ModrinthIcon
|
||||
export const BrowserWindowSuccessIllustration = _BrowserWindowSuccessIllustration
|
||||
export const FourOhFourNotFound = _FourOhFourNotFound
|
||||
export const ModrinthPlusIcon = _ModrinthPlusIcon
|
||||
export const AngryRinthbot = _AngryRinthbot
|
||||
@@ -82,7 +84,6 @@ export const TwitterIcon = _TwitterIcon
|
||||
export const WindowsIcon = _WindowsIcon
|
||||
export const YouTubeIcon = _YouTubeIcon
|
||||
|
||||
// Skin Models
|
||||
export * from './generated-icons'
|
||||
export { default as ClassicPlayerModel } from './models/classic-player.gltf?url'
|
||||
export { default as SlimPlayerModel } from './models/slim-player.gltf?url'
|
||||
|
||||
Reference in New Issue
Block a user