generated from Nodarx/template
Add office editor provider
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
<script setup lang="ts">
|
||||
import { useIntegrationStore } from '@KTXC'
|
||||
import { nextTick, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { launch, validateSession, type LaunchSession } from '../api'
|
||||
import { officeResource, type OfficeEntity } from '../resource'
|
||||
|
||||
const props = defineProps<{ entity: OfficeEntity; mime: string }>()
|
||||
const integrations = useIntegrationStore()
|
||||
const session = ref<LaunchSession | null>(null)
|
||||
const error = ref('')
|
||||
const busy = ref(false)
|
||||
const form = ref<HTMLFormElement | null>(null)
|
||||
const submitted = ref(false)
|
||||
const target = `office-${crypto.randomUUID()}`
|
||||
let generation = 0
|
||||
let timeout: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
async function open() {
|
||||
const current = ++generation
|
||||
clearTimeout(timeout)
|
||||
session.value = null
|
||||
submitted.value = false
|
||||
error.value = ''
|
||||
busy.value = true
|
||||
try {
|
||||
if (!integrations.getItems('documents_editor_service').some(item => item.id === 'service_wopi.wopi')) {
|
||||
error.value = 'The WOPI service is not available.'
|
||||
return
|
||||
}
|
||||
const result = await launch(officeResource(props.entity))
|
||||
if (current !== generation) return
|
||||
validateSession(result)
|
||||
session.value = result
|
||||
await nextTick()
|
||||
if (current !== generation) return
|
||||
form.value?.submit()
|
||||
// Remove the token-bearing form from the DOM after submission.
|
||||
submitted.value = true
|
||||
timeout = setTimeout(() => {
|
||||
error.value = 'This viewing session has expired. Reopen the document to continue.'
|
||||
session.value = null
|
||||
}, Math.min(result.accessTokenTtl - Date.now(), 2147483647))
|
||||
} catch {
|
||||
if (current === generation) error.value = 'Could not open the document. It may be unavailable, unsupported, or too large.'
|
||||
} finally {
|
||||
if (current === generation) busy.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => [props.entity.provider, props.entity.service, props.entity.collection, props.entity.identifier], open, { immediate: true })
|
||||
onBeforeUnmount(() => { generation++; clearTimeout(timeout); session.value = null })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="office-viewer">
|
||||
<p class="office-status">Read-only — reopen the document to see changes made elsewhere.</p>
|
||||
<p v-if="busy" role="status">Opening document…</p>
|
||||
<div v-if="error" role="alert">
|
||||
<p>{{ error }}</p>
|
||||
<button type="button" @click="open">Reopen document</button>
|
||||
</div>
|
||||
<template v-if="session">
|
||||
<iframe :name="target" title="Office document viewer" referrerpolicy="no-referrer" allow="fullscreen" />
|
||||
<form v-if="!submitted" ref="form" method="post" :action="session.actionUrl" :target="target" hidden>
|
||||
<input type="hidden" name="access_token" :value="session.accessToken">
|
||||
<input type="hidden" name="access_token_ttl" :value="session.accessTokenTtl">
|
||||
</form>
|
||||
</template>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.office-viewer { display: flex; flex-direction: column; height: 100%; min-height: 0; padding: 8px; }
|
||||
.office-status { margin: 0 0 8px; font-size: 0.875rem; }
|
||||
iframe { flex: 1; width: 100%; min-height: 400px; border: 0; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user