import { defineStore, storeToRefs } from 'pinia'; import { computed, reactive, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { useViewer3cr } from '@/composables/useViewer3cr'; import { mockDemoViewerStlDecimator } from '@/components/demo/mockDemoViewerStlDecimator'; import { LoadSessionState } from '@3cr/types-ts/types/LoadSessionState'; import { DEMO_BUCKET_URL } from '@/constants'; import { useUploadStore } from '@/stores/upload.store'; import { DemoType, SaveState, Upload, ViewerOptions, ViewerPayload, } from '@3cr/viewer-types-ts'; import totalSegmentator from '@/assets/images/totalSegmentator.png'; import { v4 } from 'uuid'; import avatar from '@/assets/images/courtney.png'; const now = new Date(); function createUpload(file: File): Upload { return { id: v4(), title: file.name, subtitle: 'Uploading...', status: 'uploading' as const, progress: 0, }; } function fakeUpload(upload: Upload) { upload.progress = 0; upload.status = 'uploading'; const interval = setInterval(() => { if (upload.progress! < 1) { if (Math.random() >= 0.5) { upload.progress = upload.progress! + 0.01; } } else { upload.status = 'completed'; upload.subtitle = 'Upload Complete'; clearInterval(interval); } }, 100); } export const useDemoStore = defineStore('demo', () => { const { t, tm } = useI18n(); const viewer3cr = useViewer3cr(); const { uploads } = storeToRefs(useUploadStore()); const isDemo = ref(false); const demoTitle = ref(''); const demoSubtitles = ref([]); const m_demoInfo = ref(false); const m_demoEnableCloudStorage = ref(false); const m_demoPurchaseCredits = ref(false); const m_demoUpgradePlan = ref(false); const profileDropdown = reactive({ modal: false, x: 0, y: 0 }); const notificationDropdown = reactive({ modal: false, x: 0, y: 0 }); const recentDropdown = reactive({ modal: false, x: 0, y: 0 }); const demoLicenceOptions = computed(() => ({ fileName: `${now.getUTCFullYear()}-${now.getUTCMonth() + 1}-${now.getUTCDate()} CT Chest-Scan DICOM Series`, user: { name: t('data.profiles[0].name'), avatarUrl: avatar }, getAIModels: () => [ { key: 'TotalSegmentator', image: totalSegmentator, title: t('labels.autoAnnotate'), subtitle: t('texts.supportsFormats'), description: t('texts.runAutoAi'), url: 'https://google.com', tooltip: t('labels.totalSegmentator'), cost: 5, estimatedTimeSeconds: 1200, }, ], onRunAIModel: async (_keys: string[]) => { mockDemoViewerStlDecimator(); const message: LoadSessionState = { Version: '0.0.0', Url: `${DEMO_BUCKET_URL}/state/TotalSegmentator_1722837421466.3crms`, }; await viewer3cr.loadMcadObjects({ message }); }, onCloudStorage: () => { m_demoEnableCloudStorage.value = true; }, onPurchaseCredits: () => { m_demoPurchaseCredits.value = true; }, onDownloadMcad: () => { demoTitle.value = t('texts.demo.licence.downloadMcad.title'); demoSubtitles.value = tm('texts.demo.licence.downloadMcad.subtitles'); m_demoInfo.value = true; }, onMCADUpload: (files: Array) => { const items = files.map((file) => createUpload(file)); uploads.value.push(...items); uploads.value.forEach((upload) => fakeUpload(upload)); }, onDownloadScan: () => { demoTitle.value = t('texts.demo.licence.downloadScan.title'); demoSubtitles.value = tm('texts.demo.licence.downloadScan.subtitles'); m_demoInfo.value = true; }, onExit: () => { demoTitle.value = t('texts.demo.licence.exit.title'); demoSubtitles.value = tm('texts.demo.licence.exit.subtitles'); m_demoInfo.value = true; }, onLoadScan: () => { demoTitle.value = t('texts.demo.licence.loadScan.title'); demoSubtitles.value = tm('texts.demo.licence.loadScan.subtitles'); m_demoInfo.value = true; }, onLoadSession: () => { demoTitle.value = t('texts.demo.licence.loadSession.title'); demoSubtitles.value = tm('texts.demo.licence.loadSession.subtitles'); m_demoInfo.value = true; }, onProfileDropdown: (x, y) => { profileDropdown.x = x; profileDropdown.y = y; profileDropdown.modal = true; }, onNotificationDropdown: (x: number, y: number) => { notificationDropdown.x = x; notificationDropdown.y = y; notificationDropdown.modal = true; }, onRecentDropdown: (x: number, y: number) => { recentDropdown.x = x; recentDropdown.y = y; recentDropdown.modal = true; }, onSaveSession: (scan, dataOverlay, mcad, callback) => { callback(SaveState.SAVING); setTimeout(() => { callback(SaveState.SAVED_TEMPORARY); setTimeout(() => { demoTitle.value = t('texts.demo.licence.saveSession.title'); demoSubtitles.value = tm('texts.demo.licence.saveSession.subtitles'); m_demoInfo.value = true; }, 500); }, 1000); }, onShareScan: () => {}, onUpgrade: () => { m_demoUpgradePlan.value = true; }, })); const demoPatientOptions = computed(() => ({ fileName: `${now.getUTCFullYear()}-${now.getUTCMonth() + 1}-${now.getUTCDate()} CT Chest-Scan DICOM Series`, user: { name: t('data.profiles[0].name'), avatarUrl: avatar }, getAIModels: () => [ { key: 'TotalSegmentator', image: totalSegmentator, title: t('labels.autoAnnotate'), subtitle: t('texts.supportsFormats'), description: t('texts.runAutoAi'), url: 'https://google.com', tooltip: t('labels.totalSegmentator'), cost: 5, estimatedTimeSeconds: 1200, }, ], onRunAIModel: async (_keys: string[]) => { mockDemoViewerStlDecimator(); const message: LoadSessionState = { Version: '0.0.0', Url: `${DEMO_BUCKET_URL}/state/TotalSegmentator_1722837421466.3crms`, }; await viewer3cr.loadMcadObjects({ message }); }, onCloudStorage: () => { m_demoEnableCloudStorage.value = true; }, onPurchaseCredits: () => { m_demoPurchaseCredits.value = true; }, onDownloadMcad: () => { demoTitle.value = t('texts.demo.patient.downloadMcad.title'); demoSubtitles.value = tm('texts.demo.patient.downloadMcad.subtitles'); m_demoInfo.value = true; }, onDownloadScan: () => { demoTitle.value = t('texts.demo.patient.downloadScan.title'); demoSubtitles.value = tm('texts.demo.patient.downloadScan.subtitles'); m_demoInfo.value = true; }, onMCADUpload: (files: Array) => { const items = files.map((file) => createUpload(file)); uploads.value.push(...items); uploads.value.forEach((upload) => fakeUpload(upload)); }, onExit: () => { demoTitle.value = t('texts.demo.patient.exit.title'); demoSubtitles.value = tm('texts.demo.patient.exit.subtitles'); m_demoInfo.value = true; }, onLoadScan: () => { demoTitle.value = t('texts.demo.patient.loadScan.title'); demoSubtitles.value = tm('texts.demo.patient.loadScan.subtitles'); m_demoInfo.value = true; }, onLoadSession: () => { demoTitle.value = t('texts.demo.patient.loadSession.title'); demoSubtitles.value = tm('texts.demo.patient.loadSession.subtitles'); m_demoInfo.value = true; }, onProfileDropdown: (x, y) => { profileDropdown.x = x; profileDropdown.y = y; profileDropdown.modal = true; }, onNotificationDropdown: (x: number, y: number) => { notificationDropdown.x = x; notificationDropdown.y = y; notificationDropdown.modal = true; }, onRecentDropdown: (x: number, y: number) => { recentDropdown.x = x; recentDropdown.y = y; recentDropdown.modal = true; }, onSaveSession: (scan, dataOverlay, mcad, callback) => { callback(SaveState.SAVING); setTimeout(() => { callback(SaveState.SAVED_TEMPORARY); setTimeout(() => { demoTitle.value = t('texts.demo.patient.saveSession.title'); demoSubtitles.value = tm('texts.demo.patient.saveSession.subtitles'); m_demoInfo.value = true; }, 500); }, 1000); }, onShareScan: () => {}, onUpgrade: () => { m_demoUpgradePlan.value = true; }, })); const demoType = computed(() => { const urlParams = new URLSearchParams(window.location.search); const demoType = urlParams.get('demoType')?.toLowerCase(); switch (demoType) { case 'licence': return DemoType.Licence; case 'patient': default: return DemoType.Patient; } }); const demoOptions = computed(() => { switch (demoType.value) { case DemoType.Licence: return demoLicenceOptions.value; case DemoType.Patient: return demoPatientOptions.value; } }); function setIsDemo(payload: ViewerPayload) { isDemo.value = payload.Url.startsWith( 'https://webgl-3dr.singular.health/demo', ); } return { isDemo, demoTitle, demoSubtitles, m_demoInfo, m_demoEnableCloudStorage, m_demoUpgradePlan, m_demoPurchaseCredits, profileDropdown, notificationDropdown, recentDropdown, demoLicenceOptions, demoPatientOptions, demoType, demoOptions, setIsDemo, }; });