mirror of
https://github.com/modrinth/code.git
synced 2026-08-30 11:36:05 +00:00
* start new server settings tabs
* update properties tab to match design
* better stying in general tab
* feat: add suffix input for hostname field
* implement tables for allocations and DNS records
* add tags for dns record type
* small gap adjustment
* polish advanced page
* adjust properties page hierarchy
* fix searching properties, empty state and projection radius appearing
* pnpm prepr
* update copy to match designs
* fix suffix input component
* style fixes and match heading size
* small fix
* fix search allocations placeholder
* adjust table styles
* move all installation settings helper text to below input
* update icon to use overflow menu buttons
* fix modal to be consistent
* open advanced properties when search
* remove other and custom properties, and update styles
* remove hide/show all java versions
* handle mc 26
* refactor: move server settings pages into /ui and add app ServerSettingsModal
* hook up server pages for app
* add server page header to app
* hook up server settings modal
* use large size
* fix card box shadow style
* fix hostname input for app
* fix app/website card containers
* implement external tabs for billing and admin billing
* fix save banner fixed to parent instead of page body
* remove unused prop to FriendsList causing warning in app
* fix client-only not available for app
* fix bottom cut off
* wire node auth
* implement full copy buttons
* dedup copy button tailwind styles
* fix hover class not working in @apply
* fix spacing
* fix error validation styles
* apply consistent styles and spacing
* feat: update hosting server card (#5609)
* fix type errors
* fix some stylesheets not imported for storybook
* add server listing stories
* add fix for frontend stylesheet imports
* remove props.
* convert copy code to use tailwind
* update server listing component styles
* update server info label styles
* start status/player count info label, more style updates and fixes
* add new server card buttons
* hook up server cards and implement updated styles
* hook up on download button
* fix tauri throwing error when api returns 204 No Content
* hook up purchase server modal in app
* fix upgrading state loading icon
* pnpm prepr
* filter out servers past 30 days after cancellation
* do not apply opacity on lock or spiner icons
* fix disabled server icon background
* update pending change stage
* handle known suspension states
* refactor: reduce code duplication for server listing
* update disabled state text color
* fix loading icon color
* clean up copy
* fix disabled opacity for server card
* update server listing files kept to be countdown
* implement resubscribe modal
* implement proper provisioning state for resubscribe
* fix duplicate attribute and pnpm prepr
* feat: add shared UI package auth DI
* feat: update purchase server flow (#5714)
* implement server list empty state component
* fix stories and adjust spacing
* implement select plan design refresh
* implement auth for empty server list
* use refs instead of reactive
* pnpm prepr
* fix auth usage for empty servers list
* move app auth provider setup to src/providers/setup
* pnpm prepr
* fix max height
* style fix
* fix getCreds no auth is blocking api client
* implement servers guest plan modal and signin which redirects back to modal's next step
* refactor guest plan select logic into provider
* implement sign in or create account popup
* remove force empty serverList
* add download button for suspended mod and generic
* add handling for when user logs out
* QA pass style fixes
* more consistent page styles
* fix duplicate export
* refactor: remove all fallback stuff from resubscribe modal
* implement shared download latest backup util
* i18n pass
* pnpm prepr
* fix region being selected if ping failed
* pnpm prepr
* feat: servers in app finalization (#5744)
* feat: start on shared console implementation into logs and overview pages
* fix: terminal gap issues
* feat: swap word wrap for full screen
* fix: stats cards alignment
* fix: stats
* feat: fix console clear + remove copy
* fix: lint
* fix: use reset not clear
* feat: shared server header & overview page for app and website (#5736)
* feat: implement shared server header for app and website
* feat: implement wrapped overview page with shared composable and hook it up
* pnpm prepr
* fix: bugs
* qa: cleanup
* feat: root.vue shared layout
* feat: delete old options pages + fix discovery frontend
* fix: discovery
* fix: misc style/layout issues
* fix page padding
* fix: modal height jankiness
* feat: implement server install content in app and server setup modal with DI
* fix: spacing
* remove servers in app feature flag
* Revert "remove servers in app feature flag"
This reverts commit 86e284c4bd.
* fix: qa
* feat: remove legacy components from apps/frontend/src/components/ui/servers
---------
Co-authored-by: Calum H. (IMB11) <contact@cal.engineer>
* qa pass (#5738)
* fix: qa
* feat: qa
* fix: server icon fetch fails due to global node auth race condition overriding each other
* fix: lint
* fix: server icon upload/sync and centralize logic
* fix: server settings modal not closing for server reset
* fix: better server sorting
* feat: copy address in server listing card
* fix: notification panel in modal and when overlapping with action bar
* fix: empty server list empty state flashing when refresh, fixed by adding isReady auth flag
* feat: use floating action bar for save banner
* fix: saving state in save bar
* fix: edit server icon styling
* fix: confirm modal to have consistent buttons
* feat: loading animation for server panel + caching improvements for app
* pnpm prepr
* feat: search page deduplication (#5754)
* fix: action bar behind modal
* fix: remove warning modal for stopping
* fix: server cards states
* we hate webkit we hate webkit
* fix: update allocation creation to not use modal
* fix: properties tab spacing and styles
* feat: add files tab copy
* fix: advanced properties icon
* fix: remove back to all servers link
* feat: add files tab link in copy
* fix: server header styles to be consistent with instance
* fix: add header icons back
* feat: update instance settings icon to be consistent
* fix: icon container
* feat: upload state persistence across tabs
* fix: server labels text wrapping
* fix: use surface-5 border
* fix: loading spinner showing with onboarding below
* feat: new server button shows purchase modal in website
* fix: billing page not showing quarterly interval
* fix: server downgrade not showing updated subscription notification
* fix: server settings invalidate saved state and remove server context provider since its already provided in the page
* pnpm prepr
* add stripe publishable key to app build
* feat: console highlighting
* fix: rename servers title to modrinth hosting
* feat: search fix
* fix: qa/styles
* fix: ip click active and remove power dont ask again
* fix: qa
* feat: highlighting fix console
* fix: disable conflicts action
* fix: error dismiss bug
* feat: modal clarification
* fix: files perms issue
* fix: lint
* feat: modal fix
* enable show uptime
* fix: add loading state to edit server icon
* fix: notification panel take in has sidebar from settings
* fix: consistency pass on app settings
* fix: consistency pass on instance settings
* pnpm prepr
* fix: nagivate to billing button in app to go to website
* fix: stripe return url in app causing app to open modrinth.com in tauri
* refactor: better show polling UI code
* fix: new server polling comparison to use server ids instead of length
* fix: buttonstyled story
* fix: button styling
* fix: content.vue regression
* feat: project url redirects
* fix: breadcrumbs
* fix: purchase with newly added card
* fix: console ordering problems
* fix: app-frontend missing env config and staging environment
* fix: log syncing for instances and server panel accidentally
* fix: QA issues
* fix: server page loading state
* fix: stats card logic
* fix: lint
* fix: qa
* fix: console height padding
* fix: terminal padding + loading indicator
* feat: update medal server listing styling
* fix: no upgrade button for medal server listing in app
* fix: go to overview instead of content tab after onboarding
* fix: qa
* fix: teleport modals to body
* fix: logs tab + qa
* fix: local storage for user preferences
* fix: qa loading indic
* feat: considitonal debug and trace
* fix: jump to top on install bug
* feat: swap out server hard drive icon to server stack icon
* feat: servers in app feature flag default true
* fix: highlight row ufll
* fix: webkit thing onto a tag
* fix: input field
* fix: clear fix
* fix: lint
* fix: fmt
* feat: improve share modal and bring it back for sharing log
* pnpm prepr
* fix: menu overflowing
* feat: remove servers in app feature flag
* fix: server stat charts no longer showing color
* fix: library nav no primary state
* fix: better modal height and width
* fix: highlighting bugs
* fix: empty states
* fix: delay import to fix overview page slow load on MacOS
* fix: medal server listing too bright on light mode
* fix: admon analysis + fix logs
* fix: bug
* fix: clear purchase intent from sign-in after closing modal
* performance: improve server manage stats loading by splitting reactivity
* fix: deploy + admon + disable highlighting
* fix: clippy
---------
Co-authored-by: tdgao <mr.trumgao@gmail.com>
Co-authored-by: Truman Gao <106889354+tdgao@users.noreply.github.com>
* feat: temp wrangler
* fix: lint
* fix: logs upload
* fix: console empty state and admon regressions
* fix: fields
* feat: log deleting + prefetch for Logs.vue
* feat: move delete before share
* feat: clear endpoint
* feat: we ball!
---------
Co-authored-by: Calum H. <calum@modrinth.com>
Co-authored-by: Calum H. (IMB11) <contact@cal.engineer>
338 lines
9.7 KiB
Vue
338 lines
9.7 KiB
Vue
<script setup lang="ts">
|
|
import { CopyIcon, EditIcon, PlusIcon, SpinnerIcon, TrashIcon, UploadIcon } from '@modrinth/assets'
|
|
import {
|
|
Avatar,
|
|
ButtonStyled,
|
|
Checkbox,
|
|
defineMessages,
|
|
injectNotificationManager,
|
|
OverflowMenu,
|
|
StyledInput,
|
|
useVIntl,
|
|
} from '@modrinth/ui'
|
|
import { convertFileSrc } from '@tauri-apps/api/core'
|
|
import { open } from '@tauri-apps/plugin-dialog'
|
|
import { computed, type Ref, ref, watch } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
|
|
import ConfirmDeleteInstanceModal from '@/components/ui/modal/ConfirmDeleteInstanceModal.vue'
|
|
import { trackEvent } from '@/helpers/analytics'
|
|
import { duplicate, edit, edit_icon, list, remove } from '@/helpers/profile'
|
|
import { injectInstanceSettings } from '@/providers/instance-settings'
|
|
|
|
import type { GameInstance } from '../../../helpers/types'
|
|
|
|
const { handleError } = injectNotificationManager()
|
|
const { formatMessage } = useVIntl()
|
|
const router = useRouter()
|
|
|
|
const deleteConfirmModal = ref()
|
|
|
|
const { instance } = injectInstanceSettings()
|
|
|
|
const title = ref(instance.value.name)
|
|
const icon: Ref<string | undefined> = ref(instance.value.icon_path)
|
|
const groups = ref([...instance.value.groups])
|
|
|
|
const newCategoryInput = ref('')
|
|
|
|
const installing = computed(() => instance.value.install_stage !== 'installed')
|
|
|
|
async function duplicateProfile() {
|
|
await duplicate(instance.value.path).catch(handleError)
|
|
trackEvent('InstanceDuplicate', {
|
|
loader: instance.value.loader,
|
|
game_version: instance.value.game_version,
|
|
})
|
|
}
|
|
|
|
const allInstances = ref((await list()) as GameInstance[])
|
|
const availableGroups = computed(() => [
|
|
...new Set([...allInstances.value.flatMap((instance) => instance.groups), ...groups.value]),
|
|
])
|
|
|
|
async function resetIcon() {
|
|
icon.value = undefined
|
|
await edit_icon(instance.value.path, null).catch(handleError)
|
|
trackEvent('InstanceRemoveIcon')
|
|
}
|
|
|
|
async function setIcon() {
|
|
const value = await open({
|
|
multiple: false,
|
|
filters: [
|
|
{
|
|
name: 'Image',
|
|
extensions: ['png', 'jpeg', 'svg', 'webp', 'gif', 'jpg'],
|
|
},
|
|
],
|
|
})
|
|
|
|
if (!value) return
|
|
|
|
icon.value = value
|
|
await edit_icon(instance.value.path, icon.value).catch(handleError)
|
|
|
|
trackEvent('InstanceSetIcon')
|
|
}
|
|
|
|
const editProfileObject = computed(() => ({
|
|
name: title.value.trim().substring(0, 32) ?? 'Instance',
|
|
groups: groups.value.map((x) => x.trim().substring(0, 32)).filter((x) => x.length > 0),
|
|
}))
|
|
|
|
const toggleGroup = (group: string) => {
|
|
if (groups.value.includes(group)) {
|
|
groups.value = groups.value.filter((x) => x !== group)
|
|
} else {
|
|
groups.value.push(group)
|
|
}
|
|
}
|
|
|
|
const addCategory = () => {
|
|
const text = newCategoryInput.value.trim()
|
|
|
|
if (text.length > 0) {
|
|
groups.value.push(text.substring(0, 32))
|
|
newCategoryInput.value = ''
|
|
}
|
|
}
|
|
|
|
watch(
|
|
[title, groups, groups],
|
|
async () => {
|
|
if (removing.value) return
|
|
await edit(instance.value.path, editProfileObject.value).catch(handleError)
|
|
},
|
|
{ deep: true },
|
|
)
|
|
|
|
const removing = ref(false)
|
|
async function removeProfile() {
|
|
removing.value = true
|
|
const path = instance.value.path
|
|
|
|
trackEvent('InstanceRemove', {
|
|
loader: instance.value.loader,
|
|
game_version: instance.value.game_version,
|
|
})
|
|
|
|
await router.push({ path: '/' })
|
|
await remove(path).catch(handleError)
|
|
}
|
|
|
|
const messages = defineMessages({
|
|
name: {
|
|
id: 'instance.settings.tabs.general.name',
|
|
defaultMessage: 'Name',
|
|
},
|
|
libraryGroups: {
|
|
id: 'instance.settings.tabs.general.library-groups',
|
|
defaultMessage: 'Library groups',
|
|
},
|
|
libraryGroupsDescription: {
|
|
id: 'instance.settings.tabs.general.library-groups.description',
|
|
defaultMessage:
|
|
'Library groups allow you to organize your instances into different sections in your library.',
|
|
},
|
|
libraryGroupsEnterName: {
|
|
id: 'instance.settings.tabs.general.library-groups.enter-name',
|
|
defaultMessage: 'Enter group name',
|
|
},
|
|
libraryGroupsCreate: {
|
|
id: 'instance.settings.tabs.general.library-groups.create',
|
|
defaultMessage: 'Create new group',
|
|
},
|
|
editIcon: {
|
|
id: 'instance.settings.tabs.general.edit-icon',
|
|
defaultMessage: 'Edit icon',
|
|
},
|
|
selectIcon: {
|
|
id: 'instance.settings.tabs.general.edit-icon.select',
|
|
defaultMessage: 'Select icon',
|
|
},
|
|
replaceIcon: {
|
|
id: 'instance.settings.tabs.general.edit-icon.replace',
|
|
defaultMessage: 'Replace icon',
|
|
},
|
|
removeIcon: {
|
|
id: 'instance.settings.tabs.general.edit-icon.remove',
|
|
defaultMessage: 'Remove icon',
|
|
},
|
|
duplicateInstance: {
|
|
id: 'instance.settings.tabs.general.duplicate-instance',
|
|
defaultMessage: 'Duplicate instance',
|
|
},
|
|
duplicateInstanceDescription: {
|
|
id: 'instance.settings.tabs.general.duplicate-instance.description',
|
|
defaultMessage: 'Creates a copy of this instance, including worlds, configs, mods, etc.',
|
|
},
|
|
duplicateButtonTooltipInstalling: {
|
|
id: 'instance.settings.tabs.general.duplicate-button.tooltip.installing',
|
|
defaultMessage: 'Cannot duplicate while installing.',
|
|
},
|
|
duplicateButton: {
|
|
id: 'instance.settings.tabs.general.duplicate-button',
|
|
defaultMessage: 'Duplicate',
|
|
},
|
|
deleteInstance: {
|
|
id: 'instance.settings.tabs.general.delete',
|
|
defaultMessage: 'Delete instance',
|
|
},
|
|
deleteInstanceDescription: {
|
|
id: 'instance.settings.tabs.general.delete.description',
|
|
defaultMessage:
|
|
'Permanently deletes an instance from your device, including your worlds, configs, and all installed content. Be careful, as once you delete a instance there is no way to recover it.',
|
|
},
|
|
deleteInstanceButton: {
|
|
id: 'instance.settings.tabs.general.delete.button',
|
|
defaultMessage: 'Delete instance',
|
|
},
|
|
deletingInstanceButton: {
|
|
id: 'instance.settings.tabs.general.deleting.button',
|
|
defaultMessage: 'Deleting...',
|
|
},
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<ConfirmDeleteInstanceModal ref="deleteConfirmModal" @delete="removeProfile" />
|
|
<div class="block">
|
|
<div class="float-end ml-10 relative group w-fit">
|
|
<div class="flex flex-col gap-1">
|
|
<span class="text-lg font-semibold text-contrast">Icon</span>
|
|
<div class="group relative w-fit">
|
|
<OverflowMenu
|
|
v-tooltip="formatMessage(messages.editIcon)"
|
|
class="bg-transparent border-none appearance-none p-0 m-0 cursor-pointer group-active:scale-95 transition-transform"
|
|
:options="[
|
|
{
|
|
id: 'select',
|
|
action: () => setIcon(),
|
|
},
|
|
{
|
|
id: 'remove',
|
|
color: 'danger',
|
|
action: () => resetIcon(),
|
|
shown: !!icon,
|
|
},
|
|
]"
|
|
>
|
|
<Avatar
|
|
:src="icon ? convertFileSrc(icon) : icon"
|
|
size="108px"
|
|
class="transition-[filter] group-hover:brightness-75"
|
|
:tint-by="instance.path"
|
|
no-shadow
|
|
/>
|
|
<div
|
|
class="absolute top-0 h-full w-full flex items-center justify-center opacity-0 transition-all group-hover:opacity-100"
|
|
>
|
|
<EditIcon aria-hidden="true" class="h-10 w-10 text-primary" />
|
|
</div>
|
|
<template #select>
|
|
<UploadIcon />
|
|
{{ icon ? formatMessage(messages.replaceIcon) : formatMessage(messages.selectIcon) }}
|
|
</template>
|
|
<template #remove> <TrashIcon /> {{ formatMessage(messages.removeIcon) }} </template>
|
|
</OverflowMenu>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<label for="instance-name" class="m-0 text-lg font-semibold text-contrast block">
|
|
{{ formatMessage(messages.name) }}
|
|
</label>
|
|
<div class="flex">
|
|
<StyledInput
|
|
id="instance-name"
|
|
v-model="title"
|
|
autocomplete="off"
|
|
:maxlength="80"
|
|
wrapper-class="flex-grow"
|
|
/>
|
|
</div>
|
|
<template v-if="instance.install_stage == 'installed'">
|
|
<div class="flex flex-col gap-2.5 mt-6">
|
|
<h2 id="duplicate-instance-label" class="m-0 text-lg font-semibold text-contrast block">
|
|
{{ formatMessage(messages.duplicateInstance) }}
|
|
</h2>
|
|
<ButtonStyled>
|
|
<button
|
|
v-tooltip="installing ? formatMessage(messages.duplicateButtonTooltipInstalling) : null"
|
|
aria-labelledby="duplicate-instance-label"
|
|
:disabled="installing"
|
|
class="w-max !shadow-none"
|
|
@click="duplicateProfile"
|
|
>
|
|
<CopyIcon /> {{ formatMessage(messages.duplicateButton) }}
|
|
</button>
|
|
</ButtonStyled>
|
|
<p class="m-0">
|
|
{{ formatMessage(messages.duplicateInstanceDescription) }}
|
|
</p>
|
|
</div>
|
|
</template>
|
|
<div class="flex flex-col gap-2.5 mt-6">
|
|
<h2 class="m-0 text-lg font-semibold text-contrast block">
|
|
{{ formatMessage(messages.libraryGroups) }}
|
|
</h2>
|
|
|
|
<div class="flex flex-col gap-1">
|
|
<Checkbox
|
|
v-for="group in availableGroups"
|
|
:key="group"
|
|
:model-value="groups.includes(group)"
|
|
:label="group"
|
|
@click="toggleGroup(group)"
|
|
/>
|
|
<div class="flex gap-2 items-center">
|
|
<StyledInput
|
|
v-model="newCategoryInput"
|
|
:placeholder="formatMessage(messages.libraryGroupsEnterName)"
|
|
class="w-full max-w-[300px]"
|
|
@submit="() => addCategory"
|
|
/>
|
|
<ButtonStyled>
|
|
<button class="w-fit !shadow-none" @click="() => addCategory()">
|
|
<PlusIcon /> {{ formatMessage(messages.libraryGroupsCreate) }}
|
|
</button>
|
|
</ButtonStyled>
|
|
</div>
|
|
</div>
|
|
<p class="m-0">
|
|
{{ formatMessage(messages.libraryGroupsDescription) }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-2.5 mt-6">
|
|
<h2 id="delete-instance-label" class="m-0 text-lg font-semibold text-contrast block">
|
|
{{ formatMessage(messages.deleteInstance) }}
|
|
</h2>
|
|
<ButtonStyled color="red">
|
|
<button
|
|
aria-labelledby="delete-instance-label"
|
|
:disabled="removing"
|
|
class="w-fit !shadow-none"
|
|
@click="deleteConfirmModal.show()"
|
|
>
|
|
<SpinnerIcon v-if="removing" class="animate-spin" />
|
|
<TrashIcon v-else />
|
|
{{
|
|
removing
|
|
? formatMessage(messages.deletingInstanceButton)
|
|
: formatMessage(messages.deleteInstanceButton)
|
|
}}
|
|
</button>
|
|
</ButtonStyled>
|
|
<p class="m-0">
|
|
{{ formatMessage(messages.deleteInstanceDescription) }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<style scoped lang="scss">
|
|
.hovering-icon-shadow {
|
|
box-shadow: var(--shadow-inset-sm), var(--shadow-raised);
|
|
}
|
|
</style>
|