Skip to content

Commit 3230909

Browse files
tdgaoProspector
andauthored
feat: app server projects modals + modal borders (#5256)
* feat: add modals * NewModal add stroke * update diff type sorting * update icon to match figma * fix lint ci issues * remove formatCategory * feature flag on buttons * prepr * consistent modal borders * intl --------- Co-authored-by: Prospector <[email protected]>
1 parent 16204d3 commit 3230909

File tree

16 files changed

+652
-14
lines changed

16 files changed

+652
-14
lines changed

apps/app-frontend/src/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
10021002
<transition name="popup-survey">
10031003
<div
10041004
v-if="availableSurvey"
1005-
class="w-[400px] z-20 fixed -bottom-12 pb-16 right-[--right-bar-width] mr-4 rounded-t-2xl card-shadow bg-bg-raised border-divider border-[1px] border-solid border-b-0 p-4"
1005+
class="w-[400px] z-20 fixed -bottom-12 pb-16 right-[--right-bar-width] mr-4 rounded-t-2xl card-shadow bg-bg-raised border-surface-5 border-[1px] border-solid border-b-0 p-4"
10061006
>
10071007
<h2 class="text-lg font-extrabold mt-0 mb-2">Hey there Modrinth user!</h2>
10081008
<p class="m-0 leading-tight">

apps/app-frontend/src/components/ui/UpdateAvailableToast.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const messages = defineMessages({
6565
<template>
6666
<div
6767
v-if="availableUpdate && !dismissed"
68-
class="grid grid-cols-[min-content] fixed card-shadow rounded-2xl top-[--top-bar-height] mt-6 right-6 p-4 z-10 bg-bg-raised border-divider border-solid border-[2px]"
68+
class="grid grid-cols-[min-content] fixed card-shadow rounded-2xl top-[--top-bar-height] mt-6 right-6 p-4 z-10 bg-bg-raised border-surface-5 border-solid border-[2px]"
6969
>
7070
<div class="flex min-w-[25rem] gap-4">
7171
<h2 class="whitespace-nowrap text-base text-contrast font-semibold m-0 grow">

apps/app-frontend/src/components/ui/UpdateToast.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ const messages = defineMessages({
6868
</script>
6969
<template>
7070
<div
71-
class="grid grid-cols-[min-content] fixed card-shadow rounded-2xl top-[--top-bar-height] mt-6 right-6 p-4 z-10 bg-bg-raised border-divider border-solid border-[2px]"
71+
class="grid grid-cols-[min-content] fixed card-shadow rounded-2xl top-[--top-bar-height] mt-6 right-6 p-4 z-10 bg-bg-raised border-surface-5 border-solid border-[2px]"
7272
:class="{
7373
'download-complete': progress === 1,
7474
}"
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
<template>
2+
<NewModal ref="modal" :header="formatMessage(messages.installToPlay)" :closable="true">
3+
<div class="flex flex-col gap-6 max-w-[500px]">
4+
<Admonition type="info" :header="formatMessage(messages.sharedServerInstance)">
5+
{{ formatMessage(messages.serverRequiresMods) }}
6+
</Admonition>
7+
8+
<div v-if="sharedBy?.name" class="flex items-center gap-2 text-sm text-secondary">
9+
<Avatar
10+
v-if="sharedBy?.icon_url"
11+
:src="sharedBy.icon_url"
12+
:alt="sharedBy.name"
13+
size="24px"
14+
/>
15+
<span>
16+
<IntlFormatted :message-id="messages.sharedByToday">
17+
<template #~name>
18+
<span class="font-semibold text-contrast">{{ sharedBy.name }}</span>
19+
</template>
20+
</IntlFormatted>
21+
</span>
22+
</div>
23+
24+
<div class="flex flex-col gap-2">
25+
<span class="font-semibold text-contrast">
26+
{{ formatMessage(messages.sharedInstance) }}
27+
</span>
28+
<div class="flex items-center gap-3 rounded-xl bg-surface-4 p-3">
29+
<Avatar :src="project.icon_url" :alt="project.title" size="48px" />
30+
<div class="flex flex-col gap-0.5">
31+
<span class="font-semibold text-contrast">{{ project.title }}</span>
32+
<span class="text-sm text-secondary">
33+
{{ loaderDisplay }} {{ project.game_versions?.[0] }}
34+
<template v-if="modCount">
35+
· {{ formatMessage(messages.modCount, { count: modCount }) }}
36+
</template>
37+
</span>
38+
</div>
39+
</div>
40+
</div>
41+
</div>
42+
43+
<template #actions>
44+
<div class="flex justify-end gap-2">
45+
<ButtonStyled>
46+
<button @click="handleDecline">
47+
<XIcon />
48+
{{ formatMessage(commonMessages.cancelButton) }}
49+
</button>
50+
</ButtonStyled>
51+
<ButtonStyled color="brand">
52+
<button @click="handleAccept">
53+
<DownloadIcon />
54+
{{ formatMessage(messages.installButton) }}
55+
</button>
56+
</ButtonStyled>
57+
</div>
58+
</template>
59+
</NewModal>
60+
</template>
61+
62+
<script setup lang="ts">
63+
import type { Labrinth } from '@modrinth/api-client'
64+
import { DownloadIcon, XIcon } from '@modrinth/assets'
65+
import {
66+
Admonition,
67+
Avatar,
68+
ButtonStyled,
69+
commonMessages,
70+
defineMessages,
71+
formatLoader,
72+
IntlFormatted,
73+
NewModal,
74+
useVIntl,
75+
} from '@modrinth/ui'
76+
import { useQuery } from '@tanstack/vue-query'
77+
import { computed, ref } from 'vue'
78+
79+
import { get_organization, get_team, get_version } from '@/helpers/cache.js'
80+
import { install } from '@/store/install.js'
81+
82+
const props = defineProps<{
83+
project: Labrinth.Projects.v2.Project
84+
}>()
85+
86+
const modal = ref<InstanceType<typeof NewModal>>()
87+
const { formatMessage } = useVIntl()
88+
89+
const { data: organization } = useQuery({
90+
queryKey: computed(() => ['organization', props.project.organization]),
91+
queryFn: () => get_organization(props.project.organization!, 'must_revalidate'),
92+
enabled: computed(() => !!props.project.organization),
93+
})
94+
95+
const { data: teamMembers } = useQuery({
96+
queryKey: computed(() => ['team', props.project.team]),
97+
queryFn: () => get_team(props.project.team, 'must_revalidate'),
98+
enabled: computed(() => !!props.project.team && !props.project.organization),
99+
})
100+
101+
const sharedBy = computed(() => {
102+
if (organization.value) {
103+
return {
104+
name: organization.value.name,
105+
icon_url: organization.value.icon_url,
106+
}
107+
}
108+
if (teamMembers.value) {
109+
const owner = teamMembers.value.find((member: { is_owner: boolean }) => member.is_owner)
110+
if (owner) {
111+
return {
112+
name: owner.user.username,
113+
icon_url: owner.user.avatar_url,
114+
}
115+
}
116+
}
117+
return null
118+
})
119+
120+
const loaderDisplay = computed(() => {
121+
const loader = props.project.loaders?.[0]
122+
if (!loader) return ''
123+
return formatLoader(formatMessage, loader)
124+
})
125+
126+
// Fetch the most recent version to get mod count from dependencies
127+
const latestVersionId = computed(() => props.project.versions?.[0] ?? null)
128+
const { data: latestVersion } = useQuery({
129+
queryKey: computed(() => ['version', latestVersionId.value]),
130+
queryFn: () => get_version(latestVersionId.value, 'must_revalidate'),
131+
enabled: computed(() => !!latestVersionId.value),
132+
})
133+
const modCount = computed(() => latestVersion.value?.dependencies?.length)
134+
135+
async function handleAccept() {
136+
hide()
137+
try {
138+
await install(props.project.id, null, null, 'ProjectPageInstallToPlayModal')
139+
} catch (error) {
140+
console.error('Failed to install project from InstallToPlayModal:', error)
141+
}
142+
}
143+
144+
function handleDecline() {
145+
hide()
146+
}
147+
148+
function show(e?: MouseEvent) {
149+
modal.value?.show(e)
150+
}
151+
152+
function hide() {
153+
modal.value?.hide()
154+
}
155+
156+
const messages = defineMessages({
157+
installToPlay: {
158+
id: 'app.modal.install-to-play.header',
159+
defaultMessage: 'Install to play',
160+
},
161+
sharedServerInstance: {
162+
id: 'app.modal.install-to-play.shared-server-instance',
163+
defaultMessage: 'Shared server instance',
164+
},
165+
serverRequiresMods: {
166+
id: 'app.modal.install-to-play.server-requires-mods',
167+
defaultMessage:
168+
'This server requires mods to play. Click install to set up the required files from Modrinth.',
169+
},
170+
sharedByToday: {
171+
id: 'app.modal.install-to-play.shared-by-today',
172+
defaultMessage: '{name} shared this instance with you today.',
173+
},
174+
sharedInstance: {
175+
id: 'app.modal.install-to-play.shared-instance',
176+
defaultMessage: 'Shared instance',
177+
},
178+
modCount: {
179+
id: 'app.modal.install-to-play.mod-count',
180+
defaultMessage: '{count, plural, one {# mod} other {# mods}}',
181+
},
182+
installButton: {
183+
id: 'app.modal.install-to-play.install-button',
184+
defaultMessage: 'Install',
185+
},
186+
})
187+
188+
defineExpose({ show, hide })
189+
</script>

0 commit comments

Comments
 (0)