mirror of
https://github.com/modrinth/code.git
synced 2026-09-03 13:36:48 +00:00
feat: use discord_id from discord sso for role grant (#6326)
* feat: properly use labrinth's linked discord accts sso for discord bot * Update email for fixture user in SQL insert Signed-off-by: Calum H. <calum@modrinth.com> * fix: rev changes * fix: copy on email * fix: lint * fix: rev * fix: lint --------- Signed-off-by: Calum H. <calum@modrinth.com>
This commit is contained in:
@@ -48,28 +48,69 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="normal-page__content mt-4 lg:!mt-0">
|
||||
<Admonition
|
||||
v-if="showDiscordRoleBanner"
|
||||
class="mb-3"
|
||||
type="info"
|
||||
:header="formatMessage(messages.discordRoleBannerTitle)"
|
||||
show-actions-underneath
|
||||
dismissible
|
||||
@dismiss="dismissDiscordRoleBanner"
|
||||
>
|
||||
<div class="text-primary">
|
||||
{{
|
||||
formatMessage(messages.discordRoleBannerBody, {
|
||||
roles: eligibleDiscordRolesLabel,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
<template #actions>
|
||||
<ButtonStyled color="blue">
|
||||
<NuxtLink to="/discord/link" class="w-fit !px-4">
|
||||
<ExternalIcon />
|
||||
{{ formatMessage(messages.discordRoleBannerCta) }}
|
||||
</NuxtLink>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</Admonition>
|
||||
<NuxtPage :route="route" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import {
|
||||
AffiliateIcon,
|
||||
BellIcon as NotificationsIcon,
|
||||
ChartIcon,
|
||||
CurrencyIcon,
|
||||
DashboardIcon,
|
||||
ExternalIcon,
|
||||
LibraryIcon,
|
||||
ListIcon,
|
||||
OrganizationIcon,
|
||||
ReportIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { commonMessages, defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { type User, UserBadge } from '@modrinth/utils'
|
||||
import {
|
||||
Admonition,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { UserBadge } from '@modrinth/utils'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
|
||||
import NavStack from '~/components/ui/NavStack.vue'
|
||||
|
||||
const auth = (await useAuth()) as Ref<{ user: User | null }>
|
||||
const auth = (await useAuth()) as Ref<{ user: Labrinth.Users.v3.User | null }>
|
||||
const client = injectModrinthClient()
|
||||
const dismissedDiscordRoleBannerUsers = useLocalStorage<string[]>(
|
||||
'dashboard-discord-role-banner-dismissed-users',
|
||||
[],
|
||||
)
|
||||
|
||||
const isAffiliate = computed(() => {
|
||||
return auth.value.user && auth.value.user.badges & UserBadge.AFFILIATE
|
||||
@@ -114,6 +155,31 @@ const messages = defineMessages({
|
||||
id: 'dashboard.sidebar.label.revenue',
|
||||
defaultMessage: 'Revenue',
|
||||
},
|
||||
discordRoleBannerTitle: {
|
||||
id: 'dashboard.discord-roles.banner.title',
|
||||
defaultMessage: 'Claim your Discord roles',
|
||||
},
|
||||
discordRoleBannerBody: {
|
||||
id: 'dashboard.discord-roles.banner.body',
|
||||
defaultMessage:
|
||||
"You're eligible for {roles}. Link your Discord account through Modrinth and we'll sync them automatically.",
|
||||
},
|
||||
discordRoleBannerCta: {
|
||||
id: 'dashboard.discord-roles.banner.cta',
|
||||
defaultMessage: 'Link Discord',
|
||||
},
|
||||
discordRolePride: {
|
||||
id: 'dashboard.discord-roles.role.pride',
|
||||
defaultMessage: 'Pride 2026',
|
||||
},
|
||||
discordRoleCreator: {
|
||||
id: 'dashboard.discord-roles.role.creator',
|
||||
defaultMessage: 'Creator',
|
||||
},
|
||||
discordRoleBigCreator: {
|
||||
id: 'dashboard.discord-roles.role.big-creator',
|
||||
defaultMessage: '1M+ Downloads',
|
||||
},
|
||||
})
|
||||
|
||||
definePageMeta({
|
||||
@@ -125,4 +191,60 @@ useSeoMeta({
|
||||
})
|
||||
|
||||
const route = useNativeRoute()
|
||||
|
||||
const { data: projects } = useQuery({
|
||||
queryKey: computed(() => ['dashboard-discord-role-eligibility', auth.value.user?.id, 'projects']),
|
||||
queryFn: () => {
|
||||
const userId = auth.value.user?.id
|
||||
if (!userId) return []
|
||||
|
||||
return client.labrinth.users_v2.getProjects(userId)
|
||||
},
|
||||
enabled: computed(() => !!auth.value.user?.id),
|
||||
})
|
||||
|
||||
const totalProjectDownloads = computed(() =>
|
||||
(projects.value ?? []).reduce((total, project) => total + (project.downloads ?? 0), 0),
|
||||
)
|
||||
|
||||
const eligibleDiscordRoles = computed(() => {
|
||||
const roles = []
|
||||
|
||||
if (auth.value.user?.campaigns?.pride_26?.has_badge === true) {
|
||||
roles.push(formatMessage(messages.discordRolePride))
|
||||
}
|
||||
|
||||
if (totalProjectDownloads.value >= 20_000) {
|
||||
roles.push(formatMessage(messages.discordRoleCreator))
|
||||
}
|
||||
|
||||
if (totalProjectDownloads.value >= 1_000_000) {
|
||||
roles.push(formatMessage(messages.discordRoleBigCreator))
|
||||
}
|
||||
|
||||
return roles
|
||||
})
|
||||
|
||||
const roleListFormatter = new Intl.ListFormat(undefined, {
|
||||
style: 'long',
|
||||
type: 'conjunction',
|
||||
})
|
||||
|
||||
const eligibleDiscordRolesLabel = computed(() =>
|
||||
roleListFormatter.format(eligibleDiscordRoles.value),
|
||||
)
|
||||
|
||||
const hasDismissedDiscordRoleBanner = computed(() =>
|
||||
dismissedDiscordRoleBannerUsers.value.includes(auth.value.user?.id ?? ''),
|
||||
)
|
||||
const showDiscordRoleBanner = computed(
|
||||
() => eligibleDiscordRoles.value.length > 0 && !hasDismissedDiscordRoleBanner.value,
|
||||
)
|
||||
|
||||
function dismissDiscordRoleBanner() {
|
||||
const userId = auth.value.user?.id
|
||||
if (!userId || dismissedDiscordRoleBannerUsers.value.includes(userId)) return
|
||||
|
||||
dismissedDiscordRoleBannerUsers.value = [...dismissedDiscordRoleBannerUsers.value, userId]
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user