Analytics + more bug fixes (#144)

* Analytics + more bug fixes

* debug deadlock

* Fix mostly everything

* merge fixes

* fix rest

* final fixeS
This commit is contained in:
Geometrically
2023-06-19 14:59:06 -07:00
committed by GitHub
parent 84d731b670
commit 1e78a7b6a8
51 changed files with 1285 additions and 491 deletions

View File

@@ -476,7 +476,7 @@ const showLoaders = computed(
<div class="search-container">
<aside class="filter-panel">
<div v-if="instanceContext" class="small-instance">
<div class="instance">
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
<Avatar
:src="
!instanceContext.metadata.icon ||
@@ -497,18 +497,20 @@ const showLoaders = computed(
{{ instanceContext.metadata.game_version }}
</span>
</div>
</div>
</router-link>
<Checkbox
v-model="ignoreInstanceGameVersions"
label="Override game versions"
class="filter-checkbox"
@update:model-value="onSearchChangeToTop(1)"
@click.prevent.stop
/>
<Checkbox
v-model="ignoreInstanceLoaders"
label="Override loaders"
class="filter-checkbox"
@update:model-value="onSearchChangeToTop(1)"
@click.prevent.stop
/>
</div>
<Card class="search-panel-card">

View File

@@ -5,6 +5,7 @@ import { handleError, useTheming } from '@/store/state'
import { get, set } from '@/helpers/settings'
import { get_max_memory } from '@/helpers/jre'
import JavaSelector from '@/components/ui/JavaSelector.vue'
import mixpanel from 'mixpanel-browser'
const themeStore = useTheming()
@@ -26,6 +27,12 @@ watch(
async (oldSettings, newSettings) => {
const setSettings = JSON.parse(JSON.stringify(newSettings))
if (setSettings.opt_out_analytics) {
mixpanel.opt_out_tracking()
} else {
mixpanel.opt_in_tracking()
}
if (setSettings.java_globals.JAVA_8?.path === '') {
setSettings.java_globals.JAVA_8 = undefined
}
@@ -116,6 +123,26 @@ watch(
"
/>
</div>
<div class="adjacent-input">
<label for="advanced-rendering">
<span class="label__title">Advanced rendering</span>
<span class="label__description">
Enables advanced rendering such as blur effects that may cause performance issues
without hardware-accelerated rendering.
</span>
</label>
<Toggle
id="advanced-rendering"
:model-value="themeStore.advancedRendering"
:checked="themeStore.advancedRendering"
@update:model-value="
(e) => {
themeStore.advancedRendering = e
settings.advanced_rendering = themeStore.advancedRendering
}
"
/>
</div>
</Card>
<Card>
<div class="label">
@@ -158,6 +185,23 @@ watch(
/>
</div>
</Card>
<Card>
<div class="label">
<h3>
<span class="label__title size-card-header">Privacy</span>
</h3>
</div>
<div class="adjacent-input">
<label for="opt-out-analytics">
<span class="label__title">Disable analytics</span>
<span class="label__description">
Modrinth collects anonymized analytics and usage data to improve our user experience and
customize your experience. Opting out will disable this data collection.
</span>
</label>
<Toggle id="opt-out-analytics" v-model="settings.opt_out_analytics" />
</div>
</Card>
<Card>
<div class="label">
<h3>

View File

@@ -25,7 +25,7 @@
v-else-if="playing === true"
color="danger"
class="instance-button"
@click="stopInstance"
@click="stopInstance('InstancePage')"
@mouseover="checkProcess"
>
<StopCircleIcon />
@@ -35,7 +35,7 @@
v-else-if="playing === false && loading === false"
color="primary"
class="instance-button"
@click="startInstance"
@click="startInstance('InstancePage')"
@mouseover="checkProcess"
>
<PlayIcon />
@@ -93,8 +93,6 @@
<template #open_folder> <ClipboardCopyIcon /> Open Folder </template>
<template #copy_link> <ClipboardCopyIcon /> Copy Link </template>
<template #open_link> <ClipboardCopyIcon /> Open In Modrinth <ExternalIcon /> </template>
<template #repair> <HammerIcon /> Repair </template>
<template #delete> <TrashIcon /> Delete </template>
</ContextMenu>
</template>
<script setup>
@@ -109,14 +107,12 @@ import {
PlayIcon,
StopCircleIcon,
EditIcon,
HammerIcon,
TrashIcon,
FolderOpenIcon,
ClipboardCopyIcon,
PlusIcon,
ExternalIcon,
} from 'omorphia'
import { get, install, remove, run } from '@/helpers/profile'
import { get, run } from '@/helpers/profile'
import {
get_all_running_profile_paths,
get_uuids_by_profile_path,
@@ -129,6 +125,7 @@ import { convertFileSrc } from '@tauri-apps/api/tauri'
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
import { showInFolder } from '@/helpers/utils.js'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import mixpanel from 'mixpanel-browser'
const route = useRoute()
@@ -151,11 +148,17 @@ const playing = ref(false)
const loading = ref(false)
const options = ref(null)
const startInstance = async () => {
const startInstance = async (context) => {
loading.value = true
uuid.value = await run(route.params.id).catch(handleError)
loading.value = false
playing.value = true
mixpanel.track('InstanceStart', {
loader: instance.value.metadata.loader,
game_version: instance.value.metadata.game_version,
source: context,
})
}
const checkProcess = async () => {
@@ -171,25 +174,21 @@ const checkProcess = async () => {
await checkProcess()
const stopInstance = async () => {
const stopInstance = async (context) => {
playing.value = false
if (!uuid.value) {
const uuids = await get_uuids_by_profile_path(instance.value.path).catch(handleError)
uuid.value = uuids[0] // populate Uuid to listen for in the process_listener
uuids.forEach(async (u) => await kill_by_uuid(u).catch(handleError))
} else await kill_by_uuid(uuid.value).catch(handleError)
mixpanel.track('InstanceStop', {
loader: instance.value.metadata.loader,
game_version: instance.value.metadata.game_version,
source: context,
})
}
const unlistenProfiles = await profile_listener(async (event) => {
if (event.path === route.params.id) {
instance.value = await get(route.params.id).catch(handleError)
}
})
const unlistenProcesses = await process_listener((e) => {
if (e.event === 'finished' && uuid.value === e.uuid) playing.value = false
})
const handleRightClick = (event) => {
const baseOptions = [
{ name: 'add_content' },
@@ -197,15 +196,6 @@ const handleRightClick = (event) => {
{ name: 'edit' },
{ name: 'open_folder' },
{ name: 'copy_path' },
{ type: 'divider' },
{
name: 'repair',
color: 'contrast',
},
{
name: 'delete',
color: 'danger',
},
]
options.value.showMenu(
@@ -233,10 +223,10 @@ const handleOptionsClick = async (args) => {
console.log(args)
switch (args.option) {
case 'play':
await startInstance()
await startInstance('InstancePageContextMenu')
break
case 'stop':
await stopInstance()
await stopInstance('InstancePageContextMenu')
break
case 'add_content':
await router.push({
@@ -249,33 +239,25 @@ const handleOptionsClick = async (args) => {
path: `/instance/${encodeURIComponent(route.params.id)}/options`,
})
break
case 'repair':
await install(instance.value.path).catch(handleError)
break
case 'delete':
await remove(instance.value.path).catch(handleError)
break
case 'open_folder':
await showInFolder(instance.value.path)
break
case 'copy_path':
await navigator.clipboard.writeText(instance.value.path)
break
case 'open_link':
window.__TAURI_INVOKE__('tauri', {
__tauriModule: 'Shell',
message: {
cmd: 'open',
path: args.item.link,
},
})
break
case 'copy_link':
await navigator.clipboard.writeText(args.item.link)
break
}
}
const unlistenProfiles = await profile_listener(async (event) => {
if (event.path === route.params.id) {
instance.value = await get(route.params.id).catch(handleError)
}
})
const unlistenProcesses = await process_listener((e) => {
if (e.event === 'finished' && uuid.value === e.uuid) playing.value = false
})
onUnmounted(() => {
unlistenProcesses()
unlistenProfiles()

View File

@@ -51,11 +51,11 @@ import {
SendIcon,
TrashIcon,
} from 'omorphia'
import { delete_logs_by_datetime, get_logs, get_stdout_by_datetime } from '@/helpers/logs.js'
import { delete_logs_by_datetime, get_logs, get_output_by_datetime } from '@/helpers/logs.js'
import { nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, watch } from 'vue'
import dayjs from 'dayjs'
import calendar from 'dayjs/plugin/calendar'
import { get_stdout_by_uuid, get_uuids_by_profile_path } from '@/helpers/process.js'
import { get_output_by_uuid, get_uuids_by_profile_path } from '@/helpers/process.js'
import { useRoute } from 'vue-router'
import { process_listener } from '@/helpers/events.js'
import { handleError } from '@/store/notifications.js'
@@ -78,7 +78,7 @@ async function getLiveLog() {
if (uuids.length === 0) {
returnValue = 'No live game detected. \nStart your game to proceed'
} else {
returnValue = await get_stdout_by_uuid(uuids[0]).catch(handleError)
returnValue = await get_output_by_uuid(uuids[0]).catch(handleError)
}
return { name: 'Live Log', stdout: returnValue, live: true }
@@ -120,13 +120,17 @@ watch(selectedLogIndex, async (newIndex) => {
if (newIndex !== 0) {
logs.value[newIndex].stdout = 'Loading...'
logs.value[newIndex].stdout = await get_stdout_by_datetime(
logs.value[newIndex].stdout = await get_output_by_datetime(
props.instance.uuid,
logs.value[newIndex].datetime_string
).catch(handleError)
}
})
if (logs.value.length >= 1) {
selectedLogIndex.value = 1
}
const deleteLog = async () => {
if (logs.value[selectedLogIndex.value] && selectedLogIndex.value !== 0) {
let deleteIndex = selectedLogIndex.value
@@ -165,9 +169,13 @@ interval.value = setInterval(async () => {
}, 250)
const unlistenProcesses = await process_listener(async (e) => {
if (e.event === 'launched') {
selectedLogIndex.value = 0
}
if (e.event === 'finished') {
userScrolled.value = false
await setLogs()
selectedLogIndex.value = 1
}
})

View File

@@ -37,7 +37,7 @@
>
<template #search>
<SearchIcon />
<span class="no-wrap"> Search addons </span>
<span class="no-wrap"> Add content </span>
</template>
<template #from_file>
<FolderOpenIcon />
@@ -216,6 +216,7 @@ import {
update_project,
} from '@/helpers/profile.js'
import { handleError } from '@/store/notifications.js'
import mixpanel from 'mixpanel-browser'
import { open } from '@tauri-apps/api/dialog'
import { listen } from '@tauri-apps/api/event'
@@ -255,6 +256,7 @@ const initProjects = (initInstance) => {
updateVersion: project.metadata.update_version,
outdated: !!project.metadata.update_version,
project_type: project.metadata.project.project_type,
id: project.metadata.project.id,
})
} else if (project.metadata.type === 'inferred') {
projects.value.push({
@@ -286,6 +288,11 @@ const initProjects = (initInstance) => {
initProjects(props.instance)
watch(
() => props.instance.projects,
() => initProjects(props.instance)
)
const searchFilter = ref('')
const selectAll = ref(false)
const sortFilter = ref('')
@@ -385,6 +392,13 @@ async function updateAll() {
for (const project of setProjects) {
projects.value[project].updating = false
}
mixpanel.track('InstanceUpdateAll', {
loader: props.instance.metadata.loader,
game_version: props.instance.metadata.game_version,
count: setProjects.length,
selected: selected.value.length > 1,
})
}
async function updateProject(mod) {
@@ -395,30 +409,54 @@ async function updateProject(mod) {
mod.outdated = false
mod.version = mod.updateVersion.version_number
mod.updateVersion = null
mixpanel.track('InstanceProjectUpdate', {
loader: props.instance.metadata.loader,
game_version: props.instance.metadata.game_version,
id: mod.id,
name: mod.name,
project_type: mod.project_type,
})
}
async function toggleDisableMod(mod) {
mod.path = await toggle_disable_project(props.instance.path, mod.path).catch(handleError)
mod.disabled = !mod.disabled
mixpanel.track('InstanceProjectDisable', {
loader: props.instance.metadata.loader,
game_version: props.instance.metadata.game_version,
id: mod.id,
name: mod.name,
project_type: mod.project_type,
disabled: mod.disabled,
})
}
async function removeMod(mod) {
await remove_project(props.instance.path, mod.path).catch(handleError)
projects.value = projects.value.filter((x) => mod.path !== x.path)
mixpanel.track('InstanceProjectRemove', {
loader: props.instance.metadata.loader,
game_version: props.instance.metadata.game_version,
id: mod.id,
name: mod.name,
project_type: mod.project_type,
})
}
const handleContentOptionClick = async (args) => {
if (args.option === 'search') {
await router.push({
path: `/browse/${props.instance.metadata.loader === 'vanilla' ? 'datapack' : 'mod'}`,
query: { i: props.instance.path },
})
} else if (args.option === 'from_file') {
const newProject = await open({ multiple: true })
console.log(newProject)
if (!newProject) return
for (const project of newProject) {
console.log(project)
await add_project_from_path(props.instance.path, project, 'mod').catch(handleError)
initProjects(await get(props.instance.path).catch(handleError))
}

View File

@@ -5,9 +5,14 @@
description="If you proceed, all data for your instance will be removed. You will not be able to recover it."
:has-to-type="false"
proceed-label="Delete"
:noblur="!themeStore.advancedRendering"
@proceed="removeProfile"
/>
<Modal ref="changeVersionsModal" header="Change instance versions">
<Modal
ref="changeVersionsModal"
header="Change instance versions"
:noblur="!themeStore.advancedRendering"
>
<div class="change-versions-modal universal-body">
<div class="input-row">
<p class="input-label">Loader</p>
@@ -334,6 +339,8 @@ import { open } from '@tauri-apps/api/dialog'
import { get_fabric_versions, get_forge_versions, get_quilt_versions } from '@/helpers/metadata.js'
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
import { handleError } from '@/store/notifications.js'
import mixpanel from 'mixpanel-browser'
import { useTheming } from '@/store/theme.js'
const router = useRouter()
@@ -344,6 +351,8 @@ const props = defineProps({
},
})
const themeStore = useTheming()
const title = ref(props.instance.metadata.name)
const icon = ref(props.instance.metadata.icon)
const groups = ref(props.instance.metadata.groups)
@@ -360,6 +369,7 @@ const availableGroups = ref([
async function resetIcon() {
icon.value = null
await edit_icon(props.instance.path, null).catch(handleError)
mixpanel.track('InstanceRemoveIcon')
}
async function setIcon() {
@@ -377,6 +387,8 @@ async function setIcon() {
icon.value = value
await edit_icon(props.instance.path, icon.value).catch(handleError)
mixpanel.track('InstanceSetIcon')
}
const globalSettings = await get().catch(handleError)
@@ -428,6 +440,7 @@ watch(
metadata: {
name: title.value.trim().substring(0, 16) ?? 'Instance',
groups: groups.value.map((x) => x.trim().substring(0, 32)).filter((x) => x.length > 0),
loader_version: props.instance.metadata.loader_version,
},
java: {},
}
@@ -481,6 +494,11 @@ async function repairProfile() {
repairing.value = true
await install(props.instance.path).catch(handleError)
repairing.value = false
mixpanel.track('InstanceRepair', {
loader: props.instance.metadata.loader,
game_version: props.instance.metadata.game_version,
})
}
const removing = ref(false)
@@ -489,6 +507,11 @@ async function removeProfile() {
await remove(props.instance.path).catch(handleError)
removing.value = false
mixpanel.track('InstanceRemove', {
loader: props.instance.metadata.loader,
game_version: props.instance.metadata.game_version,
})
await router.push({ path: '/' })
}

View File

@@ -23,6 +23,10 @@ export default {
<style scoped lang="scss">
.markdown-body {
:deep(table) {
width: auto;
}
:deep(hr),
:deep(h1),
:deep(h2) {

View File

@@ -93,6 +93,7 @@ import {
Button,
} from 'omorphia'
import { ref } from 'vue'
import mixpanel from 'mixpanel-browser'
const props = defineProps({
project: {
@@ -111,6 +112,10 @@ const nextImage = () => {
expandedGalleryIndex.value = 0
}
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
mixpanel.track('GalleryImageNext', {
project_id: props.project.id,
url: expandedGalleryItem.value.url,
})
}
const previousImage = () => {
@@ -119,12 +124,21 @@ const previousImage = () => {
expandedGalleryIndex.value = props.project.gallery.length - 1
}
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
mixpanel.track('GalleryImagePrevious', {
project_id: props.project.id,
url: expandedGalleryItem.value,
})
}
const expandImage = (item, index) => {
expandedGalleryItem.value = item
expandedGalleryIndex.value = index
zoomedIn.value = false
mixpanel.track('GalleryImageExpand', {
project_id: props.project.id,
url: item.url,
})
}
</script>

View File

@@ -2,7 +2,7 @@
<div class="root-container">
<div v-if="data" class="project-sidebar">
<div v-if="instance" class="small-instance">
<div class="instance">
<router-link class="instance" :to="`/instance/${encodeURIComponent(instance.path)}`">
<Avatar
:src="
!instance.metadata.icon ||
@@ -22,7 +22,7 @@
{{ instance.metadata.game_version }}
</span>
</div>
</div>
</router-link>
</div>
<Card class="sidebar-card" @contextmenu.prevent.stop="handleRightClick">
<Avatar size="lg" :src="data.icon_url" />
@@ -32,17 +32,11 @@
</div>
<Categories
class="tags"
type=""
:categories="[
...categories.filter(
:categories="
categories.filter(
(cat) => data.categories.includes(cat.name) && cat.project_type === 'mod'
),
...loaders.filter(
(loader) =>
data.categories.includes(loader.name) &&
loader.supported_project_types?.includes('modpack')
),
]"
)
"
>
<EnvironmentIndicator
:client-side="data.client_side"
@@ -258,7 +252,7 @@ import {
KoFiIcon,
OpenCollectiveIcon,
} from '@/assets/external'
import { get_categories, get_loaders } from '@/helpers/tags'
import { get_categories } from '@/helpers/tags'
import { install as packInstall } from '@/helpers/pack'
import {
list,
@@ -268,7 +262,7 @@ import {
} from '@/helpers/profile'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { useRoute, useRouter } from 'vue-router'
import { useRoute } from 'vue-router'
import { ref, shallowRef, watch } from 'vue'
import { installVersionDependencies } from '@/helpers/utils'
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
@@ -279,9 +273,9 @@ import { useFetch } from '@/helpers/fetch.js'
import { handleError } from '@/store/notifications.js'
import { convertFileSrc } from '@tauri-apps/api/tauri'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import mixpanel from 'mixpanel-browser'
const route = useRoute()
const router = useRouter()
const breadcrumbs = useBreadcrumbs()
const confirmModal = ref(null)
@@ -291,32 +285,46 @@ const incompatibilityWarning = ref(null)
const options = ref(null)
const installing = ref(false)
const [data, versions, members, dependencies, categories, loaders, instance] = await Promise.all([
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}`, 'project').then(shallowRef),
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}/version`, 'project').then(
shallowRef
),
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}/members`, 'project').then(
shallowRef
),
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}/dependencies`, 'project').then(
shallowRef
),
get_loaders().then(ref).catch(handleError),
get_categories().then(ref).catch(handleError),
route.query.i ? getInstance(route.query.i, true).then(ref) : Promise.resolve().then(ref),
])
const data = shallowRef(null)
const versions = shallowRef([])
const members = shallowRef([])
const dependencies = shallowRef([])
const categories = shallowRef([])
const instance = ref(null)
const installed = ref(
instance.value && (await check_installed(instance.value.path, data.value.id).catch(handleError))
)
const installed = ref(false)
breadcrumbs.setName('Project', data.value.title)
async function fetchProjectData() {
;[
data.value,
versions.value,
members.value,
dependencies.value,
categories.value,
instance.value,
] = await Promise.all([
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}`, 'project'),
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}/version`, 'project'),
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}/members`, 'project'),
useFetch(`https://api.modrinth.com/v2/project/${route.params.id}/dependencies`, 'project'),
get_categories().catch(handleError),
route.query.i ? getInstance(route.query.i, true).catch(handleError) : Promise.resolve(),
])
installed.value =
instance.value?.path &&
(await check_installed(instance.value.path, data.value.id).catch(handleError))
breadcrumbs.setName('Project', data.value.title)
}
await fetchProjectData()
watch(
() => route.params.id,
() => {
if (route.params.id) router.go()
async () => {
if (route.params.id && route.path.startsWith('/project')) {
await fetchProjectData()
}
}
)
@@ -356,6 +364,13 @@ async function install(version) {
data.value.title,
data.value.icon_url
).catch(handleError)
mixpanel.track('PackInstall', {
id: data.value.id,
version_id: queuedVersionData.id,
title: data.value.title,
source: 'ProjectPage',
})
} else {
confirmModal.value.show(
data.value.id,
@@ -381,14 +396,26 @@ async function install(version) {
instance.value,
data.value.title,
versions.value,
markInstalled
markInstalled,
data.value.id,
data.value.project_type
)
installing.value = false
return
} else {
queuedVersionData = selectedVersion
await installMod(instance.value.path, selectedVersion.id).catch(handleError)
installVersionDependencies(instance.value, queuedVersionData)
await installVersionDependencies(instance.value, queuedVersionData)
mixpanel.track('ProjectInstall', {
loader: instance.value.metadata.loader,
game_version: instance.value.metadata.game_version,
id: data.value.id,
project_type: data.value.project_type,
version_id: queuedVersionData.id,
title: data.value.title,
source: 'ProjectPage',
})
}
} else {
const gameVersion = instance.value.metadata.game_version
@@ -403,12 +430,24 @@ async function install(version) {
if (compatible) {
await installMod(instance.value.path, queuedVersionData.id).catch(handleError)
await installVersionDependencies(instance.value, queuedVersionData)
mixpanel.track('ProjectInstall', {
loader: instance.value.metadata.loader,
game_version: instance.value.metadata.game_version,
id: data.value.id,
project_type: data.value.project_type,
version_id: queuedVersionData.id,
title: data.value.title,
source: 'ProjectPage',
})
} else {
incompatibilityWarning.value.show(
instance.value,
data.value.title,
[queuedVersionData],
markInstalled
markInstalled,
data.value.id,
data.value.project_type
)
installing.value = false
return
@@ -416,13 +455,12 @@ async function install(version) {
}
installed.value = true
} else {
if (version) {
modInstallModal.value.show(data.value.id, [
versions.value.find((v) => v.id === queuedVersionData.id),
])
} else {
modInstallModal.value.show(data.value.id, versions.value)
}
modInstallModal.value.show(
data.value.id,
version ? [versions.value.find((v) => v.id === queuedVersionData.id)] : versions.value,
data.value.title,
data.value.project_type
)
}
}
@@ -622,7 +660,7 @@ const handleOptionsClick = (args) => {
.instance {
display: flex;
gap: 0.5rem;
margin-bottom: 0.5rem;
margin-bottom: 0;
.title {
font-weight: 600;

View File

@@ -1,9 +1,17 @@
<template>
<div>
<Card>
<Breadcrumbs
:current-title="version.name"
:link-stack="[
{
href: `/project/${route.params.id}/versions`,
label: 'Versions',
},
]"
/>
<div class="version-title">
<h2>{{ version.name }}</h2>
<span v-if="version.featured">Auto-Featured</span>
</div>
<div class="button-group">
<Button color="primary" :action="() => install(version.id)" :disabled="installed">
@@ -11,10 +19,6 @@
<CheckIcon v-else />
{{ installed ? 'Installed' : 'Install' }}
</Button>
<Button :link="`/project/${route.params.id}/versions`">
<LeftArrowIcon />
Back to list
</Button>
<Button>
<ReportIcon />
Report
@@ -65,10 +69,15 @@
</Button>
</Card>
</Card>
<Card v-if="displayDependencies[0]">
<Card v-if="displayDependencies.length > 0">
<h2>Dependencies</h2>
<div v-for="dependency in displayDependencies" :key="dependency.title">
<router-link v-if="dependency.link" class="btn dependency" :to="dependency.link">
<router-link
v-if="dependency.link"
class="btn dependency"
:to="dependency.link"
@click="testTest"
>
<Avatar size="sm" :src="dependency.icon" />
<div>
<span class="title"> {{ dependency.title }} </span> <br />
@@ -173,17 +182,17 @@ import {
DownloadIcon,
FileIcon,
Avatar,
LeftArrowIcon,
ReportIcon,
Badge,
ExternalIcon,
CopyCode,
CheckIcon,
Breadcrumbs,
formatBytes,
renderString,
} from 'omorphia'
import { releaseColor } from '@/helpers/utils'
import { ref, defineProps } from 'vue'
import { ref, defineProps, watch, computed } from 'vue'
import { useRoute } from 'vue-router'
import { useBreadcrumbs } from '@/store/breadcrumbs'
@@ -221,9 +230,21 @@ const props = defineProps({
const version = ref(props.versions.find((version) => version.id === route.params.version))
breadcrumbs.setName('Version', version.value.name)
const author = ref(props.members.find((member) => member.user.id === version.value.author_id))
watch(
() => props.versions,
async () => {
if (route.params.version) {
version.value = props.versions.find((version) => version.id === route.params.version)
breadcrumbs.setName('Version', version.value.name)
}
}
)
const displayDependencies = ref(
const author = computed(() =>
props.members.find((member) => member.user.id === version.value.author_id)
)
const displayDependencies = computed(() =>
version.value.dependencies.map((dependency) => {
const version = props.dependencies.versions.find((obj) => obj.id === dependency.version_id)
if (version) {
@@ -236,13 +257,25 @@ const displayDependencies = ref(
subtitle: `Version ${version.version_number} is ${dependency.dependency_type}`,
link: `/project/${project.slug}/version/${version.id}`,
}
} else
return {
icon: null,
title: dependency.file_name,
subtitle: `Added via overrides`,
link: null,
} else {
const project = props.dependencies.projects.find((obj) => obj.id === dependency.project_id)
if (project) {
return {
icon: project?.icon_url,
title: project?.title || project?.name,
subtitle: `${dependency.dependency_type}`,
link: `/project/${project.slug}`,
}
} else {
return {
icon: null,
title: dependency.file_name,
subtitle: `Added via overrides`,
link: null,
}
}
}
})
)
</script>