Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sse item renamed event #10567

Merged
merged 2 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 2 additions & 1 deletion packages/web-client/src/sse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { fetchEventSource, FetchEventSourceInit } from '@microsoft/fetch-event-s

export enum MESSAGE_TYPE {
NOTIFICATION = 'userlog-notification',
POSTPROCESSING_FINISHED = 'postprocessing-finished'
POSTPROCESSING_FINISHED = 'postprocessing-finished',
ITEM_RENAMED = 'item-renamed'
}

export class RetriableError extends Error {
Expand Down
102 changes: 15 additions & 87 deletions packages/web-runtime/src/container/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ import {
useSharesStore,
useResourcesStore,
ResourcesStore,
SpacesStore,
ImageDimension
SpacesStore
} from '@ownclouders/web-pkg'
import { authService } from '../services/auth'
import {
Expand All @@ -49,15 +48,14 @@ import {
} from '@ownclouders/web-pkg'
import { MESSAGE_TYPE } from '@ownclouders/web-client/src/sse'
import { getQueryParam } from '../helpers/url'
import { z } from 'zod'
import PQueue from 'p-queue'
import { extractNodeId, extractStorageId } from '@ownclouders/web-client/src/helpers'
import { storeToRefs } from 'pinia'
import { getExtensionNavItems } from '../helpers/navItems'
import {
RawConfigSchema,
SentryConfig
} from '@ownclouders/web-pkg/src/composables/piniaStores/config/types'
import { onSSEItemRenamedEvent, onSSEProcessingFinishedEvent } from './sse'

const getEmbedConfigFromQuery = (
doesEmbedEnabledOptionExists: boolean
Expand Down Expand Up @@ -641,100 +639,20 @@ export const announceCustomStyles = ({ configStore }: { configStore?: ConfigStor
})
}

const fileReadyEventSchema = z.object({
itemid: z.string(),
parentitemid: z.string()
})

const onSSEProcessingFinishedEvent = async ({
resourcesStore,
spacesStore,
msg,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
clientService,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
resourceQueue,
previewService
}: {
resourcesStore: ResourcesStore
spacesStore: SpacesStore
msg: MessageEvent
clientService: ClientService
resourceQueue: PQueue
previewService: PreviewService
}) => {
try {
const postProcessingData = fileReadyEventSchema.parse(JSON.parse(msg.data))
const currentFolder = resourcesStore.currentFolder
if (!currentFolder) {
return
}

// UPDATE_RESOURCE_FIELD only handles files in the currentFolder, so we can shortcut here for now
if (!extractNodeId(currentFolder.id)) {
// if we don't have a nodeId here, we have a space (root) as current folder and can only check against the storageId
if (currentFolder.id !== extractStorageId(postProcessingData.parentitemid)) {
return
}
} else {
if (currentFolder.id !== postProcessingData.parentitemid) {
return
}
}

const resource = resourcesStore.resources.find((f) => f.id === postProcessingData.itemid)
const matchingSpace = spacesStore.spaces.find((s) => s.id === resource.storageId)
const isFileLoaded = !!resource
if (isFileLoaded) {
resourcesStore.updateResourceField({
id: postProcessingData.itemid,
field: 'processing',
value: false
})

if (matchingSpace) {
const preview = await previewService.loadPreview({
resource,
space: matchingSpace,
dimensions: ImageDimension.Thumbnail
})

if (preview) {
resourcesStore.updateResourceField({
id: postProcessingData.itemid,
field: 'thumbnail',
value: preview
})
}
}
} else {
// FIXME: we currently cannot do this, we need to block this for ongoing uploads and copy operations
// when fixing revert the changelog removal
// resourceQueue.add(async () => {
// const { resource } = await clientService.webdav.listFilesById({
// fileId: postProcessingData.itemid
// })
// resource.path = urlJoin(currentFolder.path, resource.name)
// resourcesStore.upsertResource(resource)
// })
}
} catch (e) {
console.error('Unable to parse sse postprocessing data', e)
}
}

export const registerSSEEventListeners = ({
resourcesStore,
spacesStore,
clientService,
previewService,
configStore
configStore,
router
}: {
resourcesStore: ResourcesStore
spacesStore: SpacesStore
clientService: ClientService
previewService: PreviewService
configStore: ConfigStore
router: Router
}): void => {
const resourceQueue = new PQueue({
concurrency: configStore.options.concurrentRequests.sse
Expand All @@ -747,6 +665,16 @@ export const registerSSEEventListeners = ({
}
)

clientService.sseAuthenticated.addEventListener(MESSAGE_TYPE.ITEM_RENAMED, (msg) =>
onSSEItemRenamedEvent({
resourcesStore,
spacesStore,
msg,
clientService,
router
})
)

clientService.sseAuthenticated.addEventListener(MESSAGE_TYPE.POSTPROCESSING_FINISHED, (msg) =>
onSSEProcessingFinishedEvent({
resourcesStore,
Expand Down
176 changes: 176 additions & 0 deletions packages/web-runtime/src/container/sse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import {
ClientService,
createFileRouteOptions,
ImageDimension,
PreviewService,
ResourcesStore,
SpacesStore
} from '@ownclouders/web-pkg'
import PQueue from 'p-queue'
import { extractNodeId, extractStorageId } from '@ownclouders/web-client/src/helpers'
import { z } from 'zod'
import { Router } from 'vue-router'

const fileReadyEventSchema = z.object({
itemid: z.string(),
parentitemid: z.string()
})

type SSEMessageData = {
itemid?: string
parentitemid?: string
}

const itemInCurrentFolder = ({
resourcesStore,
sseData
}: {
resourcesStore: ResourcesStore
sseData: SSEMessageData
}) => {
const currentFolder = resourcesStore.currentFolder
if (!currentFolder) {
return false
}

if (!extractNodeId(currentFolder.id)) {
// if we don't have a nodeId here, we have a space (root) as current folder and can only check against the storageId
if (currentFolder.id !== extractStorageId(sseData.parentitemid)) {
return false
}
} else {
if (currentFolder.id !== sseData.parentitemid) {
return false
}
}

return true
}

export const onSSEItemRenamedEvent = async ({
resourcesStore,
spacesStore,
msg,
clientService,
router
}: {
resourcesStore: ResourcesStore
spacesStore: SpacesStore
msg: MessageEvent
clientService: ClientService
router: Router
}) => {
try {
const sseData = fileReadyEventSchema.parse(JSON.parse(msg.data))

const currentFolder = resourcesStore.currentFolder
const resourceIsCurrentFolder = currentFolder.id === sseData.itemid

if (!resourceIsCurrentFolder && !itemInCurrentFolder({ resourcesStore, sseData })) {
return false
}

const resource = resourceIsCurrentFolder
? currentFolder
: resourcesStore.resources.find((f) => f.id === sseData.itemid)

const space = spacesStore.spaces.find((s) => s.id === resource.storageId)

if (!resource || !space) {
return
}

const updatedResource = await clientService.webdav.getFileInfo(space, {
fileId: sseData.itemid
})

if (resourceIsCurrentFolder) {
resourcesStore.setCurrentFolder(updatedResource)
return router.push(
createFileRouteOptions(space, {
path: updatedResource.path,
fileId: updatedResource.fileId
})
)
}

resourcesStore.updateResourceField({
id: sseData.itemid,
field: 'name',
value: updatedResource.name
})

resourcesStore.updateResourceField({
id: sseData.itemid,
field: 'path',
value: updatedResource.path
})
} catch (e) {
console.error('Unable to parse sse event item renamed data', e)
}
}
export const onSSEProcessingFinishedEvent = async ({
resourcesStore,
spacesStore,
msg,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
clientService,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
resourceQueue,
previewService
}: {
resourcesStore: ResourcesStore
spacesStore: SpacesStore
msg: MessageEvent
clientService: ClientService
resourceQueue: PQueue
previewService: PreviewService
}) => {
try {
const sseData = fileReadyEventSchema.parse(JSON.parse(msg.data))

if (!itemInCurrentFolder({ resourcesStore, sseData })) {
return false
}

const resource = resourcesStore.resources.find((f) => f.id === sseData.itemid)
const space = spacesStore.spaces.find((s) => s.id === resource.storageId)
const isFileLoaded = !!resource

if (isFileLoaded) {
resourcesStore.updateResourceField({
id: sseData.itemid,
field: 'processing',
value: false
})

if (space) {
const preview = await previewService.loadPreview({
resource,
space,
dimensions: ImageDimension.Thumbnail
})

if (preview) {
resourcesStore.updateResourceField({
id: sseData.itemid,
field: 'thumbnail',
value: preview
})
}
}
} else {
// FIXME: we currently cannot do this, we need to block this for ongoing uploads and copy operations
// when fixing revert the changelog removal
// resourceQueue.add(async () => {
// const { resource } = await clientService.webdav.listFilesById({
// fileId: sseData.itemid
// })
// resource.path = urlJoin(currentFolder.path, resource.name)
// resourcesStore.upsertResource(resource)
// })
}
} catch (e) {
console.error('Unable to parse sse event postprocessing-finished data', e)
}
}
3 changes: 2 additions & 1 deletion packages/web-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ export const bootstrapApp = async (configurationPath: string): Promise<void> =>
spacesStore,
clientService,
previewService,
configStore
configStore,
router
})
}

Expand Down