Skip to content

Commit 034f745

Browse files
committed
Merge branch 'truman/new-server-settings' into truman/new-server-card-states
2 parents 51fca82 + 2b6fb96 commit 034f745

File tree

46 files changed

+3832
-2739
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+3832
-2739
lines changed

apps/app-frontend/src/App.vue

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
<script setup>
2-
import { AuthFeature, PanelVersionFeature, TauriModrinthClient } from '@modrinth/api-client'
2+
import {
3+
AuthFeature,
4+
NodeAuthFeature,
5+
nodeAuthState,
6+
PanelVersionFeature,
7+
TauriModrinthClient,
8+
} from '@modrinth/api-client'
39
import {
410
ArrowBigUpDashIcon,
511
ChangeSkinIcon,
@@ -128,6 +134,14 @@ const { addPopupNotification } = popupNotificationManager
128134
const tauriApiClient = new TauriModrinthClient({
129135
userAgent: `modrinth/theseus/${getVersion()} (support@modrinth.com)`,
130136
features: [
137+
new NodeAuthFeature({
138+
getAuth: () => nodeAuthState.getAuth?.() ?? null,
139+
refreshAuth: async () => {
140+
if (nodeAuthState.refreshAuth) {
141+
await nodeAuthState.refreshAuth()
142+
}
143+
},
144+
}),
131145
new AuthFeature({
132146
token: async () => (await getCreds())?.session,
133147
}),
@@ -1253,7 +1267,6 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
12531267
<FriendsList
12541268
:credentials="credentials"
12551269
:sign-in="() => signIn()"
1256-
:refresh-credentials="fetchCredentials"
12571270
/>
12581271
</suspense>
12591272
</div>

apps/app-frontend/src/assets/stylesheets/global.scss

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,55 @@ input[type='button'] {
178178
}
179179

180180
@import '@modrinth/assets/omorphia.scss';
181+
182+
input {
183+
border-radius: var(--size-rounded-sm);
184+
box-sizing: border-box;
185+
border: 2px solid transparent;
186+
// safari iOS rounds inputs by default
187+
// set the appearance to none to prevent this
188+
appearance: none !important;
189+
}
190+
191+
pre {
192+
font-weight: var(--font-weight-regular);
193+
}
194+
195+
input,
196+
textarea {
197+
background: var(--color-button-bg);
198+
color: var(--color-text);
199+
padding: 0.5rem 1rem;
200+
font-weight: var(--font-weight-medium);
201+
border: none;
202+
outline: 2px solid transparent;
203+
box-shadow:
204+
var(--shadow-inset-sm),
205+
0 0 0 0 transparent;
206+
transition: box-shadow 0.1s ease-in-out;
207+
min-height: 36px;
208+
209+
&:focus,
210+
&:focus-visible {
211+
box-shadow:
212+
inset 0 0 0 transparent,
213+
0 0 0 0.25rem var(--color-brand-shadow);
214+
color: var(--color-button-text-active);
215+
}
216+
217+
&:disabled,
218+
&[disabled='true'] {
219+
opacity: 0.6;
220+
pointer-events: none;
221+
cursor: not-allowed;
222+
}
223+
224+
&:focus::placeholder {
225+
opacity: 0.8;
226+
}
227+
228+
&::placeholder {
229+
color: var(--color-button-text);
230+
opacity: 0.6;
231+
}
232+
}
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
<script setup lang="ts">
2+
import {
3+
type Archon,
4+
clearNodeAuthState,
5+
type Labrinth,
6+
setNodeAuthState,
7+
} from '@modrinth/api-client'
8+
import { ChevronRightIcon } from '@modrinth/assets'
9+
import {
10+
type BusyReason,
11+
commonMessages,
12+
defineMessage,
13+
defineMessages,
14+
injectModrinthClient,
15+
injectNotificationManager,
16+
provideModrinthServerContext,
17+
provideServerSettings,
18+
ServerSettingsAdvancedPage,
19+
ServerSettingsGeneralPage,
20+
ServerSettingsInstallationPage,
21+
ServerSettingsNetworkPage,
22+
ServerSettingsPropertiesPage,
23+
serverSettingsTabDefinitions,
24+
TabbedModal,
25+
type TabbedModalTab,
26+
useVIntl,
27+
} from '@modrinth/ui'
28+
import { useQueryClient } from '@tanstack/vue-query'
29+
import { computed, nextTick, onUnmounted, reactive, ref } from 'vue'
30+
31+
import { get_user } from '@/helpers/cache'
32+
import { get as getCreds } from '@/helpers/mr_auth'
33+
34+
type ShowOptions = {
35+
serverId: string
36+
tabIndex?: number
37+
}
38+
39+
const { formatMessage } = useVIntl()
40+
const queryClient = useQueryClient()
41+
const client = injectModrinthClient()
42+
const { addNotification } = injectNotificationManager()
43+
44+
const messages = defineMessages({
45+
failedToLoadServer: {
46+
id: 'app.server-settings.failed-to-load-server',
47+
defaultMessage: 'Failed to load server settings',
48+
},
49+
})
50+
51+
const modal = ref<InstanceType<typeof TabbedModal> | null>(null)
52+
53+
const currentServerId = ref('')
54+
const worldId = ref<string | null>(null)
55+
const server = ref<Archon.Servers.v0.Server>({} as Archon.Servers.v0.Server)
56+
57+
const currentUserId = ref<string | null>(null)
58+
const currentUserRole = ref<string | null>(null)
59+
60+
const isApp = ref(true)
61+
62+
function browseModpacks() {
63+
// Stub for app browse-modpacks flow. Intentionally no-op for now.
64+
}
65+
66+
const isConnected = ref(true)
67+
const powerState = ref<Archon.Websocket.v0.PowerState>('stopped')
68+
const isServerRunning = computed(() => powerState.value === 'running')
69+
const backupsState = reactive(new Map())
70+
const isSyncingContent = ref(false)
71+
72+
const busyReasons = computed<BusyReason[]>(() => {
73+
const reasons: BusyReason[] = []
74+
if (server.value?.status === 'installing') {
75+
reasons.push({
76+
reason: defineMessage({
77+
id: 'servers.busy.installing',
78+
defaultMessage: 'Server is installing',
79+
}),
80+
})
81+
}
82+
if (isSyncingContent.value) {
83+
reasons.push({
84+
reason: defineMessage({
85+
id: 'servers.busy.syncing-content',
86+
defaultMessage: 'Content sync in progress',
87+
}),
88+
})
89+
}
90+
return reasons
91+
})
92+
93+
const fsAuth = ref<{ url: string; token: string } | null>(null)
94+
const fsOps = ref<Archon.Websocket.v0.FilesystemOperation[]>([])
95+
const fsQueuedOps = ref<Archon.Websocket.v0.QueuedFilesystemOp[]>([])
96+
97+
async function refreshFsAuth() {
98+
if (!currentServerId.value) {
99+
fsAuth.value = null
100+
return
101+
}
102+
fsAuth.value = await queryClient.fetchQuery({
103+
queryKey: ['servers', 'filesystem-auth', currentServerId.value],
104+
queryFn: () => client.archon.servers_v0.getFilesystemAuth(currentServerId.value),
105+
})
106+
}
107+
108+
function markBackupCancelled(_backupId: string) {}
109+
110+
const serverSettingsTabComponentMap = {
111+
general: ServerSettingsGeneralPage,
112+
installation: ServerSettingsInstallationPage,
113+
network: ServerSettingsNetworkPage,
114+
properties: ServerSettingsPropertiesPage,
115+
advanced: ServerSettingsAdvancedPage,
116+
} as const
117+
118+
provideServerSettings({
119+
isApp,
120+
currentUserId,
121+
currentUserRole,
122+
browseModpacks,
123+
})
124+
125+
provideModrinthServerContext({
126+
get serverId() {
127+
return currentServerId.value
128+
},
129+
worldId,
130+
server,
131+
isConnected,
132+
powerState,
133+
isServerRunning,
134+
backupsState,
135+
markBackupCancelled,
136+
isSyncingContent,
137+
busyReasons,
138+
fsAuth,
139+
fsOps,
140+
fsQueuedOps,
141+
refreshFsAuth,
142+
})
143+
144+
const ownerId = computed(() => server.value?.owner_id ?? 'Ghost')
145+
const isOwner = computed(() => currentUserId.value != null && currentUserId.value === ownerId.value)
146+
const isAdmin = computed(() => currentUserRole.value === 'admin')
147+
148+
const tabs = computed<TabbedModalTab[]>(() =>
149+
serverSettingsTabDefinitions.map((tab) => {
150+
const ctx = {
151+
serverId: currentServerId.value,
152+
ownerId: ownerId.value,
153+
serverStatus: server.value?.status,
154+
isOwner: isOwner.value,
155+
isAdmin: isAdmin.value,
156+
}
157+
const name = defineMessage({
158+
id: `server.settings.tabs.${tab.id}`,
159+
defaultMessage: tab.label,
160+
})
161+
const shown = tab.shown ? tab.shown(ctx) : true
162+
163+
if (tab.external) {
164+
return {
165+
name,
166+
icon: tab.icon,
167+
href: `https://modrinth.com${tab.href(ctx)}`,
168+
shown,
169+
}
170+
}
171+
172+
return {
173+
name,
174+
icon: tab.icon,
175+
content: serverSettingsTabComponentMap[tab.id as keyof typeof serverSettingsTabComponentMap],
176+
shown,
177+
}
178+
}),
179+
)
180+
181+
async function resolveViewer() {
182+
currentUserId.value = null
183+
currentUserRole.value = null
184+
185+
const credentials = await getCreds().catch(() => null)
186+
if (!credentials?.user_id) {
187+
return
188+
}
189+
190+
currentUserId.value = credentials.user_id
191+
192+
const user = await get_user(credentials.user_id, 'bypass').catch(() => null)
193+
const typedUser = user as Labrinth.Users.v2.User | null
194+
currentUserRole.value = typedUser?.role ?? null
195+
}
196+
197+
async function show({ serverId, tabIndex }: ShowOptions) {
198+
try {
199+
const [serverData, serverFull] = await Promise.all([
200+
queryClient.fetchQuery({
201+
queryKey: ['servers', 'detail', serverId],
202+
queryFn: () => client.archon.servers_v0.get(serverId),
203+
}),
204+
queryClient.fetchQuery({
205+
queryKey: ['servers', 'v1', 'detail', serverId],
206+
queryFn: () => client.archon.servers_v1.get(serverId),
207+
}),
208+
resolveViewer(),
209+
])
210+
211+
currentServerId.value = serverId
212+
server.value = serverData
213+
const activeWorld = serverFull.worlds.find((world) => world.is_active)
214+
worldId.value = activeWorld?.id ?? serverFull.worlds[0]?.id ?? null
215+
216+
setNodeAuthState(() => fsAuth.value, refreshFsAuth)
217+
await refreshFsAuth().catch(() => {})
218+
219+
modal.value?.show()
220+
const visibleTabsCount = tabs.value.filter((tab) => tab.shown !== false).length
221+
const requestedTab = tabIndex ?? 0
222+
const clampedTab = Math.min(Math.max(requestedTab, 0), Math.max(visibleTabsCount - 1, 0))
223+
nextTick(() => modal.value?.setTab(clampedTab))
224+
} catch (error) {
225+
console.error(error)
226+
addNotification({
227+
type: 'error',
228+
title: formatMessage(messages.failedToLoadServer),
229+
})
230+
}
231+
}
232+
233+
function hide() {
234+
modal.value?.hide()
235+
}
236+
237+
function onHide() {
238+
clearNodeAuthState()
239+
}
240+
241+
onUnmounted(() => {
242+
clearNodeAuthState()
243+
})
244+
245+
defineExpose({ show, hide })
246+
</script>
247+
248+
<template>
249+
<TabbedModal
250+
ref="modal"
251+
:tabs="tabs"
252+
:on-hide="onHide"
253+
:max-width="'min(980px, calc(95vw - 10rem))'"
254+
:width="'min(980px, calc(95vw - 10rem))'"
255+
>
256+
<template #title>
257+
<span class="flex items-center gap-2 text-lg font-semibold text-primary">
258+
{{ server.name || 'Server' }} <ChevronRightIcon />
259+
<span class="font-extrabold text-contrast">{{
260+
formatMessage(commonMessages.settingsLabel)
261+
}}</span>
262+
</span>
263+
</template>
264+
</TabbedModal>
265+
</template>

apps/app-frontend/src/locales/en-US/index.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@
185185
"app.modal.update-to-play.update-required-description": {
186186
"message": "An update is required to play {name}. Please update to the latest version to launch the game."
187187
},
188+
"app.server-settings.failed-to-load-server": {
189+
"message": "Failed to load server settings"
190+
},
188191
"app.settings.developer-mode-enabled": {
189192
"message": "Developer mode enabled."
190193
},
@@ -598,5 +601,11 @@
598601
},
599602
"search.filter.locked.server-loader.title": {
600603
"message": "Loader is provided by the server"
604+
},
605+
"servers.busy.installing": {
606+
"message": "Server is installing"
607+
},
608+
"servers.busy.syncing-content": {
609+
"message": "Content sync in progress"
601610
}
602611
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script setup lang="ts">
2+
import { injectModrinthServerContext, ServersManageBackupsPage } from '@modrinth/ui'
3+
4+
const { isServerRunning } = injectModrinthServerContext()
5+
</script>
6+
7+
<template>
8+
<ServersManageBackupsPage :is-server-running="isServerRunning" />
9+
</template>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script setup lang="ts">
2+
import { ServersManageContentPage } from '@modrinth/ui'
3+
</script>
4+
5+
<template>
6+
<ServersManageContentPage />
7+
</template>

0 commit comments

Comments
 (0)