Skip to content

Commit

Permalink
[WiP] basic mechanism of adding files
Browse files Browse the repository at this point in the history
  • Loading branch information
mkurczewski committed Jan 7, 2025
1 parent 542a6ea commit 76e6e11
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 13 deletions.
77 changes: 66 additions & 11 deletions libs/generic-view/models/src/lib/common-validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import { z } from "zod"
import { entityDataSchema } from "../../../../device/models/src/lib/entities/entity-data.validator"

export const modalSizeValidator = z.enum(["small", "medium", "large"])

Expand Down Expand Up @@ -93,20 +94,73 @@ const entityPostActionsValidator = z
)
.optional()

export const entityActionValidator = z.object({
type: z.literal("entities-delete"),
entitiesType: z.string(),
ids: z.array(z.string()),
postActions: z
.object({
success: entityPostActionsValidator,
failure: entityPostActionsValidator,
})
.optional(),
})
export const entityActionValidator = z.union([
z.object({
type: z.literal("entities-delete"),
entitiesType: z.string(),
ids: z.array(z.string()),
postActions: z
.object({
success: entityPostActionsValidator,
failure: entityPostActionsValidator,
})
.optional(),
}),
z.object({
type: z.literal("entity-create"),
entitiesType: z.string(),
data: entityDataSchema,
postActions: z
.object({
success: entityPostActionsValidator,
failure: entityPostActionsValidator,
})
.optional(),
}),
])

export type EntityAction = z.infer<typeof entityActionValidator>

const filePostActionsValidator = z
.array(
z.union([
modalActionValidator,
navigateActionValidator,
customActionValidator,
formActionValidator,
toastActionValidator,
entityActionValidator,
])
)
.optional()

export const fileActionValidator = z.union([
z.object({
type: z.literal("file-upload"),
storagePath: z.string(),
typesName: z.string(),
fileTypes: z.array(z.string()),
postActions: z
.object({
success: filePostActionsValidator,
failure: filePostActionsValidator,
})
.optional(),
}),
z.object({
type: z.literal("file-download"),
storagePath: z.string(),
postActions: z
.object({
success: filePostActionsValidator,
failure: filePostActionsValidator,
})
.optional(),
}),
])

export type FileAction = z.infer<typeof fileActionValidator>

