44 <div class =" flex flex-col gap-4 md:w-[400px]" >
55 <p class =" m-0" >
66 Are you sure you want to
7- <span class =" lowercase" >{{ confirmActionText }}</span > the server?
7+ <span class =" lowercase" >{{ pendingAction }}</span > the server?
88 </p >
99 <Checkbox
1010 v-model =" dontAskAgain"
1111 label =" Don't ask me again"
1212 class =" text-sm"
13- :disabled =" !powerAction "
13+ :disabled =" !pendingAction "
1414 />
1515 <div class =" flex flex-row gap-4" >
1616 <ButtonStyled type =" standard" color =" brand" @click =" executePowerAction" >
1717 <button >
1818 <CheckIcon class =" h-5 w-5" />
19- {{ confirmActionText }} server
19+ {{ pendingAction }} server
2020 </button >
2121 </ButtonStyled >
2222 <ButtonStyled @click =" resetPowerAction" >
3131
3232 <NewModal
3333 ref =" detailsModal"
34- :header =" `All of ${serverName || 'Server'} info`"
35- @close =" closeDetailsModal "
34+ :header =" `All of ${server.name || 'Server'} info`"
35+ @close =" detailsModal?.hide() "
3636 >
3737 <ServerInfoLabels
38- :server-data =" serverData "
38+ :server-data =" server "
3939 :show-game-label =" true"
4040 :show-loader-label =" true"
4141 :uptime-seconds =" uptimeSeconds"
4242 :column =" true"
4343 class =" mb-6 flex flex-col gap-2"
4444 />
4545 <div v-if =" flags.advancedDebugInfo" class =" markdown-body" >
46- <pre >{{ serverData }}</pre >
46+ <pre >{{ server }}</pre >
4747 </div >
48- <ButtonStyled type =" standard" color =" brand" @click =" closeDetailsModal " >
48+ <ButtonStyled type =" standard" color =" brand" @click =" detailsModal?.hide() " >
4949 <button class =" w-full" >Close</button >
5050 </ButtonStyled >
5151 </NewModal >
6262 <button :disabled =" !canTakeAction" @click =" initiateAction('Stop')" >
6363 <div class =" flex gap-1" >
6464 <StopCircleIcon class =" h-5 w-5" />
65- <span >{{ isStoppingState ? 'Stopping...' : 'Stop' }}</span >
65+ <span >{{ isStopping ? 'Stopping...' : 'Stop' }}</span >
6666 </div >
6767 </button >
6868 </ButtonStyled >
6969
7070 <ButtonStyled type =" standard" color =" brand" size =" large" >
71- <button v-tooltip =" busyReason " :disabled =" !canTakeAction" @click =" handlePrimaryAction" >
72- <div v-if =" isTransitionState " class =" grid place-content-center" >
71+ <button v-tooltip =" busyTooltip " :disabled =" !canTakeAction" @click =" handlePrimaryAction" >
72+ <div v-if =" isTransitioning " class =" grid place-content-center" >
7373 <LoadingIcon />
7474 </div >
7575 <component :is =" isRunning ? UpdatedIcon : PlayIcon" v-else />
@@ -116,8 +116,16 @@ import {
116116 UpdatedIcon ,
117117 XIcon ,
118118} from ' @modrinth/assets'
119- import { ButtonStyled , Checkbox , NewModal , ServerInfoLabels } from ' @modrinth/ui'
120- import type { PowerAction as ServerPowerAction , ServerState } from ' @modrinth/utils'
119+ import {
120+ ButtonStyled ,
121+ Checkbox ,
122+ injectModrinthClient ,
123+ injectModrinthServerContext ,
124+ injectNotificationManager ,
125+ NewModal ,
126+ ServerInfoLabels ,
127+ useVIntl ,
128+ } from ' @modrinth/ui'
121129import { useStorage } from ' @vueuse/core'
122130import { computed , ref } from ' vue'
123131import { useRouter } from ' vue-router'
@@ -126,70 +134,60 @@ import LoadingIcon from './icons/LoadingIcon.vue'
126134import PanelSpinner from ' ./PanelSpinner.vue'
127135import TeleportOverflowMenu from ' ./TeleportOverflowMenu.vue'
128136
129- const flags = useFeatureFlags ()
130-
131- interface PowerAction {
132- action: ServerPowerAction
133- nextState: ServerState
134- }
137+ type PowerAction = ' Start' | ' Stop' | ' Restart' | ' Kill'
135138
136139const props = defineProps <{
137- isOnline: boolean
138- isActioning: boolean
139- isInstalling: boolean
140- disabled: boolean
141- serverName? : string
142- serverData: object
140+ disabled? : boolean
143141 uptimeSeconds: number
144- busyReason? : string
145- }>()
146-
147- const emit = defineEmits <{
148- (e : ' action' , action : ServerPowerAction ): void
149142}>()
150143
144+ const { formatMessage } = useVIntl ()
145+ const flags = useFeatureFlags ()
151146const router = useRouter ()
152- const serverId = router .currentRoute .value .params .id
147+ const client = injectModrinthClient ()
148+ const { serverId, server, powerState, busyReasons } = injectModrinthServerContext ()
149+ const { addNotification } = injectNotificationManager ()
150+
153151const confirmActionModal = ref <InstanceType <typeof NewModal > | null >(null )
154152const detailsModal = ref <InstanceType <typeof NewModal > | null >(null )
153+ const pendingAction = ref <PowerAction | null >(null )
154+ const dontAskAgain = ref (false )
155155
156156const userPreferences = useStorage (` pyro-server-${serverId }-preferences ` , {
157157 powerDontAskAgain: false ,
158158})
159159
160- const serverState = ref <ServerState >(props .isOnline ? ' running' : ' stopped' )
161- const powerAction = ref <PowerAction | null >(null )
162- const dontAskAgain = ref (false )
163- const startingDelay = ref (false )
160+ const isInstalling = computed (() => server .value .status === ' installing' )
161+ const isRunning = computed (() => powerState .value === ' running' )
162+ const isStopping = computed (() => powerState .value === ' stopping' )
163+ const isTransitioning = computed (
164+ () => powerState .value === ' starting' || powerState .value === ' stopping' ,
165+ )
166+ const showStopButton = computed (() => isRunning .value || isStopping .value )
164167
165- const canTakeAction = computed (
166- () => ! props . isActioning && ! startingDelay .value && ! isTransitionState . value && ! props . busyReason ,
168+ const busyTooltip = computed (() =>
169+ busyReasons . value . length > 0 ? formatMessage ( busyReasons .value [ 0 ]. reason ) : undefined ,
167170)
168- const isRunning = computed (() => serverState . value === ' running ' )
169- const isTransitionState = computed (() =>
170- [ ' starting ' , ' stopping ' , ' restarting ' ]. includes ( serverState . value ) ,
171+
172+ const canTakeAction = computed (
173+ () => ! isTransitioning . value && ! props . disabled && busyReasons . value . length === 0 ,
171174)
172- const isStoppingState = computed (() => serverState .value === ' stopping' )
173- const showStopButton = computed (() => isRunning .value || isStoppingState .value )
174175
175176const primaryActionText = computed (() => {
176- const states: Partial <Record <ServerState , string >> = {
177- starting: ' Starting...' ,
178- restarting: ' Restarting...' ,
179- running: ' Restart' ,
180- stopping: ' Stopping...' ,
181- stopped: ' Start' ,
177+ switch (powerState .value ) {
178+ case ' starting' :
179+ return ' Starting...'
180+ case ' stopping' :
181+ return ' Stopping...'
182+ case ' running' :
183+ return ' Restart'
184+ default :
185+ return ' Start'
182186 }
183- return states [serverState .value ]
184- })
185-
186- const confirmActionText = computed (() => {
187- if (! powerAction .value ) return ' '
188- return powerAction .value .action .charAt (0 ).toUpperCase () + powerAction .value .action .slice (1 )
189187})
190188
191189const menuOptions = computed (() => [
192- ... (props . isInstalling
190+ ... (isInstalling . value
193191 ? []
194192 : [
195193 {
@@ -221,28 +219,31 @@ const menuOptions = computed(() => [
221219])
222220
223221async function copyId() {
224- await navigator .clipboard .writeText (serverId as string )
222+ await navigator .clipboard .writeText (serverId )
225223}
226224
227- function initiateAction(action : ServerPowerAction ) {
228- if (! canTakeAction .value ) return
229-
230- const stateMap: Record <ServerPowerAction , ServerState > = {
231- Start: ' starting' ,
232- Stop: ' stopping' ,
233- Restart: ' restarting' ,
234- Kill: ' stopping' ,
225+ async function sendPowerAction(action : PowerAction ) {
226+ try {
227+ await client .archon .servers_v0 .power (serverId , action )
228+ } catch (error ) {
229+ console .error (` Error performing ${action } on server: ` , error )
230+ addNotification ({
231+ type: ' error' ,
232+ title: ` Failed to ${action .toLowerCase ()} server ` ,
233+ text: ' An error occurred while performing this action.' ,
234+ })
235235 }
236+ }
237+
238+ function initiateAction(action : PowerAction ) {
239+ if (! canTakeAction .value ) return
236240
237241 if (action === ' Start' ) {
238- emit (' action' , action )
239- serverState .value = stateMap [action ]
240- startingDelay .value = true
241- setTimeout (() => (startingDelay .value = false ), 5000 )
242+ sendPowerAction (action )
242243 return
243244 }
244245
245- powerAction .value = { action , nextState: stateMap [ action ] }
246+ pendingAction .value = action
246247
247248 if (userPreferences .value .powerDontAskAgain ) {
248249 executePowerAction ()
@@ -256,41 +257,20 @@ function handlePrimaryAction() {
256257}
257258
258259function executePowerAction() {
259- if (! powerAction .value ) return
260+ if (! pendingAction .value ) return
260261
261- const { action, nextState } = powerAction .value
262- emit (' action' , action )
263- serverState .value = nextState
262+ sendPowerAction (pendingAction .value )
264263
265264 if (dontAskAgain .value ) {
266265 userPreferences .value .powerDontAskAgain = true
267266 }
268267
269- if (action === ' Start' ) {
270- startingDelay .value = true
271- setTimeout (() => (startingDelay .value = false ), 5000 )
272- }
273-
274268 resetPowerAction ()
275269}
276270
277271function resetPowerAction() {
278272 confirmActionModal .value ?.hide ()
279- powerAction .value = null
273+ pendingAction .value = null
280274 dontAskAgain .value = false
281275}
282-
283- function closeDetailsModal() {
284- detailsModal .value ?.hide ()
285- }
286-
287- watch (
288- () => props .isOnline ,
289- (online ) => (serverState .value = online ? ' running' : ' stopped' ),
290- )
291-
292- watch (
293- () => router .currentRoute .value .fullPath ,
294- () => closeDetailsModal (),
295- )
296276 </script >
0 commit comments