diff --git a/README.md b/README.md index 0b72eef42..56f55d13f 100755 --- a/README.md +++ b/README.md @@ -38,16 +38,16 @@ See [plugin-example](https://github.com/Ragg-/Delir/tree/alpha-release/src/delir Ctrl+C to interrupt ### Path to code -- src - - **browser** -- BrowserProcess codes - - **delir-core** -- Core module codes (Project structure, calculation, renderer) - - **deream** -- Renderered frame exporter for ffmpeg (deprecated) +- packages + - **delir** -- Electron frontend of Delir + - **domain** -- Operation / Action / Store / Utils set by domain + - **Editor** -- Editor state and actions + - **Preference** -- Editor preference state and actions + - **Project** -- Project(Document) state and actions + - **Renderer** -- Delir engine state and actions + - **modules** -- Modal windows + - **utils** -- View utilities non relate some domain + - **views** -- View components + - **delir-core** -- Core module codes (Project structure, engine, calculation, renderer) + - **deream** -- Renderered frame exporter for ffmpeg - **plugins** -- Built-in Delir plugins (build with webpack) - - **renderer** -- RendererProcess codes. It is composed with Flux architecture. - - **actions** -- ActionCreator classes - - **devel** -- Codes for development (Fixture project contained) - - **helpers** -- Helper libraries (it [MUST] be staticaly) - - **services** -- Store+ActionCreator composed classes (Delir renderer delegation, etc...) - - **stores** -- Store classes - - **views** -- View components - - **components** -- Application shared components diff --git a/packages/delir-core/src/Exporter.spec.ts b/packages/delir-core/src/Exporter.spec.ts index 742ebe39e..37bfd55ee 100644 --- a/packages/delir-core/src/Exporter.spec.ts +++ b/packages/delir-core/src/Exporter.spec.ts @@ -57,7 +57,7 @@ describe('Export', () => { }) }) - it('test', () => { + it('Should correct serialize / desrialize project', () => { const serialized = Exporter.serialize(project) expect(serialized).toMatchSnapshot() expect(Exporter.deserialize(serialized)).toEqual(project) diff --git a/packages/delir-core/src/PluginSupport/PluginRegistry.spec.ts b/packages/delir-core/src/PluginSupport/PluginRegistry.spec.ts index c4f0c38b3..a08923ac0 100644 --- a/packages/delir-core/src/PluginSupport/PluginRegistry.spec.ts +++ b/packages/delir-core/src/PluginSupport/PluginRegistry.spec.ts @@ -12,6 +12,7 @@ describe('PluginRegistry', () => { node: '10.0.0', }, delir: { + name: 'Effect', type: 'post-effect', }, extraField: {}, @@ -29,6 +30,7 @@ describe('PluginRegistry', () => { // 'delir-core': '0.0.0.1', }, delir: { + name: 'Effect', // type: 'post-effect' type: 'post-effect-lol', }, diff --git a/packages/delir-core/src/__snapshots__/Exporter.spec.ts.snap b/packages/delir-core/src/__snapshots__/Exporter.spec.ts.snap index 4486c9577..96d0e75b9 100644 --- a/packages/delir-core/src/__snapshots__/Exporter.spec.ts.snap +++ b/packages/delir-core/src/__snapshots__/Exporter.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Export test 1`] = ` +exports[`Export Should correct serialize / desrialize project 1`] = ` Object { "__type": "entity:Project", "__value": Object { @@ -52,6 +52,14 @@ Object { Object { "__type": "entity:Keyframe", "__value": Object { + "easeInParam": Array [ + 1, + 1, + ], + "easeOutParam": Array [ + 0, + 0, + ], "id": "uuid-effect-kf-1", "value": Object { "__type": "value:ColorRGB", @@ -68,6 +76,14 @@ Object { Object { "__type": "entity:Keyframe", "__value": Object { + "easeInParam": Array [ + 1, + 1, + ], + "easeOutParam": Array [ + 0, + 0, + ], "id": "uuid-effect-kf-2", "value": Object { "__type": "value:AssetPointer", @@ -97,6 +113,14 @@ Object { Object { "__type": "entity:Keyframe", "__value": Object { + "easeInParam": Array [ + 1, + 1, + ], + "easeOutParam": Array [ + 0, + 0, + ], "id": "uuid-clip-kf-1", "value": Object { "__type": "value:ColorRGB", diff --git a/packages/delir/stores/EditorStateStore.ts b/packages/delir/domain/Editor/EditorStore.ts similarity index 70% rename from packages/delir/stores/EditorStateStore.ts rename to packages/delir/domain/Editor/EditorStore.ts index 50c1eb3bd..e69011b31 100644 --- a/packages/delir/stores/EditorStateStore.ts +++ b/packages/delir/domain/Editor/EditorStore.ts @@ -1,8 +1,9 @@ import * as Delir from '@ragg/delir-core' import { ProjectHelper } from '@ragg/delir-core' import { listen, Store } from '@ragg/fleur' -import { AppActions, ProjectModActions } from '../actions/actions' -import { DragEntity } from '../actions/App' +import { ProjectActions } from '../Project/actions' +import { EditorActions } from './actions' +import { DragEntity } from './operations' export interface NotificationEntry { id: string @@ -25,8 +26,8 @@ export interface EditorState { notifications: NotificationEntry[] } -export default class EditorStateStore extends Store { - public static storeName = 'EditorStateStore' +export default class EditorStore extends Store { + public static storeName = 'EditorStore' protected state: EditorState = { project: null, @@ -42,7 +43,7 @@ export default class EditorStateStore extends Store { } // @ts-ignore - private handlesetActiveProject = listen(AppActions.setActiveProjectAction, (payload) => { + private handlesetActiveProject = listen(EditorActions.setActiveProjectAction, (payload) => { __DEV__ && console.log('✨ Project activated', payload.project) this.updateWith(draft => { @@ -58,7 +59,7 @@ export default class EditorStateStore extends Store { }) // @ts-ignore - private handleclearActiveProject = listen(AppActions.clearActiveProjectAction, () => { + private handleclearActiveProject = listen(EditorActions.clearActiveProjectAction, () => { __DEV__ && console.log('💥 Project deactivated') this.updateWith(draft => { @@ -71,7 +72,7 @@ export default class EditorStateStore extends Store { }) // @ts-ignore - private handleRemoveClip = listen(ProjectModActions.removeClipAction, (payload) => { + private handleRemoveClip = listen(ProjectActions.removeClipAction, (payload) => { const { activeClip } = this.state if (activeClip && activeClip.id === payload.targetClipId) { @@ -80,7 +81,7 @@ export default class EditorStateStore extends Store { }) // @ts-ignore - private handleRemoveLayer = listen(ProjectModActions.removeLayerAction, ({ targetLayerId }) => { + private handleRemoveLayer = listen(ProjectActions.removeLayerAction, ({ targetLayerId }) => { const { activeClip } = this.state if (!activeClip) return if (!this.state.project) return @@ -92,17 +93,17 @@ export default class EditorStateStore extends Store { }) // @ts-ignore - private handlesetDragEntity = listen(AppActions.setDragEntityAction, (payload) => { + private handlesetDragEntity = listen(EditorActions.setDragEntityAction, (payload) => { this.updateWith(d => d.dragEntity = payload) }) // @ts-ignore - private handleclearDragEntity = listen(AppActions.clearDragEntityAction, () => { + private handleclearDragEntity = listen(EditorActions.clearDragEntityAction, () => { this.updateWith(d => d.dragEntity = null) }) // @ts-ignore - private handleChangeActiveComposition = listen(AppActions.changeActiveCompositionAction, ({ compositionId }) => { + private handleChangeActiveComposition = listen(EditorActions.changeActiveCompositionAction, ({ compositionId }) => { if (this.state.project == null) return const comp = ProjectHelper.findCompositionById(this.state.project, compositionId) @@ -114,7 +115,7 @@ export default class EditorStateStore extends Store { }) // @ts-ignore - private handlechangeActiveClip = listen(AppActions.changeActiveClipAction, (payload) => { + private handlechangeActiveClip = listen(EditorActions.changeActiveClipAction, (payload) => { if (this.state.project == null) return const clip = ProjectHelper.findClipById(this.state.project, payload.clipId) @@ -122,27 +123,27 @@ export default class EditorStateStore extends Store { }) // @ts-ignore - private handleupdateProcessingState = listen(AppActions.updateProcessingStateAction, (payload) => { + private handleupdateProcessingState = listen(EditorActions.updateProcessingStateAction, (payload) => { this.updateWith(d => d.processingState = payload.stateText) }) // @ts-ignore - private handlestartPreview = listen(AppActions.startPreviewAction, () => { + private handlestartPreview = listen(EditorActions.startPreviewAction, () => { this.updateWith(d => d.previewPlayed = true) }) // @ts-ignore - private handlestopPreview = listen(AppActions.stopPreviewAction, () => { + private handlestopPreview = listen(EditorActions.stopPreviewAction, () => { this.updateWith(d => d.previewPlayed = false) }) // @ts-ignore - private handleseekPreviewFrame = listen(AppActions.seekPreviewFrameAction, (payload) => { + private handleseekPreviewFrame = listen(EditorActions.seekPreviewFrameAction, (payload) => { this.updateWith(d => d.currentPreviewFrame = Math.round(payload.frame)) }) // @ts-ignore - private handleaddMessage = listen(AppActions.addMessageAction, (payload) => { + private handleaddMessage = listen(EditorActions.addMessageAction, (payload) => { this.updateWith(d => { d.notifications.push({ id: payload.id, @@ -155,7 +156,7 @@ export default class EditorStateStore extends Store { }) // @ts-ignore - private handleRemoveMessage = listen(AppActions.removeMessageAction, (payload) => { + private handleRemoveMessage = listen(EditorActions.removeMessageAction, (payload) => { this.updateWith(d => { const idx = d.notifications.findIndex(entry => entry!.id === payload.id) d.notifications.splice(idx, 1) @@ -163,7 +164,7 @@ export default class EditorStateStore extends Store { }) // @ts-ignore - private handleChangePreferenceOpenState = listen(AppActions.changePreferenceOpenStateAction, ({ open }) => { + private handleChangePreferenceOpenState = listen(EditorActions.changePreferenceOpenStateAction, ({ open }) => { this.updateWith(draft => draft.preferenceOpened = open) }) diff --git a/packages/delir/domain/Editor/actions.ts b/packages/delir/domain/Editor/actions.ts new file mode 100644 index 000000000..39184c3d5 --- /dev/null +++ b/packages/delir/domain/Editor/actions.ts @@ -0,0 +1,21 @@ +import * as Delir from '@ragg/delir-core' +import { action } from '@ragg/fleur' + +import { DragEntity } from './operations' + +export const EditorActions = { + setActiveProjectAction: action<{ project: Delir.Entity.Project, path?: string }>(), + clearActiveProjectAction: action(), + setDragEntityAction: action(), + clearDragEntityAction: action<{}>(), + changeActiveCompositionAction: action<{ compositionId: string }>(), + changeActiveClipAction: action<{ clipId: string }>(), + startPreviewAction: action<{ compositionId: string, beginFrame: number, ignoreMissingEffect: boolean }>(), + stopPreviewAction: action<{}>(), + renderDestinateAction: action<{ compositionId: string, ignoreMissingEffect: boolean }>(), + updateProcessingStateAction: action<{ stateText: string }>(), + addMessageAction: action<{ id: string, title?: string, level: 'info' | 'error', message: string, detail?: string }>(), + removeMessageAction: action<{ id: string }>(), + seekPreviewFrameAction: action<{ frame: number }>(), + changePreferenceOpenStateAction: action<{ open: boolean }>() +} diff --git a/packages/delir/actions/App.i18n.ts b/packages/delir/domain/Editor/operations.i18n.ts similarity index 84% rename from packages/delir/actions/App.i18n.ts rename to packages/delir/domain/Editor/operations.i18n.ts index 561de459f..14e141697 100644 --- a/packages/delir/actions/App.i18n.ts +++ b/packages/delir/domain/Editor/operations.i18n.ts @@ -1,5 +1,5 @@ -import I18n from '../utils/I18n' -import { isMacOS } from '../utils/platform' +import I18n from '../../utils/I18n' +import { isMacOS } from '../../utils/platform' export default I18n({ ja: { diff --git a/packages/delir/actions/App.ts b/packages/delir/domain/Editor/operations.ts similarity index 79% rename from packages/delir/actions/App.ts rename to packages/delir/domain/Editor/operations.ts index b42661e65..f83cbfabe 100644 --- a/packages/delir/actions/App.ts +++ b/packages/delir/domain/Editor/operations.ts @@ -6,12 +6,12 @@ import * as _ from 'lodash' import * as MsgPack from 'msgpack5' import * as path from 'path' -import PreferenceStore from '../domain/Preference/PreferenceStore' -import EditorStateStore from '../stores/EditorStateStore' -import RendererStore from '../stores/RendererStore' +import PreferenceStore from '../Preference/PreferenceStore' +import RendererStore from '../Renderer/RendererStore' +import EditorStore from './EditorStore' -import { AppActions } from './actions' -import t from './App.i18n' +import { EditorActions } from './actions' +import t from './operations.i18n' export type DragEntity = | { type: 'asset', asset: Delir.Entity.Asset } @@ -39,18 +39,18 @@ export const openPluginDirectory = operation((context, arg: {}) => { // Editor Store //Delir.Entity. export const setActiveProject = operation((context, arg: { project: Delir.Entity.Project, path?: string }) => { - context.dispatch(AppActions.setActiveProjectAction, { + context.dispatch(EditorActions.setActiveProjectAction, { project: arg.project, path: arg.path, }) }) export const setDragEntity = operation((context, arg: { entity: DragEntity }) => { - context.dispatch(AppActions.setDragEntityAction, arg.entity) + context.dispatch(EditorActions.setDragEntityAction, arg.entity) }) export const clearDragEntity = operation((context, arg: {}) => { - context.dispatch(AppActions.clearDragEntityAction, {}) + context.dispatch(EditorActions.clearDragEntityAction, {}) }) export const notify = operation((context, arg: { @@ -62,7 +62,7 @@ export const notify = operation((context, arg: { }) => { const id = _.uniqueId('notify') - context.dispatch(AppActions.addMessageAction, { + context.dispatch(EditorActions.addMessageAction, { id, title: arg.title, message: arg.message, @@ -71,7 +71,7 @@ export const notify = operation((context, arg: { }) if (arg.timeout != null) { - setTimeout(() => { context.dispatch(AppActions.removeMessageAction, { id }) }, arg.timeout) + setTimeout(() => { context.dispatch(EditorActions.removeMessageAction, { id }) }, arg.timeout) } }) @@ -79,11 +79,11 @@ export const notify = operation((context, arg: { // Change active element // export const changeActiveComposition = operation((context, { compositionId }: { compositionId: string }) => { - context.dispatch(AppActions.changeActiveCompositionAction, { compositionId }) + context.dispatch(EditorActions.changeActiveCompositionAction, { compositionId }) }) export const changeActiveClip = operation((context, { clipId }: { clipId: string }) => { - context.dispatch(AppActions.changeActiveClipAction, { clipId }) + context.dispatch(EditorActions.changeActiveClipAction, { clipId }) }) // @@ -92,7 +92,7 @@ export const changeActiveClip = operation((context, { clipId }: { clipId: string export const startPreview = operation((context, { compositionId, beginFrame = 0 }: { compositionId: string, beginFrame?: number }) => { const preference = context.getStore(PreferenceStore).getPreferences() - context.dispatch(AppActions.startPreviewAction, { + context.dispatch(EditorActions.startPreviewAction, { compositionId, beginFrame, ignoreMissingEffect: preference.renderer.ignoreMissingEffect, @@ -100,40 +100,40 @@ export const startPreview = operation((context, { compositionId, beginFrame = 0 }) export const stopPreview = operation((context) => { - context.dispatch(AppActions.stopPreviewAction, {}) + context.dispatch(EditorActions.stopPreviewAction, {}) }) export const renderDestinate = operation((context, arg: { compositionId: string }) => { const preference = context.getStore(PreferenceStore).getPreferences() - context.dispatch(AppActions.renderDestinateAction, { + context.dispatch(EditorActions.renderDestinateAction, { compositionId: arg.compositionId, ignoreMissingEffect: preference.renderer.ignoreMissingEffect, }) }) export const updateProcessingState = operation((context, arg: { stateText: string }) => { - context.dispatch(AppActions.updateProcessingStateAction, { + context.dispatch(EditorActions.updateProcessingStateAction, { stateText: arg.stateText }) }) export const seekPreviewFrame = operation((context, { frame = undefined }: { frame?: number }) => { - const state = context.getStore(EditorStateStore).getState() + const state = context.getStore(EditorStore).getState() const {activeComp} = state if (!activeComp) return frame = _.isNumber(frame) ? frame : state.currentPreviewFrame const overloadGuardedFrame = _.clamp(frame, 0, activeComp.durationFrames) - context.dispatch(AppActions.seekPreviewFrameAction, { frame: overloadGuardedFrame }) + context.dispatch(EditorActions.seekPreviewFrameAction, { frame: overloadGuardedFrame }) }) // // Import & Export // export const newProject = operation(async (context) => { - const project = context.getStore(EditorStateStore).getState().project + const project = context.getStore(EditorStore).getState().project if (project) { const acceptDiscard = window.confirm('現在のプロジェクトの変更を破棄して新しいプロジェクトを開きますか?') @@ -148,7 +148,7 @@ export const newProject = operation(async (context) => { }) export const openProject = operation(async (context) => { - const project = context.getStore(EditorStateStore).getState().project + const project = context.getStore(EditorStore).getState().project if (project) { const acceptDiscard = window.confirm('現在のプロジェクトの変更を破棄してプロジェクトを開きますか?') @@ -178,7 +178,7 @@ export const saveProject = operation(async ( context, { path, silent = false, keepPath = false }: { path: string, silent?: boolean, keepPath?: boolean } ) => { - const project = context.getStore(EditorStateStore).getState().project + const project = context.getStore(EditorStore).getState().project if (!project) return await fs.writeFile(path, MsgPack().encode({  @@ -187,7 +187,7 @@ export const saveProject = operation(async ( let newPath: string | null = path if (keepPath) { - newPath = context.getStore(EditorStateStore).getState().projectPath + newPath = context.getStore(EditorStore).getState().projectPath } context.executeOperation(setActiveProject, { project, ...(keepPath ? {} : {path: newPath || undefined}) }) // update path @@ -201,7 +201,7 @@ export const saveProject = operation(async ( }) export const autoSaveProject = operation(async (context) => { - const {project, projectPath} = context.getStore(EditorStateStore).getState() + const {project, projectPath} = context.getStore(EditorStore).getState() const isInRendering = context.getStore(RendererStore).isInRendering() if (isInRendering) return @@ -232,7 +232,7 @@ export const autoSaveProject = operation(async (context) => { }) export const changePreferenceOpenState = operation((context, { open }: { open: boolean }) => { - context.dispatch(AppActions.changePreferenceOpenStateAction, { open }) + context.dispatch(EditorActions.changePreferenceOpenStateAction, { open }) }) // console.log(remote.app.getPath('userData')) diff --git a/packages/delir/domain/Preference/operations.ts b/packages/delir/domain/Preference/operations.ts index eac99688a..8248267a8 100644 --- a/packages/delir/domain/Preference/operations.ts +++ b/packages/delir/domain/Preference/operations.ts @@ -3,7 +3,7 @@ import { remote } from 'electron' import { existsSync, readFileSync, writeFile } from 'fs' import * as path from 'path' -import * as AppOps from '../../actions/App' +import * as EditorOps from '../Editor/operations' import { PreferenceActions } from './actions' import PreferenceStore from '@ragg/delir/domain/Preference/PreferenceStore' @@ -23,7 +23,7 @@ export const restoreApplicationPreference = operation((context) => { try { preference = JSON.parse(json) } catch { - context.executeOperation(AppOps.notify, { + context.executeOperation(EditorOps.notify, { title: 'App preference loading failed', message: 'preferences.json invalid. Use initial preference instead.', level: 'error', @@ -36,7 +36,7 @@ export const restoreApplicationPreference = operation((context) => { const error = validateSchema(preference) if (error) { - context.executeOperation(AppOps.notify, { + context.executeOperation(EditorOps.notify, { title: 'App preference loading failed', message: 'preferences.json invalid. Use initial preference instead.\n' + error.message, level: 'error', diff --git a/packages/delir/actions/ProjectMod.ts b/packages/delir/domain/Project/ProjectMod.ts similarity index 80% rename from packages/delir/actions/ProjectMod.ts rename to packages/delir/domain/Project/ProjectMod.ts index a8b0db9d7..bc390c890 100644 --- a/packages/delir/actions/ProjectMod.ts +++ b/packages/delir/domain/Project/ProjectMod.ts @@ -2,10 +2,10 @@ import * as Delir from '@ragg/delir-core' import { ProjectHelper } from '@ragg/delir-core' import { operation } from '@ragg/fleur' -import ProjectStore from '../stores/ProjectStore' -import RendererStore from '../stores/RendererStore' -import { ProjectModActions } from './actions' -import * as AppActions from './App' +import * as EditorOps from '../Editor/operations' +import RendererStore from '../Renderer/RendererStore' +import { ProjectActions } from './actions' +import ProjectStore from './ProjectStore' // // Modify project @@ -23,20 +23,20 @@ export const createComposition = operation((context, options: { }) => { const composition = new Delir.Entity.Composition() Object.assign(composition, options) - context.dispatch(ProjectModActions.createCompositionAction, { composition }) + context.dispatch(ProjectActions.createCompositionAction, { composition }) }) // @deprecated export const createLayer = operation((context, { compId }: { compId: string }) => { const layer = new Delir.Entity.Layer() - context.dispatch(ProjectModActions.createLayerAction, { targetCompositionId: compId, layer }) + context.dispatch(ProjectActions.createLayerAction, { targetCompositionId: compId, layer }) }) export const addLayer = operation((context, { targetComposition, layer }: { targetComposition: Delir.Entity.Composition, layer: Delir.Entity.Layer }) => { - context.dispatch(ProjectModActions.addLayerAction, { targetComposition, layer }) + context.dispatch(ProjectActions.addLayerAction, { targetComposition, layer }) }) export const addLayerWithAsset = operation((context, { targetComposition, asset }: { @@ -49,7 +49,7 @@ export const addLayerWithAsset = operation((context, { targetComposition, asset // TODO: Support selection if (processablePlugins.length === 0) { - context.executeOperation(AppActions.notify, { + context.executeOperation(EditorOps.notify, { message: `plugin not available for \`${asset.fileType}\``, title: '😢 Supported plugin not available', level: 'info', @@ -66,7 +66,7 @@ export const addLayerWithAsset = operation((context, { targetComposition, asset durationFrames: targetComposition.framerate, }) - context.dispatch(ProjectModActions.addLayerWithAssetAction, { + context.dispatch(ProjectActions.addLayerWithAssetAction, { targetComposition, clip, asset, @@ -86,7 +86,7 @@ export const createClip = operation((context, { layerId, clipRendererId, placedF durationFrames: durationFrames, }) - context.dispatch(ProjectModActions.createClipAction, { + context.dispatch(ProjectActions.createClipAction, { newClip, targetLayerId: layerId, }) @@ -106,7 +106,7 @@ export const createClipWithAsset = operation((context, { targetLayer, asset, pla // TODO: Support selection if (processablePlugins.length === 0) { - context.executeOperation(AppActions.notify, { + context.executeOperation(EditorOps.notify, { message: `plugin not available for \`${asset.fileType}\``, title: '😢 Supported plugin not available', level: 'info', @@ -132,7 +132,7 @@ export const createClipWithAsset = operation((context, { targetLayer, asset, pla value: { assetId: asset.id }, })) - context.dispatch(ProjectModActions.addClipAction, { targetLayer, newClip }) + context.dispatch(ProjectActions.addClipAction, { targetLayer, newClip }) }) export const createOrModifyKeyframeForClip = operation((context, { clipId, paramName, frameOnClip, patch }: { @@ -161,7 +161,7 @@ export const createOrModifyKeyframeForClip = operation((context, { clipId, param const keyframe = ProjectHelper.findKeyframeFromClipByPropAndFrame(clip, paramName, frameOnClip) if (keyframe) { - context.dispatch(ProjectModActions.modifyKeyframeAction, { + context.dispatch(ProjectActions.modifyKeyframeAction, { targetKeyframeId: keyframe.id, patch: propDesc.animatable === false ? Object.assign(patch, { frameOnClip: 0 }) : patch, }) @@ -172,7 +172,7 @@ export const createOrModifyKeyframeForClip = operation((context, { clipId, param frameOnClip, }, patch)) - context.dispatch(ProjectModActions.addKeyframeAction, { + context.dispatch(ProjectActions.addKeyframeAction, { targetClip: clip, paramName, keyframe: newKeyframe @@ -208,7 +208,7 @@ export const createOrModifyKeyframeForEffect = operation((context, { clipId, eff const keyframe = ProjectHelper.findKeyframeFromEffectByPropAndFrame(effect, paramName, frameOnClip) if (keyframe) { - context.dispatch(ProjectModActions.modifyEffectKeyframeAction, { + context.dispatch(ProjectActions.modifyEffectKeyframeAction, { targetClipId: clipId, effectId: effectId, targetKeyframeId: keyframe.id, @@ -218,7 +218,7 @@ export const createOrModifyKeyframeForEffect = operation((context, { clipId, eff const newKeyframe = new Delir.Entity.Keyframe() Object.assign(newKeyframe, Object.assign({ frameOnClip }, patch)) - context.dispatch(ProjectModActions.addEffectKeyframeAction, { + context.dispatch(ProjectActions.addEffectKeyframeAction, { targetClipId: clipId, targetEffectId: effectId, paramName, @@ -237,7 +237,7 @@ export const addAsset = operation((context, { name, fileType, path }: { asset.fileType = fileType asset.path = path - context.dispatch(ProjectModActions.addAssetAction, { asset }) + context.dispatch(ProjectActions.addAssetAction, { asset }) }) export const addEffectIntoClip = operation((context, { clipId, processorId }: { @@ -246,11 +246,11 @@ export const addEffectIntoClip = operation((context, { clipId, processorId }: { }) => { const effect = new Delir.Entity.Effect() effect.processor = processorId - context.dispatch(ProjectModActions.addEffectIntoClipAction, { clipId, effect }) + context.dispatch(ProjectActions.addEffectIntoClipAction, { clipId, effect }) }) export const removeAsset = operation((context, { assetId }: { assetId: string }) => { - context.dispatch(ProjectModActions.removeAssetAction, { targetAssetId: assetId }) + context.dispatch(ProjectActions.removeAssetAction, { targetAssetId: assetId }) }) // TODO: frame position @@ -258,14 +258,14 @@ export const moveClipToLayer = operation((context, { clipId, destLayerId }: { clipId: string, destLayerId: string }) => { - context.dispatch(ProjectModActions.moveClipToLayerAction, { destLayerId: destLayerId, clipId }) + context.dispatch(ProjectActions.moveClipToLayerAction, { destLayerId: destLayerId, clipId }) }) export const modifyComposition = operation((context, { compositionId, props }: { compositionId: string, props: Partial }) => { - context.dispatch(ProjectModActions.modifyCompositionAction, { + context.dispatch(ProjectActions.modifyCompositionAction, { targetCompositionId: compositionId, patch: props }) @@ -275,7 +275,7 @@ export const modifyLayer = operation((context, { layerId, props }: { layerId: string, props: Partial }) => { - context.dispatch(ProjectModActions.modifyLayerAction, { + context.dispatch(ProjectActions.modifyLayerAction, { targetLayerId: layerId, patch: props, }) @@ -285,7 +285,7 @@ export const modifyClip = operation((context, { clipId, props }: { clipId: string, props: Partial }) => { - context.dispatch(ProjectModActions.modifyClipAction, { + context.dispatch(ProjectActions.modifyClipAction, { targetClipId: clipId, patch: props, }) @@ -296,7 +296,7 @@ export const modifyClipExpression = operation((context, { clipId, property, expr property: string, expr: { language: string, code: string } }) => { - context.dispatch(ProjectModActions.modifyClipExpressionAction, { + context.dispatch(ProjectActions.modifyClipExpressionAction, { targetClipId: clipId, targetProperty: property, expr: { @@ -312,7 +312,7 @@ export const modifyEffectExpression = operation((context, { clipId, effectId, pr property: string, expr: { language: string, code: string } }) => { - context.dispatch(ProjectModActions.modifyEffectExpressionAction, { + context.dispatch(ProjectActions.modifyEffectExpressionAction, { targetClipId: clipId, targetEffectId: effectId, targetProperty: property, @@ -329,7 +329,7 @@ export const moveLayerOrder = operation((context, { layerId, newIndex }: { layer const comp = ProjectHelper.findParentCompositionByLayerId(project, layerId)! - context.dispatch(ProjectModActions.moveLayerOrderAction, { + context.dispatch(ProjectActions.moveLayerOrderAction, { parentCompositionId: comp.id, targetLayerId: layerId, newIndex, @@ -337,19 +337,19 @@ export const moveLayerOrder = operation((context, { layerId, newIndex }: { layer }) export const removeComposition = operation((context, { compositionId }: { compositionId: string }) => { - context.dispatch(ProjectModActions.removeCompositionAction, { targetCompositionId: compositionId }) + context.dispatch(ProjectActions.removeCompositionAction, { targetCompositionId: compositionId }) }) export const removeLayer = operation((context, { layerId }: { layerId: string }) => { - context.dispatch(ProjectModActions.removeLayerAction, { targetLayerId: layerId }) + context.dispatch(ProjectActions.removeLayerAction, { targetLayerId: layerId }) }) export const removeClip = operation((context, { clipId }: { clipId: string }) => { - context.dispatch(ProjectModActions.removeClipAction, { targetClipId: clipId }) + context.dispatch(ProjectActions.removeClipAction, { targetClipId: clipId }) }) export const removeKeyframe = operation((context, { keyframeId }: { keyframeId: string }) => { - context.dispatch(ProjectModActions.removeKeyframeAction, { targetKeyframeId: keyframeId }) + context.dispatch(ProjectActions.removeKeyframeAction, { targetKeyframeId: keyframeId }) }) export const removeKeyframeForEffect = operation((context, { clipId, effectId, keyframeId }: { @@ -357,12 +357,12 @@ export const removeKeyframeForEffect = operation((context, { clipId, effectId, k effectId: string, keyframeId: string }) => { - context.dispatch(ProjectModActions.removeEffectKeyframeAction, { clipId, effectId, targetKeyframeId: keyframeId }) + context.dispatch(ProjectActions.removeEffectKeyframeAction, { clipId, effectId, targetKeyframeId: keyframeId }) }) export const removeEffect = operation((context, { holderClipId, effectId }: { holderClipId: string, effectId: string }) => { - context.dispatch(ProjectModActions.removeEffectFromClipAction, { holderClipId, targetEffectId: effectId }) + context.dispatch(ProjectActions.removeEffectFromClipAction, { holderClipId, targetEffectId: effectId }) }) diff --git a/packages/delir/stores/ProjectStore.ts b/packages/delir/domain/Project/ProjectStore.ts similarity index 72% rename from packages/delir/stores/ProjectStore.ts rename to packages/delir/domain/Project/ProjectStore.ts index 5ef59d23d..9b5564c75 100644 --- a/packages/delir/stores/ProjectStore.ts +++ b/packages/delir/domain/Project/ProjectStore.ts @@ -2,7 +2,8 @@ import * as Delir from '@ragg/delir-core' import { ProjectHelper } from '@ragg/delir-core' import { listen, Store } from '@ragg/fleur' -import { AppActions, ProjectModActions } from '../actions/actions' +import { EditorActions } from '../Editor/actions' +import { ProjectActions } from './actions' export interface ProjectStoreState { project: Delir.Entity.Project | null, @@ -19,17 +20,17 @@ export default class ProjectStore extends Store } // @ts-ignore - private handleSetActiveProject = listen(AppActions.setActiveProjectAction, (payload) => { + private handleSetActiveProject = listen(EditorActions.setActiveProjectAction, (payload) => { this.updateWith(d => d.project = payload.project) }) // @ts-ignore - private handleClearActiveProject = listen(AppActions.clearActiveProjectAction, (payload) => { + private handleClearActiveProject = listen(EditorActions.clearActiveProjectAction, (payload) => { this.updateWith(d => d.project = null) }) // @ts-ignore - private handleCreateComposition = listen(ProjectModActions.createCompositionAction, (payload) => { + private handleCreateComposition = listen(ProjectActions.createCompositionAction, (payload) => { const { project } = this.state const newLayer = new Delir.Entity.Layer() ProjectHelper.addComposition(project!, payload.composition) @@ -38,21 +39,21 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleCreateLayer = listen(ProjectModActions.createLayerAction, (payload) => { + private handleCreateLayer = listen(ProjectActions.createLayerAction, (payload) => { const { project } = this.state ProjectHelper.addLayer(project!, payload.targetCompositionId, payload.layer) this.updateLastModified() }) // @ts-ignore - private handleCreateClip = listen(ProjectModActions.createClipAction, (payload) => { + private handleCreateClip = listen(ProjectActions.createClipAction, (payload) => { const { project } = this.state ProjectHelper.addClip(project!, payload.targetLayerId, payload.newClip) this.updateLastModified() }) // @ts-ignore - private handleAddClip = listen(ProjectModActions.addClipAction, (payload) => { + private handleAddClip = listen(ProjectActions.addClipAction, (payload) => { const { project } = this.state const { targetLayer, newClip } = payload ProjectHelper.addClip(project!, targetLayer, newClip) @@ -60,14 +61,14 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleAddLayer = listen(ProjectModActions.addLayerAction, (payload) => { + private handleAddLayer = listen(ProjectActions.addLayerAction, (payload) => { const { project } = this.state ProjectHelper.addLayer(project!, payload.targetComposition, payload.layer) this.updateLastModified() }) // @ts-ignore - private handleAddLayerWithAsset = listen(ProjectModActions.addLayerWithAssetAction, (payload) => { + private handleAddLayerWithAsset = listen(ProjectActions.addLayerWithAssetAction, (payload) => { const { project } = this.state const { targetComposition, clip, asset: registeredAsset } = payload const propName = Delir.Engine.Renderers.getInfo(clip.renderer).assetAssignMap[registeredAsset.fileType] @@ -85,14 +86,14 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleAddAsset = listen(ProjectModActions.addAssetAction, (payload) => { + private handleAddAsset = listen(ProjectActions.addAssetAction, (payload) => { const { project } = this.state ProjectHelper.addAsset(project!, payload.asset) this.updateLastModified() }) // @ts-ignore - private handleAddKeyframe = listen(ProjectModActions.addKeyframeAction, (payload) => { + private handleAddKeyframe = listen(ProjectActions.addKeyframeAction, (payload) => { const { project } = this.state const { targetClip, paramName, keyframe } = payload ProjectHelper.addKeyframe(project!, targetClip, paramName, keyframe) @@ -100,7 +101,7 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleAddEffectIntoClipPayload = listen(ProjectModActions.addEffectIntoClipPayloadAction, (payload) => { + private handleAddEffectIntoClipPayload = listen(ProjectActions.addEffectIntoClipPayloadAction, (payload) => { const { project } = this.state const { clipId, effect } = payload ProjectHelper.addEffect(project!, clipId, effect) @@ -108,7 +109,7 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleAddEffectKeyframe = listen(ProjectModActions.addEffectKeyframeAction, (payload) => { + private handleAddEffectKeyframe = listen(ProjectActions.addEffectKeyframeAction, (payload) => { const { project } = this.state const { targetClipId, targetEffectId, paramName, keyframe } = payload ProjectHelper.addEffectKeyframe(project!, targetClipId, targetEffectId, paramName, keyframe) @@ -116,7 +117,7 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleMoveClipToLayer = listen(ProjectModActions.moveClipToLayerAction, (payload) => { + private handleMoveClipToLayer = listen(ProjectActions.moveClipToLayerAction, (payload) => { const { project } = this.state const targetClip = ProjectHelper.findClipById(project!, payload.clipId) const sourceLayer = ProjectHelper.findParentLayerByClipId(project!, payload.clipId) @@ -130,28 +131,28 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleModifyComposition = listen(ProjectModActions.modifyCompositionAction, (payload) => { + private handleModifyComposition = listen(ProjectActions.modifyCompositionAction, (payload) => { const { project } = this.state ProjectHelper.modifyComposition(project!, payload.targetCompositionId, payload.patch) this.updateLastModified() }) // @ts-ignore - private handleModifyLayer = listen(ProjectModActions.modifyLayerAction, (payload) => { + private handleModifyLayer = listen(ProjectActions.modifyLayerAction, (payload) => { const { project } = this.state ProjectHelper.modifyLayer(project!, payload.targetLayerId, payload.patch) this.updateLastModified() }) // @ts-ignore - private handleModifyClip = listen(ProjectModActions.modifyClipAction, (payload) => { + private handleModifyClip = listen(ProjectActions.modifyClipAction, (payload) => { const { project } = this.state ProjectHelper.modifyClip(project!, payload.targetClipId, payload.patch) this.updateLastModified() }) // @ts-ignore - private handleModifyClipExpression = listen(ProjectModActions.modifyClipExpressionAction, (payload) => { + private handleModifyClipExpression = listen(ProjectActions.modifyClipExpressionAction, (payload) => { const { project } = this.state const { targetClipId, targetProperty, expr } = payload ProjectHelper.modifyClipExpression(project!, targetClipId, targetProperty, new Delir.Values.Expression(expr.language, expr.code)) @@ -159,7 +160,7 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleModifyEffectExpression = listen(ProjectModActions.modifyEffectExpressionAction, (payload) => { + private handleModifyEffectExpression = listen(ProjectActions.modifyEffectExpressionAction, (payload) => { const { project } = this.state const { targetClipId, targetEffectId, targetProperty, expr } = payload ProjectHelper.modifyEffectExpression(project!, targetClipId, targetEffectId, targetProperty, new Delir.Values.Expression(expr.language, expr.code)) @@ -167,14 +168,14 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleModifyKeyframe = listen(ProjectModActions.modifyKeyframeAction, (payload) => { + private handleModifyKeyframe = listen(ProjectActions.modifyKeyframeAction, (payload) => { const { project } = this.state ProjectHelper.modifyKeyframe(project!, payload.targetKeyframeId, payload.patch) this.updateLastModified() }) // @ts-ignore - private handleModifyEffectKeyframe = listen(ProjectModActions.modifyEffectKeyframeAction, (payload) => { + private handleModifyEffectKeyframe = listen(ProjectActions.modifyEffectKeyframeAction, (payload) => { const { project } = this.state const { targetClipId, effectId, targetKeyframeId, patch } = payload ProjectHelper.modifyEffectKeyframe(project!, targetClipId, effectId, targetKeyframeId, patch) @@ -182,7 +183,7 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleMoveLayerOrder = listen(ProjectModActions.moveLayerOrderAction, (payload) => { + private handleMoveLayerOrder = listen(ProjectActions.moveLayerOrderAction, (payload) => { const { project } = this.state const { parentCompositionId, targetLayerId, newIndex } = payload ProjectHelper.moveLayerOrder(project!, parentCompositionId, targetLayerId, newIndex) @@ -190,42 +191,42 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleRemoveComposition = listen(ProjectModActions.removeCompositionAction, (payload) => { + private handleRemoveComposition = listen(ProjectActions.removeCompositionAction, (payload) => { const { project } = this.state ProjectHelper.deleteComposition(project!, payload.targetCompositionId) this.updateLastModified() }) // @ts-ignore - private handleRemoveLayer = listen(ProjectModActions.removeLayerAction, (payload) => { + private handleRemoveLayer = listen(ProjectActions.removeLayerAction, (payload) => { const { project } = this.state ProjectHelper.deleteLayer(project!, payload.targetLayerId) this.updateLastModified() }) // @ts-ignore - private handleRemoveClip = listen(ProjectModActions.removeClipAction, (payload) => { + private handleRemoveClip = listen(ProjectActions.removeClipAction, (payload) => { const { project } = this.state ProjectHelper.deleteClip(project!, payload.targetClipId) this.updateLastModified() }) // @ts-ignore - private handleRemoveAsset = listen(ProjectModActions.removeAssetAction, (payload) => { + private handleRemoveAsset = listen(ProjectActions.removeAssetAction, (payload) => { const { project } = this.state ProjectHelper.deleteAsset(project!, payload.targetAssetId) this.updateLastModified() }) // @ts-ignore - private handleRemoveKeyframe = listen(ProjectModActions.removeKeyframeAction, (payload) => { + private handleRemoveKeyframe = listen(ProjectActions.removeKeyframeAction, (payload) => { const { project } = this.state ProjectHelper.deleteKeyframe(project!, payload.targetKeyframeId) this.updateLastModified() }) // @ts-ignore - private handleRemoveEffectKeyframe = listen(ProjectModActions.removeEffectKeyframeAction, (payload) => { + private handleRemoveEffectKeyframe = listen(ProjectActions.removeEffectKeyframeAction, (payload) => { const { project } = this.state const { clipId, effectId, targetKeyframeId } = payload ProjectHelper.deleteEffectKeyframe(project!, clipId, effectId, targetKeyframeId) @@ -233,7 +234,7 @@ export default class ProjectStore extends Store }) // @ts-ignore - private handleRemoveEffectFromClip = listen(ProjectModActions.removeEffectFromClipAction, (payload) => { + private handleRemoveEffectFromClip = listen(ProjectActions.removeEffectFromClipAction, (payload) => { const { project } = this.state const { holderClipId, targetEffectId } = payload ProjectHelper.deleteEffectFromClip(project!, holderClipId, targetEffectId) diff --git a/packages/delir/actions/actions.ts b/packages/delir/domain/Project/actions.ts similarity index 69% rename from packages/delir/actions/actions.ts rename to packages/delir/domain/Project/actions.ts index 6fb515be5..b6a48501e 100644 --- a/packages/delir/actions/actions.ts +++ b/packages/delir/domain/Project/actions.ts @@ -1,26 +1,7 @@ import * as Delir from '@ragg/delir-core' import { action } from '@ragg/fleur' -import { DragEntity } from './App' - -export const AppActions = { - setActiveProjectAction: action<{ project: Delir.Entity.Project, path?: string }>(), - clearActiveProjectAction: action(), - setDragEntityAction: action(), - clearDragEntityAction: action<{}>(), - changeActiveCompositionAction: action<{ compositionId: string }>(), - changeActiveClipAction: action<{ clipId: string }>(), - startPreviewAction: action<{ compositionId: string, beginFrame: number, ignoreMissingEffect: boolean }>(), - stopPreviewAction: action<{}>(), - renderDestinateAction: action<{ compositionId: string, ignoreMissingEffect: boolean }>(), - updateProcessingStateAction: action<{ stateText: string }>(), - addMessageAction: action<{ id: string, title?: string, level: 'info' | 'error', message: string, detail?: string }>(), - removeMessageAction: action<{ id: string }>(), - seekPreviewFrameAction: action<{ frame: number }>(), - changePreferenceOpenStateAction: action<{ open: boolean }>() -} - -export const ProjectModActions = { +export const ProjectActions = { createCompositionAction: action<{ composition: Delir.Entity.Composition }>(), createLayerAction: action<{ targetCompositionId: string, layer: Delir.Entity.Layer }>(), createClipAction: action<{ targetLayerId: string, newClip: Delir.Entity.Clip }>(), @@ -52,8 +33,3 @@ export const ProjectModActions = { removeEffectKeyframeAction: action<{ clipId: string, effectId: string, targetKeyframeId: string }>(), removeEffectFromClipAction: action<{ holderClipId: string, targetEffectId: string }>(), } - -export const RendererActions = { - addPlugins: action<{ plugins: any[] }>(), - setPreviewCanvas: action<{ canvas: HTMLCanvasElement }>(), -} diff --git a/packages/delir/domain/Project/operations.ts b/packages/delir/domain/Project/operations.ts new file mode 100644 index 000000000..bc390c890 --- /dev/null +++ b/packages/delir/domain/Project/operations.ts @@ -0,0 +1,368 @@ +import * as Delir from '@ragg/delir-core' +import { ProjectHelper } from '@ragg/delir-core' +import { operation } from '@ragg/fleur' + +import * as EditorOps from '../Editor/operations' +import RendererStore from '../Renderer/RendererStore' +import { ProjectActions } from './actions' +import ProjectStore from './ProjectStore' + +// +// Modify project +// +// @deprecated +export const createComposition = operation((context, options: { + name: string, + width: number, + height: number, + framerate: number, + durationFrames: number, + backgroundColor: Delir.Values.ColorRGB, + samplingRate: number, + audioChannels: number, +}) => { + const composition = new Delir.Entity.Composition() + Object.assign(composition, options) + context.dispatch(ProjectActions.createCompositionAction, { composition }) +}) + +// @deprecated +export const createLayer = operation((context, { compId }: { compId: string }) => { + const layer = new Delir.Entity.Layer() + context.dispatch(ProjectActions.createLayerAction, { targetCompositionId: compId, layer }) +}) + +export const addLayer = operation((context, { targetComposition, layer }: { + targetComposition: Delir.Entity.Composition, + layer: Delir.Entity.Layer +}) => { + context.dispatch(ProjectActions.addLayerAction, { targetComposition, layer }) +}) + +export const addLayerWithAsset = operation((context, { targetComposition, asset }: { + targetComposition: Delir.Entity.Composition, + asset: Delir.Entity.Asset +}) => { + const processablePlugins = Delir.Engine.Renderers.getAvailableRenderers().filter(entry => { + return entry.handlableFileTypes.includes(asset.fileType) + }) + + // TODO: Support selection + if (processablePlugins.length === 0) { + context.executeOperation(EditorOps.notify, { + message: `plugin not available for \`${asset.fileType}\``, + title: '😢 Supported plugin not available', + level: 'info', + timeout: 5000 + }) + + return + } + + const clip = new Delir.Entity.Clip() + Object.assign(clip, { + renderer: processablePlugins[0].id, + placedFrame: 0, + durationFrames: targetComposition.framerate, + }) + + context.dispatch(ProjectActions.addLayerWithAssetAction, { + targetComposition, + clip, + asset, + }) +}) + +export const createClip = operation((context, { layerId, clipRendererId, placedFrame = 0, durationFrames = 100 }: { + layerId: string, + clipRendererId: string, + placedFrame: number, + durationFrames: number, +}) => { + const newClip = new Delir.Entity.Clip() + Object.assign(newClip, { + renderer: clipRendererId, + placedFrame: placedFrame, + durationFrames: durationFrames, + }) + + context.dispatch(ProjectActions.createClipAction, { + newClip, + targetLayerId: layerId, + }) +}) + +export const createClipWithAsset = operation((context, { targetLayer, asset, placedFrame = 0, durationFrames = 100 }: { + targetLayer: Delir.Entity.Layer, + asset: Delir.Entity.Asset, + placedFrame?: number, + durationFrames?: number, +}) => { + const {project} = context.getStore(ProjectStore).getState() + if (!project) return + + const processablePlugins = Delir.Engine.Renderers.getAvailableRenderers() + .filter((entry) => entry.handlableFileTypes.includes(asset.fileType)) + + // TODO: Support selection + if (processablePlugins.length === 0) { + context.executeOperation(EditorOps.notify, { + message: `plugin not available for \`${asset.fileType}\``, + title: '😢 Supported plugin not available', + level: 'info', + timeout: 3000 + }) + + return + } + + const newClip = new Delir.Entity.Clip() + Object.assign(newClip, { + renderer: processablePlugins[0].id, + placedFrame, + durationFrames, + }) + + const paramName = Delir.Engine.Renderers.getInfo(newClip.renderer).assetAssignMap[asset.fileType] + + if (!paramName) return + + ProjectHelper.addKeyframe(project!, newClip, paramName, Object.assign(new Delir.Entity.Keyframe(), { + frameOnClip: 0, + value: { assetId: asset.id }, + })) + + context.dispatch(ProjectActions.addClipAction, { targetLayer, newClip }) +}) + +export const createOrModifyKeyframeForClip = operation((context, { clipId, paramName, frameOnClip, patch }: { + clipId: string, + paramName: string, + frameOnClip: number, + patch: Partial +}) => { + const {project} = context.getStore(ProjectStore).getState() + + if (!project) return + const clip = ProjectHelper.findClipById(project, clipId) + + if (!clip) return + + const props = Delir.Engine.Renderers.getInfo(clip.renderer!).parameter.properties + const propDesc = props ? props.find(prop => prop.paramName === paramName) : null + if (!propDesc) return + + frameOnClip = frameOnClip < 0 ? 0 : Math.round(frameOnClip) + + if (propDesc.animatable === false) { + frameOnClip = 0 + } + + const keyframe = ProjectHelper.findKeyframeFromClipByPropAndFrame(clip, paramName, frameOnClip) + + if (keyframe) { + context.dispatch(ProjectActions.modifyKeyframeAction, { + targetKeyframeId: keyframe.id, + patch: propDesc.animatable === false ? Object.assign(patch, { frameOnClip: 0 }) : patch, + }) + } else { + const newKeyframe = new Delir.Entity.Keyframe() + + Object.assign(newKeyframe, Object.assign({ + frameOnClip, + }, patch)) + + context.dispatch(ProjectActions.addKeyframeAction, { + targetClip: clip, + paramName, + keyframe: newKeyframe + }) + } +}) + +export const createOrModifyKeyframeForEffect = operation((context, { clipId, effectId, paramName, frameOnClip, patch }: { + clipId: string, + effectId: string, + paramName: string, + frameOnClip: number, + patch: Partial +}) => { + const rendererStore = context.getStore(RendererStore) + const {project} = context.getStore(ProjectStore).getState() + if (!project) return + + const clip = ProjectHelper.findClipById(project, clipId) + if (!clip) return + + const effect = ProjectHelper.findEffectFromClipById(clip, effectId) + if (!effect) return + + const props = rendererStore.getPostEffectParametersById(effect.processor) + const propDesc = props ? props.find(prop => prop.paramName === paramName) : null + if (!propDesc) return + + if (propDesc.animatable === false) { + frameOnClip = 0 + } + + const keyframe = ProjectHelper.findKeyframeFromEffectByPropAndFrame(effect, paramName, frameOnClip) + + if (keyframe) { + context.dispatch(ProjectActions.modifyEffectKeyframeAction, { + targetClipId: clipId, + effectId: effectId, + targetKeyframeId: keyframe.id, + patch: propDesc.animatable === false ? Object.assign(patch, { frameOnClip: 0 }) : patch, + }) + } else { + const newKeyframe = new Delir.Entity.Keyframe() + Object.assign(newKeyframe, Object.assign({ frameOnClip }, patch)) + + context.dispatch(ProjectActions.addEffectKeyframeAction, { + targetClipId: clipId, + targetEffectId: effectId, + paramName, + keyframe: newKeyframe, + }) + } +}) + +export const addAsset = operation((context, { name, fileType, path }: { + name: string, + fileType: string, + path: string +}) => { + const asset = new Delir.Entity.Asset() + asset.name = name + asset.fileType = fileType + asset.path = path + + context.dispatch(ProjectActions.addAssetAction, { asset }) +}) + +export const addEffectIntoClip = operation((context, { clipId, processorId }: { + clipId: string, + processorId: string +}) => { + const effect = new Delir.Entity.Effect() + effect.processor = processorId + context.dispatch(ProjectActions.addEffectIntoClipAction, { clipId, effect }) +}) + +export const removeAsset = operation((context, { assetId }: { assetId: string }) => { + context.dispatch(ProjectActions.removeAssetAction, { targetAssetId: assetId }) +}) + +// TODO: frame position +export const moveClipToLayer = operation((context, { clipId, destLayerId }: { + clipId: string, + destLayerId: string +}) => { + context.dispatch(ProjectActions.moveClipToLayerAction, { destLayerId: destLayerId, clipId }) +}) + +export const modifyComposition = operation((context, { compositionId, props }: { + compositionId: string, + props: Partial +}) => { + context.dispatch(ProjectActions.modifyCompositionAction, { + targetCompositionId: compositionId, + patch: props + }) +}) + +export const modifyLayer = operation((context, { layerId, props }: { + layerId: string, + props: Partial +}) => { + context.dispatch(ProjectActions.modifyLayerAction, { + targetLayerId: layerId, + patch: props, + }) +}) + +export const modifyClip = operation((context, { clipId, props }: { + clipId: string, + props: Partial +}) => { + context.dispatch(ProjectActions.modifyClipAction, { + targetClipId: clipId, + patch: props, + }) +}) + +export const modifyClipExpression = operation((context, { clipId, property, expr }: { + clipId: string, + property: string, + expr: { language: string, code: string } +}) => { + context.dispatch(ProjectActions.modifyClipExpressionAction, { + targetClipId: clipId, + targetProperty: property, + expr: { + language: expr.language, + code: expr.code, + } + }) +}) + +export const modifyEffectExpression = operation((context, { clipId, effectId, property, expr }: { + clipId: string, + effectId: string, + property: string, + expr: { language: string, code: string } +}) => { + context.dispatch(ProjectActions.modifyEffectExpressionAction, { + targetClipId: clipId, + targetEffectId: effectId, + targetProperty: property, + expr: { + language: expr.language, + code: expr.code, + } + }) +}) + +export const moveLayerOrder = operation((context, { layerId, newIndex }: { layerId: string, newIndex: number }) => { + const {project} = context.getStore(ProjectStore).getState() + if (!project) return + + const comp = ProjectHelper.findParentCompositionByLayerId(project, layerId)! + + context.dispatch(ProjectActions.moveLayerOrderAction, { + parentCompositionId: comp.id, + targetLayerId: layerId, + newIndex, + }) +}) + +export const removeComposition = operation((context, { compositionId }: { compositionId: string }) => { + context.dispatch(ProjectActions.removeCompositionAction, { targetCompositionId: compositionId }) +}) + +export const removeLayer = operation((context, { layerId }: { layerId: string }) => { + context.dispatch(ProjectActions.removeLayerAction, { targetLayerId: layerId }) +}) + +export const removeClip = operation((context, { clipId }: { clipId: string }) => { + context.dispatch(ProjectActions.removeClipAction, { targetClipId: clipId }) +}) + +export const removeKeyframe = operation((context, { keyframeId }: { keyframeId: string }) => { + context.dispatch(ProjectActions.removeKeyframeAction, { targetKeyframeId: keyframeId }) +}) + +export const removeKeyframeForEffect = operation((context, { clipId, effectId, keyframeId }: { + clipId: string, + effectId: string, + keyframeId: string +}) => { + context.dispatch(ProjectActions.removeEffectKeyframeAction, { clipId, effectId, targetKeyframeId: keyframeId }) +}) + +export const removeEffect = operation((context, { holderClipId, effectId }: { + holderClipId: string, + effectId: string +}) => { + context.dispatch(ProjectActions.removeEffectFromClipAction, { holderClipId, targetEffectId: effectId }) +}) diff --git a/packages/delir/utils/FSPluginLoader.ts b/packages/delir/domain/Renderer/FSPluginLoader.ts similarity index 100% rename from packages/delir/utils/FSPluginLoader.ts rename to packages/delir/domain/Renderer/FSPluginLoader.ts diff --git a/packages/delir/stores/RendererStore.ts b/packages/delir/domain/Renderer/RendererStore.ts similarity index 86% rename from packages/delir/stores/RendererStore.ts rename to packages/delir/domain/Renderer/RendererStore.ts index e467d304c..798d09fbc 100644 --- a/packages/delir/stores/RendererStore.ts +++ b/packages/delir/domain/Renderer/RendererStore.ts @@ -1,12 +1,14 @@ import * as Delir from '@ragg/delir-core' import { ProjectHelper } from '@ragg/delir-core' +import deream from '@ragg/deream' import { listen, Store } from '@ragg/fleur' import { remote } from 'electron' -import { dirname, join } from 'path' -import deream from '../../deream' +import { dirname } from 'path' -import { AppActions, RendererActions } from '../actions/actions' -import * as Platform from '../utils/platform' +import { EditorActions } from '../Editor/actions' +import { RendererActions } from './actions' + +import * as Platform from '../../utils/platform' interface State { project: Delir.Entity.Project | null @@ -41,13 +43,13 @@ export default class RendererStore extends Store { private audioBuffer: AudioBuffer | null = null // @ts-ignore: unused private but listener - private handleSetActiveProject = listen(AppActions.setActiveProjectAction, (payload) => { + private handleSetActiveProject = listen(EditorActions.setActiveProjectAction, (payload) => { this.pipeline.project = payload.project this.updateWith(d => { d.project = payload.project }) }) // @ts-ignore: unused private but listener - private handleChangeActiveComposition = listen(AppActions.changeActiveCompositionAction, (payload) => { + private handleChangeActiveComposition = listen(EditorActions.changeActiveCompositionAction, (payload) => { if (!this.state.project) return this.updateWith(d => { @@ -69,7 +71,7 @@ export default class RendererStore extends Store { }) // @ts-ignore: unused private but listener - private handleStartPreveiew = listen(AppActions.startPreviewAction, (payload) => { + private handleStartPreveiew = listen(EditorActions.startPreviewAction, (payload) => { if (!this.state.project || !this.state.composition || !this.destCanvas || !this.destCanvasCtx) return const {project} = this.state @@ -122,7 +124,7 @@ export default class RendererStore extends Store { promise.catch(e => { if (e instanceof Delir.Exceptions.RenderingAbortedException) { - // AppActions.updateProcessingState('Stop.') + // EditorOps.updateProcessingState('Stop.') return } @@ -131,12 +133,12 @@ export default class RendererStore extends Store { }) // @ts-ignore: unused private but listener - private handleStopPreview = listen(AppActions.stopPreviewAction, () => { + private handleStopPreview = listen(EditorActions.stopPreviewAction, () => { this.pipeline.stopCurrentRendering() }) // @ts-ignore: unused private but listener - private handleSeekPreviewFrame = listen(AppActions.seekPreviewFrameAction, (payload) => { + private handleSeekPreviewFrame = listen(EditorActions.seekPreviewFrameAction, (payload) => { const {frame} = payload const targetComposition = this.state.composition! @@ -148,7 +150,7 @@ export default class RendererStore extends Store { }) // @ts-ignore: unused private but listener - private handleRenderDestinate = listen(AppActions.renderDestinateAction, async (payload) => { + private handleRenderDestinate = listen(EditorActions.renderDestinateAction, async (payload) => { const appPath = dirname(remote.app.getPath('exe')) const ffmpegBin = __DEV__ ? 'ffmpeg' : require('path').resolve( appPath, @@ -174,8 +176,8 @@ export default class RendererStore extends Store { // awaitを噛ませてステータスを確実に出す // await new Promise(resolve => { // setImmediate(() => { - // AppActions.autoSaveProject() - // AppActions.updateProcessingState('Rendering: Initializing') + // EditorOps.autoSaveProject() + // EditorOps.updateProcessingState('Rendering: Initializing') // resolve() // }) // }) @@ -193,7 +195,7 @@ export default class RendererStore extends Store { ffmpegBin, onProgress: progress => { setTimeout(() => { - // AppActions.updateProcessingState(progress.state) + // EditorOps.updateProcessingState(progress.state) }, 0) } }) diff --git a/packages/delir/domain/Renderer/actions.ts b/packages/delir/domain/Renderer/actions.ts new file mode 100644 index 000000000..d2c26e4dc --- /dev/null +++ b/packages/delir/domain/Renderer/actions.ts @@ -0,0 +1,6 @@ +import { action } from '@ragg/fleur' + +export const RendererActions = { + addPlugins: action<{ plugins: any[] }>(), + setPreviewCanvas: action<{ canvas: HTMLCanvasElement }>(), +} diff --git a/packages/delir/actions/RendererOps.ts b/packages/delir/domain/Renderer/operations.ts similarity index 87% rename from packages/delir/actions/RendererOps.ts rename to packages/delir/domain/Renderer/operations.ts index d195b8139..527a76c27 100644 --- a/packages/delir/actions/RendererOps.ts +++ b/packages/delir/domain/Renderer/operations.ts @@ -1,11 +1,10 @@ -import * as Delir from '@ragg/delir-core' import { operation } from '@ragg/fleur' import { remote } from 'electron' import { join } from 'path' -import FSPluginLoader from '../utils/FSPluginLoader' +import * as EditorOps from '../Editor/operations' import { RendererActions } from './actions' -import * as AppOps from './App' +import FSPluginLoader from './FSPluginLoader' export const loadPlugins = operation(async (context) => { const userDir = remote.app.getPath('appData') @@ -23,7 +22,7 @@ export const loadPlugins = operation(async (context) => { const failedPlugins = fails.map((fail: any) => fail.package).join(', ') const message = fails.map((fail: any) => fail.reason).join('\n\n') - await context.executeOperation(AppOps.notify, { + await context.executeOperation(EditorOps.notify, { message: `${failedPlugins}`, title: `Failed to load ${fails.length} plugins`, level: 'error', diff --git a/packages/delir/main.ts b/packages/delir/main.ts index 9723a4578..cc066644d 100755 --- a/packages/delir/main.ts +++ b/packages/delir/main.ts @@ -4,16 +4,16 @@ import { createElementWithContext } from '@ragg/fleur-react' import * as os from 'os' import * as ReactDOM from 'react-dom' -import * as AppActions from './actions/App' -import * as RendererOps from './actions/RendererOps' +import * as EditorOps from './domain/Editor/operations' import * as PreferenceOps from './domain/Preference/operations' +import * as RendererOps from './domain/Renderer/operations' import AppView from './views/AppView' import PreferenceStore from '@ragg/delir/domain/Preference/PreferenceStore' -import EditorStateStore from './stores/EditorStateStore' -import ProjectStore from './stores/ProjectStore' -import RendererStore from './stores/RendererStore' +import EditorStore from './domain/Editor/EditorStore' +import ProjectStore from './domain/Project/ProjectStore' +import RendererStore from './domain/Renderer/RendererStore' // Handle errors // process.on('uncaughtException', (e: Error) => { @@ -34,7 +34,7 @@ window.addEventListener('DOMContentLoaded', async () => { case 'Linux': document.body.classList.add('platform-linux'); break } - const app = new Fleur({ stores: [ EditorStateStore, ProjectStore, RendererStore, PreferenceStore ] }) + const app = new Fleur({ stores: [ EditorStore, ProjectStore, RendererStore, PreferenceStore ] }) const context = window.delir = app.createContext() // console.log(createElementWithContext) @@ -50,12 +50,12 @@ window.addEventListener('DOMContentLoaded', async () => { await context.executeOperation(RendererOps.loadPlugins, {}) await context.executeOperation(PreferenceOps.restoreApplicationPreference, {}) - await context.executeOperation(AppActions.setActiveProject, { project: new Delir.Entity.Project() }) + await context.executeOperation(EditorOps.setActiveProject, { project: new Delir.Entity.Project() }) if (__DEV__) { const project = require('./utils/Dev/ExampleProject1').default - await context.executeOperation(AppActions.setActiveProject, { project }) - await context.executeOperation(AppActions.changeActiveComposition, { compositionId: project.compositions[0].id }) + await context.executeOperation(EditorOps.setActiveProject, { project }) + await context.executeOperation(EditorOps.changeActiveComposition, { compositionId: project.compositions[0].id }) } }) diff --git a/packages/delir/stores/history-store.ts b/packages/delir/stores/history-store.ts deleted file mode 100644 index c4c498b17..000000000 --- a/packages/delir/stores/history-store.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { ReduceStore } from 'flux/utils' -import _ from 'lodash' - -import ActionTypes from '../action-types' -import dispatcher from '../utils/Flux/Dispatcher' - -class HistoryStore extends ReduceStore -{ - - public reducers = { - [ActionTypes.HISTORY_PUSH](state, payload) - { - return { - historyStack: [...state.historyStack, payload], - redoStack: [], - } - }, - - [ActionTypes.HISTORY_UNDO](state, payload) - { - const history = state.historyState.pop() - return { - historyStack: [...state.historyStack], - redoStack: [...state.redoStack, history], - } - }, - - [ActionTypes.HISTORY_REDO](state, payload) - { - const history = state.redoStack.pop() - return { - historyStack: [...state.historyStack, history], - redoStack: [...state.redoStack], - } - }, - } - public getInitialState(): Object - { - return { - historyStack: [], - redoStack: [], - } - } - - public reduce(state: Object, {type, payload}: Object) - { - if (this.reducers[type]) { - return this.reducers[type](payload) - } - - return state - } -} - -export default new HistoryStore(dispatcher) diff --git a/packages/delir/views/AppMenu/AppMenu.tsx b/packages/delir/views/AppMenu/AppMenu.tsx index b4d359801..ffeffd760 100644 --- a/packages/delir/views/AppMenu/AppMenu.tsx +++ b/packages/delir/views/AppMenu/AppMenu.tsx @@ -3,9 +3,9 @@ import * as Electron from 'electron' import { remote } from 'electron' import * as React from 'react' -import * as AppActions from '../../actions/App' +import EditorStore, { EditorState } from '../../domain/Editor/EditorStore' +import * as EditorOps from '../../domain/Editor/operations' import * as AboutModal from '../../modules/AboutModal' -import {default as EditorStateStore, EditorState } from '../../stores/EditorStateStore' import * as Platform from '../../utils/platform' import t from './AppMenu.i18n' @@ -16,8 +16,8 @@ interface ConnectedProps { type Props = ConnectedProps & ContextProp -export default withComponentContext(connectToStores([EditorStateStore], (context) => ({ - editor: context.getStore(EditorStateStore).getState() +export default withComponentContext(connectToStores([EditorStore], (context) => ({ + editor: context.getStore(EditorStore).getState() }))(class AppMenu extends React.Component { public componentDidUpdate() { remote.Menu.setApplicationMenu( @@ -40,7 +40,7 @@ export default withComponentContext(connectToStores([EditorStateStore], (context } private handleOpenPreference = () => { - this.props.context.executeOperation(AppActions.changePreferenceOpenState, { open: true }) + this.props.context.executeOperation(EditorOps.changePreferenceOpenState, { open: true }) } private _buildMenu(): Electron.MenuItemConstructorOptions[] @@ -65,7 +65,7 @@ export default withComponentContext(connectToStores([EditorStateStore], (context {type: 'separator'}, { label: t('appMenu.openPluginDir'), - click: () => context.executeOperation(AppActions.openPluginDirectory, {}) + click: () => context.executeOperation(EditorOps.openPluginDirectory, {}) }, {type: 'separator'}, { @@ -82,20 +82,20 @@ export default withComponentContext(connectToStores([EditorStateStore], (context { label: t('file.newProject'), accelerator: 'CmdOrCtrl+N', - click: () => context.executeOperation(AppActions.newProject, {}) + click: () => context.executeOperation(EditorOps.newProject, {}) }, {type: 'separator'}, { label: t('file.openProject'), accelerator: 'CmdOrCtrl+O', - click: () => context.executeOperation(AppActions.openProject, {}) + click: () => context.executeOperation(EditorOps.openProject, {}) }, {type: 'separator'}, { label: t('file.save'), accelerator: 'CmdOrCtrl+S', click: () => { - const state = context.getStore(EditorStateStore).getState() + const state = context.getStore(EditorStore).getState() let path: string | null = state.projectPath if (!path) { @@ -114,7 +114,7 @@ export default withComponentContext(connectToStores([EditorStateStore], (context if (!path) return } - context.executeOperation(AppActions.saveProject, { path }) + context.executeOperation(EditorOps.saveProject, { path }) } }, { @@ -135,7 +135,7 @@ export default withComponentContext(connectToStores([EditorStateStore], (context // cancelled if (!path) return - context.executeOperation(AppActions.saveProject, { path }) + context.executeOperation(EditorOps.saveProject, { path }) } }, {type: 'separator'}, @@ -143,9 +143,9 @@ export default withComponentContext(connectToStores([EditorStateStore], (context label: t('file.rendering'), accelerator: 'CmdOrCtrl+Shift+R', click() { - const comp = context.getStore(EditorStateStore).getState().activeComp + const comp = context.getStore(EditorStore).getState().activeComp if (!comp) return - context.executeOperation(AppActions.renderDestinate, { compositionId: comp.id! }) + context.executeOperation(EditorOps.renderDestinate, { compositionId: comp.id! }) } }, ...(Platform.isWindows() ? [ @@ -198,11 +198,11 @@ export default withComponentContext(connectToStores([EditorStateStore], (context enabled: !!activeComp, click: () => { previewPlayed - ? context.executeOperation(AppActions.stopPreview, {}) - : context.executeOperation(AppActions.startPreview, { + ? context.executeOperation(EditorOps.stopPreview, {}) + : context.executeOperation(EditorOps.startPreview, { compositionId: activeComp!.id, // Delayed get for rendering performance - beginFrame: context.getStore(EditorStateStore).getState().currentPreviewFrame, + beginFrame: context.getStore(EditorStore).getState().currentPreviewFrame, }) } , }, diff --git a/packages/delir/views/AppView/AppView.tsx b/packages/delir/views/AppView/AppView.tsx index 806f67296..96042b712 100644 --- a/packages/delir/views/AppView/AppView.tsx +++ b/packages/delir/views/AppView/AppView.tsx @@ -2,8 +2,8 @@ import { connectToStores, ContextProp, withComponentContext } from '@ragg/fleur- import * as React from 'react' import { CSSTransitionGroup } from 'react-transition-group' -import * as AppActions from '../../actions/App' -import {default as EditorStateStore, EditorState } from '../../stores/EditorStateStore' +import EditorStore, { EditorState } from '../../domain/Editor/EditorStore' +import * as EditorOps from '../../domain/Editor/operations' import Pane from '../components/pane' import Workspace from '../components/workspace' @@ -25,8 +25,8 @@ interface ConnectedProps { type Props = ConnectedProps & ContextProp -export default withComponentContext(connectToStores([EditorStateStore], (context) => ({ - editor: context.getStore(EditorStateStore).getState() +export default withComponentContext(connectToStores([EditorStore], (context) => ({ + editor: context.getStore(EditorStore).getState() }))(class AppView extends React.PureComponent { public componentDidMount() { @@ -43,8 +43,8 @@ export default withComponentContext(connectToStores([EditorStateStore], (context if (e.code === 'Space') { previewPlayed - ? this.props.context.executeOperation(AppActions.stopPreview, {}) - : this.props.context.executeOperation(AppActions.startPreview, { + ? this.props.context.executeOperation(EditorOps.stopPreview, {}) + : this.props.context.executeOperation(EditorOps.startPreview, { compositionId: activeComp!.id, beginFrame: currentPreviewFrame }) @@ -90,10 +90,10 @@ export default withComponentContext(connectToStores([EditorStateStore], (context } private projectAutoSaveTimer = () => { - this.props.context.executeOperation(AppActions.autoSaveProject, {}) + this.props.context.executeOperation(EditorOps.autoSaveProject, {}) } private handlePreferenceClose = () => { - this.props.context.executeOperation(AppActions.changePreferenceOpenState, { open: false }) + this.props.context.executeOperation(EditorOps.changePreferenceOpenState, { open: false }) } })) diff --git a/packages/delir/views/AssetsView/AssetsView.tsx b/packages/delir/views/AssetsView/AssetsView.tsx index b4544ad81..6eca2a13b 100644 --- a/packages/delir/views/AssetsView/AssetsView.tsx +++ b/packages/delir/views/AssetsView/AssetsView.tsx @@ -8,11 +8,11 @@ import * as React from 'react' import { ProjectHelper, Values } from '@ragg/delir-core' -import * as AppActions from '../../actions/App' -import * as ProjectModActions from '../../actions/ProjectMod' +import * as EditorOps from '../../domain/Editor/operations' +import * as ProjectOps from '../../domain/Project/operations' -import { default as EditorStateStore, EditorState } from '../../stores/EditorStateStore' -import ProjectStore from '../../stores/ProjectStore' +import EditorStore, { EditorState } from '../../domain/Editor/EditorStore' +import ProjectStore from '../../domain/Project/ProjectStore' import { ContextMenu, MenuItem, MenuItemOption } from '../components/ContextMenu' import LabelInput from '../components/label-input' @@ -85,8 +85,8 @@ interface State { type Props = ConnectedProps & ContextProp -export default withComponentContext(connectToStores([EditorStateStore, ProjectStore], (context) => ({ - editor: context.getStore(EditorStateStore).getState(), +export default withComponentContext(connectToStores([EditorStore, ProjectStore], (context) => ({ + editor: context.getStore(EditorStore).getState(), }))(class AssetsView extends React.Component { public state = { newCompositionWindowOpened: false, @@ -264,7 +264,7 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt _.each(e.dataTransfer.files, (file, idx) => { if (!e.dataTransfer.items[idx].webkitGetAsEntry().isFile) return - this.props.context.executeOperation(ProjectModActions.addAsset, { + this.props.context.executeOperation(ProjectOps.addAsset, { name: file.name, fileType: path.extname(file.name).slice(1), path: file.path, @@ -275,24 +275,24 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt private removeAsset = ({ dataset }: MenuItemOption<{assetId: string}>) => { // TODO: Check references - this.props.context.executeOperation(ProjectModActions.removeAsset, { assetId: dataset.assetId }) + this.props.context.executeOperation(ProjectOps.removeAsset, { assetId: dataset.assetId }) } private changeComposition = ({currentTarget}: React.MouseEvent ) => { - this.props.context.executeOperation(AppActions.changeActiveComposition, { + this.props.context.executeOperation(EditorOps.changeActiveComposition, { compositionId: currentTarget.dataset.compositionId!, }) } private removeComposition = ({ dataset }: MenuItemOption<{compId: string}>) => { - this.props.context.executeOperation(ProjectModActions.removeComposition, { compositionId: dataset.compId }) + this.props.context.executeOperation(ProjectOps.removeComposition, { compositionId: dataset.compId }) } private modifyCompName = (compositionId: string, newName: string) => { - this.props.context.executeOperation(ProjectModActions.modifyComposition, { compositionId, props: { name: newName }}) + this.props.context.executeOperation(ProjectOps.modifyComposition, { compositionId, props: { name: newName }}) } private selectAsset = ({nativeEvent: e}: React.ChangeEvent) => @@ -301,7 +301,7 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt const files = Array.from(target.files!) files.forEach(file => { - this.props.context.executeOperation(ProjectModActions.addAsset, { + this.props.context.executeOperation(ProjectOps.addAsset, { name: file.name, fileType: path.extname(file.name).slice(1), path: file.path, @@ -320,7 +320,7 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt const req = await CompositionSettingModal.show({composition: comp}) if (!req) return - this.props.context.executeOperation(ProjectModActions.modifyComposition, { compositionId: compositionId, props: castToCompositionProps(req as any) }) + this.props.context.executeOperation(ProjectOps.modifyComposition, { compositionId: compositionId, props: castToCompositionProps(req as any) }) } private openNewCompositionWindow = async () => @@ -328,7 +328,7 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt const req = await CompositionSettingModal.show() if (!req) return - this.props.context.executeOperation(ProjectModActions.createComposition, { ...castToCompositionProps(req as any) }) + this.props.context.executeOperation(ProjectOps.createComposition, { ...castToCompositionProps(req as any) }) } private onAssetsDragStart = ({currentTarget}: React.DragEvent) => @@ -336,7 +336,7 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt const {editor: {project}} = this.props if (!project) return - this.props.context.executeOperation(AppActions.setDragEntity, { + this.props.context.executeOperation(EditorOps.setDragEntity, { entity: { type: 'asset', asset: ProjectHelper.findAssetById(project, currentTarget.dataset.assetId!)!, @@ -346,6 +346,6 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt private onAssetDragEnd = () => { - this.props.context.executeOperation(AppActions.clearDragEntity, {}) + this.props.context.executeOperation(EditorOps.clearDragEntity, {}) } })) diff --git a/packages/delir/views/KeyframeEditor/KeyframeEditor.tsx b/packages/delir/views/KeyframeEditor/KeyframeEditor.tsx index c53812852..79b47f561 100644 --- a/packages/delir/views/KeyframeEditor/KeyframeEditor.tsx +++ b/packages/delir/views/KeyframeEditor/KeyframeEditor.tsx @@ -6,12 +6,12 @@ import * as mouseWheel from 'mouse-wheel' import * as React from 'react' import { MeasurePoint } from '../../utils/TimePixelConversion' -import * as AppActions from '../../actions/App' -import * as ProjectModActions from '../../actions/ProjectMod' +import * as EditorOps from '../../domain/Editor/operations' +import * as ProjectOps from '../../domain/Project/operations' -import EditorStateStore, { EditorState } from '../../stores/EditorStateStore' -import ProjectStore, { ProjectStoreState } from '../../stores/ProjectStore' -import RendererStore from '../../stores/RendererStore' +import EditorStore, { EditorState } from '../../domain/Editor/EditorStore' +import ProjectStore, { ProjectStoreState } from '../../domain/Project/ProjectStore' +import RendererStore from '../../domain/Renderer/RendererStore' import Button from '../components/Button' import { ContextMenu, MenuItem, MenuItemOption } from '../components/ContextMenu' @@ -65,8 +65,8 @@ interface State { type Props = OwnProps & ConnectedProps & ContextProp -export default withComponentContext(connectToStores([EditorStateStore], (context) => ({ - editor: context.getStore(EditorStateStore).getState(), +export default withComponentContext(connectToStores([EditorStore], (context) => ({ + editor: context.getStore(EditorStore).getState(), project: context.getStore(ProjectStore).getState() }))(class KeyframeEditor extends React.Component { @@ -170,7 +170,7 @@ export default withComponentContext(connectToStores([EditorStateStore], (context data-entity-id={activeClip.id} data-param-name={desc.paramName} enabled={desc.animatable} - onClick={this._openExpressionEditor} + onClick={this.openExpressionEditor} /> @@ -381,7 +381,7 @@ export default withComponentContext(connectToStores([EditorStateStore], (context if (!activeClip) return const frameOnClip = currentPreviewFrame - activeClip.placedFrame - this.props.context.executeOperation(ProjectModActions.createOrModifyKeyframeForEffect, { + this.props.context.executeOperation(ProjectOps.createOrModifyKeyframeForEffect, { clipId: activeClip.id, effectId, paramName: desc.paramName, @@ -389,7 +389,7 @@ export default withComponentContext(connectToStores([EditorStateStore], (context patch: {value} }) - this.props.context.executeOperation(AppActions.seekPreviewFrame, { frame: currentPreviewFrame }) + this.props.context.executeOperation(EditorOps.seekPreviewFrame, { frame: currentPreviewFrame }) } private keyframeModified = (parentClipId: string, paramName: string, frameOnClip: number, patch: KeyframePatch) => @@ -397,21 +397,31 @@ export default withComponentContext(connectToStores([EditorStateStore], (context const { activeParam } = this.state if (!activeParam) return - if (activeParam.type === 'clip') { - this.props.context.executeOperation(ProjectModActions.createOrModifyKeyframeForClip, { - clipId: parentClipId, - paramName, - frameOnClip, - patch - }) - } else { - this.props.context.executeOperation(ProjectModActions.createOrModifyKeyframeForEffect, { - clipId: parentClipId, - effectId: activeParam.entityId, - paramName, - frameOnClip, - patch - }) + switch (activeParam.type) { + case 'clip': { + this.props.context.executeOperation(ProjectOps.createOrModifyKeyframeForClip, { + clipId: parentClipId, + paramName, + frameOnClip, + patch + }) + break + } + + case 'effect': { + this.props.context.executeOperation(ProjectOps.createOrModifyKeyframeForEffect, { + clipId: parentClipId, + effectId: activeParam.entityId, + paramName, + frameOnClip, + patch + }) + break + } + + default: { + throw new Error('unreachable') + } } } @@ -421,9 +431,9 @@ export default withComponentContext(connectToStores([EditorStateStore], (context if (!activeParam) return if (activeParam.type === 'clip') { - this.props.context.executeOperation(ProjectModActions.removeKeyframe, { keyframeId: activeParam.entityId }) + this.props.context.executeOperation(ProjectOps.removeKeyframe, { keyframeId: activeParam.entityId }) } else { - this.props.context.executeOperation(ProjectModActions.removeKeyframeForEffect, { + this.props.context.executeOperation(ProjectOps.removeKeyframeForEffect, { clipId: parentClipId, effectId: activeParam.entityId, keyframeId @@ -431,20 +441,20 @@ export default withComponentContext(connectToStores([EditorStateStore], (context } } - private _openExpressionEditor = ({ dataset }: MenuItemOption<{ type: 'clip' | 'effect', entityId: string, paramName: string }>) => { - const { type, entityId, paramName } = dataset - this.setState({editorOpened: true, activeParam: { type, entityId, paramName }}) + private openExpressionEditor = ({ dataset }: MenuItemOption<{ entityType: 'clip' | 'effect', entityId: string, paramName: string }>) => { + const { entityType, entityId, paramName } = dataset + this.setState({editorOpened: true, activeParam: { type: entityType, entityId, paramName }}) this.forceUpdate() } private removeEffect = ({dataset}: MenuItemOption<{clipId: string, effectId: string}>) => { this.setState({ editorOpened: false, activeParam: null }, () => { - this.props.context.executeOperation(ProjectModActions.removeEffect, { + this.props.context.executeOperation(ProjectOps.removeEffect, { holderClipId: dataset.clipId, effectId: dataset.effectId }) - this.props.context.executeOperation(AppActions.seekPreviewFrame, { + this.props.context.executeOperation(EditorOps.seekPreviewFrame, { frame: this.props.editor.currentPreviewFrame }) }) @@ -515,7 +525,7 @@ export default withComponentContext(connectToStores([EditorStateStore], (context data-entity-type='effect' data-entity-id={effect.id} data-param-name={desc.paramName} - onClick={this._openExpressionEditor} + onClick={this.openExpressionEditor} /> ({ - editor: context.getStore(EditorStateStore).getState() +export default withComponentContext(connectToStores([EditorStore], (context) => ({ + editor: context.getStore(EditorStore).getState() }))(class NavigationView extends React.Component { public render() @@ -51,17 +51,17 @@ export default withComponentContext(connectToStores([EditorStateStore], (context const {activeComp} = this.props.editor if (! activeComp) return - this.props.context.executeOperation(AppActions.startPreview, { compositionId: activeComp.id! }) + this.props.context.executeOperation(EditorOps.startPreview, { compositionId: activeComp.id! }) } private onClickPause = (e: React.MouseEvent) => { - this.props.context.executeOperation(AppActions.stopPreview, {}) + this.props.context.executeOperation(EditorOps.stopPreview, {}) } private onClickDest = () => { const {activeComp} = this.props.editor - activeComp && this.props.context.executeOperation(AppActions.renderDestinate, { compositionId: activeComp.id! }) + activeComp && this.props.context.executeOperation(EditorOps.renderDestinate, { compositionId: activeComp.id! }) } private titleBarDoubleClicked = () => diff --git a/packages/delir/views/Notifications/Notifications.tsx b/packages/delir/views/Notifications/Notifications.tsx index ee0a4edb2..85809c7d1 100644 --- a/packages/delir/views/Notifications/Notifications.tsx +++ b/packages/delir/views/Notifications/Notifications.tsx @@ -2,7 +2,7 @@ import { connectToStores } from '@ragg/fleur-react' import * as classnames from 'classnames' import * as React from 'react' -import EditorStateStore, { NotificationEntry } from '../../stores/EditorStateStore' +import EditorStore, { NotificationEntry } from '../../domain/Editor/EditorStore' import * as s from './style.styl' @@ -10,8 +10,8 @@ interface NotificationsProps { entries: NotificationEntry[] } -export default connectToStores([EditorStateStore], (context) => ({ - entries: context.getStore(EditorStateStore).getState().notifications, +export default connectToStores([EditorStore], (context) => ({ + entries: context.getStore(EditorStore).getState().notifications, }))(class Notifications extends React.Component { public render() { diff --git a/packages/delir/views/PreviewView/PreviewView.tsx b/packages/delir/views/PreviewView/PreviewView.tsx index c2e3e0535..cd160ab38 100644 --- a/packages/delir/views/PreviewView/PreviewView.tsx +++ b/packages/delir/views/PreviewView/PreviewView.tsx @@ -6,8 +6,8 @@ import { frameToTimeCode } from '../../utils/Timecode' import DropDown from '../components/dropdown' import Pane from '../components/pane' -import * as RendererOps from '../../actions/RendererOps' -import EditorStateStore from '../../stores/EditorStateStore' +import EditorStore from '../../domain/Editor/EditorStore' +import * as RendererOps from '../../domain/Renderer/operations' import t from './PreviewView.i18n' import * as s from './style.styl' @@ -24,12 +24,12 @@ interface State { scaleListShown: boolean } -export default withComponentContext(connectToStores([EditorStateStore], (context) => { - const editorStateStore = context.getStore(EditorStateStore) +export default withComponentContext(connectToStores([EditorStore], (context) => { + const editorStore = context.getStore(EditorStore) return { - activeComp: editorStateStore.getState().activeComp, - currentPreviewFrame: editorStateStore.getState().currentPreviewFrame, + activeComp: editorStore.getState().activeComp, + currentPreviewFrame: editorStore.getState().currentPreviewFrame, } })(class PreviewView extends React.Component { public state = { diff --git a/packages/delir/views/StatusBar/StatusBar.tsx b/packages/delir/views/StatusBar/StatusBar.tsx index 9ee84a036..289dbc2fe 100644 --- a/packages/delir/views/StatusBar/StatusBar.tsx +++ b/packages/delir/views/StatusBar/StatusBar.tsx @@ -1,15 +1,15 @@ import { connectToStores } from '@ragg/fleur-react' import * as React from 'react' -import EditorStateStore from '../../stores/EditorStateStore' +import EditorStore from '../../domain/Editor/EditorStore' import Pane from '../components/pane' interface ConnectedProps { stateText: string } -export default connectToStores([EditorStateStore], (context) => ({ - stateText: context.getStore(EditorStateStore).getState().processingState +export default connectToStores([EditorStore], (context) => ({ + stateText: context.getStore(EditorStore).getState().processingState }))(class StatusBar extends React.Component { public render() { diff --git a/packages/delir/views/Timeline/LaneLabel.tsx b/packages/delir/views/Timeline/LaneLabel.tsx index 1ef815eed..652f68ea4 100644 --- a/packages/delir/views/Timeline/LaneLabel.tsx +++ b/packages/delir/views/Timeline/LaneLabel.tsx @@ -4,7 +4,7 @@ import * as classnames from 'classnames' import * as React from 'react' import { SortableElement, SortableHandle } from 'react-sortable-hoc' -import * as ProjectModActions from '../../actions/ProjectMod' +import * as ProjectOps from '../../domain/Project/operations' import { ContextMenu, MenuItem, MenuItemOption } from '../components/ContextMenu' import LabelInput from '../components/label-input' @@ -68,7 +68,7 @@ const LaneLabel = withComponentContext(class LaneLabel extends React.Component

({ - editor: context.getStore(EditorStateStore).getState(), +export default withComponentContext(connectToStores([EditorStore, ProjectStore], context => ({ + editor: context.getStore(EditorStore).getState(), }))(class Timeline extends React.Component { public props: Props & { editor: EditorState, @@ -226,8 +226,8 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt if (!activeComp) return const layer = activeComp.layers[oldIndex] - this.props.context.executeOperation(ProjectModActions.moveLayerOrder, { layerId: layer.id, newIndex }) - this.props.context.executeOperation(AppActions.seekPreviewFrame, {}) + this.props.context.executeOperation(ProjectOps.moveLayerOrder, { layerId: layer.id, newIndex }) + this.props.context.executeOperation(EditorOps.seekPreviewFrame, {}) } private onLayerCreate = () => @@ -236,7 +236,7 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt if (!editor.activeComp) return - this.props.context.executeOperation(ProjectModActions.addLayer, { + this.props.context.executeOperation(ProjectOps.addLayer, { targetComposition: editor.activeComp, layer: new Delir.Entity.Layer() }) @@ -245,7 +245,7 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt private onLayerRemove = (layerId: string) => { if (!this.props.editor.activeComp) return - this.props.context.executeOperation(ProjectModActions.removeLayer, { layerId }) + this.props.context.executeOperation(ProjectOps.removeLayer, { layerId }) } private _scaleTimeline = (e: React.WheelEvent) => @@ -285,7 +285,7 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt const {dragEntity, activeComp} = this.props.editor if (!activeComp) { - this.props.context.executeOperation(AppActions.notify, { + this.props.context.executeOperation(EditorOps.notify, { message: 'Must be select any composition before add assets to timeline', title: 'Woops', level: 'error', @@ -297,11 +297,11 @@ export default withComponentContext(connectToStores([EditorStateStore, ProjectSt if (!activeComp || !dragEntity || dragEntity.type !== 'asset') return const {asset} = dragEntity - this.props.context.executeOperation(ProjectModActions.addLayerWithAsset, { targetComposition: activeComp, asset }) + this.props.context.executeOperation(ProjectOps.addLayerWithAsset, { targetComposition: activeComp, asset }) } private _onSeeked = (frame: number) => { - this.props.context.executeOperation(AppActions.seekPreviewFrame, { frame }) + this.props.context.executeOperation(EditorOps.seekPreviewFrame, { frame }) } })) diff --git a/packages/delir/views/Timeline/_Clip.tsx b/packages/delir/views/Timeline/_Clip.tsx index 6950069cd..108b5b848 100644 --- a/packages/delir/views/Timeline/_Clip.tsx +++ b/packages/delir/views/Timeline/_Clip.tsx @@ -5,8 +5,8 @@ import * as React from 'react' import { DraggableEventHandler } from 'react-draggable' import { Rnd, RndResizeCallback } from 'react-rnd' -import * as AppActions from '../../actions/App' -import * as ProjectModActions from '../../actions/ProjectMod' +import * as EditorOps from '../../domain/Editor/operations' +import * as ProjectOps from '../../domain/Project/operations' import { ContextMenu, MenuItem, MenuItemOption } from '../components/ContextMenu' import t from './_Clip.i18n' @@ -80,12 +80,12 @@ export default withComponentContext(class Clip extends React.Component { private handleClick = (e: React.DragEvent) => { - this.props.context.executeOperation(AppActions.changeActiveClip, { clipId: this.props.clip.id! }) + this.props.context.executeOperation(EditorOps.changeActiveClip, { clipId: this.props.clip.id! }) } private handleDragStart: DraggableEventHandler = (e) => { - this.props.context.executeOperation(AppActions.setDragEntity, { + this.props.context.executeOperation(EditorOps.setDragEntity, { entity: {type: 'clip', clip: this.props.clip} }) } @@ -106,15 +106,15 @@ export default withComponentContext(class Clip extends React.Component { private addEffect = ({dataset}: MenuItemOption<{clipId: string, effectId: string}>) => { - this.props.context.executeOperation(ProjectModActions.addEffectIntoClip, { + this.props.context.executeOperation(ProjectOps.addEffectIntoClip, { clipId: dataset.clipId, processorId: dataset.effectId }) - this.props.context.executeOperation(AppActions.seekPreviewFrame, {}) + this.props.context.executeOperation(EditorOps.seekPreviewFrame, {}) } private removeClip = ({ dataset }: MenuItemOption<{clipId: string}>) => { - this.props.context.executeOperation(ProjectModActions.removeClip, { clipId: dataset.clipId }) + this.props.context.executeOperation(ProjectOps.removeClip, { clipId: dataset.clipId }) } }) diff --git a/packages/delir/views/Timeline/_ClipSpace.tsx b/packages/delir/views/Timeline/_ClipSpace.tsx index ccb29c4a9..578b01f65 100644 --- a/packages/delir/views/Timeline/_ClipSpace.tsx +++ b/packages/delir/views/Timeline/_ClipSpace.tsx @@ -4,11 +4,11 @@ import * as classnames from 'classnames' import * as _ from 'lodash' import * as React from 'react' -import * as AppActions from '../../actions/App' -import * as ProjectModActions from '../../actions/ProjectMod' +import * as EditorOps from '../../domain/Editor/operations' +import * as ProjectOps from '../../domain/Project/operations' -import EditorStateStore, { EditorState } from '../../stores/EditorStateStore' -import RendererStore from '../../stores/RendererStore' +import EditorStore, { EditorState } from '../../domain/Editor/EditorStore' +import RendererStore from '../../domain/Renderer/RendererStore' import TimePixelConversion from '../../utils/TimePixelConversion' import { ContextMenu, MenuItem, MenuItemOption } from '../components/ContextMenu' @@ -37,8 +37,8 @@ interface State { /** * ClipSpace */ -export default withComponentContext(connectToStores([EditorStateStore, RendererStore], context => ({ - editor: context.getStore(EditorStateStore).getState(), +export default withComponentContext(connectToStores([EditorStore, RendererStore], context => ({ + editor: context.getStore(EditorStore).getState(), postEffectPlugins: context.getStore(RendererStore).getPostEffectPlugins(), }))(class ClipSpace extends React.Component { public state: State = { @@ -110,7 +110,7 @@ export default withComponentContext(connectToStores([EditorStateStore, RendererS const {asset} = dragEntity const {framerate, pxPerSec, scale} = this.props const placedFrame = TimePixelConversion.pixelToFrames({pxPerSec, framerate, pixel: ((e.nativeEvent as any).layerX as number), scale}) - this.props.context.executeOperation(ProjectModActions.createClipWithAsset, { + this.props.context.executeOperation(ProjectOps.createClipWithAsset, { targetLayer: this.props.layer, asset, placedFrame @@ -119,7 +119,7 @@ export default withComponentContext(connectToStores([EditorStateStore, RendererS return } - this.props.context.executeOperation(AppActions.clearDragEntity, {}) + this.props.context.executeOperation(EditorOps.clearDragEntity, {}) this.setState({dragovered: false}) e.preventDefault() @@ -134,13 +134,13 @@ export default withComponentContext(connectToStores([EditorStateStore, RendererS const isChildClip = !!layer.clips.find(clip => clip.id! === dragEntity.clip.id!) if (isChildClip) { - this.props.context.executeOperation(AppActions.clearDragEntity, {}) + this.props.context.executeOperation(EditorOps.clearDragEntity, {}) } else { - this.props.context.executeOperation(ProjectModActions.moveClipToLayer, { + this.props.context.executeOperation(ProjectOps.moveClipToLayer, { clipId: dragEntity.clip.id!, destLayerId: this.props.layer.id! }) - this.props.context.executeOperation(AppActions.clearDragEntity, {}) + this.props.context.executeOperation(EditorOps.clearDragEntity, {}) } } @@ -153,7 +153,7 @@ export default withComponentContext(connectToStores([EditorStateStore, RendererS scale: this.props.scale, }) - this.props.context.executeOperation(ProjectModActions.modifyClip, { + this.props.context.executeOperation(ProjectOps.modifyClip, { clipId, props: { placedFrame: newPlacedFrame } }) @@ -168,7 +168,7 @@ export default withComponentContext(connectToStores([EditorStateStore, RendererS scale: this.props.scale, }) - this.props.context.executeOperation(ProjectModActions.modifyClip, { + this.props.context.executeOperation(ProjectOps.modifyClip, { clipId: clipId, props: { durationFrames: newDurationFrames } }) @@ -176,7 +176,7 @@ export default withComponentContext(connectToStores([EditorStateStore, RendererS private addNewClip = ({ dataset }: MenuItemOption<{rendererId: string}>) => { - this.props.context.executeOperation(ProjectModActions.createClip, { + this.props.context.executeOperation(ProjectOps.createClip, { layerId: this.props.layer.id!, clipRendererId: dataset.rendererId, placedFrame: 0, diff --git a/packages/delir/views/Timeline/_Gradations.tsx b/packages/delir/views/Timeline/_Gradations.tsx index 9aa11707c..3599e726d 100644 --- a/packages/delir/views/Timeline/_Gradations.tsx +++ b/packages/delir/views/Timeline/_Gradations.tsx @@ -7,7 +7,7 @@ import TimePixelConversion, { MeasurePoint } from '../../utils/TimePixelConversi import { ContextMenu, MenuItem } from '../components/ContextMenu' -import RendererStore, { RenderState } from '../../stores/RendererStore' +import RendererStore, { RenderState } from '../../domain/Renderer/RendererStore' import t from './_Gradations.i18n' import * as s from './Gradations.styl'