Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions src/apis/Attach.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
import type { Connection } from '../composables/useConnection.js'
import { unref, type ShallowRef } from 'vue'

/**
* Upload an attachment to the server.
* @param connection the active connection.
* @param file upload this file.
*/
export function uploadAttachment(
connection: ShallowRef<Connection> | Connection,
file: string | Blob,
) {
const {
documentId,
sessionId,
sessionToken,
shareToken: token,
} = unref(connection)
const params = Object.entries({ documentId, sessionId, sessionToken, token })
.map(([k, v]) => v && [k, v.toString()]) // convert numbers to strings
.filter((pair) => !!pair) // leave out token if it's undefined.
const formData = new FormData()
formData.append('file', file)
const pub = token ? '/public' : ''
const url =
generateUrl(`apps/text${pub}/attachment/upload?`)
+ new URLSearchParams(params)
return axios.post(url, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
})
}

/**
* Create a new attachment based on the given template
* @param connection the active connection
* @param template create the attachment based on this
* @param template.app app to create the attachment with
* @param template.extension extension to use
*/
export function createAttachment(
connection: ShallowRef<Connection> | Connection,
template: { app: string; extension: string },
) {
const {
documentId,
sessionId,
sessionToken,
shareToken: token,
} = unref(connection)
const pub = token ? '/public' : ''
const url = generateUrl(`apps/text${pub}/attachment/create`)
return axios.post(url, {
documentId,
sessionId,
sessionToken,
fileName: `${template.app}${template.extension}`,
})
}

/**
* Create a new attachment based on the given template
* @param connection the active connection
* @param filePath path to the file on the server.
*/
export function insertAttachmentFile(
connection: ShallowRef<Connection> | Connection,
filePath: string,
) {
const {
documentId,
sessionId,
sessionToken,
shareToken: token,
} = unref(connection)
const pub = token ? '/public' : ''
const url = generateUrl(`apps/text${pub}/attachment/filepath`)
return axios.post(url, {
documentId,
sessionId,
sessionToken,
filePath,
})
}
15 changes: 8 additions & 7 deletions src/components/Editor/MediaHandler.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ import { getCurrentUser } from '@nextcloud/auth'
import { showError } from '@nextcloud/dialogs'
import { emit } from '@nextcloud/event-bus'
import { generateUrl } from '@nextcloud/router'
import { logger } from '../../helpers/logger.js'
import { useIsMobile } from '@nextcloud/vue/composables/useIsMobile'
import { logger } from '../../helpers/logger.js'
import { createAttachment, insertAttachmentFile, uploadAttachment } from '../../apis/Attach.ts'

import {
useFileMixin,
Expand All @@ -42,7 +43,7 @@ import {
ACTION_CREATE_ATTACHMENT,
STATE_UPLOADING,
} from './MediaHandler.provider.js'
import { useSyncService } from '../../composables/useSyncService.ts'
import { useConnection } from '../../composables/useConnection.ts'

const getDir = (val) => val.split('/').slice(0, -1).join('/')

Expand Down Expand Up @@ -70,11 +71,11 @@ export default {
return val
},
setup() {
const { connection } = useConnection()
const isMobile = useIsMobile()
const { editor } = useEditor()
const { syncService } = useSyncService()
return {
editor, isMobile, syncService
connection, editor, isMobile
}
},
data() {
Expand Down Expand Up @@ -134,7 +135,7 @@ export default {
async uploadAttachmentFile(file, position = null) {
this.state.isUploadingAttachments = true

return this.syncService.uploadAttachment(file)
return uploadAttachment(this.connection, file)
.then((response) => {
this.insertAttachment(
response.data?.name, response.data?.id, file.type,
Expand Down Expand Up @@ -168,7 +169,7 @@ export default {

this.state.isUploadingAttachments = true

return this.syncService.insertAttachmentFile(filePath).then((response) => {
return insertAttachmentFile(this.connection, filePath).then((response) => {
this.insertAttachment(
response.data?.name, response.data?.id, response.data?.mimetype,
null, response.data?.dirname,
Expand All @@ -182,7 +183,7 @@ export default {
},
createAttachment(template) {
this.state.isUploadingAttachments = true
return this.syncService.createAttachment(template).then((response) => {
return createAttachment(this.connection, template).then((response) => {
this.insertAttachmentPreview(response.data?.id)
}).catch((error) => {
logger.error('Failed to create attachment', { error })
Expand Down
38 changes: 0 additions & 38 deletions src/services/SessionConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,44 +86,6 @@ export class SessionConnection {
})
}

uploadAttachment(file) {
const formData = new FormData()
formData.append('file', file)
const url =
_endpointUrl('attachment/upload')
+ '?documentId='
+ encodeURIComponent(this.#document.id)
+ '&sessionId='
+ encodeURIComponent(this.#session.id)
+ '&sessionToken='
+ encodeURIComponent(this.#session.token)
+ '&token='
+ encodeURIComponent(this.connection.shareToken || '')
return this.#post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
}

createAttachment(template) {
return this.#post(_endpointUrl('attachment/create'), {
documentId: this.#document.id,
sessionId: this.#session.id,
sessionToken: this.#session.token,
fileName: `${template.app}${template.extension}`,
})
}

insertAttachmentFile(filePath) {
return this.#post(_endpointUrl('attachment/filepath'), {
documentId: this.#document.id,
sessionId: this.#session.id,
sessionToken: this.#session.token,
filePath,
})
}

close() {
this.closed = true
}
Expand Down
21 changes: 0 additions & 21 deletions src/services/SyncService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@
this.sessionConnection = new SessionConnection(data, this.connection.value)
this.version = this.sessionConnection.docStateVersion
if (!this.connection.value) {
console.error('Opened the connection but now it is undefined')
return
}

Check warning on line 173 in src/services/SyncService.ts

View check run for this annotation

Codecov / codecov/patch

src/services/SyncService.ts#L171-L173

Added lines #L171 - L173 were not covered by tests
this.backend = new PollingBackend(this, this.connection.value)
// Make sure to only emit this once the backend is in place.
this.emit('opened', this.sessionConnection.state)
Expand Down Expand Up @@ -367,27 +367,6 @@
this.emit('close')
}

uploadAttachment(file: object) {
if (!this.hasActiveConnection()) {
throw new Error('Not connected to server.')
}
return this.sessionConnection.uploadAttachment(file)
}

insertAttachmentFile(filePath: string) {
if (!this.hasActiveConnection()) {
throw new Error('Not connected to server.')
}
return this.sessionConnection.insertAttachmentFile(filePath)
}

createAttachment(template: object) {
if (!this.hasActiveConnection()) {
throw new Error('Not connected to server.')
}
return this.sessionConnection.createAttachment(template)
}

// For better typing use the bus directly: `syncService.bus.on()`.
on(event: keyof EventTypes, callback: Handler<unknown>) {
this.bus.on(event, callback)
Expand Down
Loading