mirror of
https://github.com/modrinth/code.git
synced 2026-07-31 13:16:38 +00:00
Compare commits
32
Commits
v0.17.1
...
4bdd0e4155
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bdd0e4155 | ||
|
|
cb4462374c | ||
|
|
f08f2d8405 | ||
|
|
65a3ac4b34 | ||
|
|
295db3fb7a | ||
|
|
7abe6f8c73 | ||
|
|
c5ce5bc9b3 | ||
|
|
572b8027ff | ||
|
|
0a53211389 | ||
|
|
22b2b7746e | ||
|
|
39b3e20d0a | ||
|
|
3892b62451 | ||
|
|
5ffffec2e5 | ||
|
|
a0724cc5fb | ||
|
|
a0320f425e | ||
|
|
06262e7bb1 | ||
|
|
9ccaab5fad | ||
|
|
39745086fa | ||
|
|
a8dcfa8a5d | ||
|
|
0f6a74b87f | ||
|
|
d5b763c8e3 | ||
|
|
7a98f45c3d | ||
|
|
0ea47ad7ef | ||
|
|
cc804625ec | ||
|
|
20235b7d32 | ||
|
|
920dc8dcbb | ||
|
|
30f64c0020 | ||
|
|
cfafa25ed4 | ||
|
|
7495b99ddd | ||
|
|
22bfca467a | ||
|
|
1186a8a027 | ||
|
|
3dd16180b0 |
@@ -92,7 +92,6 @@ jobs:
|
||||
run: corepack enable
|
||||
|
||||
- name: Set up caches
|
||||
if: contains(matrix.artifact-target-name, 'linux') || contains(matrix.artifact-target-name, 'darwin')
|
||||
uses: namespacelabs/nscloud-cache-action@c5f8dab7560444c4bf8dbc64f1b203431873c547 # v1.6.1
|
||||
with:
|
||||
cache: |
|
||||
@@ -100,7 +99,6 @@ jobs:
|
||||
pnpm
|
||||
|
||||
- name: Configure sccache
|
||||
if: contains(matrix.artifact-target-name, 'linux') || contains(matrix.artifact-target-name, 'darwin')
|
||||
run: nsc cache sccache setup --cache_name default >> "$GITHUB_ENV"
|
||||
|
||||
- name: Generate tauri-dev.conf.json
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
DropdownSelect,
|
||||
formatLoader,
|
||||
injectNotificationManager,
|
||||
StyledInput,
|
||||
Input,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
@@ -275,7 +275,7 @@ const filteredResults = computed(() => {
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex gap-2">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="search"
|
||||
:icon="SearchIcon"
|
||||
type="text"
|
||||
|
||||
@@ -6,8 +6,9 @@ import {
|
||||
defineMessages,
|
||||
FileTreeSelect,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
NewModal,
|
||||
StyledInput,
|
||||
Textarea,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { save } from '@tauri-apps/plugin-dialog'
|
||||
@@ -244,7 +245,7 @@ function isExportCandidateDisabled(path) {
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="labeled_input w-full">
|
||||
<p class="text-contrast font-semibold">{{ formatMessage(messages.modpackNameLabel) }}</p>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="nameInput"
|
||||
type="text"
|
||||
:placeholder="formatMessage(messages.modpackNamePlaceholder)"
|
||||
@@ -256,7 +257,7 @@ function isExportCandidateDisabled(path) {
|
||||
<p class="text-contrast font-semibold">
|
||||
{{ formatMessage(messages.versionNumberLabel) }}
|
||||
</p>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="versionInput"
|
||||
type="text"
|
||||
:placeholder="formatMessage(messages.versionNumberPlaceholder)"
|
||||
@@ -269,9 +270,8 @@ function isExportCandidateDisabled(path) {
|
||||
<p class="m-0 text-contrast font-semibold">
|
||||
{{ formatMessage(commonMessages.descriptionLabel) }}
|
||||
</p>
|
||||
<StyledInput
|
||||
<Textarea
|
||||
v-model="exportDescription"
|
||||
multiline
|
||||
:placeholder="formatMessage(messages.descriptionPlaceholder)"
|
||||
wrapper-class="w-full"
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<JavaDetectionModal ref="detectJavaModal" @submit="(val) => emit('update:modelValue', val)" />
|
||||
<div :id="props.id" class="toggle-setting" :class="{ compact }">
|
||||
<div class="input-with-status">
|
||||
<StyledInput
|
||||
<Input
|
||||
autocomplete="off"
|
||||
:disabled="props.disabled"
|
||||
:model-value="props.modelValue ? props.modelValue.path : ''"
|
||||
@@ -93,7 +93,7 @@ import {
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
StyledInput,
|
||||
Input,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
IntlFormatted,
|
||||
StyledInput,
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -232,7 +232,7 @@ const messages = defineMessages({
|
||||
{{ formatMessage(messages.usernameDescription) }}
|
||||
</p>
|
||||
<div class="flex items-center gap-2 mt-4">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="username"
|
||||
:icon="UserIcon"
|
||||
type="text"
|
||||
@@ -260,7 +260,7 @@ const messages = defineMessages({
|
||||
<UserPlusIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="search"
|
||||
:icon="SearchIcon"
|
||||
type="text"
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
<script setup>
|
||||
import { CheckIcon, PlusIcon, SearchIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
injectNotificationManager,
|
||||
StyledInput,
|
||||
} from '@modrinth/ui'
|
||||
import { Admonition, Avatar, ButtonStyled, injectNotificationManager, Input } from '@modrinth/ui'
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { computed, ref } from 'vue'
|
||||
@@ -85,7 +79,7 @@ async function addServer(instance) {
|
||||
<ModalWrapper ref="modal" header="Add server to instance">
|
||||
<div class="flex flex-col gap-4 min-w-[350px]">
|
||||
<Admonition type="warning" body="This server may not be compatible with all instances." />
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="searchFilter"
|
||||
:icon="SearchIcon"
|
||||
type="search"
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
Chips,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
OverflowMenu,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
@@ -339,7 +339,7 @@ const messages = defineMessages({
|
||||
{{ formatMessage(messages.name) }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<StyledInput
|
||||
<Input
|
||||
id="instance-name"
|
||||
v-model="title"
|
||||
autocomplete="off"
|
||||
@@ -382,7 +382,7 @@ const messages = defineMessages({
|
||||
@click="toggleGroup(group)"
|
||||
/>
|
||||
<div class="flex gap-2 items-center">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="newCategoryInput"
|
||||
:placeholder="formatMessage(messages.libraryGroupsEnterName)"
|
||||
class="w-full max-w-[300px]"
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Checkbox,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { Checkbox, defineMessages, injectNotificationManager, Input, useVIntl } from '@modrinth/ui'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
import { edit } from '@/helpers/instance'
|
||||
@@ -112,7 +106,7 @@ const messages = defineMessages({
|
||||
<h2 class="mt-6 m-0 text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.preLaunch) }}
|
||||
</h2>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="pre-launch"
|
||||
v-model="hooks.pre_launch"
|
||||
autocomplete="off"
|
||||
@@ -127,7 +121,7 @@ const messages = defineMessages({
|
||||
<h2 class="mt-6 m-0 text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.wrapper) }}
|
||||
</h2>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="wrapper"
|
||||
v-model="hooks.wrapper"
|
||||
autocomplete="off"
|
||||
@@ -142,7 +136,7 @@ const messages = defineMessages({
|
||||
<h2 class="mt-6 m-0 text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.postExit) }}
|
||||
</h2>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="post-exit"
|
||||
v-model="hooks.post_exit"
|
||||
autocomplete="off"
|
||||
|
||||
@@ -13,8 +13,8 @@ import {
|
||||
Checkbox,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
Slider,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
@@ -216,7 +216,7 @@ const messages = defineMessages({
|
||||
>Java {{ optimalJava?.parsed_version }}</span
|
||||
>
|
||||
<div class="flex gap-2 items-center">
|
||||
<StyledInput
|
||||
<Input
|
||||
:model-value="activePath"
|
||||
:disabled="!overrideJavaInstall"
|
||||
autocomplete="off"
|
||||
@@ -294,7 +294,7 @@ const messages = defineMessages({
|
||||
:label="formatMessage(messages.customJavaArguments)"
|
||||
class="my-2"
|
||||
/>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="java-args"
|
||||
v-model="javaArgs"
|
||||
autocomplete="off"
|
||||
@@ -310,7 +310,7 @@ const messages = defineMessages({
|
||||
:label="formatMessage(messages.customEnvironmentVariables)"
|
||||
class="mb-2"
|
||||
/>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="env-vars"
|
||||
v-model="envVars"
|
||||
autocomplete="off"
|
||||
|
||||
@@ -13,7 +13,14 @@
|
||||
<Table :columns="inviteColumns" :data="activeInvites" row-key="id" table-min-width="36rem">
|
||||
<template #empty-state>
|
||||
<div class="flex h-40 items-center justify-center text-secondary">
|
||||
{{ formatMessage(messages.noActiveInvites) }}
|
||||
<SpinnerIcon
|
||||
v-if="activeInvitesQuery.isLoading.value"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<template v-else>
|
||||
{{ formatMessage(messages.noActiveInvites) }}
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template #cell-id="{ row }">
|
||||
@@ -41,10 +48,19 @@
|
||||
code: row.id,
|
||||
})
|
||||
"
|
||||
:disabled="revokeInviteMutation.isPending.value || isBusy"
|
||||
class="text-secondary hover:!filter-none hover:text-red focus-visible:!filter-none"
|
||||
@click="revokeInviteModal?.show(row.id)"
|
||||
>
|
||||
<XIcon aria-hidden="true" />
|
||||
<SpinnerIcon
|
||||
v-if="
|
||||
revokeInviteMutation.isPending.value &&
|
||||
revokeInviteMutation.variables.value?.inviteId === row.id
|
||||
"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<XIcon v-else aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
@@ -64,7 +80,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { XIcon } from '@modrinth/assets'
|
||||
import { SpinnerIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
CopyCode,
|
||||
@@ -75,13 +91,18 @@ import {
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import ConfirmRevokeSharedInstanceInviteModal from '@/components/ui/shared-instances/ConfirmRevokeSharedInstanceInviteModal.vue'
|
||||
import SharedInstanceInstallationSettingsControls from '@/components/ui/shared-instances/SharedInstanceInstallationSettingsControls.vue'
|
||||
import { config } from '@/config'
|
||||
import { type SharedInstanceInvite, unpublish_shared_instance } from '@/helpers/instance'
|
||||
import {
|
||||
get_shared_instance_invites,
|
||||
revoke_shared_instance_invite,
|
||||
type SharedInstanceInvite,
|
||||
unpublish_shared_instance,
|
||||
} from '@/helpers/instance'
|
||||
import { useSharedInstanceErrors } from '@/helpers/shared-instance-errors'
|
||||
import { injectInstanceSettings } from '@/providers/instance-settings'
|
||||
|
||||
@@ -93,11 +114,51 @@ const unpublishing = ref(false)
|
||||
const revokeInviteModal = ref<InstanceType<typeof ConfirmRevokeSharedInstanceInviteModal>>()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({ dateStyle: 'medium', timeStyle: 'short' })
|
||||
const isBusy = computed(
|
||||
() => instance.value.install_stage !== 'installed' || unpublishing.value || !!offline,
|
||||
)
|
||||
|
||||
type InviteTableColumn = 'id' | 'uses' | 'expiration' | 'actions'
|
||||
type ActiveInvitesQueryKey = readonly ['sharedInstanceInvites', string]
|
||||
|
||||
const activeInvitesQueryKey = computed(
|
||||
() => ['sharedInstanceInvites', instance.value.id] as const satisfies ActiveInvitesQueryKey,
|
||||
)
|
||||
const activeInvitesQuery = useQuery({
|
||||
queryKey: activeInvitesQueryKey,
|
||||
queryFn: async ({ queryKey }) => {
|
||||
try {
|
||||
return await get_shared_instance_invites(queryKey[1])
|
||||
} catch (error) {
|
||||
notifySharedInstanceError(error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
enabled: () => !!instance.value.id && !offline,
|
||||
retry: false,
|
||||
staleTime: Infinity,
|
||||
refetchOnMount: 'always',
|
||||
refetchOnReconnect: false,
|
||||
refetchOnWindowFocus: false,
|
||||
})
|
||||
const activeInvites = computed(() => activeInvitesQuery.data.value ?? [])
|
||||
|
||||
const revokeInviteMutation = useMutation({
|
||||
mutationFn: ({ instanceId, inviteId }: { instanceId: string; inviteId: string }) =>
|
||||
revoke_shared_instance_invite(instanceId, inviteId),
|
||||
onSuccess: (_data, { instanceId, inviteId }) => {
|
||||
queryClient.setQueryData<SharedInstanceInvite[]>(
|
||||
['sharedInstanceInvites', instanceId],
|
||||
(invites = []) => invites.filter((invite) => invite.id !== inviteId),
|
||||
)
|
||||
},
|
||||
onError: notifySharedInstanceError,
|
||||
})
|
||||
|
||||
const isBusy = computed(
|
||||
() =>
|
||||
instance.value.install_stage !== 'installed' ||
|
||||
unpublishing.value ||
|
||||
revokeInviteMutation.isPending.value ||
|
||||
!!offline,
|
||||
)
|
||||
|
||||
const inviteColumns = computed<TableColumn<InviteTableColumn>[]>(() => [
|
||||
{
|
||||
@@ -123,32 +184,9 @@ const inviteColumns = computed<TableColumn<InviteTableColumn>[]>(() => [
|
||||
},
|
||||
])
|
||||
|
||||
const now = Date.now()
|
||||
|
||||
// TODO: Use actual endpoint
|
||||
const activeInvites = ref<SharedInstanceInvite[]>([
|
||||
{
|
||||
id: 'wqHPxNagZr',
|
||||
expiration: new Date(now + 6 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
maxUses: 10,
|
||||
uses: 0,
|
||||
},
|
||||
{
|
||||
id: 'GbRGfY7hbs',
|
||||
expiration: new Date(now + 3 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
maxUses: 10,
|
||||
uses: 2,
|
||||
},
|
||||
{
|
||||
id: 'k9mvD2QxLc',
|
||||
expiration: new Date(now + 90 * 60 * 1000).toISOString(),
|
||||
maxUses: 5,
|
||||
uses: 4,
|
||||
},
|
||||
])
|
||||
|
||||
function revokeInvite(inviteId: string) {
|
||||
activeInvites.value = activeInvites.value.filter((invite) => invite.id !== inviteId)
|
||||
if (revokeInviteMutation.isPending.value) return
|
||||
revokeInviteMutation.mutate({ instanceId: instance.value.id, inviteId })
|
||||
}
|
||||
|
||||
async function unpublishSharedInstance() {
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
Checkbox,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
StyledInput,
|
||||
Input,
|
||||
Toggle,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -129,7 +129,7 @@ const messages = defineMessages({
|
||||
{{ formatMessage(messages.widthDescription) }}
|
||||
</p>
|
||||
</div>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="width"
|
||||
v-model="resolution[0]"
|
||||
autocomplete="off"
|
||||
@@ -148,7 +148,7 @@ const messages = defineMessages({
|
||||
{{ formatMessage(messages.heightDescription) }}
|
||||
</p>
|
||||
</div>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="height"
|
||||
v-model="resolution[1]"
|
||||
autocomplete="off"
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import {
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
Slider,
|
||||
StyledInput,
|
||||
Toggle,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -187,7 +187,7 @@ watch(
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<StyledInput
|
||||
<Input
|
||||
id="width"
|
||||
v-model="settings.game_resolution[0]"
|
||||
:disabled="settings.force_fullscreen"
|
||||
@@ -207,7 +207,7 @@ watch(
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<StyledInput
|
||||
<Input
|
||||
id="height"
|
||||
v-model="settings.game_resolution[1]"
|
||||
:disabled="settings.force_fullscreen"
|
||||
@@ -244,7 +244,7 @@ watch(
|
||||
<h2 class="m-0 text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.javaArgumentsTitle) }}
|
||||
</h2>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="java-args"
|
||||
v-model="settings.launchArgs"
|
||||
autocomplete="off"
|
||||
@@ -261,7 +261,7 @@ watch(
|
||||
<h2 class="m-0 text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.environmentVariablesTitle) }}
|
||||
</h2>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="env-vars"
|
||||
v-model="settings.envVars"
|
||||
autocomplete="off"
|
||||
@@ -282,7 +282,7 @@ watch(
|
||||
<h3 class="m-0 text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.preLaunchHookTitle) }}
|
||||
</h3>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="pre-launch"
|
||||
v-model="settings.hooks.pre_launch"
|
||||
autocomplete="off"
|
||||
@@ -299,7 +299,7 @@ watch(
|
||||
<h3 class="m-0 text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.wrapperHookTitle) }}
|
||||
</h3>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="wrapper"
|
||||
v-model="settings.hooks.wrapper"
|
||||
autocomplete="off"
|
||||
@@ -316,7 +316,7 @@ watch(
|
||||
<h3 class="m-0 text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.postExitHookTitle) }}
|
||||
</h3>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="post-exit"
|
||||
v-model="settings.hooks.post_exit"
|
||||
autocomplete="off"
|
||||
|
||||
+3
-3
@@ -4,8 +4,8 @@ import {
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
Slider,
|
||||
StyledInput,
|
||||
Toggle,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -175,7 +175,7 @@ async function findLauncherDir() {
|
||||
<h2 class="m-0 text-lg font-semibold text-contrast">
|
||||
{{ formatMessage(messages.appDirectoryTitle) }}
|
||||
</h2>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="appDir"
|
||||
v-model="settings.custom_dir"
|
||||
:icon="BoxIcon"
|
||||
@@ -194,7 +194,7 @@ async function findLauncherDir() {
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</StyledInput>
|
||||
</Input>
|
||||
<p class="m-0 leading-tight text-secondary">
|
||||
{{ formatMessage(messages.appDirectoryDescription) }}
|
||||
</p>
|
||||
|
||||
@@ -26,12 +26,13 @@
|
||||
</span>
|
||||
</template>
|
||||
<div class="flex min-w-0 flex-col gap-3 pt-4">
|
||||
<div class="max-h-[292px] overflow-y-auto rounded-[20px]">
|
||||
<div ref="configFileTreeContainer" class="max-h-[292px] overflow-y-auto rounded-[20px]">
|
||||
<FileTreeSelect
|
||||
v-model="selectedConfigPaths"
|
||||
:items="configFileItems"
|
||||
:show-size="false"
|
||||
:show-modified="false"
|
||||
@navigate="scrollConfigFileTreeToTop"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,6 +76,7 @@ const emit = defineEmits<{
|
||||
const { formatMessage } = useVIntl()
|
||||
const { notifySharedInstanceError, notifySharedInstanceUnavailable } = useSharedInstanceErrors()
|
||||
const publishReviewModal = ref<InstanceType<typeof ContentDiffModal>>()
|
||||
const configFileTreeContainer = ref<HTMLElement>()
|
||||
const publishDiffs = ref<ContentDiffItem[]>([])
|
||||
const configFilePaths = ref<string[]>([])
|
||||
const selectedConfigPaths = ref<string[]>([])
|
||||
@@ -132,6 +134,12 @@ async function publishChanges() {
|
||||
}
|
||||
}
|
||||
|
||||
function scrollConfigFileTreeToTop() {
|
||||
if (configFileTreeContainer.value) {
|
||||
configFileTreeContainer.value.scrollTop = 0
|
||||
}
|
||||
}
|
||||
|
||||
function finishReview() {
|
||||
if (state.value === 'reviewing') {
|
||||
setState('idle')
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
StyledInput,
|
||||
Input,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
@@ -104,7 +104,7 @@ const messages = defineMessages({
|
||||
<h2 class="text-lg font-extrabold text-contrast mt-0 mb-1">
|
||||
{{ formatMessage(messages.name) }}
|
||||
</h2>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="name"
|
||||
:placeholder="formatMessage(messages.placeholderName)"
|
||||
autocomplete="off"
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Combobox,
|
||||
defineMessages,
|
||||
type MessageDescriptor,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { Combobox, defineMessages, Input, type MessageDescriptor, useVIntl } from '@modrinth/ui'
|
||||
|
||||
import type { ServerPackStatus } from '@/helpers/worlds.ts'
|
||||
|
||||
@@ -65,7 +59,7 @@ defineExpose({ resourcePackOptions })
|
||||
<div class="space-y-4 w-full">
|
||||
<label class="flex flex-col gap-2">
|
||||
<span class="font-semibold text-contrast">{{ formatMessage(messages.name) }}</span>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="name"
|
||||
:placeholder="formatMessage(messages.placeholderName)"
|
||||
autocomplete="off"
|
||||
@@ -74,7 +68,7 @@ defineExpose({ resourcePackOptions })
|
||||
</label>
|
||||
<label class="flex flex-col gap-2">
|
||||
<span class="font-semibold text-contrast">{{ formatMessage(messages.address) }}</span>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="address"
|
||||
:placeholder="formatMessage(messages.placeholderAddress)"
|
||||
autocomplete="off"
|
||||
|
||||
@@ -260,6 +260,9 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Hide servers already added"
|
||||
},
|
||||
"app.browse.hide-installed-modpacks": {
|
||||
"message": "Hide already installed"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
preferencesDiffer,
|
||||
provideBrowseManager,
|
||||
requestInstall,
|
||||
resolveInstallPlan,
|
||||
stripServerRuntimeInstallFilters,
|
||||
stripServerRuntimeInstallOverrides,
|
||||
useBrowseSearch,
|
||||
@@ -50,6 +51,7 @@ import { instance_listener } from '@/helpers/events.js'
|
||||
import {
|
||||
get as getInstance,
|
||||
get_installed_project_ids as getInstalledProjectIds,
|
||||
list as listInstances,
|
||||
} from '@/helpers/instance'
|
||||
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
|
||||
import { get as getSettings, set as setSettings } from '@/helpers/settings.ts'
|
||||
@@ -171,7 +173,7 @@ const instance: Ref<Instance | null> = ref(
|
||||
queryClient.getQueryData<Instance>(['instances', 'summary', initialInstanceId]) ?? null,
|
||||
)
|
||||
const installedProjectIds: Ref<string[] | null> = ref(null)
|
||||
const instanceHideInstalled = ref(false)
|
||||
const instanceHideInstalled = ref(route.query.ai === 'true')
|
||||
const newlyInstalled = ref<string[]>([])
|
||||
const hiddenInstanceProjectIds = ref<Set<string>>(new Set())
|
||||
const hiddenInstanceProjectIdsInitialized = ref(false)
|
||||
@@ -288,7 +290,17 @@ watchServerContextChanges()
|
||||
await initInstanceContext()
|
||||
|
||||
async function refreshInstalledProjectIds() {
|
||||
if (!route.query.i) return
|
||||
if (!route.query.i) {
|
||||
const instances = await listInstances().catch(handleError)
|
||||
if (!instances) return
|
||||
|
||||
const ids = instances
|
||||
.map((gameInstance) => gameInstance.link?.project_id)
|
||||
.filter((id): id is string => !!id)
|
||||
debugLog('installedInstanceProjectIds loaded', { count: ids.length })
|
||||
installedProjectIds.value = ids
|
||||
return
|
||||
}
|
||||
|
||||
if (route.query.from === 'worlds') {
|
||||
const worlds = await get_instance_worlds(route.query.i as string).catch(handleError)
|
||||
@@ -318,6 +330,7 @@ async function initInstanceContext() {
|
||||
queryFrom: route.query.from,
|
||||
})
|
||||
await initServerContext()
|
||||
await refreshInstalledProjectIds()
|
||||
|
||||
if (route.query.i) {
|
||||
instance.value = (await getInstance(route.query.i as string).catch(handleError)) ?? null
|
||||
@@ -327,8 +340,6 @@ async function initInstanceContext() {
|
||||
gameVersion: instance.value?.game_version,
|
||||
})
|
||||
|
||||
await refreshInstalledProjectIds()
|
||||
|
||||
if (instance.value?.link?.project_id) {
|
||||
debugLog('checking linked project for server status', instance.value.link.project_id)
|
||||
const projectV3 = await get_project_v3(
|
||||
@@ -341,13 +352,23 @@ async function initInstanceContext() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (route.query.ai && !(route.params.projectType === 'modpack')) {
|
||||
debugLog('setting instanceHideInstalled from query', route.query.ai)
|
||||
instanceHideInstalled.value = route.query.ai === 'true'
|
||||
}
|
||||
}
|
||||
|
||||
function setBrowseHideInstalledFlag(flag: 'hide_installed_modpacks', value: boolean) {
|
||||
themeStore.featureFlags[flag] = value
|
||||
getSettings()
|
||||
.then((settings) => {
|
||||
settings.feature_flags[flag] = value
|
||||
return setSettings(settings)
|
||||
})
|
||||
.catch(handleError)
|
||||
}
|
||||
|
||||
const hideInstalledModpacks = computed({
|
||||
get: () => themeStore.getFeatureFlag('hide_installed_modpacks'),
|
||||
set: (value: boolean) => setBrowseHideInstalledFlag('hide_installed_modpacks', value),
|
||||
})
|
||||
|
||||
const instanceFilters = computed(() => {
|
||||
const filters = []
|
||||
|
||||
@@ -367,11 +388,15 @@ const instanceFilters = computed(() => {
|
||||
if (isServerInstance.value) {
|
||||
filters.push({ type: 'environment', option: 'client' })
|
||||
}
|
||||
}
|
||||
|
||||
if (instanceHideInstalled.value && hiddenInstanceProjectIds.value.size > 0) {
|
||||
for (const id of hiddenInstanceProjectIds.value) {
|
||||
filters.push({ type: 'project_id', option: `project_id:${id}`, negative: true })
|
||||
}
|
||||
if (
|
||||
(instance.value || projectType.value === 'modpack') &&
|
||||
(projectType.value === 'modpack' ? hideInstalledModpacks.value : instanceHideInstalled.value) &&
|
||||
hiddenInstanceProjectIds.value.size > 0
|
||||
) {
|
||||
for (const id of hiddenInstanceProjectIds.value) {
|
||||
filters.push({ type: 'project_id', option: `project_id:${id}`, negative: true })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,6 +455,12 @@ const serverContextFilters = computed(() => {
|
||||
{ type: 'environment', option: 'client' },
|
||||
{ type: 'environment', option: 'server' },
|
||||
)
|
||||
|
||||
if (hideInstalledModpacks.value && hiddenInstanceProjectIds.value.size > 0) {
|
||||
for (const id of hiddenInstanceProjectIds.value) {
|
||||
filters.push({ type: 'project_id', option: `project_id:${id}`, negative: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serverHideInstalled.value && hiddenServerContentProjectIds.value.size > 0) {
|
||||
@@ -500,6 +531,10 @@ const messages = defineMessages({
|
||||
id: 'app.browse.hide-added-servers',
|
||||
defaultMessage: 'Hide servers already added',
|
||||
},
|
||||
hideInstalledModpacks: {
|
||||
id: 'app.browse.hide-installed-modpacks',
|
||||
defaultMessage: 'Hide already installed',
|
||||
},
|
||||
installingToServer: {
|
||||
id: 'app.browse.server.installing',
|
||||
defaultMessage: 'Installing',
|
||||
@@ -553,6 +588,7 @@ function resetInstanceContext() {
|
||||
hiddenInstanceProjectIdsInitialized.value = false
|
||||
isServerInstance.value = false
|
||||
browseBreadcrumb.reset()
|
||||
void refreshInstalledProjectIds()
|
||||
}
|
||||
|
||||
watch(
|
||||
@@ -763,6 +799,27 @@ async function chooseInstanceInstallVersion(
|
||||
return { versionId: selectedVersion.id }
|
||||
}
|
||||
|
||||
async function chooseFilterMatchingInstallVersion(
|
||||
project: Labrinth.Search.v3.ResultSearchProject,
|
||||
projectTypeValue: string,
|
||||
) {
|
||||
const plan = await resolveInstallPlan({
|
||||
project: {
|
||||
project_id: project.project_id,
|
||||
title: project.title,
|
||||
icon_url: project.icon_url,
|
||||
},
|
||||
contentType: projectTypeValue as BrowseInstallContentType,
|
||||
selectedFilters: searchState.currentFilters.value,
|
||||
providedFilters: combinedProvidedFilters.value,
|
||||
overriddenProvidedFilterTypes: searchState.overriddenProvidedFilterTypes.value,
|
||||
targetPreferences: {},
|
||||
getProjectVersions: getInstallProjectVersions,
|
||||
})
|
||||
|
||||
return { versionId: plan.versionId }
|
||||
}
|
||||
|
||||
function getCardActions(
|
||||
result: Labrinth.Search.v3.ResultSearchProject,
|
||||
currentProjectType: string,
|
||||
@@ -781,6 +838,7 @@ function getCardActions(
|
||||
serverContentProjectIds.value.has(projectResult.project_id || '') ||
|
||||
serverContextServerData.value?.upstream?.project_id === projectResult.project_id
|
||||
const isInstalling = installingProjectIds.value.has(projectResult.project_id)
|
||||
const showAsInstalled = isInstalled && currentProjectType !== 'modpack'
|
||||
|
||||
if (
|
||||
isServerContext.value &&
|
||||
@@ -790,7 +848,7 @@ function getCardActions(
|
||||
const isInstallingSelection = isInstallingQueuedServerInstalls.value
|
||||
const validatingInstall =
|
||||
isInstalling && currentProjectType !== 'modpack' && !isInstallingSelection
|
||||
const installLabel = isInstalled
|
||||
const installLabel = showAsInstalled
|
||||
? commonMessages.installedLabel
|
||||
: isQueued
|
||||
? isInstalling || isInstallingSelection
|
||||
@@ -810,11 +868,11 @@ function getCardActions(
|
||||
icon:
|
||||
isInstalling || isInstallingSelection
|
||||
? SpinnerIcon
|
||||
: isQueued || isInstalled
|
||||
: isQueued || showAsInstalled
|
||||
? CheckIcon
|
||||
: PlusIcon,
|
||||
iconClass: isInstalling || isInstallingSelection ? 'animate-spin' : undefined,
|
||||
disabled: isInstalled || isInstalling || isInstallingSelection,
|
||||
disabled: showAsInstalled || isInstalling || isInstallingSelection,
|
||||
color: isQueued && !isInstalling && !isInstallingSelection ? 'green' : 'brand',
|
||||
type: 'outlined',
|
||||
onClick: async () => {
|
||||
@@ -875,15 +933,15 @@ function getCardActions(
|
||||
label: formatMessage(
|
||||
isInstalling
|
||||
? messages.installingToServer
|
||||
: isInstalled
|
||||
: showAsInstalled
|
||||
? commonMessages.installedLabel
|
||||
: shouldUseInstallIcon
|
||||
? commonMessages.installButton
|
||||
: messages.addToAnInstance,
|
||||
),
|
||||
icon: isInstalling ? SpinnerIcon : isInstalled ? CheckIcon : PlusIcon,
|
||||
icon: isInstalling ? SpinnerIcon : showAsInstalled ? CheckIcon : PlusIcon,
|
||||
iconClass: isInstalling ? 'animate-spin' : undefined,
|
||||
disabled: isInstalled || isInstalling,
|
||||
disabled: showAsInstalled || isInstalling,
|
||||
color: 'brand',
|
||||
type: 'outlined',
|
||||
onClick: async () => {
|
||||
@@ -891,7 +949,9 @@ function getCardActions(
|
||||
try {
|
||||
const selectedInstall = instance.value
|
||||
? await chooseInstanceInstallVersion(projectResult, currentProjectType)
|
||||
: { versionId: null as string | null }
|
||||
: isModpack
|
||||
? await chooseFilterMatchingInstallVersion(projectResult, currentProjectType)
|
||||
: { versionId: null as string | null }
|
||||
if (selectedInstall === null) {
|
||||
setProjectInstalling(projectResult.project_id, false)
|
||||
return
|
||||
@@ -994,10 +1054,11 @@ async function search(requestParams: string) {
|
||||
...hit,
|
||||
}
|
||||
|
||||
if (instance.value || isServerContext.value) {
|
||||
const installedIds = instance.value
|
||||
? new Set([...newlyInstalled.value, ...(installedProjectIds.value ?? [])])
|
||||
: serverContentProjectIds.value
|
||||
if (instance.value || isServerContext.value || projectType.value === 'modpack') {
|
||||
const installedIds =
|
||||
isServerContext.value && projectType.value !== 'modpack'
|
||||
? serverContentProjectIds.value
|
||||
: new Set([...newlyInstalled.value, ...(installedProjectIds.value ?? [])])
|
||||
mapped.installed = installedIds.has(hit.project_id)
|
||||
}
|
||||
|
||||
@@ -1057,9 +1118,9 @@ watch(
|
||||
() => projectType.value,
|
||||
],
|
||||
() => {
|
||||
if (isServerContext.value) {
|
||||
if (isServerContext.value && projectType.value !== 'modpack') {
|
||||
syncHiddenServerContentProjectIds()
|
||||
} else if (instance.value) {
|
||||
} else if (instance.value || projectType.value === 'modpack') {
|
||||
syncHiddenInstanceProjectIds()
|
||||
}
|
||||
},
|
||||
@@ -1091,6 +1152,18 @@ let unlistenInstances: UnlistenFn | null = null
|
||||
|
||||
onMounted(() => {
|
||||
instance_listener(async (event: { event: string; instance_id: string }) => {
|
||||
if (event.event === 'added' || event.event === 'created' || event.event === 'removed') {
|
||||
if (!route.query.i) {
|
||||
await refreshInstalledProjectIds()
|
||||
if (projectType.value === 'modpack') {
|
||||
if (event.event === 'removed') {
|
||||
syncHiddenInstanceProjectIds()
|
||||
}
|
||||
await searchState.refreshSearch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (instance.value && event.instance_id === instance.value.id && event.event === 'synced') {
|
||||
await refreshInstalledProjectIds()
|
||||
await searchState.refreshSearch()
|
||||
@@ -1156,8 +1229,17 @@ provideBrowseManager({
|
||||
installContext,
|
||||
providedFilters: combinedProvidedFilters,
|
||||
hideInstalled: computed({
|
||||
get: () => (isServerContext.value ? serverHideInstalled.value : instanceHideInstalled.value),
|
||||
get: () => {
|
||||
if (projectType.value === 'modpack') return hideInstalledModpacks.value
|
||||
if (isServerContext.value) return serverHideInstalled.value
|
||||
return instanceHideInstalled.value
|
||||
},
|
||||
set: (val: boolean) => {
|
||||
if (projectType.value === 'modpack') {
|
||||
hideInstalledModpacks.value = val
|
||||
if (val) syncHiddenInstanceProjectIds()
|
||||
return
|
||||
}
|
||||
if (isServerContext.value) {
|
||||
serverHideInstalled.value = val
|
||||
if (val) syncHiddenServerContentProjectIds()
|
||||
@@ -1168,11 +1250,18 @@ provideBrowseManager({
|
||||
},
|
||||
}),
|
||||
showHideInstalled: computed(
|
||||
() => (isServerContext.value && projectType.value !== 'modpack') || !!instance.value,
|
||||
() =>
|
||||
projectType.value === 'modpack' ||
|
||||
(isServerContext.value && projectType.value !== 'modpack') ||
|
||||
!!instance.value,
|
||||
),
|
||||
hideInstalledLabel: computed(() =>
|
||||
formatMessage(
|
||||
isFromWorlds.value ? messages.hideAddedServers : commonMessages.hideInstalledContentLabel,
|
||||
isFromWorlds.value
|
||||
? messages.hideAddedServers
|
||||
: projectType.value === 'modpack'
|
||||
? messages.hideInstalledModpacks
|
||||
: commonMessages.hideInstalledContentLabel,
|
||||
),
|
||||
),
|
||||
hideSelected: hideSelectedServerInstalls,
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<ReadyTransition :pending="worldsReadyPending">
|
||||
<div v-if="dedupedWorlds.length > 0" class="flex flex-col gap-4">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="searchFilter"
|
||||
:icon="SearchIcon"
|
||||
type="text"
|
||||
@@ -154,8 +154,8 @@ import {
|
||||
GAME_MODES,
|
||||
type GameVersion,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
ReadyTransition,
|
||||
StyledInput,
|
||||
useReadyState,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="search"
|
||||
:icon="SearchIcon"
|
||||
:placeholder="`Search ${rows.length} users...`"
|
||||
@@ -150,8 +150,8 @@ import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
Input,
|
||||
type SortDirection,
|
||||
StyledInput,
|
||||
Table,
|
||||
type TableColumn,
|
||||
truncatedTooltip,
|
||||
|
||||
@@ -42,16 +42,24 @@ onUnmounted(() => {
|
||||
<template>
|
||||
<div class="p-6 flex flex-col gap-3">
|
||||
<h1 class="m-0 text-2xl hidden">Library</h1>
|
||||
<NavTabs
|
||||
:links="[
|
||||
{ label: 'All instances', href: `/library` },
|
||||
{ label: 'Modpacks', href: `/library/modpacks` },
|
||||
{ label: 'Servers', href: `/library/servers` },
|
||||
{ label: 'Custom', href: `/library/custom` },
|
||||
{ label: 'Shared with me', href: `/library/shared`, shown: false },
|
||||
{ label: 'Saved', href: `/library/saved`, shown: false },
|
||||
]"
|
||||
/>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<NavTabs
|
||||
:links="[
|
||||
{ label: 'All instances', href: `/library` },
|
||||
{ label: 'Modpacks', href: `/library/modpacks` },
|
||||
{ label: 'Servers', href: `/library/servers` },
|
||||
{ label: 'Custom', href: `/library/custom` },
|
||||
{ label: 'Shared with me', href: `/library/shared`, shown: false },
|
||||
{ label: 'Saved', href: `/library/saved`, shown: false },
|
||||
]"
|
||||
/>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="offline" @click="showCreationModal?.()">
|
||||
<PlusIcon />
|
||||
New instance
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<template v-if="instances && instances.length > 0">
|
||||
<RouterView v-if="route.path.startsWith('/library')" :instances="instances" />
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import type { ContentInstallInstance, ContentInstallProjectInfo, ContentItem } from '@modrinth/ui'
|
||||
import { createContext, defineMessage, useVIntl } from '@modrinth/ui'
|
||||
import {
|
||||
createContext,
|
||||
defineMessage,
|
||||
getLatestMatchingInstallVersion,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||
import dayjs from 'dayjs'
|
||||
@@ -813,7 +818,22 @@ export function createContentInstall(opts: {
|
||||
}
|
||||
|
||||
if (project.project_type === 'modpack') {
|
||||
const version = versionId ?? project.versions[project.versions.length - 1]
|
||||
let version = versionId ?? null
|
||||
if (!version) {
|
||||
const hasHints = !!(hints?.preferredGameVersion || hints?.preferredLoader)
|
||||
if (hasHints) {
|
||||
const versions = (await get_version_many(
|
||||
project.versions,
|
||||
'must_revalidate',
|
||||
)) as Labrinth.Versions.v2.Version[]
|
||||
const matching = getLatestMatchingInstallVersion(versions, {
|
||||
gameVersions: hints?.preferredGameVersion ? [hints.preferredGameVersion] : undefined,
|
||||
loaders: hints?.preferredLoader ? [hints.preferredLoader] : undefined,
|
||||
})
|
||||
version = matching?.id ?? null
|
||||
}
|
||||
version ??= project.versions[project.versions.length - 1]
|
||||
}
|
||||
const packs = await list()
|
||||
const existingPack = packs.find((pack) => pack.link?.project_id === project.id)
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export const DEFAULT_FEATURE_FLAGS = {
|
||||
show_instance_play_time: true,
|
||||
advanced_filters_collapsed: true,
|
||||
always_show_copy_details: false,
|
||||
hide_installed_modpacks: false,
|
||||
}
|
||||
|
||||
export const THEME_OPTIONS = ['dark', 'light', 'oled', 'retro', 'system'] as const
|
||||
|
||||
+7
@@ -27,6 +27,7 @@ import {
|
||||
LineController,
|
||||
LineElement,
|
||||
PointElement,
|
||||
type ScriptableLineSegmentContext,
|
||||
Tooltip,
|
||||
} from 'chart.js'
|
||||
|
||||
@@ -446,6 +447,12 @@ function buildDatasets() {
|
||||
pointHoverBackgroundColor: colors.borderColor,
|
||||
pointHoverBorderWidth: 0,
|
||||
pointHitRadius: 16,
|
||||
segment: dataset.lastDataPointUnavailable
|
||||
? {
|
||||
borderColor: (context: ScriptableLineSegmentContext) =>
|
||||
context.p1DataIndex === dataset.data.length - 1 ? 'transparent' : undefined,
|
||||
}
|
||||
: undefined,
|
||||
stack: props.stacked ? 'analytics' : undefined,
|
||||
}
|
||||
})
|
||||
|
||||
+10
-2
@@ -59,7 +59,9 @@
|
||||
<span class="font-medium text-primary">
|
||||
{{ formatMessage(analyticsChartMessages.total) }}
|
||||
</span>
|
||||
<span class="font-semibold text-contrast">{{ formattedTotal }}</span>
|
||||
<span class="font-semibold" :class="totalHasData ? 'text-contrast' : 'text-primary'">
|
||||
{{ formattedTotal }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="entry in entries"
|
||||
@@ -111,7 +113,11 @@
|
||||
:class="[
|
||||
'shrink-0',
|
||||
entry.isPreviousPeriod ? 'font-medium text-secondary' : 'font-semibold',
|
||||
entry.hidden ? 'text-primary line-through opacity-70' : 'text-contrast',
|
||||
entry.hidden
|
||||
? 'text-primary line-through opacity-70'
|
||||
: entry.noData
|
||||
? 'text-primary'
|
||||
: 'text-contrast',
|
||||
]"
|
||||
>
|
||||
{{ entry.formattedValue }}
|
||||
@@ -148,6 +154,7 @@ export type AnalyticsChartTooltipEntry = {
|
||||
tooltip?: string
|
||||
color: string
|
||||
formattedValue: string
|
||||
noData: boolean
|
||||
hidden: boolean
|
||||
toggleDisabled: boolean
|
||||
isPreviousPeriod?: boolean
|
||||
@@ -164,6 +171,7 @@ const props = defineProps<{
|
||||
chartStart: Date | null
|
||||
chartEnd: Date | null
|
||||
formattedTotal: string
|
||||
totalHasData: boolean
|
||||
entries: AnalyticsChartTooltipEntry[]
|
||||
containerWidth: number
|
||||
containerHeight: number
|
||||
|
||||
+28
-3
@@ -64,6 +64,7 @@
|
||||
:chart-start="chartRangeBounds?.start ?? null"
|
||||
:chart-end="chartRangeBounds?.end ?? null"
|
||||
:formatted-total="hoverFormattedTotal"
|
||||
:total-has-data="hoverHasData"
|
||||
:entries="hoverEntries"
|
||||
:container-width="containerSize.width"
|
||||
:container-height="containerSize.height"
|
||||
@@ -89,6 +90,7 @@ import type {
|
||||
AnalyticsGroupByPreset,
|
||||
} from '~/providers/analytics/analytics'
|
||||
|
||||
import { analyticsChartMessages } from '../../analytics-messages.ts'
|
||||
import type {
|
||||
AnalyticsChartLegendEntry,
|
||||
AnalyticsChartRangeBounds,
|
||||
@@ -188,7 +190,26 @@ const hoverTotalValue = computed(() => {
|
||||
}, 0)
|
||||
})
|
||||
|
||||
function hasDatasetDataAtSlice(dataset: ChartDataset | undefined, sliceIndex: number) {
|
||||
if (!dataset) return false
|
||||
return !dataset.lastDataPointUnavailable || sliceIndex !== dataset.data.length - 1
|
||||
}
|
||||
|
||||
const hoverHasData = computed(() => {
|
||||
if (hoverState.sliceIndex === null) return false
|
||||
const sliceIndex = hoverState.sliceIndex
|
||||
|
||||
return props.currentLegendEntries.some((legendEntry) => {
|
||||
if (legendEntry.hidden) return false
|
||||
const dataset = props.chartDatasetById.get(legendEntry.id)
|
||||
return hasDatasetDataAtSlice(dataset, sliceIndex)
|
||||
})
|
||||
})
|
||||
|
||||
const hoverFormattedTotal = computed(() => {
|
||||
if (!hoverHasData.value) {
|
||||
return formatMessage(analyticsChartMessages.noData)
|
||||
}
|
||||
if (props.isRatioMode) {
|
||||
return hoverTotalValue.value > 0 ? '100%' : '0%'
|
||||
}
|
||||
@@ -203,6 +224,7 @@ const hoverEntries = computed(() => {
|
||||
return props.legendEntries.map((legendEntry) => {
|
||||
const dataset = props.chartDatasetById.get(legendEntry.id)
|
||||
const value = dataset?.data[sliceIndex] ?? 0
|
||||
const hasData = hasDatasetDataAtSlice(dataset, sliceIndex)
|
||||
const ratioValue = legendEntry.hidden || totalValue === 0 ? 0 : (value / totalValue) * 100
|
||||
return {
|
||||
projectId: legendEntry.id,
|
||||
@@ -210,9 +232,12 @@ const hoverEntries = computed(() => {
|
||||
projectName: legendEntry.projectName,
|
||||
tooltip: legendEntry.tooltip,
|
||||
color: legendEntry.color,
|
||||
formattedValue: props.isRatioMode
|
||||
? `${ratioValue.toFixed(1)}%`
|
||||
: formatMetricValue(value, props.activeStat, formatNumber, formatMessage),
|
||||
formattedValue: hasData
|
||||
? props.isRatioMode
|
||||
? `${ratioValue.toFixed(1)}%`
|
||||
: formatMetricValue(value, props.activeStat, formatNumber, formatMessage)
|
||||
: formatMessage(analyticsChartMessages.noData),
|
||||
noData: !hasData,
|
||||
hidden: legendEntry.hidden,
|
||||
toggleDisabled: !legendEntry.hidden && isLegendEntryToggleDisabled(legendEntry),
|
||||
isPreviousPeriod: legendEntry.isPreviousPeriod,
|
||||
|
||||
+1
@@ -38,6 +38,7 @@ export type ChartDataset = {
|
||||
projectName?: string
|
||||
tooltip?: string
|
||||
data: number[]
|
||||
lastDataPointUnavailable?: boolean
|
||||
borderColor: string
|
||||
backgroundColor: string
|
||||
borderDash?: number[]
|
||||
|
||||
+27
-4
@@ -32,6 +32,7 @@ import {
|
||||
type ChartDataset,
|
||||
getChartDatasetTotal,
|
||||
getShortHourlyAxisTickLimit,
|
||||
getSliceBucketRange,
|
||||
getSliceCount,
|
||||
shouldCapitalizeBreakdownLabel,
|
||||
} from './analytics-chart-utils'
|
||||
@@ -161,8 +162,8 @@ export function useAnalyticsChartDatasets(
|
||||
)
|
||||
: undefined
|
||||
})
|
||||
const chartDatasetsByStat = computed<Record<AnalyticsDashboardStat, ChartDataset[]>>(() =>
|
||||
buildDatasetsByStat(
|
||||
const chartDatasetsByStat = computed<Record<AnalyticsDashboardStat, ChartDataset[]>>(() => {
|
||||
const datasets = buildDatasetsByStat(
|
||||
context.displayedTimeSlices.value,
|
||||
selectedProjects.value,
|
||||
legendPalette.value,
|
||||
@@ -175,8 +176,20 @@ export function useAnalyticsChartDatasets(
|
||||
showProjectVersionNames.value ? context.getVersionProjectName : undefined,
|
||||
formatMessage,
|
||||
sliceCount.value,
|
||||
),
|
||||
)
|
||||
)
|
||||
const nextFetchRequest = context.displayedFetchRequest.value
|
||||
if (
|
||||
nextFetchRequest &&
|
||||
context.displayedSelectedGroupBy.value === 'day' &&
|
||||
isLastBucketCurrentDay(nextFetchRequest.time_range, sliceCount.value)
|
||||
) {
|
||||
datasets.revenue = datasets.revenue.map((dataset) => ({
|
||||
...dataset,
|
||||
lastDataPointUnavailable: true,
|
||||
}))
|
||||
}
|
||||
return datasets
|
||||
})
|
||||
const previousChartDatasetsByStat = computed<Record<AnalyticsDashboardStat, ChartDataset[]>>(() =>
|
||||
buildDatasetsByStat(
|
||||
context.displayedPreviousTimeSlices.value,
|
||||
@@ -406,6 +419,16 @@ function buildDatasetsByStat(
|
||||
return datasetsByStat
|
||||
}
|
||||
|
||||
function isLastBucketCurrentDay(timeRange: Labrinth.Analytics.v3.TimeRange, sliceCount: number) {
|
||||
const lastBucket = getSliceBucketRange(timeRange, sliceCount, sliceCount - 1)
|
||||
const todayStart = new Date()
|
||||
todayStart.setHours(0, 0, 0, 0)
|
||||
const tomorrowStart = new Date(todayStart)
|
||||
tomorrowStart.setDate(tomorrowStart.getDate() + 1)
|
||||
|
||||
return lastBucket.start < tomorrowStart && lastBucket.end > todayStart
|
||||
}
|
||||
|
||||
function sortDatasetsByTotal(datasets: ChartDataset[]) {
|
||||
return [...datasets]
|
||||
.sort((a, b) => {
|
||||
|
||||
@@ -688,6 +688,10 @@ export const analyticsChartMessages = defineMessages({
|
||||
id: 'analytics.chart.tooltip.hide-entry',
|
||||
defaultMessage: 'Hide {name} in graph',
|
||||
},
|
||||
noData: {
|
||||
id: 'analytics.chart.tooltip.no-data',
|
||||
defaultMessage: 'No data',
|
||||
},
|
||||
durationDays: {
|
||||
id: 'analytics.chart.tooltip.duration.days',
|
||||
defaultMessage: '{count, plural, one {# day} other {# days}}',
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
|
||||
<div class="flex w-full flex-wrap items-center gap-2 md:w-auto">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
:icon="SearchIcon"
|
||||
:placeholder="formatMessage(analyticsTableMessages.searchPlaceholder)"
|
||||
@@ -193,10 +193,10 @@
|
||||
import { DownloadIcon, DropdownIcon, SearchIcon, UserIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Input,
|
||||
OverflowMenu,
|
||||
type OverflowMenuOption,
|
||||
Pagination,
|
||||
StyledInput,
|
||||
Table,
|
||||
useFormatNumber,
|
||||
useVIntl,
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
To confirm this transfer, type
|
||||
<span class="font-bold text-contrast">{{ organization.name }}</span> below
|
||||
</p>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="confirmationText"
|
||||
:placeholder="`Enter ${organization.name}`"
|
||||
wrapper-class="w-full"
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RightArrowIcon, TransferIcon, XIcon } from '@modrinth/assets'
|
||||
import { Admonition, Avatar, ButtonStyled, NewModal, StyledInput } from '@modrinth/ui'
|
||||
import { Admonition, Avatar, ButtonStyled, Input, NewModal } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
To confirm this transfer, type
|
||||
<span class="font-bold text-contrast">{{ project.name }}</span> below
|
||||
</p>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="confirmationText"
|
||||
:placeholder="`Enter ${project.name}`"
|
||||
wrapper-class="w-full"
|
||||
@@ -83,7 +83,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RightArrowIcon, TransferIcon, XIcon } from '@modrinth/assets'
|
||||
import { Admonition, Avatar, ButtonStyled, NewModal, StyledInput } from '@modrinth/ui'
|
||||
import { Admonition, Avatar, ButtonStyled, Input, NewModal } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
<template>
|
||||
<div v-if="shown">
|
||||
<div
|
||||
:class="{
|
||||
shown: actuallyShown,
|
||||
noblur: !$orElse(cosmetics.advancedRendering, true),
|
||||
}"
|
||||
class="modal-overlay"
|
||||
@click="hide"
|
||||
/>
|
||||
<div class="modal-container" :class="{ shown: actuallyShown }">
|
||||
<div class="modal-body">
|
||||
<div v-if="header" class="header">
|
||||
<strong>{{ header }}</strong>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<button @click="hide">
|
||||
<XIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { XIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled } from '@modrinth/ui'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ButtonStyled,
|
||||
XIcon,
|
||||
},
|
||||
props: {
|
||||
header: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const cosmetics = useCosmetics()
|
||||
|
||||
return { cosmetics }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
shown: false,
|
||||
actuallyShown: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.shown = true
|
||||
setTimeout(() => {
|
||||
this.actuallyShown = true
|
||||
}, 50)
|
||||
},
|
||||
hide() {
|
||||
this.actuallyShown = false
|
||||
setTimeout(() => {
|
||||
this.shown = false
|
||||
}, 300)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.modal-overlay {
|
||||
visibility: hidden;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 20;
|
||||
transition: all 0.3s ease-in-out;
|
||||
|
||||
&.shown {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
background: hsla(0, 0%, 0%, 0.5);
|
||||
backdrop-filter: blur(3px);
|
||||
}
|
||||
|
||||
&.noblur {
|
||||
backdrop-filter: none;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 21;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
|
||||
&.shown {
|
||||
visibility: visible;
|
||||
|
||||
.modal-body {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
position: fixed;
|
||||
box-shadow: var(--shadow-raised), var(--shadow-inset);
|
||||
border-radius: var(--size-rounded-lg);
|
||||
max-height: calc(100% - 2 * var(--spacing-card-bg));
|
||||
overflow-y: auto;
|
||||
width: 600px;
|
||||
pointer-events: auto;
|
||||
outline: 3px solid transparent;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: var(--color-bg);
|
||||
padding: var(--spacing-card-md) var(--spacing-card-lg);
|
||||
|
||||
strong {
|
||||
font-size: 1.25rem;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: var(--color-raised-bg);
|
||||
}
|
||||
|
||||
transform: translateY(50vh);
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: all 0.25s ease-in-out;
|
||||
|
||||
@media screen and (max-width: 650px) {
|
||||
width: calc(100% - 2 * var(--spacing-card-bg));
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -11,7 +11,7 @@
|
||||
>
|
||||
<BookmarkIcon aria-hidden="true" :fill="saved ? 'currentColor' : 'none'" />
|
||||
<template #menu>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="displayCollectionsSearch"
|
||||
:placeholder="formatMessage(commonMessages.searchPlaceholder)"
|
||||
wrapper-class="menu-search"
|
||||
@@ -52,14 +52,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { BookmarkIcon, PlusIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
commonMessages,
|
||||
PopoutMenu,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { ButtonStyled, Checkbox, commonMessages, Input, PopoutMenu, useVIntl } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
import type { RouteLocationRaw } from 'vue-router'
|
||||
|
||||
|
||||
@@ -86,10 +86,17 @@ export function provideDownloadModalProvider(
|
||||
const tags = useGeneratedState()
|
||||
|
||||
const shouldResolveDependencies = computed(
|
||||
() => !!options.project.value && !!options.selectedVersion.value,
|
||||
() =>
|
||||
!!options.project.value &&
|
||||
!!options.selectedVersion.value &&
|
||||
!!options.currentGameVersion.value,
|
||||
)
|
||||
const dependencyResolutionPreferences = computed(() =>
|
||||
createResolutionPreferences(options.selectedVersion.value, options.currentPlatform.value),
|
||||
createResolutionPreferences(
|
||||
options.selectedVersion.value,
|
||||
options.currentGameVersion.value,
|
||||
options.currentPlatform.value,
|
||||
),
|
||||
)
|
||||
|
||||
const { data: dependencyResolution, isFetching: dependencyResolutionFetching } = useQuery({
|
||||
@@ -309,10 +316,12 @@ export function provideDownloadModalProvider(
|
||||
})
|
||||
|
||||
async function preloadDependenciesForSelection(selection: ProjectDownloadSelection) {
|
||||
if (!options.project.value || !selection.selectedVersion) return
|
||||
if (!options.project.value || !selection.selectedVersion || !selection.currentGameVersion)
|
||||
return
|
||||
|
||||
const preferences = createResolutionPreferences(
|
||||
selection.selectedVersion,
|
||||
selection.currentGameVersion,
|
||||
selection.currentPlatform,
|
||||
)
|
||||
|
||||
@@ -548,10 +557,11 @@ function dependencyProjectsQueryOptions(
|
||||
|
||||
function createResolutionPreferences(
|
||||
version: Labrinth.Versions.v3.Version | null,
|
||||
currentGameVersion: string | null,
|
||||
currentPlatform: string | null,
|
||||
): Labrinth.Content.v3.ResolutionPreferences {
|
||||
return {
|
||||
game_versions: version?.game_versions || [],
|
||||
game_versions: currentGameVersion ? [currentGameVersion] : [],
|
||||
loaders: currentPlatform ? [currentPlatform] : version?.loaders || [],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import {
|
||||
ButtonStyled,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
NewModal,
|
||||
ServerNotice,
|
||||
StyledInput,
|
||||
TagItem,
|
||||
} from '@modrinth/ui'
|
||||
import { ref } from 'vue'
|
||||
@@ -175,7 +175,7 @@ defineExpose({ show, hide })
|
||||
<span v-else class="mb-2"> No nodes assigned yet </span>
|
||||
</div>
|
||||
<div class="flex w-[45rem] items-center gap-2">
|
||||
<StyledInput
|
||||
<Input
|
||||
id="server-assign-field"
|
||||
v-model="inputField"
|
||||
wrapper-class="w-full"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<label for="days" class="flex flex-col gap-1">
|
||||
<span class="text-lg font-semibold text-contrast"> Days to credit </span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="days"
|
||||
v-model="days"
|
||||
wrapper-class="w-32"
|
||||
@@ -36,12 +36,7 @@
|
||||
<span class="text-lg font-semibold text-contrast"> Node hostnames </span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<StyledInput
|
||||
id="node-input"
|
||||
v-model="nodeInput"
|
||||
wrapper-class="w-32"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<Input id="node-input" v-model="nodeInput" wrapper-class="w-32" autocomplete="off" />
|
||||
<ButtonStyled color="blue" color-fill="text">
|
||||
<button class="shrink-0" @click="addNode">
|
||||
<PlusIcon />
|
||||
@@ -92,13 +87,7 @@
|
||||
class="text-muted flex flex-col gap-2 rounded-lg border border-surface-5 bg-button-bg p-4"
|
||||
>
|
||||
<span>Hi {user.name},</span>
|
||||
<StyledInput
|
||||
id="message-batch"
|
||||
v-model="message"
|
||||
multiline
|
||||
:rows="3"
|
||||
input-class="!bg-surface-3"
|
||||
/>
|
||||
<Textarea id="message-batch" v-model="message" :rows="3" input-class="!bg-surface-3" />
|
||||
<span>
|
||||
To make up for it, we've added {{ days }} day{{ pluralize(days) }} to your Modrinth
|
||||
Servers subscription.
|
||||
@@ -135,9 +124,10 @@ import {
|
||||
Combobox,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
NewModal,
|
||||
StyledInput,
|
||||
TagItem,
|
||||
Textarea,
|
||||
Toggle,
|
||||
} from '@modrinth/ui'
|
||||
import { DEFAULT_CREDIT_EMAIL_MESSAGE } from '@modrinth/utils/utils.ts'
|
||||
|
||||
@@ -25,10 +25,9 @@
|
||||
</span>
|
||||
<span>Server IDs (one per line or comma-separated.)</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Textarea
|
||||
id="server-ids"
|
||||
v-model="serverIdsInput"
|
||||
multiline
|
||||
:rows="4"
|
||||
input-class="bg-surface-3"
|
||||
placeholder="123e4569-e89b-12d3-a456-426614174005 123e9569-e89b-12d3-a456-413678919876"
|
||||
@@ -48,7 +47,7 @@
|
||||
<span>Add nodes to transfer (comma or space-separated).</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<StyledInput
|
||||
<Input
|
||||
id="node-input"
|
||||
v-model="nodeInput"
|
||||
wrapper-class="w-64"
|
||||
@@ -86,7 +85,7 @@
|
||||
<span class="text-lg font-semibold text-contrast">Tag transferred nodes</span>
|
||||
<span>Optional tag to add to the transferred nodes.</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="tag-nodes"
|
||||
v-model="tagNodes"
|
||||
wrapper-class="max-w-[12rem]"
|
||||
@@ -114,7 +113,7 @@
|
||||
<span>Optional preferred node tags for node selection.</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<StyledInput
|
||||
<Input
|
||||
id="tag-input"
|
||||
v-model="tagInput"
|
||||
wrapper-class="w-40"
|
||||
@@ -147,7 +146,7 @@
|
||||
:format-label="(item) => scheduleOptionLabels[item]"
|
||||
:capitalize="false"
|
||||
/>
|
||||
<StyledInput
|
||||
<DateInput
|
||||
v-if="scheduleOption === 'later'"
|
||||
v-model="scheduledDate"
|
||||
type="datetime-local"
|
||||
@@ -164,10 +163,9 @@
|
||||
</span>
|
||||
<span>Provide a reason for this transfer batch.</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Textarea
|
||||
id="reason"
|
||||
v-model="reason"
|
||||
multiline
|
||||
:rows="2"
|
||||
input-class="bg-surface-3"
|
||||
placeholder="Node maintenance scheduled"
|
||||
@@ -198,11 +196,13 @@ import {
|
||||
ButtonStyled,
|
||||
Chips,
|
||||
Combobox,
|
||||
DateInput,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
NewModal,
|
||||
StyledInput,
|
||||
TagItem,
|
||||
Textarea,
|
||||
Toggle,
|
||||
} from '@modrinth/ui'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<label class="text-md font-semibold text-contrast" for="create-account-username">
|
||||
{{ formatMessage(messages.usernameOptionalLabel) }}
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="create-account-username"
|
||||
v-model="usernameModel"
|
||||
type="text"
|
||||
@@ -106,7 +106,7 @@ import {
|
||||
DatePicker,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
StyledInput,
|
||||
Input,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
<div class="text-md font-semibold text-contrast">
|
||||
{{ formatMessage(messages.passkeyNameLabel) }}
|
||||
</div>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="passkey-name"
|
||||
v-model="pendingPasskeyName"
|
||||
:maxlength="255"
|
||||
@@ -150,7 +150,7 @@
|
||||
<div class="text-md font-semibold text-contrast">
|
||||
{{ formatMessage(messages.passkeyNameLabel) }}
|
||||
</div>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="passkey-rename"
|
||||
v-model="passkeyToRenameName"
|
||||
:maxlength="255"
|
||||
@@ -215,8 +215,8 @@ import {
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
NewModal,
|
||||
StyledInput,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
{{ formatMessage(messages.twoFactorCodeLabelDescription) }}
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="two-factor-code"
|
||||
v-model="twoFactorCodeModel"
|
||||
:maxlength="11"
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
<section class="mx-auto flex w-full flex-col gap-2.5">
|
||||
<label for="email" hidden>{{ formatMessage(commonMessages.emailUsernameLabel) }}</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="email"
|
||||
v-model="emailModel"
|
||||
:icon="MailIcon"
|
||||
@@ -101,7 +101,7 @@
|
||||
/>
|
||||
|
||||
<label for="password" hidden>{{ formatMessage(commonMessages.passwordLabel) }}</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="password"
|
||||
v-model="passwordModel"
|
||||
:icon="KeyIcon"
|
||||
@@ -167,7 +167,7 @@ import {
|
||||
SteamColorIcon,
|
||||
UserKeyIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { ButtonStyled, commonMessages, defineMessages, StyledInput, useVIntl } from '@modrinth/ui'
|
||||
import { ButtonStyled, commonMessages, defineMessages, Input, useVIntl } from '@modrinth/ui'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { computed } from 'vue'
|
||||
import type { LocationQuery } from 'vue-router'
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
|
||||
<section class="flex flex-col gap-2.5">
|
||||
<label for="email" hidden>{{ formatMessage(commonMessages.emailLabel) }}</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="email"
|
||||
v-model="emailModel"
|
||||
:icon="MailIcon"
|
||||
@@ -95,7 +95,7 @@
|
||||
/>
|
||||
|
||||
<label for="password" hidden>{{ formatMessage(commonMessages.passwordLabel) }}</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="password"
|
||||
v-model="passwordModel"
|
||||
:icon="KeyIcon"
|
||||
@@ -162,8 +162,8 @@ import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
Input,
|
||||
IntlFormatted,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
:icon="SearchIcon"
|
||||
type="text"
|
||||
@@ -68,7 +68,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { SearchIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, Chips, StyledInput } from '@modrinth/ui'
|
||||
import { ButtonStyled, Chips, Input } from '@modrinth/ui'
|
||||
import { useMagicKeys } from '@vueuse/core'
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<span class="font-semibold text-contrast">
|
||||
{{ formatMessage(messages.versionNumber) }} <span class="text-red">*</span>
|
||||
</span>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="version-number"
|
||||
v-model="draftVersion.version_number"
|
||||
:disabled="isUploading"
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="font-semibold text-contrast">{{ formatMessage(messages.versionSubtitle) }}</span>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="version-number"
|
||||
v-model="draftVersion.name"
|
||||
:placeholder="formatMessage(messages.versionSubtitlePlaceholder)"
|
||||
@@ -66,8 +66,8 @@
|
||||
import {
|
||||
Chips,
|
||||
defineMessages,
|
||||
Input,
|
||||
MarkdownEditor,
|
||||
StyledInput,
|
||||
Tabs,
|
||||
type TabsTab,
|
||||
useVIntl,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<span class="text-brand-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="name"
|
||||
v-model="name"
|
||||
:maxlength="64"
|
||||
@@ -25,10 +25,9 @@
|
||||
}}</span>
|
||||
<span>{{ formatMessage(messages.summaryDescription) }}</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Textarea
|
||||
id="additional-information"
|
||||
v-model="description"
|
||||
multiline
|
||||
:maxlength="256"
|
||||
:placeholder="formatMessage(messages.summaryPlaceholder)"
|
||||
:disabled="hasHitLimit"
|
||||
@@ -61,8 +60,9 @@ import {
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
NewModal,
|
||||
StyledInput,
|
||||
Textarea,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<span class="text-brand-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="name"
|
||||
v-model="name"
|
||||
:maxlength="64"
|
||||
@@ -28,7 +28,7 @@
|
||||
</label>
|
||||
<div class="text-input-wrapper">
|
||||
<div class="text-input-wrapper__before">https://modrinth.com/organization/</div>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="slug"
|
||||
v-model="slug"
|
||||
:maxlength="64"
|
||||
@@ -46,10 +46,9 @@
|
||||
</span>
|
||||
<span>{{ formatMessage(messages.summaryDescription) }}</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Textarea
|
||||
id="additional-information"
|
||||
v-model="description"
|
||||
multiline
|
||||
:maxlength="256"
|
||||
:placeholder="formatMessage(messages.summaryPlaceholder)"
|
||||
:disabled="hasHitLimit"
|
||||
@@ -83,8 +82,9 @@ import {
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
NewModal,
|
||||
StyledInput,
|
||||
Textarea,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { ref } from 'vue'
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
{{ formatMessage(messages.nameLabel) }}
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="name"
|
||||
v-model="name"
|
||||
:maxlength="64"
|
||||
@@ -48,7 +48,7 @@
|
||||
</span>
|
||||
<div class="text-input-wrapper !w-full">
|
||||
<div class="text-input-wrapper__before">https://modrinth.com/project/</div>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="slug"
|
||||
v-model="slug"
|
||||
:maxlength="64"
|
||||
@@ -100,10 +100,9 @@
|
||||
{{ formatMessage(messages.summaryLabel) }}
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Textarea
|
||||
id="additional-information"
|
||||
v-model="description"
|
||||
multiline
|
||||
:maxlength="256"
|
||||
:placeholder="formatMessage(messages.summaryPlaceholder)"
|
||||
:disabled="hasHitLimit"
|
||||
@@ -140,8 +139,9 @@ import {
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
NewModal,
|
||||
StyledInput,
|
||||
Textarea,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed, defineAsyncComponent, h } from 'vue'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="relative flex-1">
|
||||
<StyledInput
|
||||
<Input
|
||||
ref="amountInput"
|
||||
:model-value="modelValue"
|
||||
type="number"
|
||||
@@ -12,7 +12,7 @@
|
||||
:disabled="isDisabled"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.amountPlaceholder)"
|
||||
wrapper-class="w-full"
|
||||
@update:model-value="handleStyledInput"
|
||||
@update:model-value="handleInput"
|
||||
/>
|
||||
</div>
|
||||
<Combobox
|
||||
@@ -54,7 +54,7 @@ import {
|
||||
Combobox,
|
||||
commonMessages,
|
||||
formFieldPlaceholders,
|
||||
StyledInput,
|
||||
Input,
|
||||
useFormatMoney,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -83,7 +83,7 @@ const emit = defineEmits<{
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatMoney = useFormatMoney()
|
||||
const amountInput = ref<InstanceType<typeof StyledInput> | null>(null)
|
||||
const amountInput = ref<InstanceType<typeof Input> | null>(null)
|
||||
|
||||
const safeMaxAmount = computed(() => {
|
||||
return Math.max(0, props.maxAmount)
|
||||
@@ -108,7 +108,7 @@ function setMaxAmount() {
|
||||
emit('update:modelValue', maxValue)
|
||||
}
|
||||
|
||||
function handleStyledInput(val: string | number) {
|
||||
function handleInput(val: string | number) {
|
||||
const value = String(val)
|
||||
|
||||
if (value && value.includes('.')) {
|
||||
|
||||
+2
-2
@@ -41,7 +41,7 @@
|
||||
>
|
||||
</label>
|
||||
<div class="flex flex-row gap-2">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="venmoHandle"
|
||||
:placeholder="formatMessage(messages.venmoHandlePlaceholder)"
|
||||
wrapper-class="w-full"
|
||||
@@ -110,9 +110,9 @@ import {
|
||||
defineMessages,
|
||||
financialMessages,
|
||||
formFieldLabels,
|
||||
Input,
|
||||
IntlFormatted,
|
||||
normalizeChildren,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
</span>
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:gap-4">
|
||||
<div class="flex flex-1 flex-col gap-2.5">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="formData.bankAccountOwnerFirstName"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.firstNamePlaceholder)"
|
||||
autocomplete="given-name"
|
||||
@@ -64,7 +64,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col gap-2.5">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="formData.bankAccountOwnerLastName"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.lastNamePlaceholder)"
|
||||
autocomplete="family-name"
|
||||
@@ -91,7 +91,7 @@
|
||||
class="h-10"
|
||||
/>
|
||||
|
||||
<StyledInput
|
||||
<Input
|
||||
v-else
|
||||
v-model="formData.bankName"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.bankNamePlaceholder)"
|
||||
@@ -108,7 +108,7 @@
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<StyledInput
|
||||
<Input
|
||||
v-if="['text', 'email', 'tel'].includes(field.type)"
|
||||
v-model="formData[field.name]"
|
||||
:type="field.type === 'tel' ? undefined : field.type === 'text' ? undefined : field.type"
|
||||
@@ -158,7 +158,7 @@
|
||||
<span v-if="dynamicDocumentNumberField.required" class="text-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="formData.documentNumber"
|
||||
:placeholder="dynamicDocumentNumberField.placeholder"
|
||||
autocomplete="off"
|
||||
@@ -238,9 +238,9 @@ import {
|
||||
getBlockchainIcon,
|
||||
getCurrencyColor,
|
||||
getCurrencyIcon,
|
||||
Input,
|
||||
IntlFormatted,
|
||||
normalizeChildren,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<span class="text-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="formData.businessName"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.businessNamePlaceholder)"
|
||||
autocomplete="organization"
|
||||
@@ -47,7 +47,7 @@
|
||||
<span class="text-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="formData.email"
|
||||
type="email"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.emailPlaceholder)"
|
||||
@@ -65,7 +65,7 @@
|
||||
<span class="text-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="formData.firstName"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.firstNamePlaceholder)"
|
||||
autocomplete="given-name"
|
||||
@@ -79,7 +79,7 @@
|
||||
<span class="text-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="formData.lastName"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.lastNamePlaceholder)"
|
||||
autocomplete="family-name"
|
||||
@@ -112,7 +112,7 @@
|
||||
<span class="text-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="formData.physicalAddress.address1"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.addressPlaceholder)"
|
||||
autocomplete="address-line1"
|
||||
@@ -126,7 +126,7 @@
|
||||
{{ formatMessage(formFieldLabels.addressLine2) }}
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="formData.physicalAddress.address2"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.address2Placeholder)"
|
||||
autocomplete="address-line2"
|
||||
@@ -142,7 +142,7 @@
|
||||
<span class="text-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="formData.physicalAddress.city"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.cityPlaceholder)"
|
||||
autocomplete="address-level2"
|
||||
@@ -164,7 +164,7 @@
|
||||
searchable
|
||||
:search-placeholder="formatMessage(messages.searchSubdivisionsPlaceholder)"
|
||||
/>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-else
|
||||
v-model="formData.physicalAddress.state"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.statePlaceholder)"
|
||||
@@ -182,7 +182,7 @@
|
||||
<span class="text-red">*</span>
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="formData.physicalAddress.zip"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.postalCodePlaceholder)"
|
||||
autocomplete="postal-code"
|
||||
@@ -217,7 +217,7 @@ import {
|
||||
defineMessages,
|
||||
formFieldLabels,
|
||||
formFieldPlaceholders,
|
||||
StyledInput,
|
||||
Input,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
// TODO: Switch to using Muralpay's improved endpoint when it's available.
|
||||
|
||||
+3
-3
@@ -39,7 +39,7 @@
|
||||
>{{ formatMessage(formFieldLabels.email) }} <span class="text-red">*</span></span
|
||||
>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="deliveryEmail"
|
||||
type="email"
|
||||
:placeholder="formatMessage(formFieldPlaceholders.emailPlaceholder)"
|
||||
@@ -131,7 +131,7 @@
|
||||
|
||||
<div v-if="showGiftCardSelector && useFixedDenominations" class="flex flex-col gap-2.5">
|
||||
<template v-if="useDenominationSuggestions">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="denominationSearchInput"
|
||||
type="number"
|
||||
:icon="SearchIcon"
|
||||
@@ -321,10 +321,10 @@ import {
|
||||
financialMessages,
|
||||
formFieldLabels,
|
||||
formFieldPlaceholders,
|
||||
Input,
|
||||
IntlFormatted,
|
||||
normalizeChildren,
|
||||
paymentMethodMessages,
|
||||
StyledInput,
|
||||
useDebugLogger,
|
||||
useFormatMoney,
|
||||
useVIntl,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<form class="flex flex-col gap-2 sm:flex-row" @submit.prevent="executeSearch">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="query"
|
||||
:icon="SearchIcon"
|
||||
type="text"
|
||||
@@ -128,8 +128,8 @@ import {
|
||||
EmptyState,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
Pagination,
|
||||
StyledInput,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
import GlobalDetailLocalTraceCard from '~/components/ui/moderation/GlobalDetailLocalTraceCard.vue'
|
||||
|
||||
@@ -218,10 +218,9 @@
|
||||
:heading-buttons="false"
|
||||
:on-image-upload="onUploadHandler"
|
||||
/>
|
||||
<StyledInput
|
||||
<Textarea
|
||||
v-else
|
||||
v-model="messageText"
|
||||
multiline
|
||||
placeholder="No message generated."
|
||||
autocomplete="off"
|
||||
input-class="h-[400px] font-mono"
|
||||
@@ -414,7 +413,7 @@ import {
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
MarkdownEditor,
|
||||
StyledInput,
|
||||
Textarea,
|
||||
useDebugLogger,
|
||||
} from '@modrinth/ui'
|
||||
import TeleportOverflowMenu from '@modrinth/ui/src/components/base/TeleportOverflowMenu.vue'
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<label for="proof">
|
||||
<span class="label__title">Proof</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="proof"
|
||||
v-model="(modPackData[currentIndex] as ModerationUnknownModpackItem).proof"
|
||||
autocomplete="off"
|
||||
@@ -44,7 +44,7 @@
|
||||
<label for="link">
|
||||
<span class="label__title">Link</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="link"
|
||||
v-model="(modPackData[currentIndex] as ModerationUnknownModpackItem).url"
|
||||
autocomplete="off"
|
||||
@@ -54,7 +54,7 @@
|
||||
<label for="title">
|
||||
<span class="label__title">Title</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="title"
|
||||
v-model="(modPackData[currentIndex] as ModerationUnknownModpackItem).title"
|
||||
autocomplete="off"
|
||||
@@ -143,7 +143,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { LeftArrowIcon, RightArrowIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, StyledInput } from '@modrinth/ui'
|
||||
import { ButtonStyled, Input } from '@modrinth/ui'
|
||||
import type {
|
||||
ModerationFlameModpackItem,
|
||||
ModerationJudgements,
|
||||
|
||||
@@ -30,11 +30,11 @@ import {
|
||||
Checkbox,
|
||||
Combobox,
|
||||
injectProjectPageContext,
|
||||
Input,
|
||||
MarkdownEditor,
|
||||
StyledInput,
|
||||
} from '@modrinth/ui'
|
||||
import { renderHighlightedString, renderString } from '@modrinth/utils'
|
||||
import { inject, nextTick, onMounted, reactive, watchEffect } from 'vue'
|
||||
import { inject, onMounted, reactive, watchEffect } from 'vue'
|
||||
|
||||
import { NODE_META_KEY, STATE_KEY } from './checklist-context'
|
||||
|
||||
@@ -266,7 +266,6 @@ function setTextState(node: IdentifiedNodeBuilder, v: string): void {
|
||||
const def = resolveDefault(node as ValueNodeBuilder)
|
||||
const defStr = typeof def === 'string' ? def : ''
|
||||
setNodeState(node, ov === defStr ? undefined : ov || undefined)
|
||||
nextTick(() => textInputRefs.get(node)?.setValue(ov))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -277,7 +276,7 @@ function setTextState(node: IdentifiedNodeBuilder, v: string): void {
|
||||
|
||||
function handleButtonClick(node: ButtonNodeBuilder): void {
|
||||
const before = new Map<NodeBuilder, string>()
|
||||
for (const inputNode of textInputRefs.keys()) {
|
||||
for (const inputNode of textInputNodes) {
|
||||
before.set(inputNode, getTextState(asIdentified(inputNode)))
|
||||
}
|
||||
node._onClick?.(props.showContext)
|
||||
@@ -288,7 +287,7 @@ function handleButtonClick(node: ButtonNodeBuilder): void {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
for (const inputNode of textInputRefs.keys()) {
|
||||
for (const inputNode of textInputNodes) {
|
||||
setTextState(asIdentified(inputNode), getTextState(asIdentified(inputNode)))
|
||||
}
|
||||
})
|
||||
@@ -325,7 +324,7 @@ function toggleChip(parent: IdentifiedNodeBuilder, child: IdentifiedNodeBuilder)
|
||||
setNodeState(parent, selected.size > 0 ? selected : undefined)
|
||||
}
|
||||
|
||||
const textInputRefs = new Map<NodeBuilder, { setValue: (v: string) => void }>()
|
||||
const textInputNodes = new Set<NodeBuilder>()
|
||||
const overrideHelpers = { override: (v: string): OverrideValue => ({ __override: v }) }
|
||||
|
||||
function isOverrideValue(v: unknown): v is OverrideValue {
|
||||
@@ -720,9 +719,9 @@ watchEffect(async () => {
|
||||
|
||||
<!-- text -->
|
||||
<template v-else-if="item.type === 'text'">
|
||||
<StyledInput
|
||||
<Input
|
||||
:id="`node-${asIdentified(item).id}`"
|
||||
:ref="(el: any) => (el ? textInputRefs.set(item, el) : textInputRefs.delete(item))"
|
||||
:ref="(el: any) => (el ? textInputNodes.add(item) : textInputNodes.delete(item))"
|
||||
v-tooltip="getTooltipConfig(item, showContext)"
|
||||
:model-value="getTextState(asIdentified(item))"
|
||||
:placeholder="getPlaceholder(asInput(item))"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
'no-actions': noLinks,
|
||||
private: isPrivateMessage,
|
||||
'show-private-bg': settings.get(moderationSettings.General.PrivateMessageHighlight),
|
||||
'show-info-bg mb-2 !flex-nowrap py-6': message.body.type === 'legacy_project_message',
|
||||
}"
|
||||
>
|
||||
<template v-if="members[message.author_id]">
|
||||
@@ -51,23 +52,28 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<div
|
||||
class="message__icon backed-svg circle moderation-color"
|
||||
class="message__icon backed-svg circle moderation-color shrink-0"
|
||||
:class="{
|
||||
raised: raised,
|
||||
'system-message-icon': [
|
||||
'legacy_project_message',
|
||||
'tech_review_entered',
|
||||
'tech_review_exited',
|
||||
'tech_review_exit_file_deleted',
|
||||
].includes(message.body.type),
|
||||
}"
|
||||
>
|
||||
<ScaleIcon />
|
||||
<InfoIcon v-if="message.body.type === 'legacy_project_message'" class="text-blue" />
|
||||
<ScaleIcon v-else />
|
||||
</div>
|
||||
<span
|
||||
v-if="
|
||||
!['tech_review_entered', 'tech_review_exited', 'tech_review_exit_file_deleted'].includes(
|
||||
message.body.type,
|
||||
)
|
||||
![
|
||||
'legacy_project_message',
|
||||
'tech_review_entered',
|
||||
'tech_review_exited',
|
||||
'tech_review_exit_file_deleted',
|
||||
].includes(message.body.type)
|
||||
"
|
||||
class="message__author moderation-color"
|
||||
>
|
||||
@@ -81,6 +87,10 @@
|
||||
v-html="formattedMessage"
|
||||
/>
|
||||
<div v-else class="message__body status-message">
|
||||
<span v-if="message.body.type === 'legacy_project_message'">
|
||||
This project was published on Modrinth before moderation threads existed and may be missing
|
||||
moderation history.
|
||||
</span>
|
||||
<span v-if="message.body.type === 'deleted'"> posted a message that has been deleted. </span>
|
||||
<template v-else-if="message.body.type === 'status_change'">
|
||||
<span v-if="message.body.new_status === 'processing'">
|
||||
@@ -114,7 +124,7 @@
|
||||
the user.
|
||||
</span>
|
||||
</div>
|
||||
<span class="message__date">
|
||||
<span class="message__date shrink-0">
|
||||
<span v-tooltip="formatDateTime(message.created)">
|
||||
{{ timeSincePosted }}
|
||||
</span>
|
||||
@@ -143,6 +153,7 @@
|
||||
<script setup>
|
||||
import {
|
||||
EyeOffIcon,
|
||||
InfoIcon,
|
||||
MicrophoneIcon,
|
||||
ModrinthIcon,
|
||||
MoreHorizontalIcon,
|
||||
@@ -293,6 +304,15 @@ async function deleteMessage() {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.show-info-bg::before {
|
||||
content: '';
|
||||
inset: 0;
|
||||
position: absolute;
|
||||
background-color: var(--color-blue);
|
||||
opacity: 0.05;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.no-actions {
|
||||
padding: 0;
|
||||
|
||||
|
||||
@@ -30,6 +30,29 @@ const normalizeAuthToken = (value: unknown) => {
|
||||
return ''
|
||||
}
|
||||
|
||||
const getErrorStatus = (error: unknown): number | undefined => {
|
||||
if (!error || typeof error !== 'object') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const typedError = error as { statusCode?: unknown; status?: unknown }
|
||||
const status = typedError.statusCode ?? typedError.status
|
||||
|
||||
return typeof status === 'number' ? status : undefined
|
||||
}
|
||||
|
||||
// only when labrinth actually gives us an auth error
|
||||
const isAuthFailure = (error: unknown): boolean => {
|
||||
const status = getErrorStatus(error)
|
||||
return status === 401 || status === 403
|
||||
}
|
||||
|
||||
const clearAuthCookie = (auth: AuthState, authCookie: { value: string | null }) => {
|
||||
authCookie.value = null
|
||||
auth.token = ''
|
||||
auth.user = null
|
||||
}
|
||||
|
||||
const getQueryString = (value: QueryValue) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value[0] ?? null
|
||||
@@ -90,6 +113,7 @@ export const initAuth = async (oldToken: string | null | undefined = null) => {
|
||||
}
|
||||
|
||||
const tokenStr = normalizeAuthToken(authCookie.value)
|
||||
let shouldRefresh = false
|
||||
|
||||
if (authCookie.value != null && tokenStr === '') {
|
||||
authCookie.value = null
|
||||
@@ -111,12 +135,13 @@ export const initAuth = async (oldToken: string | null | undefined = null) => {
|
||||
},
|
||||
true,
|
||||
)) as Labrinth.Users.v2.User
|
||||
} catch {
|
||||
/* empty */
|
||||
} catch (error) {
|
||||
// only refresh when the token was rejected. not on timeouts or other errors (think this was the cause of random logouts)
|
||||
shouldRefresh = isAuthFailure(error)
|
||||
}
|
||||
}
|
||||
|
||||
if (!auth.user && auth.token) {
|
||||
if (!auth.user && auth.token && shouldRefresh) {
|
||||
try {
|
||||
const session = (await useBaseFetch(
|
||||
'session/refresh',
|
||||
@@ -132,22 +157,29 @@ export const initAuth = async (oldToken: string | null | undefined = null) => {
|
||||
auth.token = normalizeAuthToken(session.session)
|
||||
if (auth.token) {
|
||||
authCookie.value = auth.token
|
||||
auth.user = (await useBaseFetch(
|
||||
'user',
|
||||
{
|
||||
apiVersion: 3,
|
||||
headers: {
|
||||
Authorization: auth.token,
|
||||
try {
|
||||
auth.user = (await useBaseFetch(
|
||||
'user',
|
||||
{
|
||||
apiVersion: 3,
|
||||
headers: {
|
||||
Authorization: auth.token,
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
)) as Labrinth.Users.v2.User
|
||||
true,
|
||||
)) as Labrinth.Users.v2.User
|
||||
} catch (error) {
|
||||
if (isAuthFailure(error)) {
|
||||
clearAuthCookie(auth, authCookie)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
authCookie.value = null
|
||||
auth.token = ''
|
||||
clearAuthCookie(auth, authCookie)
|
||||
}
|
||||
} catch (error) {
|
||||
if (isAuthFailure(error)) {
|
||||
clearAuthCookie(auth, authCookie)
|
||||
}
|
||||
} catch {
|
||||
authCookie.value = null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,10 @@ const useModerationCookies = () => {
|
||||
moderationKeybindsId,
|
||||
getCookieOptions(),
|
||||
)
|
||||
const optionsCookie = useCookie<Partial<StoredOptions>>(moderationSettingsId, getCookieOptions())
|
||||
const optionsCookie = useCookie<Partial<StoredOptions> | null>(
|
||||
moderationSettingsId,
|
||||
getCookieOptions(),
|
||||
)
|
||||
|
||||
if (keybindCookie.value && !optionsCookie.value) {
|
||||
optionsCookie.value = {
|
||||
@@ -45,16 +48,17 @@ const useModerationCookies = () => {
|
||||
const useModerationOptions = () =>
|
||||
useState<{ keybinds: StoredKeybinds; settings: StoredSettings }>(moderationKeybindsId, () => {
|
||||
const cookie = useModerationCookies()
|
||||
const stored = cookie.value ?? {}
|
||||
|
||||
const keybindOutput: StoredKeybinds = {}
|
||||
|
||||
for (const [id, definition] of Object.entries(cookie.value.keybinds || {})) {
|
||||
for (const [id, definition] of Object.entries(stored.keybinds || {})) {
|
||||
if (!definition) continue
|
||||
keybindOutput[id] = definition
|
||||
}
|
||||
|
||||
const settingsOutput: StoredSettings = {}
|
||||
for (const [id, setting] of Object.entries(cookie.value.settings || {})) {
|
||||
for (const [id, setting] of Object.entries(stored.settings || {})) {
|
||||
settingsOutput[id] = setting
|
||||
}
|
||||
|
||||
|
||||
@@ -2744,24 +2744,15 @@
|
||||
"settings.applications.about": {
|
||||
"message": "O"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Zrušit"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Vytvořit aplikaci"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Smazat"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Upravit"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Nová aplikace"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Uložit změny"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Nahrát ikonu"
|
||||
},
|
||||
|
||||
@@ -2945,24 +2945,15 @@
|
||||
"settings.applications.button.add-more": {
|
||||
"message": "Tilføj mere"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Annuller"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Opret app"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Slet"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Rediger"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Ny applikation"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Gem ændringer"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Upload ikon"
|
||||
},
|
||||
|
||||
@@ -4679,24 +4679,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Weiterleitungs-URI hinzufügen"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Abbrechen"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "App erstellen"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Löschen"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Bearbeiten"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Neue Anwendung"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Änderungen speichern"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Icon hochladen"
|
||||
},
|
||||
|
||||
@@ -4679,24 +4679,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Weiterleitungs-URI hinzufügen"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Abbrechen"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Anwendung erstellen"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Löschen"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Bearbeiten"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Neue Anwendung"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Änderungen speichern"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Icon hochladen"
|
||||
},
|
||||
|
||||
@@ -191,6 +191,9 @@
|
||||
"analytics.chart.tooltip.hide-entry": {
|
||||
"message": "Hide {name} in graph"
|
||||
},
|
||||
"analytics.chart.tooltip.no-data": {
|
||||
"message": "No data"
|
||||
},
|
||||
"analytics.chart.tooltip.pinned": {
|
||||
"message": "Chart tooltip pinned"
|
||||
},
|
||||
@@ -4709,24 +4712,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Add a redirect URI"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Cancel"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Create app"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Delete"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Edit"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "New application"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Save changes"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Upload icon"
|
||||
},
|
||||
|
||||
@@ -4187,24 +4187,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Agregar un URI de redirección"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Cancelar"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Crear aplicación"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Borrar"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Editar"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Nueva aplicación"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Guardar cambios"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Cargar icono"
|
||||
},
|
||||
|
||||
@@ -3860,24 +3860,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Añadir una URI de redirección"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Cancelar"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Crear aplicación"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Eliminar"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Editar"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Nueva aplicación"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Guardar cambios"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Subir icono"
|
||||
},
|
||||
|
||||
@@ -2840,24 +2840,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Magdagdag ng redirect URI"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Kanselahin"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Lumikha ng app"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Tanggalin"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Baguhin"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Bagong aplikasyon"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "I-save ang mga pagbabago"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "I-upload ang ikono"
|
||||
},
|
||||
|
||||
@@ -4667,24 +4667,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Ajouter une URI de redirection"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Annuler"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Créer une appli"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Supprimer"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Modifier"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Nouvelle application"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Sauvegarder les changements"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Charger une icône"
|
||||
},
|
||||
|
||||
@@ -2282,9 +2282,6 @@
|
||||
"settings.account.data-export.description": {
|
||||
"message": "תבקש עותק של כל הדאטה האישי שהעלית ל-Modrinth. זה יכול לקחת כמה דקות."
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "ערוך"
|
||||
},
|
||||
"settings.applications.field.name": {
|
||||
"message": "שם"
|
||||
},
|
||||
|
||||
@@ -4130,24 +4130,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Átírányítás URI hozzáadása"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Mégse"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Alkalmazás létrehozása"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Törlés"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Szerkesztés"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Új alkalmazás"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Módosítások mentése"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Ikon feltöltése"
|
||||
},
|
||||
|
||||
@@ -2918,24 +2918,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Tambah URI pengarahan ulang"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Batal"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Buat aplikasi"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Hapus"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Sunting"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Aplikasi baru"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Simpan perubahan"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Unggah ikon"
|
||||
},
|
||||
|
||||
@@ -4667,24 +4667,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Aggiungi reindirizzamento"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Annulla"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Crea app"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Elimina"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Modifica"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Nuova applicazione"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Salva modifiche"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Carica icona"
|
||||
},
|
||||
|
||||
@@ -3620,24 +3620,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "リダイレクトURIを追加"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "キャンセル"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "アプリを作成する"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "削除"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "編集"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "新規アプリケーション"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "変更を保存する"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "アイコンをアップロード"
|
||||
},
|
||||
|
||||
@@ -4631,24 +4631,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "리디렉션 URI 를 추가하세요"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "취소"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "앱 만들기"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "지우기"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "편집"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "새 애플리케이션"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "변경사항 저장"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "아이콘 업로드"
|
||||
},
|
||||
|
||||
@@ -3653,24 +3653,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Tambah URI pengubah hala"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Batal"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Cipta aplikasi"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Padam"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Sunting"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Aplikasi baharu"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Simpan perubahan"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Muat naik ikon"
|
||||
},
|
||||
|
||||
@@ -4706,24 +4706,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Voeg een URI toe"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Annuleren"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Maak nieuwe"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Verwijder"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Bewerk"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Nieuw project"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Wijzigingen opslaan"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Pictogram uploaden"
|
||||
},
|
||||
|
||||
@@ -3539,24 +3539,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Legg til en omdirings-URI"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Avbryt"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Lag app"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Slett"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Regiser"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Ny søknad"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Lagre endringer"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Last opp ikon"
|
||||
},
|
||||
|
||||
@@ -4622,24 +4622,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Dodaj URI przekierowywania"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Anuluj"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Stwórz aplikację"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Usuń"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Edytuj"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Nowa aplikacja"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Zapisz zmiany"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Prześlij ikonę"
|
||||
},
|
||||
|
||||
@@ -4709,24 +4709,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Adicionar URI de redirecionamento"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Cancelar"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Criar app"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Excluir"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Editar"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Novo aplicativo"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Salvar alterações"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Enviar ícone"
|
||||
},
|
||||
|
||||
@@ -3350,24 +3350,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Adicionar um endereço de redirecionamento"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Cancelar"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Criar app"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Apagar"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Editar"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Nova aplicação"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Guardar alterações"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Carregar ícone"
|
||||
},
|
||||
|
||||
@@ -1814,21 +1814,12 @@
|
||||
"settings.applications.about": {
|
||||
"message": "Despre"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Anuleaza"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Creaza aplicația"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Sterge"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Editeaza"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Salvează schimbările"
|
||||
},
|
||||
"settings.applications.client-id": {
|
||||
"message": "ID Client"
|
||||
},
|
||||
|
||||
@@ -4655,24 +4655,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Добавить адрес перенаправления"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Отмена"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Создать приложение"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Удалить"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Изменить"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Новое приложение"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Сохранить изменения"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Загрузить иконку"
|
||||
},
|
||||
|
||||
@@ -3713,24 +3713,15 @@
|
||||
"settings.applications.button.add-more": {
|
||||
"message": "Lägg till mer"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Avbryt"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Skapa app"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Ta bort"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Redigera"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Ny applikation"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Spara ändringar"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Ladda upp ikon"
|
||||
},
|
||||
|
||||
@@ -4118,24 +4118,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Yönlendirme URI'ları ekle"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "İptal"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Uygulama oluştur"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Sil"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Düzenle"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Yeni uygulama"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Değişiklikleri kaydet"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Simge yükle"
|
||||
},
|
||||
|
||||
@@ -4631,24 +4631,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Додати переадресацію URI"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Скасувати"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Створити застосунок"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Видалити"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Редагувати"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Новий застосунок"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Зберегти зміни"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Завантажити значок"
|
||||
},
|
||||
|
||||
@@ -3818,24 +3818,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "Thêm URI chuyển hướng"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "Huỷ"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "Tạo ứng dụng"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "Xoá"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "Chỉnh sửa"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "Ứng dụng mới"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "Lưu thay đổi"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "Tải lên biểu tượng"
|
||||
},
|
||||
|
||||
@@ -4709,24 +4709,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "添加重定向 URI"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "取消"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "新建 App"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "删除"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "编辑"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "新建应用程序"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "保存更改"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "上传图标"
|
||||
},
|
||||
|
||||
@@ -4709,24 +4709,15 @@
|
||||
"settings.applications.button.add-redirect-uri": {
|
||||
"message": "新增重新導向 URI"
|
||||
},
|
||||
"settings.applications.button.cancel": {
|
||||
"message": "取消"
|
||||
},
|
||||
"settings.applications.button.create": {
|
||||
"message": "建立應用程式"
|
||||
},
|
||||
"settings.applications.button.delete": {
|
||||
"message": "刪除"
|
||||
},
|
||||
"settings.applications.button.edit": {
|
||||
"message": "編輯"
|
||||
},
|
||||
"settings.applications.button.new": {
|
||||
"message": "新的應用程式"
|
||||
},
|
||||
"settings.applications.button.save-changes": {
|
||||
"message": "儲存變更"
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "上傳圖示"
|
||||
},
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<label for="gallery-image-title">
|
||||
<span class="label__title">Title</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="gallery-image-title"
|
||||
v-model="editTitle"
|
||||
:maxlength="64"
|
||||
@@ -52,17 +52,16 @@
|
||||
<label for="gallery-image-desc">
|
||||
<span class="label__title">Description</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Textarea
|
||||
id="gallery-image-desc"
|
||||
v-model="editDescription"
|
||||
multiline
|
||||
:maxlength="255"
|
||||
placeholder="Enter description..."
|
||||
/>
|
||||
<label for="gallery-image-ordering">
|
||||
<span class="label__title">Order Index</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="gallery-image-ordering"
|
||||
v-model="editOrder"
|
||||
type="number"
|
||||
@@ -305,8 +304,9 @@ import {
|
||||
DropArea,
|
||||
FileInput,
|
||||
injectProjectPageContext,
|
||||
Input,
|
||||
NewModal as Modal,
|
||||
StyledInput,
|
||||
Textarea,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
|
||||
@@ -109,8 +109,8 @@
|
||||
</template>
|
||||
</div>
|
||||
<ConversationThread
|
||||
v-if="thread"
|
||||
:thread="thread"
|
||||
v-if="prefixedThread"
|
||||
:thread="prefixedThread"
|
||||
:project="project"
|
||||
:set-status="setStatus"
|
||||
:current-member="currentMember ?? undefined"
|
||||
@@ -151,6 +151,7 @@ import {
|
||||
} from '@modrinth/ui'
|
||||
import { isStaff } from '@modrinth/utils'
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed, watch } from 'vue'
|
||||
|
||||
import ConversationThread from '~/components/ui/thread/ConversationThread.vue'
|
||||
@@ -217,6 +218,26 @@ const {
|
||||
thread,
|
||||
} = injectProjectPageContext()
|
||||
|
||||
const THREADS_RELEASE_DATE = '2023-08-05T12:00:00-07:00'
|
||||
|
||||
const prefixedThread = computed(() => {
|
||||
const projectDate = project.value?.queued ?? project.value?.approved ?? project.value?.published
|
||||
if (thread.value && projectDate && dayjs(projectDate).isBefore(dayjs(THREADS_RELEASE_DATE))) {
|
||||
const newThread = JSON.parse(JSON.stringify(thread.value))
|
||||
newThread.messages.unshift({
|
||||
id: '69',
|
||||
author_id: null,
|
||||
body: {
|
||||
type: 'legacy_project_message',
|
||||
},
|
||||
created: THREADS_RELEASE_DATE,
|
||||
hide_identity: false,
|
||||
})
|
||||
return newThread
|
||||
}
|
||||
return thread.value
|
||||
})
|
||||
|
||||
const canAccess = computed(() => !!currentMember.value)
|
||||
const staff = computed(() => isStaff(currentMember.value?.user))
|
||||
const userFacingUiVisible = computed(
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<label for="gallery-image-title">
|
||||
<span class="label__title">Title</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="gallery-image-title"
|
||||
v-model="editTitle"
|
||||
:maxlength="64"
|
||||
@@ -52,17 +52,16 @@
|
||||
<label for="gallery-image-desc">
|
||||
<span class="label__title">Description</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Textarea
|
||||
id="gallery-image-desc"
|
||||
v-model="editDescription"
|
||||
multiline
|
||||
:maxlength="255"
|
||||
placeholder="Enter description..."
|
||||
/>
|
||||
<label for="gallery-image-ordering">
|
||||
<span class="label__title">Order Index</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="gallery-image-ordering"
|
||||
v-model="editOrder"
|
||||
type="number"
|
||||
@@ -295,8 +294,9 @@ import {
|
||||
DropArea,
|
||||
FileInput,
|
||||
injectProjectPageContext,
|
||||
Input,
|
||||
NewModal as Modal,
|
||||
StyledInput,
|
||||
Textarea,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import {
|
||||
defineMessages,
|
||||
IconSelect,
|
||||
injectProjectPageContext,
|
||||
Input,
|
||||
type MessageDescriptor,
|
||||
SettingsLabel,
|
||||
StyledInput,
|
||||
UnsavedChangesPopup,
|
||||
usePageLeaveSafety,
|
||||
useSavable,
|
||||
@@ -140,7 +140,7 @@ const placeholder = computed(() => placeholders[placeholderIndex.value] ?? place
|
||||
:description="messages.nameDescription"
|
||||
/>
|
||||
<div class="flex">
|
||||
<StyledInput
|
||||
<Input
|
||||
id="project-name"
|
||||
v-model="current.title"
|
||||
:placeholder="formatMessage(placeholder.name)"
|
||||
@@ -156,7 +156,7 @@ const placeholder = computed(() => placeholders[placeholderIndex.value] ?? place
|
||||
:title="messages.taglineTitle"
|
||||
:description="messages.taglineDescription"
|
||||
/>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="project-tagline"
|
||||
v-model="current.tagline"
|
||||
:placeholder="formatMessage(placeholder.tagline)"
|
||||
@@ -169,7 +169,7 @@ const placeholder = computed(() => placeholders[placeholderIndex.value] ?? place
|
||||
<SettingsLabel id="project-url" :title="messages.urlTitle" />
|
||||
<div class="text-input-wrapper">
|
||||
<div class="text-input-wrapper__before">https://modrinth.com/project/</div>
|
||||
<StyledInput id="project-url" v-model="current.url" :maxlength="64" autocomplete="off" />
|
||||
<Input id="project-url" v-model="current.url" :maxlength="64" autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,12 +21,7 @@
|
||||
<label for="project-name">
|
||||
<span class="label__title">Name</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
id="project-name"
|
||||
v-model="name"
|
||||
:maxlength="2048"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<Input id="project-name" v-model="name" :maxlength="2048" :disabled="!hasPermission" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -39,7 +34,7 @@
|
||||
$getProjectTypeForUrl(project.project_type, project.loaders)
|
||||
}}/
|
||||
</div>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="project-slug"
|
||||
v-model="slug"
|
||||
:maxlength="64"
|
||||
@@ -53,10 +48,9 @@
|
||||
<label for="project-summary">
|
||||
<span class="label__title">Summary</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Textarea
|
||||
id="project-summary"
|
||||
v-model="summary"
|
||||
multiline
|
||||
:maxlength="256"
|
||||
:disabled="!hasPermission"
|
||||
resize="vertical"
|
||||
@@ -341,7 +335,8 @@ import {
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
StyledInput,
|
||||
Input,
|
||||
Textarea,
|
||||
Toggle,
|
||||
UnsavedChangesPopup,
|
||||
useFormatBytes,
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
</label>
|
||||
|
||||
<div class="w-1/2">
|
||||
<StyledInput
|
||||
<Input
|
||||
id="license-url"
|
||||
v-model="current.licenseUrl"
|
||||
type="url"
|
||||
@@ -105,7 +105,7 @@
|
||||
</label>
|
||||
|
||||
<div class="flex w-1/2 flex-col gap-2">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-if="!current.nonSpdxLicense"
|
||||
id="license-spdx"
|
||||
v-model="current.license.short"
|
||||
@@ -114,7 +114,7 @@
|
||||
placeholder="SPDX identifier"
|
||||
:disabled="!hasPermission"
|
||||
/>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-else
|
||||
id="license-name"
|
||||
v-model="current.license.short"
|
||||
@@ -158,7 +158,7 @@ import {
|
||||
ConfirmLeaveModal,
|
||||
DropdownSelect,
|
||||
injectProjectPageContext,
|
||||
StyledInput,
|
||||
Input,
|
||||
UnsavedChangesPopup,
|
||||
usePageLeaveSafety,
|
||||
useSavable,
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
v-tooltip="`Link includes a domain which isn't common for this link type.`"
|
||||
class="size-6 animate-pulse text-orange"
|
||||
/>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="project-issue-tracker"
|
||||
v-model="issuesUrl"
|
||||
type="url"
|
||||
@@ -176,7 +176,7 @@
|
||||
v-tooltip="`Link includes a domain which isn't common for this link type.`"
|
||||
class="size-6 animate-pulse text-orange"
|
||||
/>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="project-source-code"
|
||||
v-model="sourceUrl"
|
||||
type="url"
|
||||
@@ -205,7 +205,7 @@
|
||||
v-tooltip="`Discord invites are not appropriate for this link type.`"
|
||||
class="size-6 animate-pulse text-orange"
|
||||
/>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="project-wiki-page"
|
||||
v-model="wikiUrl"
|
||||
type="url"
|
||||
@@ -229,7 +229,7 @@
|
||||
v-tooltip="`You're using a link which isn't common for this link type.`"
|
||||
class="size-6 animate-pulse text-orange"
|
||||
/>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="project-discord-invite"
|
||||
v-model="discordUrl"
|
||||
type="url"
|
||||
@@ -250,7 +250,7 @@
|
||||
:key="`donation-link-${index}`"
|
||||
class="input-group donation-link-group"
|
||||
>
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="donationLink.url"
|
||||
type="url"
|
||||
:maxlength="2048"
|
||||
@@ -292,7 +292,7 @@ import {
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
StyledInput,
|
||||
Input,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
const tags = useGeneratedState()
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</span>
|
||||
</span>
|
||||
<div class="input-group">
|
||||
<StyledInput
|
||||
<Input
|
||||
id="username"
|
||||
v-model="currentUsername"
|
||||
placeholder="Username"
|
||||
@@ -112,7 +112,7 @@
|
||||
The title of the role that this member plays for this project.
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
:id="`member-${allTeamMembers[index].user.username}-role`"
|
||||
v-model="allTeamMembers[index].role"
|
||||
:disabled="(currentMember?.permissions & EDIT_MEMBER) !== EDIT_MEMBER"
|
||||
@@ -126,7 +126,7 @@
|
||||
this project's revenue goes to this member.
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
:id="`member-${allTeamMembers[index].user.username}-monetization-weight`"
|
||||
v-model="allTeamMembers[index].payouts_split"
|
||||
type="number"
|
||||
@@ -378,7 +378,7 @@
|
||||
The title of the role that this member plays for this project.
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
:id="`member-${allOrgMembers[index].user.username}-role`"
|
||||
v-model="allOrgMembers[index].role"
|
||||
:disabled="
|
||||
@@ -395,7 +395,7 @@
|
||||
this project's revenue goes to this member.
|
||||
</span>
|
||||
</label>
|
||||
<StyledInput
|
||||
<Input
|
||||
:id="`member-${allOrgMembers[index].user.username}-monetization-weight`"
|
||||
v-model="allOrgMembers[index].payouts_split"
|
||||
type="number"
|
||||
@@ -568,7 +568,7 @@ import {
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
StyledInput,
|
||||
Input,
|
||||
Toggle,
|
||||
} from '@modrinth/ui'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
@@ -29,8 +29,8 @@ import {
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
Input,
|
||||
IntlFormatted,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { isStaff } from '@modrinth/utils'
|
||||
@@ -563,7 +563,7 @@ function dismissInfoBanner() {
|
||||
</template>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-[minmax(0,1fr)_auto_auto_auto]">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="java-address"
|
||||
v-model="javaAddress"
|
||||
:placeholder="formatMessage(messages.enterAddressPlaceholder)"
|
||||
@@ -142,7 +142,7 @@
|
||||
</span>
|
||||
</label>
|
||||
<div class="mt-2 flex items-center gap-2">
|
||||
<StyledInput
|
||||
<Input
|
||||
id="bedrock-address"
|
||||
v-model="bedrockAddress"
|
||||
:placeholder="formatMessage(messages.enterAddressPlaceholder)"
|
||||
@@ -177,12 +177,12 @@ import {
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
Input,
|
||||
IntlFormatted,
|
||||
MultiSelect,
|
||||
normalizeChildren,
|
||||
SERVER_LANGUAGES,
|
||||
SERVER_REGIONS,
|
||||
StyledInput,
|
||||
UnsavedChangesPopup,
|
||||
usePageLeaveSafety,
|
||||
useVIntl,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
>
|
||||
<h1 class="m-0 grow text-2xl font-extrabold">Manage affiliate links</h1>
|
||||
<div class="flex items-center gap-2">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="filterQuery"
|
||||
:icon="SearchIcon"
|
||||
type="text"
|
||||
@@ -94,7 +94,7 @@ import {
|
||||
ConfirmModal,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
StyledInput,
|
||||
Input,
|
||||
} from '@modrinth/ui'
|
||||
import type { User } from '@modrinth/utils'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<div class="flex flex-col gap-5" @submit.prevent="saveEvent">
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="label__title font-semibold">Title</span>
|
||||
<StyledInput
|
||||
<Input
|
||||
id="analytics-event-title"
|
||||
ref="titleInput"
|
||||
v-model="form.title"
|
||||
@@ -48,7 +48,7 @@
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<StyledInput
|
||||
<Input
|
||||
id="analytics-event-link"
|
||||
v-model="form.announcementUrl"
|
||||
type="url"
|
||||
@@ -129,7 +129,7 @@
|
||||
<h1 class="m-0 text-2xl font-extrabold text-contrast">Analytics Events</h1>
|
||||
|
||||
<div class="flex flex-col gap-2 sm:flex-row sm:items-center">
|
||||
<StyledInput
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
:icon="SearchIcon"
|
||||
type="search"
|
||||
@@ -245,11 +245,11 @@ import {
|
||||
DatePicker,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
Input,
|
||||
MultiSelect,
|
||||
type MultiSelectOption,
|
||||
NewModal,
|
||||
type SortDirection,
|
||||
StyledInput,
|
||||
Table,
|
||||
type TableColumn,
|
||||
} from '@modrinth/ui'
|
||||
@@ -319,7 +319,7 @@ const allMetricKinds = metricKindOptions.map((option) => option.value)
|
||||
|
||||
const deleteEventModal = ref<InstanceType<typeof ConfirmModal> | null>(null)
|
||||
const eventModal = ref<InstanceType<typeof NewModal> | null>(null)
|
||||
const titleInput = ref<InstanceType<typeof StyledInput> | null>(null)
|
||||
const titleInput = ref<InstanceType<typeof Input> | null>(null)
|
||||
const searchQuery = ref('')
|
||||
const sortColumn = ref<EventColumnKey | undefined>('date')
|
||||
const sortDirection = ref<SortDirection>('desc')
|
||||
@@ -485,9 +485,9 @@ async function saveEvent() {
|
||||
const payload = buildEventPayload()
|
||||
|
||||
if (modalMode.value === 'edit' && editingEventId.value !== null) {
|
||||
await client.labrinth.analytics_v3.editEvent(editingEventId.value, payload)
|
||||
await client.labrinth.analytics_internal.editEvent(editingEventId.value, payload)
|
||||
} else {
|
||||
await client.labrinth.analytics_v3.createEvent(payload)
|
||||
await client.labrinth.analytics_internal.createEvent(payload)
|
||||
}
|
||||
|
||||
await queryClient.invalidateQueries({ queryKey: analyticsEventsQueryKey })
|
||||
@@ -528,7 +528,7 @@ async function deleteEvent(eventId: Labrinth.Analytics.v3.AnalyticsEventId) {
|
||||
setDeletingEvent(eventId, true)
|
||||
|
||||
try {
|
||||
await client.labrinth.analytics_v3.deleteEvent(eventId)
|
||||
await client.labrinth.analytics_internal.deleteEvent(eventId)
|
||||
await queryClient.invalidateQueries({ queryKey: analyticsEventsQueryKey })
|
||||
addNotification({
|
||||
title: 'Analytics event deleted',
|
||||
@@ -581,7 +581,7 @@ function commitAnnouncementUrl() {
|
||||
committedAnnouncementUrl.value = form.value.announcementUrl
|
||||
}
|
||||
|
||||
function buildEventPayload(): Labrinth.Analytics.v3.AnalyticsEventUpsert {
|
||||
function buildEventPayload(): Labrinth.Analytics.Internal.AnalyticsEventUpsert {
|
||||
const selectedRange = getEventFormDateRange()
|
||||
if (!selectedRange) {
|
||||
throw new Error('Select a valid start and end date')
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user