export const buttonActionsValidator = z.array(
z.union([
modalActionValidator,
Expand All @@ -115,6 +169,7 @@ export const buttonActionsValidator = z.array(
formActionValidator,
entityActionValidator,
toastActionValidator,
fileActionValidator,
])
)

Expand Down
13 changes: 13 additions & 0 deletions libs/generic-view/models/src/lib/mc-file-manager-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ const configValidator = z.object({
entityTypes: z.array(z.string()).min(1),
})

//
// const configValidator = z.object({
// entityTypes: z
// .array(
// z.object({
// entityType: z.string(),
// storagePath: z.string(),
// supportedFileTypes: z.array(z.string()),
// })
// )
// .min(1),
// })

export type McFileManagerView = z.infer<typeof configValidator>

export const mcFileManagerView = {
Expand Down
1 change: 1 addition & 0 deletions libs/generic-view/store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export * from "./lib/file-transfer/reducer"
export * from "./lib/file-transfer/actions"
export * from "./lib/file-transfer/send-file.action"
export * from "./lib/file-transfer/get-file.action"
export * from "./lib/file-transfer/select-and-send-files.action"
export * from "./lib/backup/load-backup-metadata.action"
export * from "./lib/backup/restore-backup.action"
export * from "./lib/backup/backup.types"
Expand Down
1 change: 1 addition & 0 deletions libs/generic-view/store/src/lib/action-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export enum ActionName {
ChunkFileTransferGet = "generic-file-transfer/chunk-get",
ClearFileTransferGetError = "generic-file-transfer/clear-get-errors",
TransferDataToDevice = "generic-file-transfer/transfer-data-to-device",
SendSelectedFiles = "generic-file-transfer/send-selected-files",

SetDataTransfer = "generic-data-transfer/set-data-transfer",
SetDataTransferStatus = "generic-data-transfer/set-data-transfer-status",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,23 @@ export const createEntityDataAction = createAsyncThunk<
entitiesType: string
data: EntityData
deviceId: DeviceId
onSuccess?: () => Promise<void> | void
onError?: () => Promise<void> | void
},
{ state: ReduxRootState }
>(
ActionName.CreateEntityData,
async ({ entitiesType, data, deviceId }, { rejectWithValue, getState }) => {
async (
{ entitiesType, data, deviceId, onError, onSuccess },
{ rejectWithValue, getState }
) => {
const response = await createEntityDataRequest({
entitiesType,
data,
deviceId,
})
if (!response.ok) {
await onError?.()
return rejectWithValue(response.error)
}

Expand All @@ -39,6 +45,7 @@ export const createEntityDataAction = createAsyncThunk<
logger.error(
`Entities of type ${entitiesType} for device ${deviceId} not found`
)
await onError?.()
return rejectWithValue(undefined)
}
if (
Expand All @@ -51,11 +58,13 @@ export const createEntityDataAction = createAsyncThunk<
response.data.data[idFieldKey] as string
} already exists`
)
await onError?.()
return rejectWithValue(undefined)
}

const computedFields =
genericEntities[deviceId]?.[entitiesType]?.config.computedFields || {}
await onSuccess?.()
return enhanceEntity(response.data.data, { computedFields })
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* Copyright (c) Mudita sp. z o.o. All rights reserved.
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
*/

import { createAsyncThunk } from "@reduxjs/toolkit"
import { ActionName } from "../action-names"
import { ReduxRootState } from "Core/__deprecated__/renderer/store"
import { openFileRequest } from "system-utils/feature"
import { sendFile } from "./send-file.action"
import { selectActiveApiDeviceId } from "../selectors"
import * as path from "node:path"
import { isEmpty } from "lodash"

interface SendSelectedFilesActionPayload {
typesName: string
fileTypes: string[]
storagePath: string
onFileSuccess?: (file: string) => Promise<void>
onFileError?: (file: string) => Promise<void>
onSuccess?: () => Promise<void>
onError?: () => Promise<void>
}

export const selectAndSendFilesAction = createAsyncThunk<
undefined,
SendSelectedFilesActionPayload,
{ state: ReduxRootState }
>(
ActionName.SendSelectedFiles,
async (
{
typesName,
fileTypes,
storagePath,
onFileSuccess,
onFileError,
onSuccess,
onError,
},
{ rejectWithValue, dispatch, getState }
) => {
const failedFiles = []

const deviceId = selectActiveApiDeviceId(getState())
const openFileResult = await openFileRequest({
filters: [
{
name: typesName,
extensions: fileTypes,
},
],
properties: ["openFile", "multiSelections"],
})

if (!openFileResult.ok) {
return rejectWithValue("cancelled")
}

if (!deviceId) {
return rejectWithValue(undefined)
}

for (const file of openFileResult.data) {
const fileName = path.basename(file)
const sentFile = await dispatch(
sendFile({
filePath: file,
targetPath: storagePath + fileName,
deviceId,
})
)
if (sentFile.meta.requestStatus === "fulfilled") {
await onFileSuccess?.(file)
} else {
failedFiles.push(file)
await onFileError?.(file)
}
}
if (isEmpty(failedFiles)) {
await onSuccess?.()
} else {
await onError?.()
}
return
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import {
closeAllModals,
closeDomainModals,
closeModal,
createEntityDataAction,
deleteEntitiesDataAction,
openModal,
openToastAction,
replaceModal,
selectActiveApiDeviceId,
selectAndSendFilesAction,
useScreenTitle,
} from "generic-view/store"
import { useDispatch, useSelector } from "react-redux"
Expand Down Expand Up @@ -127,6 +129,30 @@ const runActions = (actions?: ButtonActions) => {
})
)
break
case "entity-create":
await dispatch(
createEntityDataAction({
data: action.data,
entitiesType: action.entitiesType,
deviceId: activeDeviceId,
onSuccess: () => {
return runActions(action.postActions?.success)(providers)
},
onError: () => {
return runActions(action.postActions?.failure)(providers)
},
})
)
break
case "file-upload":
await dispatch(
selectAndSendFilesAction({
storagePath: action.storagePath,
typesName: action.typesName,
fileTypes: action.fileTypes,
})
)
break
case "open-toast":
await dispatch(openToastAction(action.toastKey))
break
Expand Down
Loading

0 comments on commit 76e6e11

Please sign in to comment.