Files

333 lines
11 KiB
Vue

<template>
<ModelCardBase
:model-card="modelCard"
:project="project"
:can-edit="canEdit"
@manual-publish-or-load="handleMainButtonClick"
>
<div class="flex max-[275px]:w-full items-center space-x-2 my-2">
<FormButton
v-tippy="
isExpired
? 'A new version was pushed ' +
latestVersionCreatedAt +
'. Click to load a different version.'
: 'Load a different version'
"
:icon-left="ClockIcon"
size="sm"
color="subtle"
class="block text-foreground-2 hover:text-foreground overflow-hidden max-w-full !justify-start"
full-width
:disabled="!!modelCard.progress || !canEdit || isReceiveSettingsMissing"
@click.stop="openVersionsDialog = true"
>
<span>
Loaded
<b>version</b>
</span>
&nbsp;from&nbsp;
<span class="truncate">{{ createdAgo }}</span>
</FormButton>
</div>
<!-- <div
class="min-w-0 truncate text-foreground-2 -mt-1"
:title="
versionDetailsResult?.project.model.version.message || 'No message provided'
"
>
<span class="truncate max-[275px]:truncate-no select-none text-xs">
{{ createdAgo }}
</span>
</div> -->
<CommonDialog
v-model:open="openVersionsDialog"
fullscreen="none"
title="Change loaded version"
>
<WizardVersionSelector
:account-id="modelCard.accountId"
:project-id="modelCard.projectId"
:model-id="modelCard.modelId"
:workspace-slug="modelCard.workspaceSlug"
:selected-version-id="modelCard.selectedVersionId"
:settings="modelCard.settings"
@next="handleVersionSelection"
@update:settings="handleUpdateSettings"
/>
</CommonDialog>
<template #states>
<CommonModelNotification
v-if="isReceiveSettingsMissing"
:notification="receiveSettingsMissingNotification"
/>
<CommonModelNotification
v-if="expiredNotification"
:notification="expiredNotification"
@dismiss="
store.patchModel(modelCard.modelCardId, {
hasDismissedUpdateWarning: true
})
"
/>
<CommonModelNotification
v-if="errorNotification"
:notification="errorNotification"
@dismiss="store.patchModel(modelCard.modelCardId, { error: undefined })"
/>
<CommonModelNotification
v-if="receiveResultNotification"
:notification="receiveResultNotification"
@dismiss="
store.patchModel(modelCard.modelCardId, {
displayReceiveComplete: false
})
"
/>
</template>
</ModelCardBase>
</template>
<script setup lang="ts">
import dayjs from 'dayjs'
import { useQuery } from '@vue/apollo-composable'
import { ClockIcon } from '@heroicons/vue/24/solid'
import type { ModelCardNotification } from '~/lib/models/card/notification'
import type { ProjectModelGroup } from '~/store/hostApp'
import { useHostAppStore } from '~/store/hostApp'
import type { IReceiverModelCard } from '~/lib/models/card/receiver'
import { versionDetailsQuery } from '~/lib/graphql/mutationsAndQueries'
import type { VersionListItemFragment } from '~/lib/common/generated/gql/graphql'
import { useMixpanel } from '~/lib/core/composables/mixpanel'
import { useInterval, watchOnce } from '@vueuse/core'
import { useAccountStore } from '~~/store/accounts'
import type { CardSetting } from '~/lib/models/card/setting'
const { trackEvent } = useMixpanel()
const app = useNuxtApp()
const accountStore = useAccountStore()
const props = defineProps<{
modelCard: IReceiverModelCard
project: ProjectModelGroup
canEdit: boolean
}>()
const store = useHostAppStore()
const openVersionsDialog = ref(false)
const projectAccount = computed(() =>
accountStore.accountWithFallback(props.project.accountId, props.project.serverUrl)
)
app.$baseBinding?.on('documentChanged', () => {
openVersionsDialog.value = false
})
const isReceiveSettingsMissing = computed(
() =>
store.receiveSettings &&
store.receiveSettings.length > 0 &&
!props.modelCard.settings
)
const receiveSettingsMissingNotification = computed(() => {
const notification = {} as ModelCardNotification
notification.dismissible = false
notification.level = 'danger'
notification.text = 'Load settings are corrupted for some reason.'
notification.cta = {
name: 'Refresh',
action: async () => {
await store.patchModel(props.modelCard.modelCardId, {
settings: store.receiveSettings
})
}
}
return notification
})
const isExpired = computed(() => {
return props.modelCard.latestVersionId !== props.modelCard.selectedVersionId
})
const handleUpdateSettings = async (settings: CardSetting[]) => {
await store.patchModel(props.modelCard.modelCardId, {
settings
})
}
// Cancels any in progress receive AND load selected version
const handleVersionSelection = async (
selectedVersion: VersionListItemFragment,
latestVersion: VersionListItemFragment
) => {
openVersionsDialog.value = false
void trackEvent('DUI3 Action', {
name: 'Load Card Version Change',
isLatestVersion: selectedVersion === latestVersion
})
if (props.modelCard.progress) {
await store.receiveModelCancel(props.modelCard.modelCardId)
}
await store.patchModel(props.modelCard.modelCardId, {
selectedVersionId: selectedVersion.id,
selectedVersionSourceApp: selectedVersion.sourceApplication,
selectedVersionUserId: selectedVersion.authorUser?.id,
latestVersionId: latestVersion.id, // patch this dude as well, to make sure
latestVersionSourceApp: latestVersion.sourceApplication,
latestVersionUserId: latestVersion.authorUser?.id,
hasSelectedOldVersion: selectedVersion.id === latestVersion.id
})
await store.receiveModel(props.modelCard.modelCardId, 'VersionSelector')
}
// Cancels any in progress receive OR receives latest version
const handleMainButtonClick = async () => {
if (props.modelCard.progress)
return await store.receiveModelCancel(props.modelCard.modelCardId)
await receiveCurrentVersion()
}
const receiveCurrentVersion = async () => {
await store.receiveModel(props.modelCard.modelCardId, 'ModelCardButton')
}
// Cancels any in progress receive AND receives latest version
const receiveLatestVersion = async () => {
// Note: here we're updating the model card info, and afterwards we're hitting the receive action
await store.patchModel(props.modelCard.modelCardId, {
selectedVersionId: props.modelCard.latestVersionId,
selectedVersionSourceApp: props.modelCard.latestVersionSourceApp,
selectedVersionUserId: props.modelCard.latestVersionUserId
})
if (props.modelCard.progress)
await store.receiveModelCancel(props.modelCard.modelCardId)
await store.receiveModel(props.modelCard.modelCardId, 'UpdateNotification')
}
const expiredNotification = computed(() => {
if (!props.modelCard.latestVersionId || props.modelCard.hasDismissedUpdateWarning)
return
if (props.modelCard.latestVersionId === props.modelCard.selectedVersionId) return
const notification = {} as ModelCardNotification
notification.dismissible = true
notification.level = 'success'
notification.text = 'Newer version available!'
notification.cta = {
name: 'Update',
action: receiveLatestVersion
}
return notification
})
const failRate = computed(() => {
if (!props.modelCard.report) return 0
return (
(props.modelCard.report.filter((r) => r.status === 4).length /
props.modelCard.report.length) *
100
)
})
const receiveResultNotificationText = computed(() => {
if (failRate.value > 80) {
return 'Model loaded. Some objects have failed to convert!'
}
return 'Model loaded!'
})
const receiveResultNotification = computed(() => {
if (
!props.modelCard.bakedObjectIds ||
props.modelCard.displayReceiveComplete !== true
)
return
const notification = {} as ModelCardNotification
notification.dismissible = true
notification.level = 'success'
notification.text = receiveResultNotificationText.value
notification.report = props.modelCard.report
notification.cta = {
name: 'Highlight',
action: () => {
app.$baseBinding.highlightModel(props.modelCard.modelCardId)
}
}
return notification
})
const errorNotification = computed(() => {
if (!props.modelCard.error) return
const notification = {} as ModelCardNotification
notification.dismissible = true
notification.level = 'danger'
notification.text = props.modelCard.error.errorMessage
notification.report = props.modelCard.report
return notification
})
const { result: versionDetailsResult, refetch } = useQuery(
versionDetailsQuery,
() => ({
projectId: props.modelCard.projectId,
modelId: props.modelCard.modelId,
versionId: props.modelCard.selectedVersionId
}),
() => ({
clientId: projectAccount.value.accountInfo.id
})
)
const createdAgoUpdater = useInterval(10_000) // refresh the created ago, and latestversion etc. every 10s
const createdAgo = computed(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
createdAgoUpdater.value
return dayjs(versionDetailsResult.value?.project.model.version.createdAt).from(
dayjs()
)
})
const latestVersionCreatedAt = computed(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
createdAgoUpdater.value
return dayjs(props.modelCard.latestVersionCreatedAt).from(dayjs())
})
onMounted(() => {
refetch()
})
// On initialisation, we check whether there was a never version created while we were offline. If so, flagging this dude as expired.
watchOnce(versionDetailsResult, async (newVal) => {
if (!newVal) return
let patchObject = {}
if (
newVal?.project.model.versions.items &&
newVal?.project.model.versions.items.length !== 0 &&
newVal?.project.model.versions.items[0].id !== props.modelCard.selectedVersionId
) {
patchObject = {
latestVersionId: newVal?.project.model.versions.items[0].id,
latestVersionCreatedAt: newVal?.project.model.versions.items[0].createdAt,
latestVersionSourceApp: newVal?.project.model.versions.items[0].sourceApplication,
latestVersionUserId: newVal?.project.model.versions.items[0].authorUser?.id,
hasDismissedUpdateWarning: props.modelCard.hasSelectedOldVersion ? true : false
}
}
// Always update the card's project name and model name, if needed. Note, this is not needed for senders (senders do not need to create layers).
await store.patchModel(props.modelCard.modelCardId, {
...patchObject,
projectName: newVal?.project.name as string,
modelName: newVal?.project.model.name as string
})
})
</script>