From e34350f1b7cdbf3290efd25a3db7058bf5ce8b71 Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Fri, 13 Dec 2024 17:49:55 +0000 Subject: [PATCH 1/7] moved parameters to a flat map in instances reducers rather than nesting them in the instance model --- src/actions/graph.ts | 12 ++- src/actions/instances.ts | 133 +++++++++++++++++++++------ src/components/instance/index.tsx | 7 +- src/components/instance/paramTab.tsx | 28 ++++-- src/lib/types.ts | 1 + src/models/instance.ts | 49 +--------- src/models/parameter.ts | 24 ++++- src/pages/instances/[index].tsx | 7 +- src/reducers/instances.ts | 52 ++++++++++- src/selectors/instances.ts | 47 ++++++++++ 10 files changed, 263 insertions(+), 97 deletions(-) diff --git a/src/actions/graph.ts b/src/actions/graph.ts index adec1325..987e5b83 100644 --- a/src/actions/graph.ts +++ b/src/actions/graph.ts @@ -8,12 +8,13 @@ import { getConnectionsForSourceNodeAndPort, getNode, getPatcherNodeByIndex, get import { showNotification } from "./notifications"; import { NotificationLevel } from "../models/notification"; import { InstanceStateRecord } from "../models/instance"; -import { deleteInstance, setInstance, setInstances } from "./instances"; +import { deleteInstance, setInstance, setInstanceParameters, setInstances } from "./instances"; import { getInstance } from "../selectors/instances"; import { PatcherRecord } from "../models/patcher"; import { getPatchers } from "../selectors/patchers"; import { defaultNodeGap, nodeDefaultWidth, nodeHeaderHeight } from "../lib/constants"; import { getGraphEditorInstance } from "../selectors/editor"; +import { ParameterRecord } from "../models/parameter"; const getPatcherOrControlNodeCoordinates = (node: GraphPatcherNodeRecord | GraphControlNodeRecord, nodes: GraphNodeRecord[]): { x: number, y: number } => { @@ -343,6 +344,7 @@ export const initNodes = (jackPortsInfo: OSCQueryRNBOJackPortInfo, instanceInfo: const existingNodes = getNodes(state); const instances: InstanceStateRecord[] = []; + const instanceParameters: ParameterRecord[] = []; const patcherAndControlNodes: Array = []; const meta: OSCQuerySetMeta = deserializeSetMeta(instanceInfo.CONTENTS.control.CONTENTS.sets.CONTENTS.meta.VALUE as string); @@ -356,7 +358,9 @@ export const initNodes = (jackPortsInfo: OSCQueryRNBOJackPortInfo, instanceInfo: node = node.updatePosition(x, y); patcherAndControlNodes.push(node); - instances.push(InstanceStateRecord.fromDescription(info)); + const instance = InstanceStateRecord.fromDescription(info); + instances.push(instance); + instanceParameters.push(...ParameterRecord.fromDescription(instance.index, info.CONTENTS.params)); } // Build a list of all Jack generated names that have not been used for PatcherNodes above @@ -421,6 +425,7 @@ export const initNodes = (jackPortsInfo: OSCQueryRNBOJackPortInfo, instanceInfo: dispatch(deleteNodes(existingNodes.valueSeq().toArray())); dispatch(setNodes([...systemNodes, ...patcherAndControlNodes])); dispatch(setInstances(instances)); + dispatch(setInstanceParameters(instanceParameters)); dispatch(setPortsAliases(portAliases)); }; @@ -684,7 +689,9 @@ export const addPatcherNode = (desc: OSCQueryRNBOInstance, metaString: string): // Create Instance State const instance = InstanceStateRecord.fromDescription(desc); + const parameters = ParameterRecord.fromDescription(instance.index, desc.CONTENTS.params); dispatch(setInstance(instance)); + dispatch(setInstanceParameters(parameters)); }; export const removePatcherNode = (index: number): AppThunk => @@ -698,6 +705,7 @@ export const removePatcherNode = (index: number): AppThunk => const instance = getInstance(state, node.id); if (!instance) return; + dispatch(deleteInstance(instance)); } catch (e) { console.log(e); diff --git a/src/actions/instances.ts b/src/actions/instances.ts index 3241354a..05ebd6fc 100644 --- a/src/actions/instances.ts +++ b/src/actions/instances.ts @@ -2,7 +2,7 @@ import Router from "next/router"; import { ActionBase, AppThunk } from "../lib/store"; import { OSCQueryRNBOInstance, OSCQueryRNBOInstancePresetEntries, OSCValue } from "../lib/types"; import { InstanceStateRecord } from "../models/instance"; -import { getInstanceByIndex, getInstance } from "../selectors/instances"; +import { getInstanceByIndex, getInstance, getParameter, getInstanceParameters, getInstanceParameterByName, getParameterByPath } from "../selectors/instances"; import { getAppSetting } from "../selectors/settings"; import { ParameterRecord } from "../models/parameter"; import { MessagePortRecord } from "../models/messageport"; @@ -20,7 +20,11 @@ export enum InstanceActionType { SET_INSTANCE = "SET_INSTANCE", SET_INSTANCES = "SET_INSTANCES", DELETE_INSTANCE = "DELETE_INSTANCE", - DELETE_INSTANCES = "DELETE_INSTANCES" + DELETE_INSTANCES = "DELETE_INSTANCES", + SET_PARAMETER = "SET_PARAMETER", + SET_PARAMETERS = "SET_PARAMETERS", + DELETE_PARAMETER = "DELETE_PARAMETER", + DELETE_PARAMETERS = "DELETE_PARAMETERS" } export interface ISetInstance extends ActionBase { @@ -51,7 +55,36 @@ export interface IDeleteInstances extends ActionBase { }; } -export type InstanceAction = ISetInstance | ISetInstances | IDeleteInstance | IDeleteInstances; +export interface ISetInstanceParameter extends ActionBase { + type: InstanceActionType.SET_PARAMETER; + payload: { + parameter: ParameterRecord; + }; +} + +export interface ISetInstanceParameters extends ActionBase { + type: InstanceActionType.SET_PARAMETERS; + payload: { + parameters: ParameterRecord[]; + }; +} + +export interface IDeleteInstanceParameter extends ActionBase { + type: InstanceActionType.DELETE_PARAMETER; + payload: { + parameter: ParameterRecord; + }; +} + +export interface IDeleteInstanceParameters extends ActionBase { + type: InstanceActionType.DELETE_PARAMETERS; + payload: { + parameters: ParameterRecord[]; + }; +} + +export type InstanceAction = ISetInstance | ISetInstances | IDeleteInstance | IDeleteInstances | +ISetInstanceParameter | ISetInstanceParameters | IDeleteInstanceParameter | IDeleteInstanceParameters; export const setInstance = (instance: InstanceStateRecord): ISetInstance => ({ type: InstanceActionType.SET_INSTANCE, @@ -81,6 +114,34 @@ export const deleteInstances = (instances: InstanceStateRecord[]): IDeleteInstan } }); +export const setInstanceParameter = (param: ParameterRecord): ISetInstanceParameter => ({ + type: InstanceActionType.SET_PARAMETER, + payload: { + parameter: param + } +}); + +export const setInstanceParameters = (params: ParameterRecord[]): ISetInstanceParameters => ({ + type: InstanceActionType.SET_PARAMETERS, + payload: { + parameters: params + } +}); + +export const deleteInstanceParameter = (param: ParameterRecord): IDeleteInstanceParameter => ({ + type: InstanceActionType.DELETE_PARAMETER, + payload: { + parameter: param + } +}); + +export const deleteInstanceParameters = (params: ParameterRecord[]): IDeleteInstanceParameters => ({ + type: InstanceActionType.DELETE_PARAMETERS, + payload: { + parameters: params + } +}); + // Trigger Events on Remote OSCQuery Runner export const loadPresetOnRemoteInstance = (instance: InstanceStateRecord, preset: PresetRecord): AppThunk => (dispatch) => { @@ -250,8 +311,8 @@ export const setInstanceParameterValueNormalizedOnRemote = throttle((instance: I oscQueryBridge.sendPacket(writePacket(message)); // optimistic local state update - dispatch(setInstance(instance.setParameterNormalizedValue(param.id, value))); - }, 25); + dispatch(setInstanceParameter(param.setNormalizedValue(value))); + }, 10); export const setInstanceDataRefValueOnRemote = (instance: InstanceStateRecord, dataref: DataRefRecord, file?: DataFileRecord): AppThunk => () => { @@ -291,8 +352,14 @@ export const restoreDefaultParameterMetaOnRemote = (_instance: InstanceStateReco }; export const activateParameterMIDIMappingFocus = (instance: InstanceStateRecord, param: ParameterRecord): AppThunk => - (dispatch) => { - dispatch(setInstance(instance.setParameterWaitingForMidiMapping(param.id))); + (dispatch, getState) => { + + const state = getState(); + const params = getInstanceParameters(state, instance.index); + + dispatch(setInstanceParameters( + params.valueSeq().toArray().map(p => p.setWaitingForMidiMapping(p.id === param.id)) + )); }; export const clearParameterMidiMappingOnRemote = (id: InstanceStateRecord["id"], paramId: ParameterRecord["id"]): AppThunk => @@ -301,7 +368,7 @@ export const clearParameterMidiMappingOnRemote = (id: InstanceStateRecord["id"], const instance = getInstance(state, id); if (!instance) return; - const param = instance.parameters.get(paramId); + const param = getParameter(state, paramId); if (!param) return; const meta = param.getParsedMetaObject(); @@ -432,9 +499,11 @@ export const updateInstanceParameters = (index: number, desc: OSCQueryRNBOInstan const instance = getInstanceByIndex(state, index); if (!instance) return; - dispatch(setInstance( - instance.set("parameters", InstanceStateRecord.parametersFromDescription(desc)) - )); + const currentParams = getInstanceParameters(state, instance.index); + dispatch(deleteInstanceParameters(currentParams.valueSeq().toArray())); + + const newParams = ParameterRecord.fromDescription(instance.index, desc); + dispatch(setInstanceParameters(newParams)); } catch (e) { console.log(e); } @@ -456,27 +525,27 @@ export const updateInstanceDataRefValue = (index: number, name: string, value: s } }; -export const updateInstanceParameterValue = (index: number, id: ParameterRecord["id"], value: number): AppThunk => +export const updateInstanceParameterValue = (index: number, name: ParameterRecord["name"], value: number): AppThunk => (dispatch, getState) => { try { const state = getState(); - const instance = getInstanceByIndex(state, index); - if (!instance) return; + const param = getInstanceParameterByName(state, index, name); + if (!param) return; - dispatch(setInstance(instance.setParameterValue(id, value))); + dispatch(setInstanceParameter(param.setValue(value))); } catch (e) { console.log(e); } }; -export const updateInstanceParameterValueNormalized = (index: number, id: ParameterRecord["id"], value: number): AppThunk => +export const updateInstanceParameterValueNormalized = (index: number, name: ParameterRecord["name"], value: number): AppThunk => (dispatch, getState) => { try { const state = getState(); - const instance = getInstanceByIndex(state, index); - if (!instance) return; + const param = getInstanceParameterByName(state, index, name); + if (!param) return; - dispatch(setInstance(instance.setParameterNormalizedValue(id, value))); + dispatch(setInstanceParameter(param.setNormalizedValue(value))); } catch (e) { console.log(e); } @@ -490,6 +559,8 @@ export const setInstanceWaitingForMidiMappingOnRemote = (id: InstanceStateRecord if (!instance) return; dispatch(setInstance(instance.setWaitingForMapping(value))); + const params = getInstanceParameters(state, instance.index).valueSeq().map(p => p.setWaitingForMidiMapping(false)); + dispatch(setInstanceParameters(params.toArray())); try { const message = { @@ -519,6 +590,8 @@ export const updateInstanceMIDIReport = (index: number, value: boolean): AppThun const instance = getInstanceByIndex(state, index); if (!instance) return; dispatch(setInstance(instance.setWaitingForMapping(value))); + const params = getInstanceParameters(state, instance.index).valueSeq().map(p => p.setWaitingForMidiMapping(false)); + dispatch(setInstanceParameters(params.toArray())); } catch (e) { console.log(e); } @@ -530,13 +603,14 @@ export const updateInstanceMIDILastValue = (index: number, value: string): AppTh const state = getState(); - let instance = getInstanceByIndex(state, index); + const instance = getInstanceByIndex(state, index); if (!instance?.waitingForMidiMapping) return; const midiMeta = JSON.parse(value); // find waiting, update their meta, set them no longer waiting and update map - instance = instance.set("parameters", instance.parameters.map(param => { + const parameters: ParameterRecord[] = []; + getInstanceParameters(state, instance.index).forEach(param => { if (param.waitingForMidiMapping) { const meta = param.getParsedMetaObject(); meta.midi = midiMeta; @@ -549,26 +623,25 @@ export const updateInstanceMIDILastValue = (index: number, value: string): AppTh }; oscQueryBridge.sendPacket(writePacket(message)); - return param.setWaitingForMidiMapping(false); + parameters.push(param.setWaitingForMidiMapping(false)); } - return param; - })); + }); - dispatch(setInstance(instance)); + dispatch(setInstanceParameters(parameters)); } catch (e) { console.log(e); } }; -export const updateInstanceParameterMeta = (index: number, id: ParameterRecord["id"], value: string): AppThunk => +export const updateInstanceParameterMeta = (index: number, name: ParameterRecord["name"], value: string): AppThunk => (dispatch, getState) => { try { const state = getState(); - const instance = getInstanceByIndex(state, index); - if (!instance) return; + const param = getInstanceParameterByName(state, index, name); + if (!param) return; - dispatch(setInstance(instance.setParameterMeta(id, value))); + dispatch(setInstanceParameter(param.setMeta(value))); } catch (e) { console.log(e); } @@ -599,3 +672,5 @@ export const updateInstanceMessageInportMeta = (index: number, id: MessagePortRe console.log(e); } }; + +// Events from remote diff --git a/src/components/instance/index.tsx b/src/components/instance/index.tsx index 9fc1a8c3..eadb47de 100644 --- a/src/components/instance/index.tsx +++ b/src/components/instance/index.tsx @@ -8,9 +8,10 @@ import InstanceDataRefsTab from "./datarefTab"; import { InstanceStateRecord } from "../../models/instance"; import { AppSettingRecord } from "../../models/settings"; import { DataFileRecord } from "../../models/datafile"; -import { Seq } from "immutable"; +import { Map as ImmuMap, Seq } from "immutable"; import { IconElement } from "../elements/icon"; import { mdiFileMusic, mdiSwapHorizontal, mdiTune } from "@mdi/js"; +import { ParameterRecord } from "../../models/parameter"; const tabs = [ { icon: mdiTune, value: InstanceTab.Parameters, label: "Parameters" }, @@ -22,6 +23,7 @@ export type InstanceProps = { instance: InstanceStateRecord; datafiles: Seq.Indexed enabledMessageOuput: AppSettingRecord; + parameters: ImmuMap; paramSortOrder: AppSettingRecord; paramSortAttr: AppSettingRecord; } @@ -30,6 +32,7 @@ const Instance: FunctionComponent = memo(function WrappedInstance instance, datafiles, enabledMessageOuput, + parameters, paramSortOrder, paramSortAttr }) { @@ -60,7 +63,7 @@ const Instance: FunctionComponent = memo(function WrappedInstance
- + diff --git a/src/components/instance/paramTab.tsx b/src/components/instance/paramTab.tsx index 38086071..ff1eaaca 100644 --- a/src/components/instance/paramTab.tsx +++ b/src/components/instance/paramTab.tsx @@ -12,7 +12,7 @@ import { setInstanceWaitingForMidiMappingOnRemote, clearParameterMidiMappingOnRemote, activateParameterMIDIMappingFocus } from "../../actions/instances"; -import { OrderedSet as ImmuOrderedSet } from "immutable"; +import { OrderedSet as ImmuOrderedSet, Map as ImmuMap } from "immutable"; import { setAppSetting } from "../../actions/settings"; import { AppSetting, AppSettingRecord } from "../../models/settings"; import { useDebouncedCallback, useDisclosure } from "@mantine/hooks"; @@ -106,24 +106,26 @@ const parameterComparators: Record => { - return ImmuOrderedSet(params.valueSeq().sort(parameterComparators[attr][order]).map(p => p.id)); +const getSortedParameterIds = (params: ImmuMap, attr: ParameterSortAttr, order: SortOrder): ImmuOrderedSet => { + return ImmuOrderedSet(params.valueSeq().sort(parameterComparators[attr][order]).map(p => p.id)); }; export type InstanceParameterTabProps = { instance: InstanceStateRecord; + parameters: ImmuMap; sortAttr: AppSettingRecord; sortOrder: AppSettingRecord; } const InstanceParameterTab: FunctionComponent = memo(function WrappedInstanceParameterTab({ instance, + parameters, sortAttr, sortOrder }) { const [searchValue, setSearchValue] = useState(""); - const [sortedParamIds, setSortedParamIds] = useState>(getSortedParameterIds(instance.parameters, sortAttr.value as ParameterSortAttr, sortOrder.value as SortOrder)); + const [sortedParameterIds, setSortedParameterIds] = useState>(ImmuOrderedSet()); const dispatch = useAppDispatch(); @@ -165,8 +167,8 @@ const InstanceParameterTab: FunctionComponent = memo( }, 150); useEffect(() => { - setSortedParamIds(getSortedParameterIds(instance.parameters, sortAttr.value as ParameterSortAttr, sortOrder.value as SortOrder)); - }, [instance, sortAttr, sortOrder]); + setSortedParameterIds(getSortedParameterIds(parameters, sortAttr.value as ParameterSortAttr, sortOrder.value as SortOrder)); + }, [instance, parameters.size, sortAttr, sortOrder]); useEffect(() => { const onKeyDown = (e: KeyboardEvent) => { @@ -187,8 +189,14 @@ const InstanceParameterTab: FunctionComponent = memo( }; }, [instance.id, dispatch]); - let parameters = sortedParamIds.map(id => instance.parameters.get(id)).filter(p => !!p); - if (searchValue?.length) parameters = parameters.filter(p => p.matchesQuery(searchValue)); + const displayParameters = ImmuOrderedSet().withMutations(set => { + sortedParameterIds.forEach(id => { + const p = parameters.get(id); + if (p && (!searchValue?.length || p.matchesQuery(searchValue))) { + set.add(p); + } + }); + }); return ( @@ -236,14 +244,14 @@ const InstanceParameterTab: FunctionComponent = memo( { - !instance.parameters.size ? ( + !parameters.size ? (
This patcher instance has no parameters
) : (
; + VALUE: undefined } data_refs: OSCQueryBaseNode & { CONTENTS: Record; diff --git a/src/models/instance.ts b/src/models/instance.ts index 84653d31..cfde3e35 100644 --- a/src/models/instance.ts +++ b/src/models/instance.ts @@ -1,5 +1,4 @@ import { Map as ImmuMap, Record as ImmuRecord, OrderedMap as ImmuOrderedMap } from "immutable"; -import { ParameterRecord } from "./parameter"; import { PresetRecord } from "./preset"; import { DataRefRecord } from "./dataref"; import { MessagePortRecord } from "./messageport"; @@ -16,7 +15,6 @@ export type InstanceStateProps = { messageInports: ImmuMap; messageOutports: ImmuMap; - parameters: ImmuMap; presets: ImmuOrderedMap; datarefs: ImmuOrderedMap; @@ -46,7 +44,6 @@ export class InstanceStateRecord extends ImmuRecord({ messageInports: ImmuMap(), messageOutports: ImmuMap(), - parameters: ImmuMap(), presets: ImmuMap(), datarefs: ImmuMap(), @@ -58,6 +55,10 @@ export class InstanceStateRecord extends ImmuRecord({ return this.name; } + public setWaitingForMapping(value: boolean): InstanceStateRecord { + return this.set("waitingForMidiMapping", value); + } + public setMessageOutportValue(id: string, value: string): InstanceStateRecord { const p = this.messageOutports.get(id); if (!p) return this; @@ -84,38 +85,6 @@ export class InstanceStateRecord extends ImmuRecord({ return this.set("datarefs", this.datarefs.set(dataref.id, dataref.setFileId(fileId))); } - public setParameterValue(id: ParameterRecord["id"], value: number): InstanceStateRecord { - const param = this.parameters.get(id); - if (!param) return this; - - return this.set("parameters", this.parameters.set(param.id, param.setValue(value))); - } - - public setWaitingForMapping(value: boolean): InstanceStateRecord { - return this.set("waitingForMidiMapping", value).clearParametersWaitingForMidiMapping(); - } - - public clearParametersWaitingForMidiMapping(): InstanceStateRecord { - return this.set("parameters", this.parameters.map(p => p.setWaitingForMidiMapping(false))); - } - - public setParameterWaitingForMidiMapping(id: ParameterRecord["id"]): InstanceStateRecord { - return this.set("parameters", this.parameters.map(p => p.setWaitingForMidiMapping(p.id === id))); - } - - public setParameterNormalizedValue(id: ParameterRecord["id"], value: number): InstanceStateRecord { - const param = this.parameters.get(id); - if (!param) return this; - - return this.set("parameters", this.parameters.set(param.id, param.setNormalizedValue(value))); - } - - public setParameterMeta(id: ParameterRecord["id"], value: string): InstanceStateRecord { - const param = this.parameters.get(id); - if (!param) return this; - return this.set("parameters", this.parameters.set(param.id, param.setMeta(value))); - } - public static presetsFromDescription(entries: OSCQueryRNBOInstancePresetEntries, latest: string, initial: string): ImmuMap { return ImmuOrderedMap().withMutations((map) => { for (const name of entries.VALUE) { @@ -159,15 +128,6 @@ export class InstanceStateRecord extends ImmuRecord({ }); } - public static parametersFromDescription(paramsDesc: OSCQueryRNBOInstance["CONTENTS"]["params"]): ImmuMap { - return ImmuMap().withMutations((map) => { - for (const [name, desc] of Object.entries(paramsDesc.CONTENTS || {})) { - const params = ParameterRecord.arrayFromDescription(desc, name); - params.forEach(pr => map.set(pr.id, pr)); - } - }); - } - public static datarefsFromDescription(datarefsDesc: OSCQueryRNBOInstance["CONTENTS"]["data_refs"]): ImmuMap { return ImmuMap().withMutations((map) => { for (const [name, desc] of Object.entries(datarefsDesc.CONTENTS || {})) { @@ -194,7 +154,6 @@ export class InstanceStateRecord extends ImmuRecord({ path: desc.FULL_PATH, messageInports: this.messagesFromDescription(desc.CONTENTS.messages?.CONTENTS?.in), messageOutports: this.messagesFromDescription(desc.CONTENTS.messages?.CONTENTS?.out), - parameters: this.parametersFromDescription(desc.CONTENTS.params), presets: this.presetsFromDescription(desc.CONTENTS.presets.CONTENTS.entries, latestPreset, initialPreset), datarefs: this.datarefsFromDescription(desc.CONTENTS.data_refs) }); diff --git a/src/models/parameter.ts b/src/models/parameter.ts index a408e727..13be8795 100644 --- a/src/models/parameter.ts +++ b/src/models/parameter.ts @@ -1,10 +1,12 @@ import { Record as ImmuRecord } from "immutable"; -import { AnyJson, JsonMap, OSCQueryRNBOInstanceParameterInfo, OSCQueryRNBOInstanceParameterValue } from "../lib/types"; +import { AnyJson, JsonMap, OSCQueryRNBOInstance, OSCQueryRNBOInstanceParameterInfo, OSCQueryRNBOInstanceParameterValue } from "../lib/types"; import { parseParamMetaJSONString } from "../lib/util"; export type ParameterRecordProps = { + enumVals: Array; index: number; + instanceIndex: number; min: number; max: number; meta: string; @@ -20,6 +22,7 @@ export class ParameterRecord extends ImmuRecord({ enumVals: [], index: 0, + instanceIndex: 0, min: 0, max: 1, meta: "", @@ -32,7 +35,11 @@ export class ParameterRecord extends ImmuRecord({ isMidiMapped: false }) { - public static arrayFromDescription(desc: OSCQueryRNBOInstanceParameterInfo, name?: string): ParameterRecord[] { + private static arrayFromDescription( + instanceIndex: number, + desc: OSCQueryRNBOInstanceParameterInfo, + name?: string + ): ParameterRecord[] { const result: ParameterRecord[] = []; if (typeof desc.VALUE !== "undefined") { const paramInfo = desc as OSCQueryRNBOInstanceParameterValue; @@ -41,6 +48,7 @@ export class ParameterRecord extends ImmuRecord({ result.push((new ParameterRecord({ enumVals: paramInfo.RANGE?.[0]?.VALS || [], index: paramInfo.CONTENTS?.index?.VALUE || 0, + instanceIndex, min: paramInfo.RANGE?.[0]?.MIN, max: paramInfo.RANGE?.[0]?.MAX, name, @@ -53,14 +61,22 @@ export class ParameterRecord extends ImmuRecord({ // Polyphonic params for (const [subParamName, subDesc] of Object.entries(desc.CONTENTS) as Array<[string, OSCQueryRNBOInstanceParameterInfo]>) { const subPrefix = name ? `${name}/${subParamName}` : subParamName; - result.push(...this.arrayFromDescription(subDesc, subPrefix)); + result.push(...this.arrayFromDescription(instanceIndex, subDesc, subPrefix)); } } return result; } + public static fromDescription(instanceIndex: number, paramsDesc: OSCQueryRNBOInstance["CONTENTS"]["params"]): ParameterRecord[] { + const params: ParameterRecord[] = []; + for (const [name, desc] of Object.entries(paramsDesc.CONTENTS || {})) { + params.push(...ParameterRecord.arrayFromDescription(instanceIndex, desc, name)); + } + return params; + } + public get id(): string { - return this.name; + return this.path; } public get isEnum(): boolean { diff --git a/src/pages/instances/[index].tsx b/src/pages/instances/[index].tsx index 6e28e45b..44ecb7fa 100644 --- a/src/pages/instances/[index].tsx +++ b/src/pages/instances/[index].tsx @@ -8,7 +8,7 @@ import classes from "../../components/instance/instance.module.css"; import { getAppStatus } from "../../selectors/appStatus"; import { AppStatus, SortOrder } from "../../lib/constants"; import Link from "next/link"; -import { getInstanceByIndex, getInstances } from "../../selectors/instances"; +import { getInstanceByIndex, getInstanceParameters, getInstances } from "../../selectors/instances"; import { unloadPatcherNodeByIndexOnRemote } from "../../actions/graph"; import { getAppSetting } from "../../selectors/settings"; import { AppSetting } from "../../models/settings"; @@ -36,6 +36,7 @@ export default function Instance() { const [ currentInstance, + parameters, appStatus, instances, datafiles, @@ -48,6 +49,7 @@ export default function Instance() { return [ currentInstance, + currentInstance ? getInstanceParameters(state, currentInstance.index) : undefined, getAppStatus(state), getInstances(state), getDataFilesSortedByName(state, SortOrder.Asc), @@ -102,7 +104,7 @@ export default function Instance() { if (!isReady || appStatus !== AppStatus.Ready) return null; - if (!currentInstance) { + if (!currentInstance || !parameters) { // Instance not found / doesn't exist return (
@@ -157,6 +159,7 @@ export default function Instance() { ; + parameters: ImmuMap; } export const instances = (state: InstanceInstancesState = { - instances: ImmuMap() + instances: ImmuMap(), + parameters: ImmuMap() }, action: InstanceAction): InstanceInstancesState => { @@ -41,16 +44,59 @@ export const instances = (state: InstanceInstancesState = { return { ...state, - instances: state.instances.delete(instance.id) + instances: state.instances.delete(instance.id), + parameters: state.parameters.filter(param => param.instanceIndex !== instance.index) }; } case InstanceActionType.DELETE_INSTANCES: { const { instances } = action.payload; + const indexSet = new Set(instances.map(i => i.index)); return { ...state, - instances: state.instances.deleteAll(instances.map(d => d.id)) + instances: state.instances.deleteAll(instances.map(d => d.id)), + parameters: state.parameters.filter(param => !indexSet.has(param.instanceIndex)) + }; + } + + case InstanceActionType.SET_PARAMETER: { + const { parameter } = action.payload; + + return { + ...state, + parameters: state.parameters.set(parameter.id, parameter) + }; + } + + case InstanceActionType.SET_PARAMETERS: { + const { parameters } = action.payload; + + return { + ...state, + parameters: state.parameters.withMutations(map => { + for (const param of parameters) { + map.set(param.id, param); + } + }) + }; + } + + case InstanceActionType.DELETE_PARAMETER: { + const { parameter } = action.payload; + + return { + ...state, + parameters: state.parameters.delete(parameter.id) + }; + } + + case InstanceActionType.DELETE_PARAMETERS: { + const { parameters } = action.payload; + + return { + ...state, + parameters: state.parameters.deleteAll(parameters.map(d => d.id)) }; } diff --git a/src/selectors/instances.ts b/src/selectors/instances.ts index 64ec0bcb..65663ccc 100644 --- a/src/selectors/instances.ts +++ b/src/selectors/instances.ts @@ -3,6 +3,7 @@ import { RootStateType } from "../lib/store"; import { InstanceStateRecord } from "../models/instance"; import { createSelector } from "reselect"; import { getPatcherIdsByIndex } from "./graph"; +import { ParameterRecord } from "../models/parameter"; export const getInstances = (state: RootStateType): ImmuMap => state.instances.instances; @@ -36,3 +37,49 @@ export const getInstancesByIndex = createSelector( }); } ); + +export const getParameters = (state: RootStateType): ImmuMap => state.instances.parameters; + +export const getParameter = createSelector( + [ + getParameters, + (state: RootStateType, id: ParameterRecord["id"]): ParameterRecord["id"] => id + ], + (parameters, id): ParameterRecord | undefined => { + return parameters.get(id); + } +); + +export const getParameterByPath = createSelector( + [ + getParameters, + (state: RootStateType, path: ParameterRecord["path"]): ParameterRecord["path"] => path + ], + (parameters, path): ParameterRecord | undefined => { + return parameters.find(p => p.path === path); + } +); + +export const getInstanceParameters = createSelector( + [ + getParameters, + (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex + ], + (parameters, instanceIndex): ImmuMap => { + return parameters.filter(p => { + return p.instanceIndex === instanceIndex; + }); + } +); + + +export const getInstanceParameterByName = createSelector( + [ + getParameters, + (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex, + (state: RootStateType, instanceIndex: InstanceStateRecord["index"], name: ParameterRecord["name"]): ParameterRecord["name"] => name + ], + (parameters, instanceIndex, name): ParameterRecord | undefined => { + return parameters.find(p => p.instanceIndex === instanceIndex && p.name === name); + } +); From 7668b1a372763684fdec4a7aba15462e2162f2de Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Fri, 13 Dec 2024 17:52:18 +0000 Subject: [PATCH 2/7] support for removing individual parameters by path upon info from the runner --- src/actions/instances.ts | 13 +++++++++++++ src/controller/oscqueryBridgeController.ts | 8 +++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/actions/instances.ts b/src/actions/instances.ts index 05ebd6fc..bde504f1 100644 --- a/src/actions/instances.ts +++ b/src/actions/instances.ts @@ -490,6 +490,19 @@ export const updateInstanceMessageOutportValue = (index: number, name: string, v } }; +export const removeInstanceParameterByPath = (path: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const param = getParameterByPath(state, path); + if (!param) return; + + dispatch(deleteInstanceParameters([param])); + } catch (e) { + console.log(e); + } + }; + export const updateInstanceParameters = (index: number, desc: OSCQueryRNBOInstance["CONTENTS"]["params"]): AppThunk => (dispatch, getState) => { try { diff --git a/src/controller/oscqueryBridgeController.ts b/src/controller/oscqueryBridgeController.ts index 7c62fbbf..2cbc49ef 100644 --- a/src/controller/oscqueryBridgeController.ts +++ b/src/controller/oscqueryBridgeController.ts @@ -17,7 +17,8 @@ import { updateInstanceMessageOutportValue, updateInstanceMessages, updateInstanceMessageOutportMeta, updateInstanceMessageInportMeta, updateInstanceParameterValue, updateInstanceParameterValueNormalized, updateInstanceParameters, updateInstanceParameterMeta, updateInstancePresetEntries, updateInstancePresetLatest, updateInstancePresetInitial, - updateInstanceMIDILastValue, updateInstanceMIDIReport + updateInstanceMIDILastValue, updateInstanceMIDIReport, + removeInstanceParameterByPath } from "../actions/instances"; import { ConnectionType, PortDirection } from "../models/graph"; import { showNotification } from "../actions/notifications"; @@ -417,10 +418,11 @@ export class OSCQueryBridgeControllerPrivate { // Removed Parameter if ( instInfoMatch.groups.content === "params" && + !instInfoMatch.groups.rest.endsWith("/index") && + !instInfoMatch.groups.rest.endsWith("/meta") && !instInfoMatch.groups.rest.endsWith("/normalized") ) { - const paramInfo = await this._requestState< OSCQueryRNBOInstance["CONTENTS"]["params"]>(`/rnbo/inst/${index}/params`); - return void dispatch(updateInstanceParameters(index, paramInfo)); + return void dispatch(removeInstanceParameterByPath(path)); } // Removed Message Inport From 639a24ee543b8065b4c1cfcc08c21315135301e2 Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Fri, 13 Dec 2024 19:21:02 +0000 Subject: [PATCH 3/7] flatted instance inport and outports into instances reducer --- src/actions/graph.ts | 14 +- src/actions/instances.ts | 198 +++++++++++++++++++-- src/components/instance/index.tsx | 7 +- src/components/instance/messageTab.tsx | 13 +- src/controller/oscqueryBridgeController.ts | 20 ++- src/models/instance.ts | 52 +----- src/models/messageport.ts | 42 +++-- src/pages/instances/[index].tsx | 10 +- src/reducers/instances.ts | 93 +++++++++- src/selectors/instances.ts | 91 ++++++++++ 10 files changed, 447 insertions(+), 93 deletions(-) diff --git a/src/actions/graph.ts b/src/actions/graph.ts index 987e5b83..7b3c9515 100644 --- a/src/actions/graph.ts +++ b/src/actions/graph.ts @@ -8,13 +8,14 @@ import { getConnectionsForSourceNodeAndPort, getNode, getPatcherNodeByIndex, get import { showNotification } from "./notifications"; import { NotificationLevel } from "../models/notification"; import { InstanceStateRecord } from "../models/instance"; -import { deleteInstance, setInstance, setInstanceParameters, setInstances } from "./instances"; +import { deleteInstance, setInstance, setInstanceMessageInports, setInstanceMessageOutports, setInstanceParameters, setInstances } from "./instances"; import { getInstance } from "../selectors/instances"; import { PatcherRecord } from "../models/patcher"; import { getPatchers } from "../selectors/patchers"; import { defaultNodeGap, nodeDefaultWidth, nodeHeaderHeight } from "../lib/constants"; import { getGraphEditorInstance } from "../selectors/editor"; import { ParameterRecord } from "../models/parameter"; +import { MessagePortRecord } from "../models/messageport"; const getPatcherOrControlNodeCoordinates = (node: GraphPatcherNodeRecord | GraphControlNodeRecord, nodes: GraphNodeRecord[]): { x: number, y: number } => { @@ -345,6 +346,8 @@ export const initNodes = (jackPortsInfo: OSCQueryRNBOJackPortInfo, instanceInfo: const instances: InstanceStateRecord[] = []; const instanceParameters: ParameterRecord[] = []; + const instanceMessageInports: MessagePortRecord[] = []; + const instanceMessageOutports: MessagePortRecord[] = []; const patcherAndControlNodes: Array = []; const meta: OSCQuerySetMeta = deserializeSetMeta(instanceInfo.CONTENTS.control.CONTENTS.sets.CONTENTS.meta.VALUE as string); @@ -361,6 +364,8 @@ export const initNodes = (jackPortsInfo: OSCQueryRNBOJackPortInfo, instanceInfo: const instance = InstanceStateRecord.fromDescription(info); instances.push(instance); instanceParameters.push(...ParameterRecord.fromDescription(instance.index, info.CONTENTS.params)); + instanceMessageInports.push(...MessagePortRecord.fromDescription(info.CONTENTS.messages?.CONTENTS?.in)); + instanceMessageOutports.push(...MessagePortRecord.fromDescription(info.CONTENTS.messages?.CONTENTS?.out)); } // Build a list of all Jack generated names that have not been used for PatcherNodes above @@ -426,6 +431,8 @@ export const initNodes = (jackPortsInfo: OSCQueryRNBOJackPortInfo, instanceInfo: dispatch(setNodes([...systemNodes, ...patcherAndControlNodes])); dispatch(setInstances(instances)); dispatch(setInstanceParameters(instanceParameters)); + dispatch(setInstanceMessageInports(instanceMessageInports)); + dispatch(setInstanceMessageOutports(instanceMessageOutports)); dispatch(setPortsAliases(portAliases)); }; @@ -690,8 +697,13 @@ export const addPatcherNode = (desc: OSCQueryRNBOInstance, metaString: string): // Create Instance State const instance = InstanceStateRecord.fromDescription(desc); const parameters = ParameterRecord.fromDescription(instance.index, desc.CONTENTS.params); + const messageInports = MessagePortRecord.fromDescription(desc.CONTENTS.messages?.CONTENTS?.in); + const messageOutports = MessagePortRecord.fromDescription(desc.CONTENTS.messages?.CONTENTS?.out); + dispatch(setInstance(instance)); dispatch(setInstanceParameters(parameters)); + dispatch(setInstanceMessageInports(messageInports)); + dispatch(setInstanceMessageOutports(messageOutports)); }; export const removePatcherNode = (index: number): AppThunk => diff --git a/src/actions/instances.ts b/src/actions/instances.ts index bde504f1..e44cfe9e 100644 --- a/src/actions/instances.ts +++ b/src/actions/instances.ts @@ -2,7 +2,7 @@ import Router from "next/router"; import { ActionBase, AppThunk } from "../lib/store"; import { OSCQueryRNBOInstance, OSCQueryRNBOInstancePresetEntries, OSCValue } from "../lib/types"; import { InstanceStateRecord } from "../models/instance"; -import { getInstanceByIndex, getInstance, getParameter, getInstanceParameters, getInstanceParameterByName, getParameterByPath } from "../selectors/instances"; +import { getInstanceByIndex, getInstance, getParameter, getInstanceParameters, getInstanceParameterByName, getParameterByPath, getInstanceMessageInports, getInstanceMessageOutports, getInstanceMessageOutportByTag, getInstanceMessageInportByTag, getMessageInportByPath, getMessageOutportByPath } from "../selectors/instances"; import { getAppSetting } from "../selectors/settings"; import { ParameterRecord } from "../models/parameter"; import { MessagePortRecord } from "../models/messageport"; @@ -21,10 +21,21 @@ export enum InstanceActionType { SET_INSTANCES = "SET_INSTANCES", DELETE_INSTANCE = "DELETE_INSTANCE", DELETE_INSTANCES = "DELETE_INSTANCES", + SET_PARAMETER = "SET_PARAMETER", SET_PARAMETERS = "SET_PARAMETERS", DELETE_PARAMETER = "DELETE_PARAMETER", - DELETE_PARAMETERS = "DELETE_PARAMETERS" + DELETE_PARAMETERS = "DELETE_PARAMETERS", + + SET_MESSAGE_INPORT = "SET_MESSAGE_INPORT", + SET_MESSAGE_INPORTS = "SET_MESSAGE_INPORTS", + DELETE_MESSAGE_INPORT = "DELETE_MESSAGE_INPORT", + DELETE_MESSAGE_INPORTS = "DELETE_MESSAGE_INPORTS", + + SET_MESSAGE_OUTPORT = "SET_MESSAGE_OUTPORT", + SET_MESSAGE_OUTPORTS = "SET_MESSAGE_OUTPORTS", + DELETE_MESSAGE_OUTPORT = "DELETE_MESSAGE_OUTPORT", + DELETE_MESSAGE_OUTPORTS = "DELETE_MESSAGE_OUTPORTS" } export interface ISetInstance extends ActionBase { @@ -83,8 +94,66 @@ export interface IDeleteInstanceParameters extends ActionBase { }; } +export interface ISetInstanceMessageInport extends ActionBase { + type: InstanceActionType.SET_MESSAGE_INPORT; + payload: { + port: MessagePortRecord; + }; +} + +export interface ISetInstanceMessageInports extends ActionBase { + type: InstanceActionType.SET_MESSAGE_INPORTS; + payload: { + ports: MessagePortRecord[]; + }; +} + +export interface IDeleteInstanceMessageInport extends ActionBase { + type: InstanceActionType.DELETE_MESSAGE_INPORT; + payload: { + port: MessagePortRecord; + }; +} + +export interface IDeleteInstanceMessageInports extends ActionBase { + type: InstanceActionType.DELETE_MESSAGE_INPORTS; + payload: { + ports: MessagePortRecord[]; + }; +} + +export interface ISetInstanceMessageOutport extends ActionBase { + type: InstanceActionType.SET_MESSAGE_OUTPORT; + payload: { + port: MessagePortRecord; + }; +} + +export interface ISetInstanceMessageOutports extends ActionBase { + type: InstanceActionType.SET_MESSAGE_OUTPORTS; + payload: { + ports: MessagePortRecord[]; + }; +} + +export interface IDeleteInstanceMessageOutport extends ActionBase { + type: InstanceActionType.DELETE_MESSAGE_OUTPORT; + payload: { + port: MessagePortRecord; + }; +} + +export interface IDeleteInstanceMessageOutports extends ActionBase { + type: InstanceActionType.DELETE_MESSAGE_OUTPORTS; + payload: { + ports: MessagePortRecord[]; + }; +} + export type InstanceAction = ISetInstance | ISetInstances | IDeleteInstance | IDeleteInstances | -ISetInstanceParameter | ISetInstanceParameters | IDeleteInstanceParameter | IDeleteInstanceParameters; +ISetInstanceParameter | ISetInstanceParameters | IDeleteInstanceParameter | IDeleteInstanceParameters | +ISetInstanceMessageInport | ISetInstanceMessageInports | IDeleteInstanceMessageInport | IDeleteInstanceMessageInports | +ISetInstanceMessageOutport | ISetInstanceMessageOutports | IDeleteInstanceMessageOutport | IDeleteInstanceMessageOutports export const setInstance = (instance: InstanceStateRecord): ISetInstance => ({ type: InstanceActionType.SET_INSTANCE, @@ -142,6 +211,62 @@ export const deleteInstanceParameters = (params: ParameterRecord[]): IDeleteInst } }); +export const setInstanceMessageInport = (port: MessagePortRecord): ISetInstanceMessageInport => ({ + type: InstanceActionType.SET_MESSAGE_INPORT, + payload: { + port + } +}); + +export const setInstanceMessageInports = (ports: MessagePortRecord[]): ISetInstanceMessageInports => ({ + type: InstanceActionType.SET_MESSAGE_INPORTS, + payload: { + ports + } +}); + +export const deleteInstanceMessageInport = (port: MessagePortRecord): IDeleteInstanceMessageInport => ({ + type: InstanceActionType.DELETE_MESSAGE_INPORT, + payload: { + port + } +}); + +export const deleteInstanceMessageInports = (ports: MessagePortRecord[]): IDeleteInstanceMessageInports => ({ + type: InstanceActionType.DELETE_MESSAGE_INPORTS, + payload: { + ports + } +}); + +export const setInstanceMessageOutport = (port: MessagePortRecord): ISetInstanceMessageOutport => ({ + type: InstanceActionType.SET_MESSAGE_OUTPORT, + payload: { + port + } +}); + +export const setInstanceMessageOutports = (ports: MessagePortRecord[]): ISetInstanceMessageOutports => ({ + type: InstanceActionType.SET_MESSAGE_OUTPORTS, + payload: { + ports + } +}); + +export const deleteInstanceMessageOutport = (port: MessagePortRecord): IDeleteInstanceMessageOutport => ({ + type: InstanceActionType.DELETE_MESSAGE_OUTPORT, + payload: { + port + } +}); + +export const deleteInstanceMessageOutports = (ports: MessagePortRecord[]): IDeleteInstanceMessageOutports => ({ + type: InstanceActionType.DELETE_MESSAGE_OUTPORTS, + payload: { + ports + } +}); + // Trigger Events on Remote OSCQuery Runner export const loadPresetOnRemoteInstance = (instance: InstanceStateRecord, preset: PresetRecord): AppThunk => (dispatch) => { @@ -456,17 +581,49 @@ export const updateInstanceMessages = (index: number, desc: OSCQueryRNBOInstance const instance = getInstanceByIndex(state, index); if (!instance) return; - dispatch(setInstance( - instance - .set("messageInports", InstanceStateRecord.messagesFromDescription(desc.CONTENTS?.in)) - .set("messageOutports", InstanceStateRecord.messagesFromDescription(desc.CONTENTS?.out)) - )); + const currentMessageInports = getInstanceMessageInports(state, instance.index); + const currentMessageOutports = getInstanceMessageOutports(state, instance.index); + dispatch(deleteInstanceMessageInports(currentMessageInports.valueSeq().toArray())); + dispatch(deleteInstanceMessageOutports(currentMessageOutports.valueSeq().toArray())); + + const messageInports = MessagePortRecord.fromDescription(desc.CONTENTS?.in); + const messageOutports = MessagePortRecord.fromDescription(desc.CONTENTS?.out); + + dispatch(setInstanceMessageInports(messageInports)); + dispatch(setInstanceMessageOutports(messageOutports)); + } catch (e) { console.log(e); } }; -export const updateInstanceMessageOutportValue = (index: number, name: string, value: OSCValue | OSCValue[]): AppThunk => +export const removeInstanceMessageInportByPath = (path: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const port = getMessageInportByPath(state, path); + if (!port) return; + + dispatch(deleteInstanceMessageInport(port)); + } catch (e) { + console.log(e); + } + }; + +export const removeInstanceMessageOutportByPath = (path: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const port = getMessageOutportByPath(state, path); + if (!port) return; + + dispatch(deleteInstanceMessageOutport(port)); + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceMessageOutportValue = (index: number, tag: MessagePortRecord["tag"], value: OSCValue | OSCValue[]): AppThunk => (dispatch, getState) => { try { @@ -482,9 +639,10 @@ export const updateInstanceMessageOutportValue = (index: number, name: string, v const instance = getInstanceByIndex(state, index); if (!instance) return; - dispatch(setInstance( - instance.setMessageOutportValue(name, Array.isArray(value) ? value.join(", ") : `${value}`) - )); + const port = getInstanceMessageOutportByTag(state, instance.index, tag); + if (!port) return; + + dispatch(setInstanceMessageOutport(port.setValue(Array.isArray(value) ? value.join(", ") : `${value}`))); } catch (e) { console.log(e); } @@ -497,7 +655,7 @@ export const removeInstanceParameterByPath = (path: string): AppThunk => const param = getParameterByPath(state, path); if (!param) return; - dispatch(deleteInstanceParameters([param])); + dispatch(deleteInstanceParameter(param)); } catch (e) { console.log(e); } @@ -660,27 +818,33 @@ export const updateInstanceParameterMeta = (index: number, name: ParameterRecord } }; -export const updateInstanceMessageOutportMeta = (index: number, id: MessagePortRecord["id"], value: string): AppThunk => +export const updateInstanceMessageOutportMeta = (index: number, tag: MessagePortRecord["tag"], value: string): AppThunk => (dispatch, getState) => { try { const state = getState(); const instance = getInstanceByIndex(state, index); if (!instance) return; - dispatch(setInstance(instance.setMessageOutportMeta(id, value))); + const port = getInstanceMessageOutportByTag(state, instance.index, tag); + if (!port) return; + + dispatch(setInstanceMessageOutport(port.setMeta(value))); } catch (e) { console.log(e); } }; -export const updateInstanceMessageInportMeta = (index: number, id: MessagePortRecord["id"], value: string): AppThunk => +export const updateInstanceMessageInportMeta = (index: number, tag: MessagePortRecord["tag"], value: string): AppThunk => (dispatch, getState) => { try { const state = getState(); const instance = getInstanceByIndex(state, index); if (!instance) return; - dispatch(setInstance(instance.setMessageInportMeta(id, value))); + const port = getInstanceMessageInportByTag(state, instance.index, tag); + if (!port) return; + + dispatch(setInstanceMessageInport(port.setMeta(value))); } catch (e) { console.log(e); } diff --git a/src/components/instance/index.tsx b/src/components/instance/index.tsx index eadb47de..b5f43755 100644 --- a/src/components/instance/index.tsx +++ b/src/components/instance/index.tsx @@ -12,6 +12,7 @@ import { Map as ImmuMap, Seq } from "immutable"; import { IconElement } from "../elements/icon"; import { mdiFileMusic, mdiSwapHorizontal, mdiTune } from "@mdi/js"; import { ParameterRecord } from "../../models/parameter"; +import { MessagePortRecord } from "../../models/messageport"; const tabs = [ { icon: mdiTune, value: InstanceTab.Parameters, label: "Parameters" }, @@ -23,6 +24,8 @@ export type InstanceProps = { instance: InstanceStateRecord; datafiles: Seq.Indexed enabledMessageOuput: AppSettingRecord; + messageInports: ImmuMap; + messageOutports: ImmuMap; parameters: ImmuMap; paramSortOrder: AppSettingRecord; paramSortAttr: AppSettingRecord; @@ -32,6 +35,8 @@ const Instance: FunctionComponent = memo(function WrappedInstance instance, datafiles, enabledMessageOuput, + messageInports, + messageOutports, parameters, paramSortOrder, paramSortAttr @@ -66,7 +71,7 @@ const Instance: FunctionComponent = memo(function WrappedInstance - + diff --git a/src/components/instance/messageTab.tsx b/src/components/instance/messageTab.tsx index 4a4f8270..b6a1040b 100644 --- a/src/components/instance/messageTab.tsx +++ b/src/components/instance/messageTab.tsx @@ -1,4 +1,5 @@ import { Stack } from "@mantine/core"; +import { Map as ImmuMap } from "immutable"; import { FunctionComponent, memo, useCallback } from "react"; import { useAppDispatch } from "../../hooks/useAppDispatch"; import MessageInportList from "../messages/inportList"; @@ -12,11 +13,15 @@ import { restoreDefaultMessagePortMetaOnRemote, setInstanceMessagePortMetaOnRemo export type InstanceMessageTabProps = { instance: InstanceStateRecord; + messageInports: ImmuMap; + messageOutports: ImmuMap; outputEnabled: boolean; } const InstanceMessagesTab: FunctionComponent = memo(function WrappedInstanceMessagesTab({ instance, + messageInports, + messageOutports, outputEnabled }) { @@ -37,13 +42,13 @@ const InstanceMessagesTab: FunctionComponent = memo(fun Input Ports { - !instance.messageInports.size ? ( + !messageInports.size ? (
This patcher instance has no message input ports.
) : = memo(fun } Output Ports { - !instance.messageOutports.size ? ( + !messageOutports.size ? (
This patcher instance has no output ports.
) : (`/rnbo/inst/${index}/messages`); - return void dispatch(updateInstanceMessages(index, messagesInfo)); + return void dispatch(removeInstanceMessageInportByPath(path)); + } + + // Removed Message Outport + if ( + instInfoMatch.groups.content === "messages/out" && + !instInfoMatch.groups.rest.endsWith("meta") + ) { + return void dispatch(removeInstanceMessageOutportByPath(path)); } } @@ -610,7 +620,7 @@ export class OSCQueryBridgeControllerPrivate { return void dispatch(updateInstancePresetEntries(index, presetInfo.CONTENTS.entries)); } - // port meta + // Port meta if (packetMatch.groups.rest.endsWith("/meta")) { if (packetMatch.groups.content === "messages/out") { return void dispatch(updateInstanceMessageOutportMeta(index, packetMatch.groups.rest.replace(/\/meta$/, ""), packet.args[0] as unknown as string)); diff --git a/src/models/instance.ts b/src/models/instance.ts index cfde3e35..3baabd9a 100644 --- a/src/models/instance.ts +++ b/src/models/instance.ts @@ -1,8 +1,7 @@ import { Map as ImmuMap, Record as ImmuRecord, OrderedMap as ImmuOrderedMap } from "immutable"; import { PresetRecord } from "./preset"; import { DataRefRecord } from "./dataref"; -import { MessagePortRecord } from "./messageport"; -import { OSCQueryRNBOInstance, OSCQueryRNBOInstanceMessages, OSCQueryRNBOInstanceMessageInfo, OSCQueryRNBOInstanceMessageValue, OSCQueryRNBOInstancePresetEntries } from "../lib/types"; +import { OSCQueryRNBOInstance, OSCQueryRNBOInstancePresetEntries } from "../lib/types"; export type InstanceStateProps = { index: number; @@ -13,8 +12,6 @@ export type InstanceStateProps = { presetInitial: string; presetLatest: string; - messageInports: ImmuMap; - messageOutports: ImmuMap; presets: ImmuOrderedMap; datarefs: ImmuOrderedMap; @@ -41,9 +38,6 @@ export class InstanceStateRecord extends ImmuRecord({ path: "", presetInitial: "", presetLatest: "", - - messageInports: ImmuMap(), - messageOutports: ImmuMap(), presets: ImmuMap(), datarefs: ImmuMap(), @@ -59,25 +53,6 @@ export class InstanceStateRecord extends ImmuRecord({ return this.set("waitingForMidiMapping", value); } - public setMessageOutportValue(id: string, value: string): InstanceStateRecord { - const p = this.messageOutports.get(id); - if (!p) return this; - - return this.set("messageOutports", this.messageOutports.set(p.id, p.setValue(value))); - } - - public setMessageOutportMeta(id: MessagePortRecord["id"], value: string): InstanceStateRecord { - const p = this.messageOutports.get(id); - if (!p) return this; - return this.set("messageOutports", this.messageOutports.set(p.id, p.setMeta(value))); - } - - public setMessageInportMeta(id: MessagePortRecord["id"], value: string): InstanceStateRecord { - const p = this.messageInports.get(id); - if (!p) return this; - return this.set("messageInports", this.messageInports.set(p.id, p.setMeta(value))); - } - public setDataRefValue(id: string, fileId: string): InstanceStateRecord { const dataref = this.datarefs.get(id); if (!dataref) return this; @@ -106,28 +81,6 @@ export class InstanceStateRecord extends ImmuRecord({ return this.set("presets", InstanceStateRecord.presetsFromDescription(entries, this.presetLatest, this.presetInitial)); } - private static messagesArrayFromDescription(desc: OSCQueryRNBOInstanceMessageInfo, name: string): MessagePortRecord[] { - if (typeof desc.VALUE !== "undefined") { - return [MessagePortRecord.fromDescription(name, desc as OSCQueryRNBOInstanceMessageValue)]; - } - - const result: MessagePortRecord[] = []; - for (const [subKey, subDesc] of Object.entries(desc.CONTENTS)) { - const subPrefix = name ? `${name}/${subKey}` : subKey; - result.push(...this.messagesArrayFromDescription(subDesc, subPrefix)); - } - return result; - } - - public static messagesFromDescription(messagesDesc?: OSCQueryRNBOInstanceMessages): ImmuMap { - return ImmuMap().withMutations((map) => { - for (const [name, desc] of Object.entries(messagesDesc?.CONTENTS || {})) { - const ports = this.messagesArrayFromDescription(desc, name); - ports.forEach(n => map.set(n.id, n)); - } - }); - } - public static datarefsFromDescription(datarefsDesc: OSCQueryRNBOInstance["CONTENTS"]["data_refs"]): ImmuMap { return ImmuMap().withMutations((map) => { for (const [name, desc] of Object.entries(datarefsDesc.CONTENTS || {})) { @@ -146,14 +99,11 @@ export class InstanceStateRecord extends ImmuRecord({ const initialPreset: string = desc.CONTENTS.presets.CONTENTS?.initial?.VALUE || ""; const latestPreset: string = desc.CONTENTS.presets.CONTENTS?.loaded?.VALUE || ""; - return new InstanceStateRecord({ index: parseInt(desc.FULL_PATH.split("/").pop(), 10), name: this.getJackName(desc.CONTENTS.jack), patcher: desc.CONTENTS.name.VALUE, path: desc.FULL_PATH, - messageInports: this.messagesFromDescription(desc.CONTENTS.messages?.CONTENTS?.in), - messageOutports: this.messagesFromDescription(desc.CONTENTS.messages?.CONTENTS?.out), presets: this.presetsFromDescription(desc.CONTENTS.presets.CONTENTS.entries, latestPreset, initialPreset), datarefs: this.datarefsFromDescription(desc.CONTENTS.data_refs) }); diff --git a/src/models/messageport.ts b/src/models/messageport.ts index 68a03862..7ca76b7c 100644 --- a/src/models/messageport.ts +++ b/src/models/messageport.ts @@ -1,7 +1,8 @@ import { Record as ImmuRecord } from "immutable"; -import { OSCQueryRNBOInstanceMessageValue } from "../lib/types"; +import { OSCQueryRNBOInstanceMessageInfo, OSCQueryRNBOInstanceMessages, OSCQueryRNBOInstanceMessageValue } from "../lib/types"; export type MessagePortRecordProps = { + instanceIndex: number; tag: string; meta: string; value: string; @@ -10,14 +11,43 @@ export type MessagePortRecordProps = { export class MessagePortRecord extends ImmuRecord({ + instanceIndex: 0, tag: "", meta: "", value: "", path: "" }) { + private static messagesArrayFromDescription(desc: OSCQueryRNBOInstanceMessageInfo, name: string): MessagePortRecord[] { + if (typeof desc.VALUE !== "undefined") { + return [ + new MessagePortRecord({ + instanceIndex: 0, + tag: name, + path: (desc as OSCQueryRNBOInstanceMessageValue).FULL_PATH, + meta: (desc as OSCQueryRNBOInstanceMessageValue).CONTENTS?.meta?.VALUE || "" + }) + ]; + } + + const result: MessagePortRecord[] = []; + for (const [subKey, subDesc] of Object.entries(desc.CONTENTS)) { + const subPrefix = name ? `${name}/${subKey}` : subKey; + result.push(...this.messagesArrayFromDescription(subDesc, subPrefix)); + } + return result; + } + + public static fromDescription(messagesDesc?: OSCQueryRNBOInstanceMessages): MessagePortRecord[] { + const ports: MessagePortRecord[] = []; + for (const [name, desc] of Object.entries(messagesDesc?.CONTENTS || {})) { + ports.push(...this.messagesArrayFromDescription(desc, name)); + } + return ports; + } + public get id(): string { - return this.tag; + return this.path; } public get name(): string { @@ -32,12 +62,4 @@ export class MessagePortRecord extends ImmuRecord({ return this.set("value", value); } - public static fromDescription(tag: string, desc: OSCQueryRNBOInstanceMessageValue): MessagePortRecord { - return new MessagePortRecord({ - tag, - path: desc.FULL_PATH, - meta: desc.CONTENTS?.meta?.VALUE || "" - }); - } - } diff --git a/src/pages/instances/[index].tsx b/src/pages/instances/[index].tsx index 44ecb7fa..622831aa 100644 --- a/src/pages/instances/[index].tsx +++ b/src/pages/instances/[index].tsx @@ -8,7 +8,7 @@ import classes from "../../components/instance/instance.module.css"; import { getAppStatus } from "../../selectors/appStatus"; import { AppStatus, SortOrder } from "../../lib/constants"; import Link from "next/link"; -import { getInstanceByIndex, getInstanceParameters, getInstances } from "../../selectors/instances"; +import { getInstanceByIndex, getInstanceMessageInports, getInstanceMessageOutports, getInstanceParameters, getInstances } from "../../selectors/instances"; import { unloadPatcherNodeByIndexOnRemote } from "../../actions/graph"; import { getAppSetting } from "../../selectors/settings"; import { AppSetting } from "../../models/settings"; @@ -37,6 +37,8 @@ export default function Instance() { const [ currentInstance, parameters, + messageInports, + messageOutports, appStatus, instances, datafiles, @@ -50,6 +52,8 @@ export default function Instance() { return [ currentInstance, currentInstance ? getInstanceParameters(state, currentInstance.index) : undefined, + currentInstance ? getInstanceMessageInports(state, currentInstance.index) : undefined, + currentInstance ? getInstanceMessageOutports(state, currentInstance.index) : undefined, getAppStatus(state), getInstances(state), getDataFilesSortedByName(state, SortOrder.Asc), @@ -104,7 +108,7 @@ export default function Instance() { if (!isReady || appStatus !== AppStatus.Ready) return null; - if (!currentInstance || !parameters) { + if (!currentInstance || !parameters || !messageInports || !messageOutports) { // Instance not found / doesn't exist return (
@@ -160,6 +164,8 @@ export default function Instance() { ; + messageInports: ImmuMap; + messageOutports: ImmuMap; parameters: ImmuMap; } export const instances = (state: InstanceInstancesState = { instances: ImmuMap(), + messageInports: ImmuMap(), + messageOutports: ImmuMap(), parameters: ImmuMap() }, action: InstanceAction): InstanceInstancesState => { @@ -45,7 +50,9 @@ export const instances = (state: InstanceInstancesState = { return { ...state, instances: state.instances.delete(instance.id), - parameters: state.parameters.filter(param => param.instanceIndex !== instance.index) + parameters: state.parameters.filter(param => param.instanceIndex !== instance.index), + messageInports: state.messageInports.filter(port => port.instanceIndex !== instance.index), + messageOutports: state.messageOutports.filter(port => port.instanceIndex !== instance.index) }; } @@ -56,7 +63,9 @@ export const instances = (state: InstanceInstancesState = { return { ...state, instances: state.instances.deleteAll(instances.map(d => d.id)), - parameters: state.parameters.filter(param => !indexSet.has(param.instanceIndex)) + parameters: state.parameters.filter(param => !indexSet.has(param.instanceIndex)), + messageInports: state.messageInports.filter(port => !indexSet.has(port.instanceIndex)), + messageOutports: state.messageOutports.filter(port => !indexSet.has(port.instanceIndex)) }; } @@ -100,6 +109,86 @@ export const instances = (state: InstanceInstancesState = { }; } + case InstanceActionType.SET_MESSAGE_INPORT: { + const { port } = action.payload; + + return { + ...state, + messageInports: state.messageInports.set(port.id, port) + }; + } + + case InstanceActionType.SET_MESSAGE_INPORTS: { + const { ports } = action.payload; + + return { + ...state, + messageInports: state.messageInports.withMutations(map => { + for (const port of ports) { + map.set(port.id, port); + } + }) + }; + } + + case InstanceActionType.DELETE_MESSAGE_INPORT: { + const { port } = action.payload; + + return { + ...state, + messageInports: state.messageInports.delete(port.id) + }; + } + + case InstanceActionType.DELETE_MESSAGE_INPORTS: { + const { ports } = action.payload; + + return { + ...state, + messageInports: state.messageInports.deleteAll(ports.map(d => d.id)) + }; + } + + case InstanceActionType.SET_MESSAGE_OUTPORT: { + const { port } = action.payload; + + return { + ...state, + messageOutports: state.messageOutports.set(port.id, port) + }; + } + + case InstanceActionType.SET_MESSAGE_OUTPORTS: { + const { ports } = action.payload; + + return { + ...state, + messageOutports: state.messageOutports.withMutations(map => { + for (const port of ports) { + map.set(port.id, port); + } + }) + }; + } + + case InstanceActionType.DELETE_MESSAGE_OUTPORT: { + const { port } = action.payload; + + return { + ...state, + messageOutports: state.messageOutports.delete(port.id) + }; + } + + case InstanceActionType.DELETE_MESSAGE_OUTPORTS: { + const { ports } = action.payload; + + return { + ...state, + messageOutports: state.messageOutports.deleteAll(ports.map(d => d.id)) + }; + } + default: return state; } diff --git a/src/selectors/instances.ts b/src/selectors/instances.ts index 65663ccc..5e6b9a90 100644 --- a/src/selectors/instances.ts +++ b/src/selectors/instances.ts @@ -4,6 +4,7 @@ import { InstanceStateRecord } from "../models/instance"; import { createSelector } from "reselect"; import { getPatcherIdsByIndex } from "./graph"; import { ParameterRecord } from "../models/parameter"; +import { MessagePortRecord } from "../models/messageport"; export const getInstances = (state: RootStateType): ImmuMap => state.instances.instances; @@ -83,3 +84,93 @@ export const getInstanceParameterByName = createSelector( return parameters.find(p => p.instanceIndex === instanceIndex && p.name === name); } ); + +export const getMessageInports = (state: RootStateType): ImmuMap => state.instances.messageInports; + +export const getMessageInport = createSelector( + [ + getMessageInports, + (state: RootStateType, id: MessagePortRecord["id"]): MessagePortRecord["id"] => id + ], + (ports, id): MessagePortRecord | undefined => { + return ports.get(id); + } +); + +export const getMessageInportByPath = createSelector( + [ + getMessageInports, + (state: RootStateType, path: MessagePortRecord["path"]): MessagePortRecord["path"] => path + ], + (ports, path): MessagePortRecord | undefined => { + return ports.find(p => p.path === path); + } +); + +export const getInstanceMessageInports = createSelector( + [ + getMessageInports, + (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex + ], + (ports, instanceIndex): ImmuMap => { + return ports.filter(p => { + return p.instanceIndex === instanceIndex; + }); + } +); + +export const getInstanceMessageInportByTag = createSelector( + [ + getMessageInports, + (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex, + (state: RootStateType, instanceIndex: InstanceStateRecord["index"], tag: MessagePortRecord["tag"]): MessagePortRecord["tag"] => tag + ], + (ports, instanceIndex, tag): MessagePortRecord | undefined => { + return ports.find(p => p.instanceIndex === instanceIndex && p.tag === tag); + } +); + +export const getMessageOutports = (state: RootStateType): ImmuMap => state.instances.messageOutports; + +export const getMessageOutport = createSelector( + [ + getMessageOutports, + (state: RootStateType, id: MessagePortRecord["id"]): MessagePortRecord["id"] => id + ], + (ports, id): MessagePortRecord | undefined => { + return ports.get(id); + } +); + +export const getMessageOutportByPath = createSelector( + [ + getMessageOutports, + (state: RootStateType, path: MessagePortRecord["path"]): MessagePortRecord["path"] => path + ], + (ports, path): MessagePortRecord | undefined => { + return ports.find(p => p.path === path); + } +); + +export const getInstanceMessageOutports = createSelector( + [ + getMessageOutports, + (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex + ], + (ports, instanceIndex): ImmuMap => { + return ports.filter(p => { + return p.instanceIndex === instanceIndex; + }); + } +); + +export const getInstanceMessageOutportByTag = createSelector( + [ + getMessageOutports, + (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex, + (state: RootStateType, instanceIndex: InstanceStateRecord["index"], tag: MessagePortRecord["tag"]): MessagePortRecord["tag"] => tag + ], + (ports, instanceIndex, tag): MessagePortRecord | undefined => { + return ports.find(p => p.instanceIndex === instanceIndex && p.tag === tag); + } +); From 7a85b650da469bda81831e089a96db6a6c15bc5d Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Mon, 16 Dec 2024 12:31:22 +0000 Subject: [PATCH 4/7] renamed InstanceStateRecord to PatcherInstanceRecord --- src/actions/graph.ts | 8 ++-- src/actions/instances.ts | 52 +++++++++++++------------- src/components/dataref/list.tsx | 4 +- src/components/instance/datarefTab.tsx | 4 +- src/components/instance/index.tsx | 4 +- src/components/instance/messageTab.tsx | 4 +- src/components/instance/paramTab.tsx | 4 +- src/components/keyroll/modal.tsx | 4 +- src/models/instance.ts | 18 ++++----- src/reducers/instances.ts | 6 +-- src/selectors/instances.ts | 34 ++++++++--------- 11 files changed, 71 insertions(+), 71 deletions(-) diff --git a/src/actions/graph.ts b/src/actions/graph.ts index 7b3c9515..51e12ecc 100644 --- a/src/actions/graph.ts +++ b/src/actions/graph.ts @@ -7,7 +7,7 @@ import { ConnectionType, GraphConnectionRecord, GraphControlNodeRecord, GraphNod import { getConnectionsForSourceNodeAndPort, getNode, getPatcherNodeByIndex, getNodes, getSystemNodeByJackNameAndDirection, getConnections, getPatcherNodes, getSystemNodes, getControlNodes } from "../selectors/graph"; import { showNotification } from "./notifications"; import { NotificationLevel } from "../models/notification"; -import { InstanceStateRecord } from "../models/instance"; +import { PatcherInstanceRecord } from "../models/instance"; import { deleteInstance, setInstance, setInstanceMessageInports, setInstanceMessageOutports, setInstanceParameters, setInstances } from "./instances"; import { getInstance } from "../selectors/instances"; import { PatcherRecord } from "../models/patcher"; @@ -344,7 +344,7 @@ export const initNodes = (jackPortsInfo: OSCQueryRNBOJackPortInfo, instanceInfo: const state = getState(); const existingNodes = getNodes(state); - const instances: InstanceStateRecord[] = []; + const instances: PatcherInstanceRecord[] = []; const instanceParameters: ParameterRecord[] = []; const instanceMessageInports: MessagePortRecord[] = []; const instanceMessageOutports: MessagePortRecord[] = []; @@ -361,7 +361,7 @@ export const initNodes = (jackPortsInfo: OSCQueryRNBOJackPortInfo, instanceInfo: node = node.updatePosition(x, y); patcherAndControlNodes.push(node); - const instance = InstanceStateRecord.fromDescription(info); + const instance = PatcherInstanceRecord.fromDescription(info); instances.push(instance); instanceParameters.push(...ParameterRecord.fromDescription(instance.index, info.CONTENTS.params)); instanceMessageInports.push(...MessagePortRecord.fromDescription(info.CONTENTS.messages?.CONTENTS?.in)); @@ -695,7 +695,7 @@ export const addPatcherNode = (desc: OSCQueryRNBOInstance, metaString: string): dispatch(setNode(node)); // Create Instance State - const instance = InstanceStateRecord.fromDescription(desc); + const instance = PatcherInstanceRecord.fromDescription(desc); const parameters = ParameterRecord.fromDescription(instance.index, desc.CONTENTS.params); const messageInports = MessagePortRecord.fromDescription(desc.CONTENTS.messages?.CONTENTS?.in); const messageOutports = MessagePortRecord.fromDescription(desc.CONTENTS.messages?.CONTENTS?.out); diff --git a/src/actions/instances.ts b/src/actions/instances.ts index e44cfe9e..91978c7f 100644 --- a/src/actions/instances.ts +++ b/src/actions/instances.ts @@ -1,7 +1,7 @@ import Router from "next/router"; import { ActionBase, AppThunk } from "../lib/store"; import { OSCQueryRNBOInstance, OSCQueryRNBOInstancePresetEntries, OSCValue } from "../lib/types"; -import { InstanceStateRecord } from "../models/instance"; +import { PatcherInstanceRecord } from "../models/instance"; import { getInstanceByIndex, getInstance, getParameter, getInstanceParameters, getInstanceParameterByName, getParameterByPath, getInstanceMessageInports, getInstanceMessageOutports, getInstanceMessageOutportByTag, getInstanceMessageInportByTag, getMessageInportByPath, getMessageOutportByPath } from "../selectors/instances"; import { getAppSetting } from "../selectors/settings"; import { ParameterRecord } from "../models/parameter"; @@ -41,28 +41,28 @@ export enum InstanceActionType { export interface ISetInstance extends ActionBase { type: InstanceActionType.SET_INSTANCE; payload: { - instance: InstanceStateRecord; + instance: PatcherInstanceRecord; }; } export interface ISetInstances extends ActionBase { type: InstanceActionType.SET_INSTANCES; payload: { - instances: InstanceStateRecord[]; + instances: PatcherInstanceRecord[]; }; } export interface IDeleteInstance extends ActionBase { type: InstanceActionType.DELETE_INSTANCE; payload: { - instance: InstanceStateRecord; + instance: PatcherInstanceRecord; }; } export interface IDeleteInstances extends ActionBase { type: InstanceActionType.DELETE_INSTANCES; payload: { - instances: InstanceStateRecord[]; + instances: PatcherInstanceRecord[]; }; } @@ -155,28 +155,28 @@ ISetInstanceParameter | ISetInstanceParameters | IDeleteInstanceParameter | IDel ISetInstanceMessageInport | ISetInstanceMessageInports | IDeleteInstanceMessageInport | IDeleteInstanceMessageInports | ISetInstanceMessageOutport | ISetInstanceMessageOutports | IDeleteInstanceMessageOutport | IDeleteInstanceMessageOutports -export const setInstance = (instance: InstanceStateRecord): ISetInstance => ({ +export const setInstance = (instance: PatcherInstanceRecord): ISetInstance => ({ type: InstanceActionType.SET_INSTANCE, payload: { instance } }); -export const setInstances = (instances: InstanceStateRecord[]): ISetInstances => ({ +export const setInstances = (instances: PatcherInstanceRecord[]): ISetInstances => ({ type: InstanceActionType.SET_INSTANCES, payload: { instances } }); -export const deleteInstance = (instance: InstanceStateRecord): IDeleteInstance => ({ +export const deleteInstance = (instance: PatcherInstanceRecord): IDeleteInstance => ({ type: InstanceActionType.DELETE_INSTANCE, payload: { instance } }); -export const deleteInstances = (instances: InstanceStateRecord[]): IDeleteInstances => ({ +export const deleteInstances = (instances: PatcherInstanceRecord[]): IDeleteInstances => ({ type: InstanceActionType.DELETE_INSTANCES, payload: { instances @@ -268,7 +268,7 @@ export const deleteInstanceMessageOutports = (ports: MessagePortRecord[]): IDele }); // Trigger Events on Remote OSCQuery Runner -export const loadPresetOnRemoteInstance = (instance: InstanceStateRecord, preset: PresetRecord): AppThunk => +export const loadPresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord): AppThunk => (dispatch) => { try { const message = { @@ -288,7 +288,7 @@ export const loadPresetOnRemoteInstance = (instance: InstanceStateRecord, preset } }; -export const savePresetToRemoteInstance = (instance: InstanceStateRecord, name: string): AppThunk => +export const savePresetToRemoteInstance = (instance: PatcherInstanceRecord, name: string): AppThunk => (dispatch) => { try { const message = { @@ -308,7 +308,7 @@ export const savePresetToRemoteInstance = (instance: InstanceStateRecord, name: } }; -export const destroyPresetOnRemoteInstance = (instance: InstanceStateRecord, preset: PresetRecord): AppThunk => +export const destroyPresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord): AppThunk => (dispatch) => { try { const message = { @@ -328,7 +328,7 @@ export const destroyPresetOnRemoteInstance = (instance: InstanceStateRecord, pre } }; -export const renamePresetOnRemoteInstance = (instance: InstanceStateRecord, preset: PresetRecord, name: string): AppThunk => +export const renamePresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord, name: string): AppThunk => (dispatch) => { try { const message = { @@ -349,7 +349,7 @@ export const renamePresetOnRemoteInstance = (instance: InstanceStateRecord, pres } }; -export const setInitialPresetOnRemoteInstance = (instance: InstanceStateRecord, preset: PresetRecord): AppThunk => +export const setInitialPresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord): AppThunk => (dispatch) => { try { const message = { @@ -369,7 +369,7 @@ export const setInitialPresetOnRemoteInstance = (instance: InstanceStateRecord, } }; -export const sendInstanceMessageToRemote = (instance: InstanceStateRecord, inportId: string, value: string): AppThunk => +export const sendInstanceMessageToRemote = (instance: PatcherInstanceRecord, inportId: string, value: string): AppThunk => (dispatch) => { const values = value.split(" ").reduce((values, v) => { const fv = parseFloat(v.replaceAll(",", ".").trim()); @@ -394,7 +394,7 @@ export const sendInstanceMessageToRemote = (instance: InstanceStateRecord, inpor oscQueryBridge.sendPacket(writePacket(message)); }; -export const triggerInstanceMidiNoteOnEventOnRemote = (instance: InstanceStateRecord, note: number): AppThunk => +export const triggerInstanceMidiNoteOnEventOnRemote = (instance: PatcherInstanceRecord, note: number): AppThunk => () => { const midiChannel = 0; @@ -409,7 +409,7 @@ export const triggerInstanceMidiNoteOnEventOnRemote = (instance: InstanceStateRe oscQueryBridge.sendPacket(writePacket(message)); }; -export const triggerInstanceMidiNoteOffEventOnRemote = (instance: InstanceStateRecord, note: number): AppThunk => +export const triggerInstanceMidiNoteOffEventOnRemote = (instance: PatcherInstanceRecord, note: number): AppThunk => () => { const midiChannel = 0; @@ -424,7 +424,7 @@ export const triggerInstanceMidiNoteOffEventOnRemote = (instance: InstanceStateR oscQueryBridge.sendPacket(writePacket(message)); }; -export const setInstanceParameterValueNormalizedOnRemote = throttle((instance: InstanceStateRecord, param: ParameterRecord, value: number): AppThunk => +export const setInstanceParameterValueNormalizedOnRemote = throttle((instance: PatcherInstanceRecord, param: ParameterRecord, value: number): AppThunk => (dispatch) => { const message = { @@ -439,7 +439,7 @@ export const setInstanceParameterValueNormalizedOnRemote = throttle((instance: I dispatch(setInstanceParameter(param.setNormalizedValue(value))); }, 10); -export const setInstanceDataRefValueOnRemote = (instance: InstanceStateRecord, dataref: DataRefRecord, file?: DataFileRecord): AppThunk => +export const setInstanceDataRefValueOnRemote = (instance: PatcherInstanceRecord, dataref: DataRefRecord, file?: DataFileRecord): AppThunk => () => { const message = { @@ -452,7 +452,7 @@ export const setInstanceDataRefValueOnRemote = (instance: InstanceStateRecord, d oscQueryBridge.sendPacket(writePacket(message)); }; -export const setInstanceParameterMetaOnRemote = (_instance: InstanceStateRecord, param: ParameterRecord, value: string): AppThunk => +export const setInstanceParameterMetaOnRemote = (_instance: PatcherInstanceRecord, param: ParameterRecord, value: string): AppThunk => () => { const message = { address: `${param.path}/meta`, @@ -464,7 +464,7 @@ export const setInstanceParameterMetaOnRemote = (_instance: InstanceStateRecord, oscQueryBridge.sendPacket(writePacket(message)); }; -export const restoreDefaultParameterMetaOnRemote = (_instance: InstanceStateRecord, param: ParameterRecord): AppThunk => +export const restoreDefaultParameterMetaOnRemote = (_instance: PatcherInstanceRecord, param: ParameterRecord): AppThunk => () => { const message = { address: `${param.path}/meta`, @@ -476,7 +476,7 @@ export const restoreDefaultParameterMetaOnRemote = (_instance: InstanceStateReco oscQueryBridge.sendPacket(writePacket(message)); }; -export const activateParameterMIDIMappingFocus = (instance: InstanceStateRecord, param: ParameterRecord): AppThunk => +export const activateParameterMIDIMappingFocus = (instance: PatcherInstanceRecord, param: ParameterRecord): AppThunk => (dispatch, getState) => { const state = getState(); @@ -487,7 +487,7 @@ export const activateParameterMIDIMappingFocus = (instance: InstanceStateRecord, )); }; -export const clearParameterMidiMappingOnRemote = (id: InstanceStateRecord["id"], paramId: ParameterRecord["id"]): AppThunk => +export const clearParameterMidiMappingOnRemote = (id: PatcherInstanceRecord["id"], paramId: ParameterRecord["id"]): AppThunk => (_dispatch, getState) => { const state = getState(); const instance = getInstance(state, id); @@ -508,7 +508,7 @@ export const clearParameterMidiMappingOnRemote = (id: InstanceStateRecord["id"], oscQueryBridge.sendPacket(writePacket(message)); }; -export const setInstanceMessagePortMetaOnRemote = (_instance: InstanceStateRecord, port: MessagePortRecord, value: string): AppThunk => +export const setInstanceMessagePortMetaOnRemote = (_instance: PatcherInstanceRecord, port: MessagePortRecord, value: string): AppThunk => () => { const message = { address: `${port.path}/meta`, @@ -520,7 +520,7 @@ export const setInstanceMessagePortMetaOnRemote = (_instance: InstanceStateRecor oscQueryBridge.sendPacket(writePacket(message)); }; -export const restoreDefaultMessagePortMetaOnRemote = (_instance: InstanceStateRecord, port: MessagePortRecord): AppThunk => +export const restoreDefaultMessagePortMetaOnRemote = (_instance: PatcherInstanceRecord, port: MessagePortRecord): AppThunk => () => { const message = { address: `${port.path}/meta`, @@ -722,7 +722,7 @@ export const updateInstanceParameterValueNormalized = (index: number, name: Para } }; -export const setInstanceWaitingForMidiMappingOnRemote = (id: InstanceStateRecord["id"], value: boolean): AppThunk => +export const setInstanceWaitingForMidiMappingOnRemote = (id: PatcherInstanceRecord["id"], value: boolean): AppThunk => (dispatch, getState) => { try { const state = getState(); diff --git a/src/components/dataref/list.tsx b/src/components/dataref/list.tsx index 5d77e440..a2f9a439 100644 --- a/src/components/dataref/list.tsx +++ b/src/components/dataref/list.tsx @@ -1,6 +1,6 @@ import { FunctionComponent, memo } from "react"; import DataRefEntry from "./item"; -import { InstanceStateRecord } from "../../models/instance"; +import { PatcherInstanceRecord } from "../../models/instance"; import { DataRefRecord } from "../../models/dataref"; import { Seq } from "immutable"; import { Table } from "@mantine/core"; @@ -10,7 +10,7 @@ import { DataFileRecord } from "../../models/datafile"; export type DataRefListProps = { onClearDataRef: (dataref: DataRefRecord) => any; onSetDataRef: (dataref: DataRefRecord, file: DataFileRecord) => any; - datarefs: InstanceStateRecord["datarefs"]; + datarefs: PatcherInstanceRecord["datarefs"]; options: Seq.Indexed; // soundfile list } diff --git a/src/components/instance/datarefTab.tsx b/src/components/instance/datarefTab.tsx index cde8fe64..9e2829c4 100644 --- a/src/components/instance/datarefTab.tsx +++ b/src/components/instance/datarefTab.tsx @@ -3,7 +3,7 @@ import { FunctionComponent, memo, useCallback } from "react"; import { useAppDispatch } from "../../hooks/useAppDispatch"; import DataRefList from "../dataref/list"; import classes from "./instance.module.css"; -import { InstanceStateRecord } from "../../models/instance"; +import { PatcherInstanceRecord } from "../../models/instance"; import { setInstanceDataRefValueOnRemote } from "../../actions/instances"; import { DataRefRecord } from "../../models/dataref"; import { modals } from "@mantine/modals"; @@ -11,7 +11,7 @@ import { DataFileRecord } from "../../models/datafile"; import { Seq } from "immutable"; export type InstanceDataRefTabProps = { - instance: InstanceStateRecord; + instance: PatcherInstanceRecord; datafiles: Seq.Indexed; } diff --git a/src/components/instance/index.tsx b/src/components/instance/index.tsx index b5f43755..720a8913 100644 --- a/src/components/instance/index.tsx +++ b/src/components/instance/index.tsx @@ -5,7 +5,7 @@ import { InstanceTab } from "../../lib/constants"; import InstanceParameterTab from "./paramTab"; import InstanceMessagesTab from "./messageTab"; import InstanceDataRefsTab from "./datarefTab"; -import { InstanceStateRecord } from "../../models/instance"; +import { PatcherInstanceRecord } from "../../models/instance"; import { AppSettingRecord } from "../../models/settings"; import { DataFileRecord } from "../../models/datafile"; import { Map as ImmuMap, Seq } from "immutable"; @@ -21,7 +21,7 @@ const tabs = [ ]; export type InstanceProps = { - instance: InstanceStateRecord; + instance: PatcherInstanceRecord; datafiles: Seq.Indexed enabledMessageOuput: AppSettingRecord; messageInports: ImmuMap; diff --git a/src/components/instance/messageTab.tsx b/src/components/instance/messageTab.tsx index b6a1040b..a386f29a 100644 --- a/src/components/instance/messageTab.tsx +++ b/src/components/instance/messageTab.tsx @@ -6,13 +6,13 @@ import MessageInportList from "../messages/inportList"; import { SectionTitle } from "../page/sectionTitle"; import MessageOutportList from "../messages/outportList"; import classes from "./instance.module.css"; -import { InstanceStateRecord } from "../../models/instance"; +import { PatcherInstanceRecord } from "../../models/instance"; import { sendInstanceMessageToRemote } from "../../actions/instances"; import { MessagePortRecord } from "../../models/messageport"; import { restoreDefaultMessagePortMetaOnRemote, setInstanceMessagePortMetaOnRemote } from "../../actions/instances"; export type InstanceMessageTabProps = { - instance: InstanceStateRecord; + instance: PatcherInstanceRecord; messageInports: ImmuMap; messageOutports: ImmuMap; outputEnabled: boolean; diff --git a/src/components/instance/paramTab.tsx b/src/components/instance/paramTab.tsx index ff1eaaca..a4f7d355 100644 --- a/src/components/instance/paramTab.tsx +++ b/src/components/instance/paramTab.tsx @@ -5,7 +5,7 @@ import ParameterList from "../parameter/list"; import { ParameterRecord } from "../../models/parameter"; import classes from "./instance.module.css"; import { useAppDispatch } from "../../hooks/useAppDispatch"; -import { InstanceStateRecord } from "../../models/instance"; +import { PatcherInstanceRecord } from "../../models/instance"; import { restoreDefaultParameterMetaOnRemote, setInstanceParameterMetaOnRemote, setInstanceParameterValueNormalizedOnRemote, @@ -111,7 +111,7 @@ const getSortedParameterIds = (params: ImmuMap; sortAttr: AppSettingRecord; sortOrder: AppSettingRecord; diff --git a/src/components/keyroll/modal.tsx b/src/components/keyroll/modal.tsx index cbb74d78..5b60bdc6 100644 --- a/src/components/keyroll/modal.tsx +++ b/src/components/keyroll/modal.tsx @@ -1,7 +1,7 @@ import { FunctionComponent, memo, useCallback, useEffect } from "react"; import { useAppDispatch } from "../../hooks/useAppDispatch"; import KeyRoll from "../keyroll"; -import { InstanceStateRecord } from "../../models/instance"; +import { PatcherInstanceRecord } from "../../models/instance"; import { triggerInstanceMidiNoteOffEventOnRemote, triggerInstanceMidiNoteOnEventOnRemote } from "../../actions/instances"; import { Group, Modal } from "@mantine/core"; import { useIsMobileDevice } from "../../hooks/useIsMobileDevice"; @@ -9,7 +9,7 @@ import { IconElement } from "../elements/icon"; import { mdiPiano } from "@mdi/js"; export type InstanceKeyboardModalProps = { - instance: InstanceStateRecord; + instance: PatcherInstanceRecord; keyboardEnabled: boolean; open: boolean; onClose: () => any; diff --git a/src/models/instance.ts b/src/models/instance.ts index 3baabd9a..48075b60 100644 --- a/src/models/instance.ts +++ b/src/models/instance.ts @@ -30,7 +30,7 @@ function sortPresets(left: PresetRecord, right: PresetRecord) : number { return collator.compare(left.name, right.name); } -export class InstanceStateRecord extends ImmuRecord({ +export class PatcherInstanceRecord extends ImmuRecord({ index: 0, name: "", @@ -49,11 +49,11 @@ export class InstanceStateRecord extends ImmuRecord({ return this.name; } - public setWaitingForMapping(value: boolean): InstanceStateRecord { + public setWaitingForMapping(value: boolean): PatcherInstanceRecord { return this.set("waitingForMidiMapping", value); } - public setDataRefValue(id: string, fileId: string): InstanceStateRecord { + public setDataRefValue(id: string, fileId: string): PatcherInstanceRecord { const dataref = this.datarefs.get(id); if (!dataref) return this; @@ -69,16 +69,16 @@ export class InstanceStateRecord extends ImmuRecord({ }).sort(sortPresets); } - public setPresetLatest(latest: string): InstanceStateRecord { + public setPresetLatest(latest: string): PatcherInstanceRecord { return this.set("presets", this.presets.map(preset => preset.setLatest(preset.name === latest))).set("presetLatest", latest); } - public setPresetInitial(initial: string): InstanceStateRecord { + public setPresetInitial(initial: string): PatcherInstanceRecord { return this.set("presetInitial", initial).set("presets", this.presets.map(preset => preset.setInitial(preset.name === initial)).sort(sortPresets)); } - public updatePresets(entries: OSCQueryRNBOInstancePresetEntries): InstanceStateRecord { - return this.set("presets", InstanceStateRecord.presetsFromDescription(entries, this.presetLatest, this.presetInitial)); + public updatePresets(entries: OSCQueryRNBOInstancePresetEntries): PatcherInstanceRecord { + return this.set("presets", PatcherInstanceRecord.presetsFromDescription(entries, this.presetLatest, this.presetInitial)); } public static datarefsFromDescription(datarefsDesc: OSCQueryRNBOInstance["CONTENTS"]["data_refs"]): ImmuMap { @@ -94,12 +94,12 @@ export class InstanceStateRecord extends ImmuRecord({ return desc.CONTENTS.name.VALUE as string; } - public static fromDescription(desc: OSCQueryRNBOInstance): InstanceStateRecord { + public static fromDescription(desc: OSCQueryRNBOInstance): PatcherInstanceRecord { const initialPreset: string = desc.CONTENTS.presets.CONTENTS?.initial?.VALUE || ""; const latestPreset: string = desc.CONTENTS.presets.CONTENTS?.loaded?.VALUE || ""; - return new InstanceStateRecord({ + return new PatcherInstanceRecord({ index: parseInt(desc.FULL_PATH.split("/").pop(), 10), name: this.getJackName(desc.CONTENTS.jack), patcher: desc.CONTENTS.name.VALUE, diff --git a/src/reducers/instances.ts b/src/reducers/instances.ts index 4f2c4c22..fcb68b28 100644 --- a/src/reducers/instances.ts +++ b/src/reducers/instances.ts @@ -1,11 +1,11 @@ import { Map as ImmuMap } from "immutable"; -import { InstanceStateRecord } from "../models/instance"; +import { PatcherInstanceRecord } from "../models/instance"; import { InstanceAction, InstanceActionType } from "../actions/instances"; import { ParameterRecord } from "../models/parameter"; import { MessagePortRecord } from "../models/messageport"; export interface InstanceInstancesState { - instances: ImmuMap; + instances: ImmuMap; messageInports: ImmuMap; messageOutports: ImmuMap; parameters: ImmuMap; @@ -13,7 +13,7 @@ export interface InstanceInstancesState { export const instances = (state: InstanceInstancesState = { - instances: ImmuMap(), + instances: ImmuMap(), messageInports: ImmuMap(), messageOutports: ImmuMap(), parameters: ImmuMap() diff --git a/src/selectors/instances.ts b/src/selectors/instances.ts index 5e6b9a90..3286e552 100644 --- a/src/selectors/instances.ts +++ b/src/selectors/instances.ts @@ -1,27 +1,27 @@ import { Map as ImmuMap } from "immutable"; import { RootStateType } from "../lib/store"; -import { InstanceStateRecord } from "../models/instance"; +import { PatcherInstanceRecord } from "../models/instance"; import { createSelector } from "reselect"; import { getPatcherIdsByIndex } from "./graph"; import { ParameterRecord } from "../models/parameter"; import { MessagePortRecord } from "../models/messageport"; -export const getInstances = (state: RootStateType): ImmuMap => state.instances.instances; +export const getInstances = (state: RootStateType): ImmuMap => state.instances.instances; export const getInstance = createSelector( [ getInstances, - (state: RootStateType, id: InstanceStateRecord["id"]): InstanceStateRecord["id"] => id + (state: RootStateType, id: PatcherInstanceRecord["id"]): PatcherInstanceRecord["id"] => id ], - (instances, id): InstanceStateRecord | undefined => instances.get(id) + (instances, id): PatcherInstanceRecord | undefined => instances.get(id) ); export const getInstanceByIndex = createSelector( [ getInstances, - (state: RootStateType, index: InstanceStateRecord["index"]): InstanceStateRecord["id"] | undefined => state.graph.patcherNodeIdByIndex.get(index) + (state: RootStateType, index: PatcherInstanceRecord["index"]): PatcherInstanceRecord["id"] | undefined => state.graph.patcherNodeIdByIndex.get(index) ], - (instances, id): InstanceStateRecord | undefined => id ? instances.get(id) : undefined + (instances, id): PatcherInstanceRecord | undefined => id ? instances.get(id) : undefined ); export const getInstancesByIndex = createSelector( @@ -29,8 +29,8 @@ export const getInstancesByIndex = createSelector( getInstances, getPatcherIdsByIndex ], - (instances, idsByIndex): ImmuMap => { - return ImmuMap().withMutations(map => { + (instances, idsByIndex): ImmuMap => { + return ImmuMap().withMutations(map => { idsByIndex.forEach((id, index) => { const node = instances.get(id); if (node) map.set(index, node); @@ -64,7 +64,7 @@ export const getParameterByPath = createSelector( export const getInstanceParameters = createSelector( [ getParameters, - (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex ], (parameters, instanceIndex): ImmuMap => { return parameters.filter(p => { @@ -77,8 +77,8 @@ export const getInstanceParameters = createSelector( export const getInstanceParameterByName = createSelector( [ getParameters, - (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex, - (state: RootStateType, instanceIndex: InstanceStateRecord["index"], name: ParameterRecord["name"]): ParameterRecord["name"] => name + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"], name: ParameterRecord["name"]): ParameterRecord["name"] => name ], (parameters, instanceIndex, name): ParameterRecord | undefined => { return parameters.find(p => p.instanceIndex === instanceIndex && p.name === name); @@ -110,7 +110,7 @@ export const getMessageInportByPath = createSelector( export const getInstanceMessageInports = createSelector( [ getMessageInports, - (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex ], (ports, instanceIndex): ImmuMap => { return ports.filter(p => { @@ -122,8 +122,8 @@ export const getInstanceMessageInports = createSelector( export const getInstanceMessageInportByTag = createSelector( [ getMessageInports, - (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex, - (state: RootStateType, instanceIndex: InstanceStateRecord["index"], tag: MessagePortRecord["tag"]): MessagePortRecord["tag"] => tag + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"], tag: MessagePortRecord["tag"]): MessagePortRecord["tag"] => tag ], (ports, instanceIndex, tag): MessagePortRecord | undefined => { return ports.find(p => p.instanceIndex === instanceIndex && p.tag === tag); @@ -155,7 +155,7 @@ export const getMessageOutportByPath = createSelector( export const getInstanceMessageOutports = createSelector( [ getMessageOutports, - (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex ], (ports, instanceIndex): ImmuMap => { return ports.filter(p => { @@ -167,8 +167,8 @@ export const getInstanceMessageOutports = createSelector( export const getInstanceMessageOutportByTag = createSelector( [ getMessageOutports, - (state: RootStateType, instanceIndex: InstanceStateRecord["index"]): InstanceStateRecord["index"] => instanceIndex, - (state: RootStateType, instanceIndex: InstanceStateRecord["index"], tag: MessagePortRecord["tag"]): MessagePortRecord["tag"] => tag + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"], tag: MessagePortRecord["tag"]): MessagePortRecord["tag"] => tag ], (ports, instanceIndex, tag): MessagePortRecord | undefined => { return ports.find(p => p.instanceIndex === instanceIndex && p.tag === tag); From 436f92b9981fb390b4fbfb85d99974d163fa75cd Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Mon, 16 Dec 2024 12:42:44 +0000 Subject: [PATCH 5/7] consolidated instance and patcher state --- src/actions/graph.ts | 2 +- src/actions/instances.ts | 73 +++++++++++++++++++-- src/actions/patchers.ts | 75 ---------------------- src/controller/oscqueryBridgeController.ts | 2 +- src/models/instance.ts | 4 +- src/pages/index.tsx | 4 +- src/reducers/index.ts | 2 - src/reducers/instances.ts | 14 +++- src/reducers/patchers.ts | 29 --------- src/selectors/instances.ts | 32 ++++++++- 10 files changed, 119 insertions(+), 118 deletions(-) delete mode 100644 src/actions/patchers.ts delete mode 100644 src/reducers/patchers.ts diff --git a/src/actions/graph.ts b/src/actions/graph.ts index 51e12ecc..7fc10b35 100644 --- a/src/actions/graph.ts +++ b/src/actions/graph.ts @@ -11,7 +11,7 @@ import { PatcherInstanceRecord } from "../models/instance"; import { deleteInstance, setInstance, setInstanceMessageInports, setInstanceMessageOutports, setInstanceParameters, setInstances } from "./instances"; import { getInstance } from "../selectors/instances"; import { PatcherRecord } from "../models/patcher"; -import { getPatchers } from "../selectors/patchers"; +import { getPatchers } from "../selectors/instances"; import { defaultNodeGap, nodeDefaultWidth, nodeHeaderHeight } from "../lib/constants"; import { getGraphEditorInstance } from "../selectors/editor"; import { ParameterRecord } from "../models/parameter"; diff --git a/src/actions/instances.ts b/src/actions/instances.ts index 91978c7f..68fd76e8 100644 --- a/src/actions/instances.ts +++ b/src/actions/instances.ts @@ -1,12 +1,12 @@ import Router from "next/router"; import { ActionBase, AppThunk } from "../lib/store"; -import { OSCQueryRNBOInstance, OSCQueryRNBOInstancePresetEntries, OSCValue } from "../lib/types"; +import { OSCQueryRNBOInstance, OSCQueryRNBOInstancePresetEntries, OSCQueryRNBOPatchersState, OSCValue } from "../lib/types"; import { PatcherInstanceRecord } from "../models/instance"; import { getInstanceByIndex, getInstance, getParameter, getInstanceParameters, getInstanceParameterByName, getParameterByPath, getInstanceMessageInports, getInstanceMessageOutports, getInstanceMessageOutportByTag, getInstanceMessageInportByTag, getMessageInportByPath, getMessageOutportByPath } from "../selectors/instances"; import { getAppSetting } from "../selectors/settings"; import { ParameterRecord } from "../models/parameter"; import { MessagePortRecord } from "../models/messageport"; -import { OSCArgument, writePacket } from "osc"; +import { OSCArgument, OSCMessage, writePacket } from "osc"; import { showNotification } from "./notifications"; import { NotificationLevel } from "../models/notification"; import { oscQueryBridge } from "../controller/oscqueryBridgeController"; @@ -15,8 +15,11 @@ import { PresetRecord } from "../models/preset"; import { AppSetting } from "../models/settings"; import { DataRefRecord } from "../models/dataref"; import { DataFileRecord } from "../models/datafile"; +import { PatcherRecord } from "../models/patcher"; export enum InstanceActionType { + INIT_PATCHERS = "INIT_PATCHERS", + SET_INSTANCE = "SET_INSTANCE", SET_INSTANCES = "SET_INSTANCES", DELETE_INSTANCE = "DELETE_INSTANCE", @@ -38,6 +41,13 @@ export enum InstanceActionType { DELETE_MESSAGE_OUTPORTS = "DELETE_MESSAGE_OUTPORTS" } +export interface IInitPatchers extends ActionBase { + type: InstanceActionType.INIT_PATCHERS; + payload: { + patchers: PatcherRecord[]; + }; +} + export interface ISetInstance extends ActionBase { type: InstanceActionType.SET_INSTANCE; payload: { @@ -150,10 +160,65 @@ export interface IDeleteInstanceMessageOutports extends ActionBase { }; } -export type InstanceAction = ISetInstance | ISetInstances | IDeleteInstance | IDeleteInstances | +export type InstanceAction = IInitPatchers | ISetInstance | ISetInstances | IDeleteInstance | IDeleteInstances | ISetInstanceParameter | ISetInstanceParameters | IDeleteInstanceParameter | IDeleteInstanceParameters | ISetInstanceMessageInport | ISetInstanceMessageInports | IDeleteInstanceMessageInport | IDeleteInstanceMessageInports | -ISetInstanceMessageOutport | ISetInstanceMessageOutports | IDeleteInstanceMessageOutport | IDeleteInstanceMessageOutports +ISetInstanceMessageOutport | ISetInstanceMessageOutports | IDeleteInstanceMessageOutport | IDeleteInstanceMessageOutports; + +export const initPatchers = (patchersInfo: OSCQueryRNBOPatchersState): IInitPatchers => { + + const patchers: PatcherRecord[] = []; + for (const [name, desc] of Object.entries(patchersInfo.CONTENTS || {})) { + patchers.push(PatcherRecord.fromDescription(name, desc)); + } + + return { + type: InstanceActionType.INIT_PATCHERS, + payload: { + patchers + } + }; +}; + +export const destroyPatcherOnRemote = (patcher: PatcherRecord): AppThunk => + (dispatch) => { + try { + const message: OSCMessage = { + address: `/rnbo/patchers/${patcher.name}/destroy`, + args: [] + }; + oscQueryBridge.sendPacket(writePacket(message)); + } catch (err) { + dispatch(showNotification({ + level: NotificationLevel.error, + title: `Error while trying to delete patcher ${patcher.name}`, + message: "Please check the console for further details." + })); + console.error(err); + } + }; + + +export const renamePatcherOnRemote = (patcher: PatcherRecord, newName: string): AppThunk => + (dispatch) => { + try { + const message = { + address: `/rnbo/patchers/${patcher.name}/rename`, + args: [ + { type: "s", value: newName } + ] + }; + oscQueryBridge.sendPacket(writePacket(message)); + } catch (err) { + dispatch(showNotification({ + level: NotificationLevel.error, + title: `Error while trying to rename patcher ${patcher.name} -> ${newName}`, + message: "Please check the console for further details." + })); + console.error(err); + } + }; + export const setInstance = (instance: PatcherInstanceRecord): ISetInstance => ({ type: InstanceActionType.SET_INSTANCE, diff --git a/src/actions/patchers.ts b/src/actions/patchers.ts deleted file mode 100644 index 6990aa22..00000000 --- a/src/actions/patchers.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { ActionBase, AppThunk } from "../lib/store"; -import { OSCQueryRNBOPatchersState } from "../lib/types"; -import { PatcherRecord } from "../models/patcher"; -import { oscQueryBridge } from "../controller/oscqueryBridgeController"; -import { writePacket, OSCMessage } from "osc"; -import { showNotification } from "./notifications"; -import { NotificationLevel } from "../models/notification"; - -export enum PatcherActionType { - INIT = "INIT_PATCHERS" -} - -export interface IInitPatchers extends ActionBase { - type: PatcherActionType.INIT; - payload: { - patchers: PatcherRecord[] - } -} - -export type PatcherAction = IInitPatchers; - - -export const initPatchers = (patchersInfo: OSCQueryRNBOPatchersState): PatcherAction => { - - const patchers: PatcherRecord[] = []; - for (const [name, desc] of Object.entries(patchersInfo.CONTENTS || {})) { - patchers.push(PatcherRecord.fromDescription(name, desc)); - } - - return { - type: PatcherActionType.INIT, - payload: { - patchers - } - }; -}; - -export const destroyPatcherOnRemote = (patcher: PatcherRecord): AppThunk => - (dispatch) => { - try { - const message: OSCMessage = { - address: `/rnbo/patchers/${patcher.name}/destroy`, - args: [] - }; - oscQueryBridge.sendPacket(writePacket(message)); - } catch (err) { - dispatch(showNotification({ - level: NotificationLevel.error, - title: `Error while trying to delete patcher ${patcher.name}`, - message: "Please check the console for further details." - })); - console.error(err); - } - }; - - -export const renamePatcherOnRemote = (patcher: PatcherRecord, newName: string): AppThunk => - (dispatch) => { - try { - const message = { - address: `/rnbo/patchers/${patcher.name}/rename`, - args: [ - { type: "s", value: newName } - ] - }; - oscQueryBridge.sendPacket(writePacket(message)); - } catch (err) { - dispatch(showNotification({ - level: NotificationLevel.error, - title: `Error while trying to rename patcher ${patcher.name} -> ${newName}`, - message: "Please check the console for further details." - })); - console.error(err); - } - }; diff --git a/src/controller/oscqueryBridgeController.ts b/src/controller/oscqueryBridgeController.ts index d89eae31..d2a70efa 100644 --- a/src/controller/oscqueryBridgeController.ts +++ b/src/controller/oscqueryBridgeController.ts @@ -6,7 +6,7 @@ import { ReconnectingWebsocket } from "../lib/reconnectingWs"; import { AppStatus, RunnerCmdMethod } from "../lib/constants"; import { OSCQueryRNBOState, OSCQueryRNBOInstance, OSCQueryRNBOJackConnections, OSCQueryRNBOPatchersState, OSCValue, OSCQueryRNBOInstancesMetaState, OSCQueryListValue } from "../lib/types"; import { addPatcherNode, deletePortAliases, initConnections, initNodes, removePatcherNode, setPortAliases, updateSetMetaFromRemote, updateSourcePortConnections, updateSystemOrControlPortInfo } from "../actions/graph"; -import { initPatchers } from "../actions/patchers"; +import { initPatchers } from "../actions/instances"; import { initRunnerConfig, updateRunnerConfig } from "../actions/settings"; import { initSets, setGraphSetLatest, initSetPresets, setGraphSetPresetLatest } from "../actions/sets"; import { initDataFiles } from "../actions/datafiles"; diff --git a/src/models/instance.ts b/src/models/instance.ts index 48075b60..af5fe77f 100644 --- a/src/models/instance.ts +++ b/src/models/instance.ts @@ -3,7 +3,7 @@ import { PresetRecord } from "./preset"; import { DataRefRecord } from "./dataref"; import { OSCQueryRNBOInstance, OSCQueryRNBOInstancePresetEntries } from "../lib/types"; -export type InstanceStateProps = { +export type PatcherInstanceProps = { index: number; patcher: string; path: string; @@ -30,7 +30,7 @@ function sortPresets(left: PresetRecord, right: PresetRecord) : number { return collator.compare(left.name, right.name); } -export class PatcherInstanceRecord extends ImmuRecord({ +export class PatcherInstanceRecord extends ImmuRecord({ index: 0, name: "", diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 81b5e1a2..037d2158 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -2,7 +2,7 @@ import { Button, Group, Stack, Text, Tooltip } from "@mantine/core"; import { FunctionComponent, useCallback, useEffect } from "react"; import { useAppDispatch, useAppSelector } from "../hooks/useAppDispatch"; import { RootStateType } from "../lib/store"; -import { getPatchersSortedByName } from "../selectors/patchers"; +import { getPatchersSortedByName } from "../selectors/instances"; import { getConnections, getNodes } from "../selectors/graph"; import GraphEditor from "../components/editor"; import PresetDrawer from "../components/presets"; @@ -19,7 +19,7 @@ import { } from "../actions/editor"; import SetsDrawer from "../components/sets"; import { destroySetPresetOnRemote, loadSetPresetOnRemote, saveSetPresetToRemote, renameSetPresetOnRemote, clearGraphSetOnRemote, destroyGraphSetOnRemote, loadGraphSetOnRemote, renameGraphSetOnRemote, saveGraphSetOnRemote } from "../actions/sets"; -import { destroyPatcherOnRemote, renamePatcherOnRemote } from "../actions/patchers"; +import { destroyPatcherOnRemote, renamePatcherOnRemote } from "../actions/instances"; import { PresetRecord } from "../models/preset"; import { getGraphSetPresetsSortedByName, getGraphSetsSortedByName } from "../selectors/sets"; import { useDisclosure } from "@mantine/hooks"; diff --git a/src/reducers/index.ts b/src/reducers/index.ts index 4328d1c3..5281ed1e 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -6,7 +6,6 @@ import { editor } from "./editor"; import { instances } from "./instances"; import { graph } from "./graph"; import { nofitications } from "./notifications"; -import { patchers } from "./patchers"; import { settings } from "./settings"; import { sets } from "./sets"; import { transport } from "./transport"; @@ -18,7 +17,6 @@ export const rootReducer = combineReducers({ instances, graph, nofitications, - patchers, settings, sets, transport diff --git a/src/reducers/instances.ts b/src/reducers/instances.ts index fcb68b28..77a1fe52 100644 --- a/src/reducers/instances.ts +++ b/src/reducers/instances.ts @@ -3,12 +3,14 @@ import { PatcherInstanceRecord } from "../models/instance"; import { InstanceAction, InstanceActionType } from "../actions/instances"; import { ParameterRecord } from "../models/parameter"; import { MessagePortRecord } from "../models/messageport"; +import { PatcherRecord } from "../models/patcher"; export interface InstanceInstancesState { instances: ImmuMap; messageInports: ImmuMap; messageOutports: ImmuMap; parameters: ImmuMap; + patchers: ImmuMap; } export const instances = (state: InstanceInstancesState = { @@ -16,12 +18,22 @@ export const instances = (state: InstanceInstancesState = { instances: ImmuMap(), messageInports: ImmuMap(), messageOutports: ImmuMap(), - parameters: ImmuMap() + parameters: ImmuMap(), + patchers: ImmuMap() }, action: InstanceAction): InstanceInstancesState => { switch(action.type) { + case InstanceActionType.INIT_PATCHERS: { + const { patchers } = action.payload; + + return { + ...state, + patchers: ImmuMap(patchers.map(p => [p.id, p])) + }; + } + case InstanceActionType.SET_INSTANCE: { const { instance } = action.payload; diff --git a/src/reducers/patchers.ts b/src/reducers/patchers.ts deleted file mode 100644 index d8bee179..00000000 --- a/src/reducers/patchers.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Map as ImmuMap } from "immutable"; -import { PatcherRecord } from "../models/patcher"; -import { PatcherAction, PatcherActionType } from "../actions/patchers"; - -export interface PatcherState { - patchers: ImmuMap; -} - -export const patchers = (state: PatcherState = { - - patchers: ImmuMap() - -}, action: PatcherAction): PatcherState => { - - switch (action.type) { - - case PatcherActionType.INIT: { - const { patchers } = action.payload; - - return { - ...state, - patchers: ImmuMap(patchers.map(p => [p.id, p])) - }; - } - - default: - return state; - } -}; diff --git a/src/selectors/instances.ts b/src/selectors/instances.ts index 3286e552..aa182792 100644 --- a/src/selectors/instances.ts +++ b/src/selectors/instances.ts @@ -1,10 +1,40 @@ -import { Map as ImmuMap } from "immutable"; +import { Map as ImmuMap, Seq } from "immutable"; import { RootStateType } from "../lib/store"; import { PatcherInstanceRecord } from "../models/instance"; import { createSelector } from "reselect"; import { getPatcherIdsByIndex } from "./graph"; import { ParameterRecord } from "../models/parameter"; import { MessagePortRecord } from "../models/messageport"; +import { PatcherRecord } from "../models/patcher"; +import { SortOrder } from "../lib/constants"; + +export const getPatchers = (state: RootStateType): ImmuMap => { + return state.instances.patchers; +}; + +export const getPatcher = createSelector( + [ + getPatchers, + (state: RootStateType, name: string): string => name + ], + (patchers, name): PatcherRecord | undefined => { + return patchers.get(name); + } +); + +const collator = new Intl.Collator("en-US"); +export const getPatchersSortedByName = createSelector( + [ + getPatchers, + (state: RootStateType, order: SortOrder): SortOrder => order + ], + (patchers, order): Seq.Indexed => { + return patchers.valueSeq().sort((pA, pB) => { + return collator.compare(pA.name.toLowerCase(), pB.name.toLowerCase()) * (order === SortOrder.Asc ? 1 : -1); + }); + } +); + export const getInstances = (state: RootStateType): ImmuMap => state.instances.instances; From 1920e517ca980eaffa92e58c472619732efbd649 Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Mon, 16 Dec 2024 13:47:40 +0000 Subject: [PATCH 6/7] pass PatcherInstance.index when creating message ports --- src/actions/graph.ts | 8 +- src/actions/patchers.ts | 919 ++++++++++++++++++++++++++++++++++++++ src/models/messageport.ts | 11 +- src/reducers/patchers.ts | 207 +++++++++ 4 files changed, 1136 insertions(+), 9 deletions(-) create mode 100644 src/actions/patchers.ts create mode 100644 src/reducers/patchers.ts diff --git a/src/actions/graph.ts b/src/actions/graph.ts index 7fc10b35..914a411f 100644 --- a/src/actions/graph.ts +++ b/src/actions/graph.ts @@ -364,8 +364,8 @@ export const initNodes = (jackPortsInfo: OSCQueryRNBOJackPortInfo, instanceInfo: const instance = PatcherInstanceRecord.fromDescription(info); instances.push(instance); instanceParameters.push(...ParameterRecord.fromDescription(instance.index, info.CONTENTS.params)); - instanceMessageInports.push(...MessagePortRecord.fromDescription(info.CONTENTS.messages?.CONTENTS?.in)); - instanceMessageOutports.push(...MessagePortRecord.fromDescription(info.CONTENTS.messages?.CONTENTS?.out)); + instanceMessageInports.push(...MessagePortRecord.fromDescription(instance.index, info.CONTENTS.messages?.CONTENTS?.in)); + instanceMessageOutports.push(...MessagePortRecord.fromDescription(instance.index, info.CONTENTS.messages?.CONTENTS?.out)); } // Build a list of all Jack generated names that have not been used for PatcherNodes above @@ -697,8 +697,8 @@ export const addPatcherNode = (desc: OSCQueryRNBOInstance, metaString: string): // Create Instance State const instance = PatcherInstanceRecord.fromDescription(desc); const parameters = ParameterRecord.fromDescription(instance.index, desc.CONTENTS.params); - const messageInports = MessagePortRecord.fromDescription(desc.CONTENTS.messages?.CONTENTS?.in); - const messageOutports = MessagePortRecord.fromDescription(desc.CONTENTS.messages?.CONTENTS?.out); + const messageInports = MessagePortRecord.fromDescription(instance.index, desc.CONTENTS.messages?.CONTENTS?.in); + const messageOutports = MessagePortRecord.fromDescription(instance.index, desc.CONTENTS.messages?.CONTENTS?.out); dispatch(setInstance(instance)); dispatch(setInstanceParameters(parameters)); diff --git a/src/actions/patchers.ts b/src/actions/patchers.ts new file mode 100644 index 00000000..a0f7c54f --- /dev/null +++ b/src/actions/patchers.ts @@ -0,0 +1,919 @@ +import Router from "next/router"; +import { ActionBase, AppThunk } from "../lib/store"; +import { OSCQueryRNBOInstance, OSCQueryRNBOInstancePresetEntries, OSCQueryRNBOPatchersState, OSCValue } from "../lib/types"; +import { PatcherInstanceRecord } from "../models/instance"; +import { getPatcherInstanceByIndex, getPatcherInstance, getPatcherInstanceParametersByInstanceIndex, getPatcherInstanceParameter, getPatcherInstanceMessageInportsByInstanceIndex, getPatcherInstanceMesssageOutportsByInstanceIndex, getPatcherInstanceMessageInportByPath, getPatcherInstanceMessageOutportByPath, getPatcherInstanceMesssageOutportsByInstanceIndexAndTag, getPatcherInstanceParameterByPath, getPatcherInstanceParametersByInstanceIndexAndName, getPatcherInstanceMessageInportsByInstanceIndexAndTag } from "../selectors/patchers"; +import { getAppSetting } from "../selectors/settings"; +import { ParameterRecord } from "../models/parameter"; +import { MessagePortRecord } from "../models/messageport"; +import { OSCArgument, OSCMessage, writePacket } from "osc"; +import { showNotification } from "./notifications"; +import { NotificationLevel } from "../models/notification"; +import { oscQueryBridge } from "../controller/oscqueryBridgeController"; +import throttle from "lodash.throttle"; +import { PresetRecord } from "../models/preset"; +import { AppSetting } from "../models/settings"; +import { DataRefRecord } from "../models/dataref"; +import { DataFileRecord } from "../models/datafile"; +import { PatcherExportRecord } from "../models/patcher"; + +export enum PatcherActionType { + INIT_PATCHERS = "INIT_PATCHERS", + + SET_INSTANCE = "SET_INSTANCE", + SET_INSTANCES = "SET_INSTANCES", + DELETE_INSTANCE = "DELETE_INSTANCE", + DELETE_INSTANCES = "DELETE_INSTANCES", + + SET_PARAMETER = "SET_PARAMETER", + SET_PARAMETERS = "SET_PARAMETERS", + DELETE_PARAMETER = "DELETE_PARAMETER", + DELETE_PARAMETERS = "DELETE_PARAMETERS", + + SET_MESSAGE_INPORT = "SET_MESSAGE_INPORT", + SET_MESSAGE_INPORTS = "SET_MESSAGE_INPORTS", + DELETE_MESSAGE_INPORT = "DELETE_MESSAGE_INPORT", + DELETE_MESSAGE_INPORTS = "DELETE_MESSAGE_INPORTS", + + SET_MESSAGE_OUTPORT = "SET_MESSAGE_OUTPORT", + SET_MESSAGE_OUTPORTS = "SET_MESSAGE_OUTPORTS", + DELETE_MESSAGE_OUTPORT = "DELETE_MESSAGE_OUTPORT", + DELETE_MESSAGE_OUTPORTS = "DELETE_MESSAGE_OUTPORTS" +} + +export interface IInitPatchers extends ActionBase { + type: PatcherActionType.INIT_PATCHERS; + payload: { + patchers: PatcherExportRecord[]; + }; +} + +export interface ISetInstance extends ActionBase { + type: PatcherActionType.SET_INSTANCE; + payload: { + instance: PatcherInstanceRecord; + }; +} + +export interface ISetInstances extends ActionBase { + type: PatcherActionType.SET_INSTANCES; + payload: { + instances: PatcherInstanceRecord[]; + }; +} + +export interface IDeleteInstance extends ActionBase { + type: PatcherActionType.DELETE_INSTANCE; + payload: { + instance: PatcherInstanceRecord; + }; +} + +export interface IDeleteInstances extends ActionBase { + type: PatcherActionType.DELETE_INSTANCES; + payload: { + instances: PatcherInstanceRecord[]; + }; +} + +export interface ISetInstanceParameter extends ActionBase { + type: PatcherActionType.SET_PARAMETER; + payload: { + parameter: ParameterRecord; + }; +} + +export interface ISetInstanceParameters extends ActionBase { + type: PatcherActionType.SET_PARAMETERS; + payload: { + parameters: ParameterRecord[]; + }; +} + +export interface IDeleteInstanceParameter extends ActionBase { + type: PatcherActionType.DELETE_PARAMETER; + payload: { + parameter: ParameterRecord; + }; +} + +export interface IDeleteInstanceParameters extends ActionBase { + type: PatcherActionType.DELETE_PARAMETERS; + payload: { + parameters: ParameterRecord[]; + }; +} + +export interface ISetInstanceMessageInport extends ActionBase { + type: PatcherActionType.SET_MESSAGE_INPORT; + payload: { + port: MessagePortRecord; + }; +} + +export interface ISetInstanceMessageInports extends ActionBase { + type: PatcherActionType.SET_MESSAGE_INPORTS; + payload: { + ports: MessagePortRecord[]; + }; +} + +export interface IDeleteInstanceMessageInport extends ActionBase { + type: PatcherActionType.DELETE_MESSAGE_INPORT; + payload: { + port: MessagePortRecord; + }; +} + +export interface IDeleteInstanceMessageInports extends ActionBase { + type: PatcherActionType.DELETE_MESSAGE_INPORTS; + payload: { + ports: MessagePortRecord[]; + }; +} + +export interface ISetInstanceMessageOutport extends ActionBase { + type: PatcherActionType.SET_MESSAGE_OUTPORT; + payload: { + port: MessagePortRecord; + }; +} + +export interface ISetInstanceMessageOutports extends ActionBase { + type: PatcherActionType.SET_MESSAGE_OUTPORTS; + payload: { + ports: MessagePortRecord[]; + }; +} + +export interface IDeleteInstanceMessageOutport extends ActionBase { + type: PatcherActionType.DELETE_MESSAGE_OUTPORT; + payload: { + port: MessagePortRecord; + }; +} + +export interface IDeleteInstanceMessageOutports extends ActionBase { + type: PatcherActionType.DELETE_MESSAGE_OUTPORTS; + payload: { + ports: MessagePortRecord[]; + }; +} + +export type InstanceAction = IInitPatchers | ISetInstance | ISetInstances | IDeleteInstance | IDeleteInstances | +ISetInstanceParameter | ISetInstanceParameters | IDeleteInstanceParameter | IDeleteInstanceParameters | +ISetInstanceMessageInport | ISetInstanceMessageInports | IDeleteInstanceMessageInport | IDeleteInstanceMessageInports | +ISetInstanceMessageOutport | ISetInstanceMessageOutports | IDeleteInstanceMessageOutport | IDeleteInstanceMessageOutports; + +export const initPatchers = (patchersInfo: OSCQueryRNBOPatchersState): IInitPatchers => { + + const patchers: PatcherExportRecord[] = []; + for (const [name, desc] of Object.entries(patchersInfo.CONTENTS || {})) { + patchers.push(PatcherExportRecord.fromDescription(name, desc)); + } + + return { + type: PatcherActionType.INIT_PATCHERS, + payload: { + patchers + } + }; +}; + +export const destroyPatcherOnRemote = (patcher: PatcherExportRecord): AppThunk => + (dispatch) => { + try { + const message: OSCMessage = { + address: `/rnbo/patchers/${patcher.name}/destroy`, + args: [] + }; + oscQueryBridge.sendPacket(writePacket(message)); + } catch (err) { + dispatch(showNotification({ + level: NotificationLevel.error, + title: `Error while trying to delete patcher ${patcher.name}`, + message: "Please check the console for further details." + })); + console.error(err); + } + }; + + +export const renamePatcherOnRemote = (patcher: PatcherExportRecord, newName: string): AppThunk => + (dispatch) => { + try { + const message = { + address: `/rnbo/patchers/${patcher.name}/rename`, + args: [ + { type: "s", value: newName } + ] + }; + oscQueryBridge.sendPacket(writePacket(message)); + } catch (err) { + dispatch(showNotification({ + level: NotificationLevel.error, + title: `Error while trying to rename patcher ${patcher.name} -> ${newName}`, + message: "Please check the console for further details." + })); + console.error(err); + } + }; + + +export const setInstance = (instance: PatcherInstanceRecord): ISetInstance => ({ + type: PatcherActionType.SET_INSTANCE, + payload: { + instance + } +}); + +export const setInstances = (instances: PatcherInstanceRecord[]): ISetInstances => ({ + type: PatcherActionType.SET_INSTANCES, + payload: { + instances + } +}); + +export const deleteInstance = (instance: PatcherInstanceRecord): IDeleteInstance => ({ + type: PatcherActionType.DELETE_INSTANCE, + payload: { + instance + } +}); + +export const deleteInstances = (instances: PatcherInstanceRecord[]): IDeleteInstances => ({ + type: PatcherActionType.DELETE_INSTANCES, + payload: { + instances + } +}); + +export const setInstanceParameter = (param: ParameterRecord): ISetInstanceParameter => ({ + type: PatcherActionType.SET_PARAMETER, + payload: { + parameter: param + } +}); + +export const setInstanceParameters = (params: ParameterRecord[]): ISetInstanceParameters => ({ + type: PatcherActionType.SET_PARAMETERS, + payload: { + parameters: params + } +}); + +export const deleteInstanceParameter = (param: ParameterRecord): IDeleteInstanceParameter => ({ + type: PatcherActionType.DELETE_PARAMETER, + payload: { + parameter: param + } +}); + +export const deleteInstanceParameters = (params: ParameterRecord[]): IDeleteInstanceParameters => ({ + type: PatcherActionType.DELETE_PARAMETERS, + payload: { + parameters: params + } +}); + +export const setInstanceMessageInport = (port: MessagePortRecord): ISetInstanceMessageInport => ({ + type: PatcherActionType.SET_MESSAGE_INPORT, + payload: { + port + } +}); + +export const setInstanceMessageInports = (ports: MessagePortRecord[]): ISetInstanceMessageInports => ({ + type: PatcherActionType.SET_MESSAGE_INPORTS, + payload: { + ports + } +}); + +export const deleteInstanceMessageInport = (port: MessagePortRecord): IDeleteInstanceMessageInport => ({ + type: PatcherActionType.DELETE_MESSAGE_INPORT, + payload: { + port + } +}); + +export const deleteInstanceMessageInports = (ports: MessagePortRecord[]): IDeleteInstanceMessageInports => ({ + type: PatcherActionType.DELETE_MESSAGE_INPORTS, + payload: { + ports + } +}); + +export const setInstanceMessageOutport = (port: MessagePortRecord): ISetInstanceMessageOutport => ({ + type: PatcherActionType.SET_MESSAGE_OUTPORT, + payload: { + port + } +}); + +export const setInstanceMessageOutports = (ports: MessagePortRecord[]): ISetInstanceMessageOutports => ({ + type: PatcherActionType.SET_MESSAGE_OUTPORTS, + payload: { + ports + } +}); + +export const deleteInstanceMessageOutport = (port: MessagePortRecord): IDeleteInstanceMessageOutport => ({ + type: PatcherActionType.DELETE_MESSAGE_OUTPORT, + payload: { + port + } +}); + +export const deleteInstanceMessageOutports = (ports: MessagePortRecord[]): IDeleteInstanceMessageOutports => ({ + type: PatcherActionType.DELETE_MESSAGE_OUTPORTS, + payload: { + ports + } +}); + +// Trigger Events on Remote OSCQuery Runner +export const loadPresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord): AppThunk => + (dispatch) => { + try { + const message = { + address: `${instance.path}/presets/load`, + args: [ + { type: "s", value: preset.name } + ] + }; + oscQueryBridge.sendPacket(writePacket(message)); + } catch (err) { + dispatch(showNotification({ + level: NotificationLevel.error, + title: `Error while trying to load preset ${preset.name}`, + message: "Please check the console for further details." + })); + console.log(err); + } + }; + +export const savePresetToRemoteInstance = (instance: PatcherInstanceRecord, name: string): AppThunk => + (dispatch) => { + try { + const message = { + address: `${instance.path}/presets/save`, + args: [ + { type: "s", value: name } + ] + }; + oscQueryBridge.sendPacket(writePacket(message)); + } catch (err) { + dispatch(showNotification({ + level: NotificationLevel.error, + title: `Error while trying to save preset ${name}`, + message: "Please check the console for further details." + })); + console.log(err); + } + }; + +export const destroyPresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord): AppThunk => + (dispatch) => { + try { + const message = { + address: `${instance.path}/presets/delete`, + args: [ + { type: "s", value: preset.name } + ] + }; + oscQueryBridge.sendPacket(writePacket(message)); + } catch (err) { + dispatch(showNotification({ + level: NotificationLevel.error, + title: `Error while trying to delete preset ${preset.name}`, + message: "Please check the console for further details." + })); + console.log(err); + } + }; + +export const renamePresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord, name: string): AppThunk => + (dispatch) => { + try { + const message = { + address: `${instance.path}/presets/rename`, + args: [ + { type: "s", value: preset.name }, + { type: "s", value: name } + ] + }; + oscQueryBridge.sendPacket(writePacket(message)); + } catch (err) { + dispatch(showNotification({ + level: NotificationLevel.error, + title: `Error while trying to rename preset ${preset.name} to ${name}`, + message: "Please check the console for further details." + })); + console.log(err); + } + }; + +export const setInitialPresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord): AppThunk => + (dispatch) => { + try { + const message = { + address: `${instance.path}/presets/initial`, + args: [ + { type: "s", value: preset.name } + ] + }; + oscQueryBridge.sendPacket(writePacket(message)); + } catch (err) { + dispatch(showNotification({ + level: NotificationLevel.error, + title: `Error while trying to set initial preset to ${preset.name}`, + message: "Please check the console for further details." + })); + console.log(err); + } + }; + +export const sendInstanceMessageToRemote = (instance: PatcherInstanceRecord, inportId: string, value: string): AppThunk => + (dispatch) => { + const values = value.split(" ").reduce((values, v) => { + const fv = parseFloat(v.replaceAll(",", ".").trim()); + if (!isNaN(fv)) values.push({ type: "f", value: fv }); + return values; + }, [] as OSCArgument[]); + + if (!values.length) { + dispatch(showNotification({ + title: "Invalid Message Input", + level: NotificationLevel.warn, + message: `Could not send message input "${value}" as it appears to contain non-valid number input. Please provide a single or multiple numbers separated by a space.` + })); + return; + } + + + const message = { + address: `/rnbo/inst/${instance.index}/messages/in/${inportId}`, + args: values + }; + oscQueryBridge.sendPacket(writePacket(message)); + }; + +export const triggerInstanceMidiNoteOnEventOnRemote = (instance: PatcherInstanceRecord, note: number): AppThunk => + () => { + + const midiChannel = 0; + const routeByte = 144 + midiChannel; + const velocityByte = 100; + + const message = { + address: `${instance.path}/midi/in`, + args: [routeByte, note, velocityByte].map(byte => ({ type: "i", value: byte })) + }; + + oscQueryBridge.sendPacket(writePacket(message)); + }; + +export const triggerInstanceMidiNoteOffEventOnRemote = (instance: PatcherInstanceRecord, note: number): AppThunk => + () => { + + const midiChannel = 0; + const routeByte = 128 + midiChannel; + const velocityByte = 0; + + const message = { + address: `${instance.path}/midi/in`, + args: [routeByte, note, velocityByte].map(byte => ({ type: "i", value: byte })) + }; + + oscQueryBridge.sendPacket(writePacket(message)); + }; + +export const setInstanceParameterValueNormalizedOnRemote = throttle((instance: PatcherInstanceRecord, param: ParameterRecord, value: number): AppThunk => + (dispatch) => { + + const message = { + address: `${param.path}/normalized`, + args: [ + { type: "f", value } + ] + }; + + oscQueryBridge.sendPacket(writePacket(message)); + // optimistic local state update + dispatch(setInstanceParameter(param.setNormalizedValue(value))); + }, 10); + +export const setInstanceDataRefValueOnRemote = (instance: PatcherInstanceRecord, dataref: DataRefRecord, file?: DataFileRecord): AppThunk => + () => { + + const message = { + address: `${instance.path}/data_refs/${dataref.id}`, + args: [ + { type: "s", value: file?.fileName || "" } // no files unsets + ] + }; + + oscQueryBridge.sendPacket(writePacket(message)); + }; + +export const setInstanceParameterMetaOnRemote = (_instance: PatcherInstanceRecord, param: ParameterRecord, value: string): AppThunk => + () => { + const message = { + address: `${param.path}/meta`, + args: [ + { type: "s", value } + ] + }; + + oscQueryBridge.sendPacket(writePacket(message)); + }; + +export const restoreDefaultParameterMetaOnRemote = (_instance: PatcherInstanceRecord, param: ParameterRecord): AppThunk => + () => { + const message = { + address: `${param.path}/meta`, + args: [ + { type: "s", value: "" } + ] + }; + + oscQueryBridge.sendPacket(writePacket(message)); + }; + +export const activateParameterMIDIMappingFocus = (instance: PatcherInstanceRecord, param: ParameterRecord): AppThunk => + (dispatch, getState) => { + + const state = getState(); + const params = getPatcherInstanceParametersByInstanceIndex(state, instance.index); + + dispatch(setInstanceParameters( + params.valueSeq().toArray().map(p => p.setWaitingForMidiMapping(p.id === param.id)) + )); + }; + +export const clearParameterMidiMappingOnRemote = (id: PatcherInstanceRecord["id"], paramId: ParameterRecord["id"]): AppThunk => + (_dispatch, getState) => { + const state = getState(); + const instance = getPatcherInstance(state, id); + if (!instance) return; + + const param = getPatcherInstanceParameter(state, paramId); + if (!param) return; + + const meta = param.getParsedMetaObject(); + delete meta.midi; + const message = { + address: `${param.path}/meta`, + args: [ + { type: "s", value: JSON.stringify(meta) } + ] + }; + + oscQueryBridge.sendPacket(writePacket(message)); + }; + +export const setInstanceMessagePortMetaOnRemote = (_instance: PatcherInstanceRecord, port: MessagePortRecord, value: string): AppThunk => + () => { + const message = { + address: `${port.path}/meta`, + args: [ + { type: "s", value } + ] + }; + + oscQueryBridge.sendPacket(writePacket(message)); + }; + +export const restoreDefaultMessagePortMetaOnRemote = (_instance: PatcherInstanceRecord, port: MessagePortRecord): AppThunk => + () => { + const message = { + address: `${port.path}/meta`, + args: [ + { type: "s", value: "" } + ] + }; + + oscQueryBridge.sendPacket(writePacket(message)); + }; + +// Updates in response to remote OSCQuery Updates +export const updateInstancePresetEntries = (index: number, entries: OSCQueryRNBOInstancePresetEntries): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const instance = getPatcherInstanceByIndex(state, index); + if (!instance) return; + + dispatch(setInstance(instance.updatePresets(entries))); + } catch (e) { + console.log(e); + } + }; + +export const updateInstancePresetLatest = (index: number, name: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const instance = getPatcherInstanceByIndex(state, index); + if (!instance) return; + + dispatch(setInstance(instance.setPresetLatest(name))); + } catch (e) { + console.log(e); + } + }; + +export const updateInstancePresetInitial = (index: number, name: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const instance = getPatcherInstanceByIndex(state, index); + if (!instance) return; + + dispatch(setInstance(instance.setPresetInitial(name))); + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceMessages = (index: number, desc: OSCQueryRNBOInstance["CONTENTS"]["messages"]): AppThunk => + (dispatch, getState) => { + try { + if (!desc) return; + + const state = getState(); + const instance = getPatcherInstanceByIndex(state, index); + if (!instance) return; + + const currentMessageInports = getPatcherInstanceMessageInportsByInstanceIndex(state, instance.index); + const currentMessageOutports = getPatcherInstanceMesssageOutportsByInstanceIndex(state, instance.index); + dispatch(deleteInstanceMessageInports(currentMessageInports.valueSeq().toArray())); + dispatch(deleteInstanceMessageOutports(currentMessageOutports.valueSeq().toArray())); + + const messageInports = MessagePortRecord.fromDescription(instance.index, desc.CONTENTS?.in); + const messageOutports = MessagePortRecord.fromDescription(instance.index, desc.CONTENTS?.out); + + dispatch(setInstanceMessageInports(messageInports)); + dispatch(setInstanceMessageOutports(messageOutports)); + + } catch (e) { + console.log(e); + } + }; + +export const removeInstanceMessageInportByPath = (path: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const port = getPatcherInstanceMessageInportByPath(state, path); + if (!port) return; + + dispatch(deleteInstanceMessageInport(port)); + } catch (e) { + console.log(e); + } + }; + +export const removeInstanceMessageOutportByPath = (path: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const port = getPatcherInstanceMessageOutportByPath(state, path); + if (!port) return; + + dispatch(deleteInstanceMessageOutport(port)); + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceMessageOutportValue = (index: number, tag: MessagePortRecord["tag"], value: OSCValue | OSCValue[]): AppThunk => + (dispatch, getState) => { + try { + + const state = getState(); + + // Debug enabled?! + const enabled = getAppSetting(state, AppSetting.debugMessageOutput)?.value || false; + if (!enabled) return; + + // Active Instance view?! + if (Router.pathname !== "/instances/[index]" || Router.query.index !== `${index}`) return; + + const instance = getPatcherInstanceByIndex(state, index); + if (!instance) return; + + const port = getPatcherInstanceMesssageOutportsByInstanceIndexAndTag(state, instance.index, tag); + if (!port) return; + + dispatch(setInstanceMessageOutport(port.setValue(Array.isArray(value) ? value.join(", ") : `${value}`))); + } catch (e) { + console.log(e); + } + }; + +export const removeInstanceParameterByPath = (path: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const param = getPatcherInstanceParameterByPath(state, path); + if (!param) return; + + dispatch(deleteInstanceParameter(param)); + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceParameters = (index: number, desc: OSCQueryRNBOInstance["CONTENTS"]["params"]): AppThunk => + (dispatch, getState) => { + try { + if (!desc) return; + + const state = getState(); + const instance = getPatcherInstanceByIndex(state, index); + if (!instance) return; + + const currentParams = getPatcherInstanceParametersByInstanceIndex(state, instance.index); + dispatch(deleteInstanceParameters(currentParams.valueSeq().toArray())); + + const newParams = ParameterRecord.fromDescription(instance.index, desc); + dispatch(setInstanceParameters(newParams)); + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceDataRefValue = (index: number, name: string, value: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + + const instance = getPatcherInstanceByIndex(state, index); + if (!instance) return; + + dispatch(setInstance( + instance.setDataRefValue(name, value) + )); + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceParameterValue = (index: number, name: ParameterRecord["name"], value: number): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const param = getPatcherInstanceParametersByInstanceIndexAndName(state, index, name); + if (!param) return; + + dispatch(setInstanceParameter(param.setValue(value))); + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceParameterValueNormalized = (index: number, name: ParameterRecord["name"], value: number): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const param = getPatcherInstanceParametersByInstanceIndexAndName(state, index, name); + if (!param) return; + + dispatch(setInstanceParameter(param.setNormalizedValue(value))); + } catch (e) { + console.log(e); + } + }; + +export const setInstanceWaitingForMidiMappingOnRemote = (id: PatcherInstanceRecord["id"], value: boolean): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const instance = getPatcherInstance(state, id); + if (!instance) return; + + dispatch(setInstance(instance.setWaitingForMapping(value))); + const params = getPatcherInstanceParametersByInstanceIndex(state, instance.index).valueSeq().map(p => p.setWaitingForMidiMapping(false)); + dispatch(setInstanceParameters(params.toArray())); + + try { + const message = { + address: `${instance.path}/midi/last/report`, + args: [ + { type: value ? "T" : "F", value: value ? "true" : "false" } + ] + }; + oscQueryBridge.sendPacket(writePacket(message)); + } catch (err) { + dispatch(showNotification({ + level: NotificationLevel.error, + title: "Error while trying set midi mapping mode on remote", + message: "Please check the console for further details." + })); + console.log(err); + } + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceMIDIReport = (index: number, value: boolean): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const instance = getPatcherInstanceByIndex(state, index); + if (!instance) return; + dispatch(setInstance(instance.setWaitingForMapping(value))); + const params = getPatcherInstanceParametersByInstanceIndex(state, instance.index).valueSeq().map(p => p.setWaitingForMidiMapping(false)); + dispatch(setInstanceParameters(params.toArray())); + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceMIDILastValue = (index: number, value: string): AppThunk => + (dispatch, getState) => { + try { + + const state = getState(); + + const instance = getPatcherInstanceByIndex(state, index); + if (!instance?.waitingForMidiMapping) return; + + const midiMeta = JSON.parse(value); + + // find waiting, update their meta, set them no longer waiting and update map + const parameters: ParameterRecord[] = []; + getPatcherInstanceParametersByInstanceIndex(state, instance.index).forEach(param => { + if (param.waitingForMidiMapping) { + const meta = param.getParsedMetaObject(); + meta.midi = midiMeta; + + const message = { + address: `${param.path}/meta`, + args: [ + { type: "s", value: JSON.stringify(meta) } + ] + }; + + oscQueryBridge.sendPacket(writePacket(message)); + parameters.push(param.setWaitingForMidiMapping(false)); + } + }); + + dispatch(setInstanceParameters(parameters)); + + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceParameterMeta = (index: number, name: ParameterRecord["name"], value: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const param = getPatcherInstanceParametersByInstanceIndexAndName(state, index, name); + if (!param) return; + + dispatch(setInstanceParameter(param.setMeta(value))); + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceMessageOutportMeta = (index: number, tag: MessagePortRecord["tag"], value: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const instance = getPatcherInstanceByIndex(state, index); + if (!instance) return; + + + const port = getPatcherInstanceMessageInportsByInstanceIndexAndTag(state, instance.index, tag); + if (!port) return; + + dispatch(setInstanceMessageOutport(port.setMeta(value))); + } catch (e) { + console.log(e); + } + }; + +export const updateInstanceMessageInportMeta = (index: number, tag: MessagePortRecord["tag"], value: string): AppThunk => + (dispatch, getState) => { + try { + const state = getState(); + const instance = getPatcherInstanceByIndex(state, index); + if (!instance) return; + + const port = getPatcherInstanceMessageInportsByInstanceIndexAndTag(state, instance.index, tag); + if (!port) return; + + dispatch(setInstanceMessageInport(port.setMeta(value))); + } catch (e) { + console.log(e); + } + }; + +// Events from remote diff --git a/src/models/messageport.ts b/src/models/messageport.ts index 7ca76b7c..62f0fa0c 100644 --- a/src/models/messageport.ts +++ b/src/models/messageport.ts @@ -1,5 +1,6 @@ import { Record as ImmuRecord } from "immutable"; import { OSCQueryRNBOInstanceMessageInfo, OSCQueryRNBOInstanceMessages, OSCQueryRNBOInstanceMessageValue } from "../lib/types"; +import { PatcherInstanceRecord } from "./instance"; export type MessagePortRecordProps = { instanceIndex: number; @@ -18,11 +19,11 @@ export class MessagePortRecord extends ImmuRecord({ path: "" }) { - private static messagesArrayFromDescription(desc: OSCQueryRNBOInstanceMessageInfo, name: string): MessagePortRecord[] { + private static messagesArrayFromDescription(instanceIndex: PatcherInstanceRecord["index"], desc: OSCQueryRNBOInstanceMessageInfo, name: string): MessagePortRecord[] { if (typeof desc.VALUE !== "undefined") { return [ new MessagePortRecord({ - instanceIndex: 0, + instanceIndex, tag: name, path: (desc as OSCQueryRNBOInstanceMessageValue).FULL_PATH, meta: (desc as OSCQueryRNBOInstanceMessageValue).CONTENTS?.meta?.VALUE || "" @@ -33,15 +34,15 @@ export class MessagePortRecord extends ImmuRecord({ const result: MessagePortRecord[] = []; for (const [subKey, subDesc] of Object.entries(desc.CONTENTS)) { const subPrefix = name ? `${name}/${subKey}` : subKey; - result.push(...this.messagesArrayFromDescription(subDesc, subPrefix)); + result.push(...this.messagesArrayFromDescription(instanceIndex, subDesc, subPrefix)); } return result; } - public static fromDescription(messagesDesc?: OSCQueryRNBOInstanceMessages): MessagePortRecord[] { + public static fromDescription(instanceIndex: PatcherInstanceRecord["index"], messagesDesc?: OSCQueryRNBOInstanceMessages): MessagePortRecord[] { const ports: MessagePortRecord[] = []; for (const [name, desc] of Object.entries(messagesDesc?.CONTENTS || {})) { - ports.push(...this.messagesArrayFromDescription(desc, name)); + ports.push(...this.messagesArrayFromDescription(instanceIndex, desc, name)); } return ports; } diff --git a/src/reducers/patchers.ts b/src/reducers/patchers.ts new file mode 100644 index 00000000..d4843699 --- /dev/null +++ b/src/reducers/patchers.ts @@ -0,0 +1,207 @@ +import { Map as ImmuMap } from "immutable"; +import { PatcherInstanceRecord } from "../models/instance"; +import { InstanceAction, PatcherActionType } from "../actions/patchers"; +import { ParameterRecord } from "../models/parameter"; +import { MessagePortRecord } from "../models/messageport"; +import { PatcherExportRecord } from "../models/patcher"; + +export interface PatcherState { + exports: ImmuMap; + instances: ImmuMap; + instanceMessageInports: ImmuMap; + instanceMessageOutports: ImmuMap; + instanceParameters: ImmuMap; +} + +export const patchers = (state: PatcherState = { + + exports: ImmuMap(), + instances: ImmuMap(), + instanceMessageInports: ImmuMap(), + instanceMessageOutports: ImmuMap(), + instanceParameters: ImmuMap() + +}, action: InstanceAction): PatcherState => { + + switch(action.type) { + + case PatcherActionType.INIT_PATCHERS: { + const { patchers } = action.payload; + + return { + ...state, + exports: ImmuMap(patchers.map(p => [p.id, p])) + }; + } + + case PatcherActionType.SET_INSTANCE: { + const { instance } = action.payload; + + return { + ...state, + instances: state.instances.set(instance.id, instance) + }; + } + + case PatcherActionType.SET_INSTANCES: { + const { instances } = action.payload; + + return { + ...state, + instances: state.instances.withMutations(map => { + for (const instance of instances) { + map.set(instance.id, instance); + } + }) + }; + } + + case PatcherActionType.DELETE_INSTANCE: { + const { instance } = action.payload; + + return { + ...state, + instances: state.instances.delete(instance.id), + instanceParameters: state.instanceParameters.filter(param => param.instanceIndex !== instance.index), + instanceMessageInports: state.instanceMessageInports.filter(port => port.instanceIndex !== instance.index), + instanceMessageOutports: state.instanceMessageOutports.filter(port => port.instanceIndex !== instance.index) + }; + } + + case PatcherActionType.DELETE_INSTANCES: { + const { instances } = action.payload; + const indexSet = new Set(instances.map(i => i.index)); + + return { + ...state, + instances: state.instances.deleteAll(instances.map(d => d.id)), + instanceParameters: state.instanceParameters.filter(param => !indexSet.has(param.instanceIndex)), + instanceMessageInports: state.instanceMessageInports.filter(port => !indexSet.has(port.instanceIndex)), + instanceMessageOutports: state.instanceMessageOutports.filter(port => !indexSet.has(port.instanceIndex)) + }; + } + + case PatcherActionType.SET_PARAMETER: { + const { parameter } = action.payload; + + return { + ...state, + instanceParameters: state.instanceParameters.set(parameter.id, parameter) + }; + } + + case PatcherActionType.SET_PARAMETERS: { + const { parameters } = action.payload; + + return { + ...state, + instanceParameters: state.instanceParameters.withMutations(map => { + for (const param of parameters) { + map.set(param.id, param); + } + }) + }; + } + + case PatcherActionType.DELETE_PARAMETER: { + const { parameter } = action.payload; + + return { + ...state, + instanceParameters: state.instanceParameters.delete(parameter.id) + }; + } + + case PatcherActionType.DELETE_PARAMETERS: { + const { parameters } = action.payload; + + return { + ...state, + instanceParameters: state.instanceParameters.deleteAll(parameters.map(d => d.id)) + }; + } + + case PatcherActionType.SET_MESSAGE_INPORT: { + const { port } = action.payload; + + return { + ...state, + instanceMessageInports: state.instanceMessageInports.set(port.id, port) + }; + } + + case PatcherActionType.SET_MESSAGE_INPORTS: { + const { ports } = action.payload; + + return { + ...state, + instanceMessageInports: state.instanceMessageInports.withMutations(map => { + for (const port of ports) { + map.set(port.id, port); + } + }) + }; + } + + case PatcherActionType.DELETE_MESSAGE_INPORT: { + const { port } = action.payload; + + return { + ...state, + instanceMessageInports: state.instanceMessageInports.delete(port.id) + }; + } + + case PatcherActionType.DELETE_MESSAGE_INPORTS: { + const { ports } = action.payload; + + return { + ...state, + instanceMessageInports: state.instanceMessageInports.deleteAll(ports.map(d => d.id)) + }; + } + + case PatcherActionType.SET_MESSAGE_OUTPORT: { + const { port } = action.payload; + + return { + ...state, + instanceMessageOutports: state.instanceMessageOutports.set(port.id, port) + }; + } + + case PatcherActionType.SET_MESSAGE_OUTPORTS: { + const { ports } = action.payload; + + return { + ...state, + instanceMessageOutports: state.instanceMessageOutports.withMutations(map => { + for (const port of ports) { + map.set(port.id, port); + } + }) + }; + } + + case PatcherActionType.DELETE_MESSAGE_OUTPORT: { + const { port } = action.payload; + + return { + ...state, + instanceMessageOutports: state.instanceMessageOutports.delete(port.id) + }; + } + + case PatcherActionType.DELETE_MESSAGE_OUTPORTS: { + const { ports } = action.payload; + + return { + ...state, + instanceMessageOutports: state.instanceMessageOutports.deleteAll(ports.map(d => d.id)) + }; + } + + default: + return state; + } +}; From 67ad278e6e3dbd53f2d429c555f0b93d62d48d8f Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Mon, 16 Dec 2024 13:48:22 +0000 Subject: [PATCH 7/7] renamed instances to patchers state and overall polished action and state naming to be more coherent with naming convention of PatcherExport and PatcherInstance --- src/actions/graph.ts | 13 +- src/actions/instances.ts | 918 --------------------- src/components/instance/datarefTab.tsx | 2 +- src/components/instance/messageTab.tsx | 4 +- src/components/instance/paramTab.tsx | 2 +- src/components/keyroll/modal.tsx | 2 +- src/components/patchers/index.tsx | 12 +- src/components/patchers/item.tsx | 10 +- src/controller/oscqueryBridgeController.ts | 4 +- src/models/patcher.ts | 8 +- src/pages/index.tsx | 12 +- src/pages/instances/[index].tsx | 14 +- src/reducers/index.ts | 4 +- src/reducers/instances.ts | 207 ----- src/selectors/instances.ts | 206 ----- src/selectors/patchers.ts | 191 ++++- 16 files changed, 226 insertions(+), 1383 deletions(-) delete mode 100644 src/actions/instances.ts delete mode 100644 src/reducers/instances.ts delete mode 100644 src/selectors/instances.ts diff --git a/src/actions/graph.ts b/src/actions/graph.ts index 914a411f..7a5a3939 100644 --- a/src/actions/graph.ts +++ b/src/actions/graph.ts @@ -8,10 +8,9 @@ import { getConnectionsForSourceNodeAndPort, getNode, getPatcherNodeByIndex, get import { showNotification } from "./notifications"; import { NotificationLevel } from "../models/notification"; import { PatcherInstanceRecord } from "../models/instance"; -import { deleteInstance, setInstance, setInstanceMessageInports, setInstanceMessageOutports, setInstanceParameters, setInstances } from "./instances"; -import { getInstance } from "../selectors/instances"; -import { PatcherRecord } from "../models/patcher"; -import { getPatchers } from "../selectors/instances"; +import { deleteInstance, setInstance, setInstanceMessageInports, setInstanceMessageOutports, setInstanceParameters, setInstances } from "./patchers"; +import { getPatcherInstance, getPatcherExports } from "../selectors/patchers"; +import { PatcherExportRecord } from "../models/patcher"; import { defaultNodeGap, nodeDefaultWidth, nodeHeaderHeight } from "../lib/constants"; import { getGraphEditorInstance } from "../selectors/editor"; import { ParameterRecord } from "../models/parameter"; @@ -545,7 +544,7 @@ export const updateSystemOrControlPortInfo = (type: ConnectionType, direction: P let systemInputY = -defaultNodeGap; let systemOutputY = -defaultNodeGap; - const patchers = getPatchers(state).valueSeq(); + const patchers = getPatcherExports(state).valueSeq(); const missingSystemOrControlJackName = Array.from(systemOrControlJackNames.values()) .filter(name => !patchers.find(patcher => name.startsWith(`${patcher.name}-`))); @@ -629,7 +628,7 @@ export const unloadPatcherNodeByIndexOnRemote = (instanceIndex: number): AppThun } }; -export const loadPatcherNodeOnRemote = (patcher: PatcherRecord): AppThunk => +export const loadPatcherNodeOnRemote = (patcher: PatcherExportRecord): AppThunk => (dispatch) => { try { const message = { @@ -715,7 +714,7 @@ export const removePatcherNode = (index: number): AppThunk => if (node?.type !== NodeType.Patcher) return; dispatch(deleteNode(node)); - const instance = getInstance(state, node.id); + const instance = getPatcherInstance(state, node.id); if (!instance) return; dispatch(deleteInstance(instance)); diff --git a/src/actions/instances.ts b/src/actions/instances.ts deleted file mode 100644 index 68fd76e8..00000000 --- a/src/actions/instances.ts +++ /dev/null @@ -1,918 +0,0 @@ -import Router from "next/router"; -import { ActionBase, AppThunk } from "../lib/store"; -import { OSCQueryRNBOInstance, OSCQueryRNBOInstancePresetEntries, OSCQueryRNBOPatchersState, OSCValue } from "../lib/types"; -import { PatcherInstanceRecord } from "../models/instance"; -import { getInstanceByIndex, getInstance, getParameter, getInstanceParameters, getInstanceParameterByName, getParameterByPath, getInstanceMessageInports, getInstanceMessageOutports, getInstanceMessageOutportByTag, getInstanceMessageInportByTag, getMessageInportByPath, getMessageOutportByPath } from "../selectors/instances"; -import { getAppSetting } from "../selectors/settings"; -import { ParameterRecord } from "../models/parameter"; -import { MessagePortRecord } from "../models/messageport"; -import { OSCArgument, OSCMessage, writePacket } from "osc"; -import { showNotification } from "./notifications"; -import { NotificationLevel } from "../models/notification"; -import { oscQueryBridge } from "../controller/oscqueryBridgeController"; -import throttle from "lodash.throttle"; -import { PresetRecord } from "../models/preset"; -import { AppSetting } from "../models/settings"; -import { DataRefRecord } from "../models/dataref"; -import { DataFileRecord } from "../models/datafile"; -import { PatcherRecord } from "../models/patcher"; - -export enum InstanceActionType { - INIT_PATCHERS = "INIT_PATCHERS", - - SET_INSTANCE = "SET_INSTANCE", - SET_INSTANCES = "SET_INSTANCES", - DELETE_INSTANCE = "DELETE_INSTANCE", - DELETE_INSTANCES = "DELETE_INSTANCES", - - SET_PARAMETER = "SET_PARAMETER", - SET_PARAMETERS = "SET_PARAMETERS", - DELETE_PARAMETER = "DELETE_PARAMETER", - DELETE_PARAMETERS = "DELETE_PARAMETERS", - - SET_MESSAGE_INPORT = "SET_MESSAGE_INPORT", - SET_MESSAGE_INPORTS = "SET_MESSAGE_INPORTS", - DELETE_MESSAGE_INPORT = "DELETE_MESSAGE_INPORT", - DELETE_MESSAGE_INPORTS = "DELETE_MESSAGE_INPORTS", - - SET_MESSAGE_OUTPORT = "SET_MESSAGE_OUTPORT", - SET_MESSAGE_OUTPORTS = "SET_MESSAGE_OUTPORTS", - DELETE_MESSAGE_OUTPORT = "DELETE_MESSAGE_OUTPORT", - DELETE_MESSAGE_OUTPORTS = "DELETE_MESSAGE_OUTPORTS" -} - -export interface IInitPatchers extends ActionBase { - type: InstanceActionType.INIT_PATCHERS; - payload: { - patchers: PatcherRecord[]; - }; -} - -export interface ISetInstance extends ActionBase { - type: InstanceActionType.SET_INSTANCE; - payload: { - instance: PatcherInstanceRecord; - }; -} - -export interface ISetInstances extends ActionBase { - type: InstanceActionType.SET_INSTANCES; - payload: { - instances: PatcherInstanceRecord[]; - }; -} - -export interface IDeleteInstance extends ActionBase { - type: InstanceActionType.DELETE_INSTANCE; - payload: { - instance: PatcherInstanceRecord; - }; -} - -export interface IDeleteInstances extends ActionBase { - type: InstanceActionType.DELETE_INSTANCES; - payload: { - instances: PatcherInstanceRecord[]; - }; -} - -export interface ISetInstanceParameter extends ActionBase { - type: InstanceActionType.SET_PARAMETER; - payload: { - parameter: ParameterRecord; - }; -} - -export interface ISetInstanceParameters extends ActionBase { - type: InstanceActionType.SET_PARAMETERS; - payload: { - parameters: ParameterRecord[]; - }; -} - -export interface IDeleteInstanceParameter extends ActionBase { - type: InstanceActionType.DELETE_PARAMETER; - payload: { - parameter: ParameterRecord; - }; -} - -export interface IDeleteInstanceParameters extends ActionBase { - type: InstanceActionType.DELETE_PARAMETERS; - payload: { - parameters: ParameterRecord[]; - }; -} - -export interface ISetInstanceMessageInport extends ActionBase { - type: InstanceActionType.SET_MESSAGE_INPORT; - payload: { - port: MessagePortRecord; - }; -} - -export interface ISetInstanceMessageInports extends ActionBase { - type: InstanceActionType.SET_MESSAGE_INPORTS; - payload: { - ports: MessagePortRecord[]; - }; -} - -export interface IDeleteInstanceMessageInport extends ActionBase { - type: InstanceActionType.DELETE_MESSAGE_INPORT; - payload: { - port: MessagePortRecord; - }; -} - -export interface IDeleteInstanceMessageInports extends ActionBase { - type: InstanceActionType.DELETE_MESSAGE_INPORTS; - payload: { - ports: MessagePortRecord[]; - }; -} - -export interface ISetInstanceMessageOutport extends ActionBase { - type: InstanceActionType.SET_MESSAGE_OUTPORT; - payload: { - port: MessagePortRecord; - }; -} - -export interface ISetInstanceMessageOutports extends ActionBase { - type: InstanceActionType.SET_MESSAGE_OUTPORTS; - payload: { - ports: MessagePortRecord[]; - }; -} - -export interface IDeleteInstanceMessageOutport extends ActionBase { - type: InstanceActionType.DELETE_MESSAGE_OUTPORT; - payload: { - port: MessagePortRecord; - }; -} - -export interface IDeleteInstanceMessageOutports extends ActionBase { - type: InstanceActionType.DELETE_MESSAGE_OUTPORTS; - payload: { - ports: MessagePortRecord[]; - }; -} - -export type InstanceAction = IInitPatchers | ISetInstance | ISetInstances | IDeleteInstance | IDeleteInstances | -ISetInstanceParameter | ISetInstanceParameters | IDeleteInstanceParameter | IDeleteInstanceParameters | -ISetInstanceMessageInport | ISetInstanceMessageInports | IDeleteInstanceMessageInport | IDeleteInstanceMessageInports | -ISetInstanceMessageOutport | ISetInstanceMessageOutports | IDeleteInstanceMessageOutport | IDeleteInstanceMessageOutports; - -export const initPatchers = (patchersInfo: OSCQueryRNBOPatchersState): IInitPatchers => { - - const patchers: PatcherRecord[] = []; - for (const [name, desc] of Object.entries(patchersInfo.CONTENTS || {})) { - patchers.push(PatcherRecord.fromDescription(name, desc)); - } - - return { - type: InstanceActionType.INIT_PATCHERS, - payload: { - patchers - } - }; -}; - -export const destroyPatcherOnRemote = (patcher: PatcherRecord): AppThunk => - (dispatch) => { - try { - const message: OSCMessage = { - address: `/rnbo/patchers/${patcher.name}/destroy`, - args: [] - }; - oscQueryBridge.sendPacket(writePacket(message)); - } catch (err) { - dispatch(showNotification({ - level: NotificationLevel.error, - title: `Error while trying to delete patcher ${patcher.name}`, - message: "Please check the console for further details." - })); - console.error(err); - } - }; - - -export const renamePatcherOnRemote = (patcher: PatcherRecord, newName: string): AppThunk => - (dispatch) => { - try { - const message = { - address: `/rnbo/patchers/${patcher.name}/rename`, - args: [ - { type: "s", value: newName } - ] - }; - oscQueryBridge.sendPacket(writePacket(message)); - } catch (err) { - dispatch(showNotification({ - level: NotificationLevel.error, - title: `Error while trying to rename patcher ${patcher.name} -> ${newName}`, - message: "Please check the console for further details." - })); - console.error(err); - } - }; - - -export const setInstance = (instance: PatcherInstanceRecord): ISetInstance => ({ - type: InstanceActionType.SET_INSTANCE, - payload: { - instance - } -}); - -export const setInstances = (instances: PatcherInstanceRecord[]): ISetInstances => ({ - type: InstanceActionType.SET_INSTANCES, - payload: { - instances - } -}); - -export const deleteInstance = (instance: PatcherInstanceRecord): IDeleteInstance => ({ - type: InstanceActionType.DELETE_INSTANCE, - payload: { - instance - } -}); - -export const deleteInstances = (instances: PatcherInstanceRecord[]): IDeleteInstances => ({ - type: InstanceActionType.DELETE_INSTANCES, - payload: { - instances - } -}); - -export const setInstanceParameter = (param: ParameterRecord): ISetInstanceParameter => ({ - type: InstanceActionType.SET_PARAMETER, - payload: { - parameter: param - } -}); - -export const setInstanceParameters = (params: ParameterRecord[]): ISetInstanceParameters => ({ - type: InstanceActionType.SET_PARAMETERS, - payload: { - parameters: params - } -}); - -export const deleteInstanceParameter = (param: ParameterRecord): IDeleteInstanceParameter => ({ - type: InstanceActionType.DELETE_PARAMETER, - payload: { - parameter: param - } -}); - -export const deleteInstanceParameters = (params: ParameterRecord[]): IDeleteInstanceParameters => ({ - type: InstanceActionType.DELETE_PARAMETERS, - payload: { - parameters: params - } -}); - -export const setInstanceMessageInport = (port: MessagePortRecord): ISetInstanceMessageInport => ({ - type: InstanceActionType.SET_MESSAGE_INPORT, - payload: { - port - } -}); - -export const setInstanceMessageInports = (ports: MessagePortRecord[]): ISetInstanceMessageInports => ({ - type: InstanceActionType.SET_MESSAGE_INPORTS, - payload: { - ports - } -}); - -export const deleteInstanceMessageInport = (port: MessagePortRecord): IDeleteInstanceMessageInport => ({ - type: InstanceActionType.DELETE_MESSAGE_INPORT, - payload: { - port - } -}); - -export const deleteInstanceMessageInports = (ports: MessagePortRecord[]): IDeleteInstanceMessageInports => ({ - type: InstanceActionType.DELETE_MESSAGE_INPORTS, - payload: { - ports - } -}); - -export const setInstanceMessageOutport = (port: MessagePortRecord): ISetInstanceMessageOutport => ({ - type: InstanceActionType.SET_MESSAGE_OUTPORT, - payload: { - port - } -}); - -export const setInstanceMessageOutports = (ports: MessagePortRecord[]): ISetInstanceMessageOutports => ({ - type: InstanceActionType.SET_MESSAGE_OUTPORTS, - payload: { - ports - } -}); - -export const deleteInstanceMessageOutport = (port: MessagePortRecord): IDeleteInstanceMessageOutport => ({ - type: InstanceActionType.DELETE_MESSAGE_OUTPORT, - payload: { - port - } -}); - -export const deleteInstanceMessageOutports = (ports: MessagePortRecord[]): IDeleteInstanceMessageOutports => ({ - type: InstanceActionType.DELETE_MESSAGE_OUTPORTS, - payload: { - ports - } -}); - -// Trigger Events on Remote OSCQuery Runner -export const loadPresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord): AppThunk => - (dispatch) => { - try { - const message = { - address: `${instance.path}/presets/load`, - args: [ - { type: "s", value: preset.name } - ] - }; - oscQueryBridge.sendPacket(writePacket(message)); - } catch (err) { - dispatch(showNotification({ - level: NotificationLevel.error, - title: `Error while trying to load preset ${preset.name}`, - message: "Please check the console for further details." - })); - console.log(err); - } - }; - -export const savePresetToRemoteInstance = (instance: PatcherInstanceRecord, name: string): AppThunk => - (dispatch) => { - try { - const message = { - address: `${instance.path}/presets/save`, - args: [ - { type: "s", value: name } - ] - }; - oscQueryBridge.sendPacket(writePacket(message)); - } catch (err) { - dispatch(showNotification({ - level: NotificationLevel.error, - title: `Error while trying to save preset ${name}`, - message: "Please check the console for further details." - })); - console.log(err); - } - }; - -export const destroyPresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord): AppThunk => - (dispatch) => { - try { - const message = { - address: `${instance.path}/presets/delete`, - args: [ - { type: "s", value: preset.name } - ] - }; - oscQueryBridge.sendPacket(writePacket(message)); - } catch (err) { - dispatch(showNotification({ - level: NotificationLevel.error, - title: `Error while trying to delete preset ${preset.name}`, - message: "Please check the console for further details." - })); - console.log(err); - } - }; - -export const renamePresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord, name: string): AppThunk => - (dispatch) => { - try { - const message = { - address: `${instance.path}/presets/rename`, - args: [ - { type: "s", value: preset.name }, - { type: "s", value: name } - ] - }; - oscQueryBridge.sendPacket(writePacket(message)); - } catch (err) { - dispatch(showNotification({ - level: NotificationLevel.error, - title: `Error while trying to rename preset ${preset.name} to ${name}`, - message: "Please check the console for further details." - })); - console.log(err); - } - }; - -export const setInitialPresetOnRemoteInstance = (instance: PatcherInstanceRecord, preset: PresetRecord): AppThunk => - (dispatch) => { - try { - const message = { - address: `${instance.path}/presets/initial`, - args: [ - { type: "s", value: preset.name } - ] - }; - oscQueryBridge.sendPacket(writePacket(message)); - } catch (err) { - dispatch(showNotification({ - level: NotificationLevel.error, - title: `Error while trying to set initial preset to ${preset.name}`, - message: "Please check the console for further details." - })); - console.log(err); - } - }; - -export const sendInstanceMessageToRemote = (instance: PatcherInstanceRecord, inportId: string, value: string): AppThunk => - (dispatch) => { - const values = value.split(" ").reduce((values, v) => { - const fv = parseFloat(v.replaceAll(",", ".").trim()); - if (!isNaN(fv)) values.push({ type: "f", value: fv }); - return values; - }, [] as OSCArgument[]); - - if (!values.length) { - dispatch(showNotification({ - title: "Invalid Message Input", - level: NotificationLevel.warn, - message: `Could not send message input "${value}" as it appears to contain non-valid number input. Please provide a single or multiple numbers separated by a space.` - })); - return; - } - - - const message = { - address: `/rnbo/inst/${instance.index}/messages/in/${inportId}`, - args: values - }; - oscQueryBridge.sendPacket(writePacket(message)); - }; - -export const triggerInstanceMidiNoteOnEventOnRemote = (instance: PatcherInstanceRecord, note: number): AppThunk => - () => { - - const midiChannel = 0; - const routeByte = 144 + midiChannel; - const velocityByte = 100; - - const message = { - address: `${instance.path}/midi/in`, - args: [routeByte, note, velocityByte].map(byte => ({ type: "i", value: byte })) - }; - - oscQueryBridge.sendPacket(writePacket(message)); - }; - -export const triggerInstanceMidiNoteOffEventOnRemote = (instance: PatcherInstanceRecord, note: number): AppThunk => - () => { - - const midiChannel = 0; - const routeByte = 128 + midiChannel; - const velocityByte = 0; - - const message = { - address: `${instance.path}/midi/in`, - args: [routeByte, note, velocityByte].map(byte => ({ type: "i", value: byte })) - }; - - oscQueryBridge.sendPacket(writePacket(message)); - }; - -export const setInstanceParameterValueNormalizedOnRemote = throttle((instance: PatcherInstanceRecord, param: ParameterRecord, value: number): AppThunk => - (dispatch) => { - - const message = { - address: `${param.path}/normalized`, - args: [ - { type: "f", value } - ] - }; - - oscQueryBridge.sendPacket(writePacket(message)); - // optimistic local state update - dispatch(setInstanceParameter(param.setNormalizedValue(value))); - }, 10); - -export const setInstanceDataRefValueOnRemote = (instance: PatcherInstanceRecord, dataref: DataRefRecord, file?: DataFileRecord): AppThunk => - () => { - - const message = { - address: `${instance.path}/data_refs/${dataref.id}`, - args: [ - { type: "s", value: file?.fileName || "" } // no files unsets - ] - }; - - oscQueryBridge.sendPacket(writePacket(message)); - }; - -export const setInstanceParameterMetaOnRemote = (_instance: PatcherInstanceRecord, param: ParameterRecord, value: string): AppThunk => - () => { - const message = { - address: `${param.path}/meta`, - args: [ - { type: "s", value } - ] - }; - - oscQueryBridge.sendPacket(writePacket(message)); - }; - -export const restoreDefaultParameterMetaOnRemote = (_instance: PatcherInstanceRecord, param: ParameterRecord): AppThunk => - () => { - const message = { - address: `${param.path}/meta`, - args: [ - { type: "s", value: "" } - ] - }; - - oscQueryBridge.sendPacket(writePacket(message)); - }; - -export const activateParameterMIDIMappingFocus = (instance: PatcherInstanceRecord, param: ParameterRecord): AppThunk => - (dispatch, getState) => { - - const state = getState(); - const params = getInstanceParameters(state, instance.index); - - dispatch(setInstanceParameters( - params.valueSeq().toArray().map(p => p.setWaitingForMidiMapping(p.id === param.id)) - )); - }; - -export const clearParameterMidiMappingOnRemote = (id: PatcherInstanceRecord["id"], paramId: ParameterRecord["id"]): AppThunk => - (_dispatch, getState) => { - const state = getState(); - const instance = getInstance(state, id); - if (!instance) return; - - const param = getParameter(state, paramId); - if (!param) return; - - const meta = param.getParsedMetaObject(); - delete meta.midi; - const message = { - address: `${param.path}/meta`, - args: [ - { type: "s", value: JSON.stringify(meta) } - ] - }; - - oscQueryBridge.sendPacket(writePacket(message)); - }; - -export const setInstanceMessagePortMetaOnRemote = (_instance: PatcherInstanceRecord, port: MessagePortRecord, value: string): AppThunk => - () => { - const message = { - address: `${port.path}/meta`, - args: [ - { type: "s", value } - ] - }; - - oscQueryBridge.sendPacket(writePacket(message)); - }; - -export const restoreDefaultMessagePortMetaOnRemote = (_instance: PatcherInstanceRecord, port: MessagePortRecord): AppThunk => - () => { - const message = { - address: `${port.path}/meta`, - args: [ - { type: "s", value: "" } - ] - }; - - oscQueryBridge.sendPacket(writePacket(message)); - }; - -// Updates in response to remote OSCQuery Updates -export const updateInstancePresetEntries = (index: number, entries: OSCQueryRNBOInstancePresetEntries): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const instance = getInstanceByIndex(state, index); - if (!instance) return; - - dispatch(setInstance(instance.updatePresets(entries))); - } catch (e) { - console.log(e); - } - }; - -export const updateInstancePresetLatest = (index: number, name: string): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const instance = getInstanceByIndex(state, index); - if (!instance) return; - - dispatch(setInstance(instance.setPresetLatest(name))); - } catch (e) { - console.log(e); - } - }; - -export const updateInstancePresetInitial = (index: number, name: string): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const instance = getInstanceByIndex(state, index); - if (!instance) return; - - dispatch(setInstance(instance.setPresetInitial(name))); - } catch (e) { - console.log(e); - } - }; - -export const updateInstanceMessages = (index: number, desc: OSCQueryRNBOInstance["CONTENTS"]["messages"]): AppThunk => - (dispatch, getState) => { - try { - if (!desc) return; - - const state = getState(); - const instance = getInstanceByIndex(state, index); - if (!instance) return; - - const currentMessageInports = getInstanceMessageInports(state, instance.index); - const currentMessageOutports = getInstanceMessageOutports(state, instance.index); - dispatch(deleteInstanceMessageInports(currentMessageInports.valueSeq().toArray())); - dispatch(deleteInstanceMessageOutports(currentMessageOutports.valueSeq().toArray())); - - const messageInports = MessagePortRecord.fromDescription(desc.CONTENTS?.in); - const messageOutports = MessagePortRecord.fromDescription(desc.CONTENTS?.out); - - dispatch(setInstanceMessageInports(messageInports)); - dispatch(setInstanceMessageOutports(messageOutports)); - - } catch (e) { - console.log(e); - } - }; - -export const removeInstanceMessageInportByPath = (path: string): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const port = getMessageInportByPath(state, path); - if (!port) return; - - dispatch(deleteInstanceMessageInport(port)); - } catch (e) { - console.log(e); - } - }; - -export const removeInstanceMessageOutportByPath = (path: string): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const port = getMessageOutportByPath(state, path); - if (!port) return; - - dispatch(deleteInstanceMessageOutport(port)); - } catch (e) { - console.log(e); - } - }; - -export const updateInstanceMessageOutportValue = (index: number, tag: MessagePortRecord["tag"], value: OSCValue | OSCValue[]): AppThunk => - (dispatch, getState) => { - try { - - const state = getState(); - - // Debug enabled?! - const enabled = getAppSetting(state, AppSetting.debugMessageOutput)?.value || false; - if (!enabled) return; - - // Active Instance view?! - if (Router.pathname !== "/instances/[index]" || Router.query.index !== `${index}`) return; - - const instance = getInstanceByIndex(state, index); - if (!instance) return; - - const port = getInstanceMessageOutportByTag(state, instance.index, tag); - if (!port) return; - - dispatch(setInstanceMessageOutport(port.setValue(Array.isArray(value) ? value.join(", ") : `${value}`))); - } catch (e) { - console.log(e); - } - }; - -export const removeInstanceParameterByPath = (path: string): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const param = getParameterByPath(state, path); - if (!param) return; - - dispatch(deleteInstanceParameter(param)); - } catch (e) { - console.log(e); - } - }; - -export const updateInstanceParameters = (index: number, desc: OSCQueryRNBOInstance["CONTENTS"]["params"]): AppThunk => - (dispatch, getState) => { - try { - if (!desc) return; - - const state = getState(); - const instance = getInstanceByIndex(state, index); - if (!instance) return; - - const currentParams = getInstanceParameters(state, instance.index); - dispatch(deleteInstanceParameters(currentParams.valueSeq().toArray())); - - const newParams = ParameterRecord.fromDescription(instance.index, desc); - dispatch(setInstanceParameters(newParams)); - } catch (e) { - console.log(e); - } - }; - -export const updateInstanceDataRefValue = (index: number, name: string, value: string): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - - const instance = getInstanceByIndex(state, index); - if (!instance) return; - - dispatch(setInstance( - instance.setDataRefValue(name, value) - )); - } catch (e) { - console.log(e); - } - }; - -export const updateInstanceParameterValue = (index: number, name: ParameterRecord["name"], value: number): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const param = getInstanceParameterByName(state, index, name); - if (!param) return; - - dispatch(setInstanceParameter(param.setValue(value))); - } catch (e) { - console.log(e); - } - }; - -export const updateInstanceParameterValueNormalized = (index: number, name: ParameterRecord["name"], value: number): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const param = getInstanceParameterByName(state, index, name); - if (!param) return; - - dispatch(setInstanceParameter(param.setNormalizedValue(value))); - } catch (e) { - console.log(e); - } - }; - -export const setInstanceWaitingForMidiMappingOnRemote = (id: PatcherInstanceRecord["id"], value: boolean): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const instance = getInstance(state, id); - if (!instance) return; - - dispatch(setInstance(instance.setWaitingForMapping(value))); - const params = getInstanceParameters(state, instance.index).valueSeq().map(p => p.setWaitingForMidiMapping(false)); - dispatch(setInstanceParameters(params.toArray())); - - try { - const message = { - address: `${instance.path}/midi/last/report`, - args: [ - { type: value ? "T" : "F", value: value ? "true" : "false" } - ] - }; - oscQueryBridge.sendPacket(writePacket(message)); - } catch (err) { - dispatch(showNotification({ - level: NotificationLevel.error, - title: "Error while trying set midi mapping mode on remote", - message: "Please check the console for further details." - })); - console.log(err); - } - } catch (e) { - console.log(e); - } - }; - -export const updateInstanceMIDIReport = (index: number, value: boolean): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const instance = getInstanceByIndex(state, index); - if (!instance) return; - dispatch(setInstance(instance.setWaitingForMapping(value))); - const params = getInstanceParameters(state, instance.index).valueSeq().map(p => p.setWaitingForMidiMapping(false)); - dispatch(setInstanceParameters(params.toArray())); - } catch (e) { - console.log(e); - } - }; - -export const updateInstanceMIDILastValue = (index: number, value: string): AppThunk => - (dispatch, getState) => { - try { - - const state = getState(); - - const instance = getInstanceByIndex(state, index); - if (!instance?.waitingForMidiMapping) return; - - const midiMeta = JSON.parse(value); - - // find waiting, update their meta, set them no longer waiting and update map - const parameters: ParameterRecord[] = []; - getInstanceParameters(state, instance.index).forEach(param => { - if (param.waitingForMidiMapping) { - const meta = param.getParsedMetaObject(); - meta.midi = midiMeta; - - const message = { - address: `${param.path}/meta`, - args: [ - { type: "s", value: JSON.stringify(meta) } - ] - }; - - oscQueryBridge.sendPacket(writePacket(message)); - parameters.push(param.setWaitingForMidiMapping(false)); - } - }); - - dispatch(setInstanceParameters(parameters)); - - } catch (e) { - console.log(e); - } - }; - -export const updateInstanceParameterMeta = (index: number, name: ParameterRecord["name"], value: string): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const param = getInstanceParameterByName(state, index, name); - if (!param) return; - - dispatch(setInstanceParameter(param.setMeta(value))); - } catch (e) { - console.log(e); - } - }; - -export const updateInstanceMessageOutportMeta = (index: number, tag: MessagePortRecord["tag"], value: string): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const instance = getInstanceByIndex(state, index); - if (!instance) return; - - const port = getInstanceMessageOutportByTag(state, instance.index, tag); - if (!port) return; - - dispatch(setInstanceMessageOutport(port.setMeta(value))); - } catch (e) { - console.log(e); - } - }; - -export const updateInstanceMessageInportMeta = (index: number, tag: MessagePortRecord["tag"], value: string): AppThunk => - (dispatch, getState) => { - try { - const state = getState(); - const instance = getInstanceByIndex(state, index); - if (!instance) return; - - const port = getInstanceMessageInportByTag(state, instance.index, tag); - if (!port) return; - - dispatch(setInstanceMessageInport(port.setMeta(value))); - } catch (e) { - console.log(e); - } - }; - -// Events from remote diff --git a/src/components/instance/datarefTab.tsx b/src/components/instance/datarefTab.tsx index 9e2829c4..26b5fbe0 100644 --- a/src/components/instance/datarefTab.tsx +++ b/src/components/instance/datarefTab.tsx @@ -4,7 +4,7 @@ import { useAppDispatch } from "../../hooks/useAppDispatch"; import DataRefList from "../dataref/list"; import classes from "./instance.module.css"; import { PatcherInstanceRecord } from "../../models/instance"; -import { setInstanceDataRefValueOnRemote } from "../../actions/instances"; +import { setInstanceDataRefValueOnRemote } from "../../actions/patchers"; import { DataRefRecord } from "../../models/dataref"; import { modals } from "@mantine/modals"; import { DataFileRecord } from "../../models/datafile"; diff --git a/src/components/instance/messageTab.tsx b/src/components/instance/messageTab.tsx index a386f29a..bb4a7a9d 100644 --- a/src/components/instance/messageTab.tsx +++ b/src/components/instance/messageTab.tsx @@ -7,9 +7,9 @@ import { SectionTitle } from "../page/sectionTitle"; import MessageOutportList from "../messages/outportList"; import classes from "./instance.module.css"; import { PatcherInstanceRecord } from "../../models/instance"; -import { sendInstanceMessageToRemote } from "../../actions/instances"; +import { sendInstanceMessageToRemote } from "../../actions/patchers"; import { MessagePortRecord } from "../../models/messageport"; -import { restoreDefaultMessagePortMetaOnRemote, setInstanceMessagePortMetaOnRemote } from "../../actions/instances"; +import { restoreDefaultMessagePortMetaOnRemote, setInstanceMessagePortMetaOnRemote } from "../../actions/patchers"; export type InstanceMessageTabProps = { instance: PatcherInstanceRecord; diff --git a/src/components/instance/paramTab.tsx b/src/components/instance/paramTab.tsx index a4f7d355..893939c1 100644 --- a/src/components/instance/paramTab.tsx +++ b/src/components/instance/paramTab.tsx @@ -11,7 +11,7 @@ import { setInstanceParameterValueNormalizedOnRemote, setInstanceWaitingForMidiMappingOnRemote, clearParameterMidiMappingOnRemote, activateParameterMIDIMappingFocus -} from "../../actions/instances"; +} from "../../actions/patchers"; import { OrderedSet as ImmuOrderedSet, Map as ImmuMap } from "immutable"; import { setAppSetting } from "../../actions/settings"; import { AppSetting, AppSettingRecord } from "../../models/settings"; diff --git a/src/components/keyroll/modal.tsx b/src/components/keyroll/modal.tsx index 5b60bdc6..918cc479 100644 --- a/src/components/keyroll/modal.tsx +++ b/src/components/keyroll/modal.tsx @@ -2,7 +2,7 @@ import { FunctionComponent, memo, useCallback, useEffect } from "react"; import { useAppDispatch } from "../../hooks/useAppDispatch"; import KeyRoll from "../keyroll"; import { PatcherInstanceRecord } from "../../models/instance"; -import { triggerInstanceMidiNoteOffEventOnRemote, triggerInstanceMidiNoteOnEventOnRemote } from "../../actions/instances"; +import { triggerInstanceMidiNoteOffEventOnRemote, triggerInstanceMidiNoteOnEventOnRemote } from "../../actions/patchers"; import { Group, Modal } from "@mantine/core"; import { useIsMobileDevice } from "../../hooks/useIsMobileDevice"; import { IconElement } from "../elements/icon"; diff --git a/src/components/patchers/index.tsx b/src/components/patchers/index.tsx index f35dafe9..766ce702 100644 --- a/src/components/patchers/index.tsx +++ b/src/components/patchers/index.tsx @@ -1,7 +1,7 @@ import { Alert, Drawer, Group, Stack, Text } from "@mantine/core"; import { FunctionComponent, memo, useCallback } from "react"; import { Seq } from "immutable"; -import { PatcherRecord } from "../../models/patcher"; +import { PatcherExportRecord } from "../../models/patcher"; import { PatcherItem } from "./item"; import { modals } from "@mantine/modals"; import { IconElement } from "../elements/icon"; @@ -10,10 +10,10 @@ import { mdiFileExport } from "@mdi/js"; export type PatcherDrawerProps = { open: boolean; onClose: () => any; - onLoadPatcher: (patcher: PatcherRecord) => any; - onDeletePatcher: (patcher: PatcherRecord) => any; - onRenamePatcher: (patcher: PatcherRecord, newName: string) => any; - patchers: Seq.Indexed; + onLoadPatcher: (patcher: PatcherExportRecord) => any; + onDeletePatcher: (patcher: PatcherExportRecord) => any; + onRenamePatcher: (patcher: PatcherExportRecord, newName: string) => any; + patchers: Seq.Indexed; }; const PatcherDrawer: FunctionComponent = memo(function WrappedPatcherDrawer({ @@ -25,7 +25,7 @@ const PatcherDrawer: FunctionComponent = memo(function Wrapp patchers }: PatcherDrawerProps) { - const onTriggerDeletePatcher = useCallback((p: PatcherRecord) => { + const onTriggerDeletePatcher = useCallback((p: PatcherExportRecord) => { modals.openConfirmModal({ title: "Delete Patcher", centered: true, diff --git a/src/components/patchers/item.tsx b/src/components/patchers/item.tsx index cae1dbe1..5d2c7535 100644 --- a/src/components/patchers/item.tsx +++ b/src/components/patchers/item.tsx @@ -1,16 +1,16 @@ import { ChangeEvent, FormEvent, FunctionComponent, KeyboardEvent, MouseEvent, memo, useCallback, useEffect, useRef, useState } from "react"; import { ActionIcon, Button, Group, Menu, TextInput, Tooltip } from "@mantine/core"; -import { PatcherRecord } from "../../models/patcher"; +import { PatcherExportRecord } from "../../models/patcher"; import { keyEventIsValidForName, replaceInvalidNameChars } from "../../lib/util"; import classes from "./patchers.module.css"; import { IconElement } from "../elements/icon"; import { mdiCheck, mdiClose, mdiDotsVertical, mdiPencil, mdiPlus, mdiTrashCan } from "@mdi/js"; export type PatcherItemProps = { - patcher: PatcherRecord; - onLoad: (p: PatcherRecord) => any; - onDelete: (p: PatcherRecord) => any; - onRename: (p: PatcherRecord, name: string) => any; + patcher: PatcherExportRecord; + onLoad: (p: PatcherExportRecord) => any; + onDelete: (p: PatcherExportRecord) => any; + onRename: (p: PatcherExportRecord, name: string) => any; }; export const PatcherItem: FunctionComponent = memo(function WrappedPatcherItem({ diff --git a/src/controller/oscqueryBridgeController.ts b/src/controller/oscqueryBridgeController.ts index d2a70efa..1a392d19 100644 --- a/src/controller/oscqueryBridgeController.ts +++ b/src/controller/oscqueryBridgeController.ts @@ -6,7 +6,7 @@ import { ReconnectingWebsocket } from "../lib/reconnectingWs"; import { AppStatus, RunnerCmdMethod } from "../lib/constants"; import { OSCQueryRNBOState, OSCQueryRNBOInstance, OSCQueryRNBOJackConnections, OSCQueryRNBOPatchersState, OSCValue, OSCQueryRNBOInstancesMetaState, OSCQueryListValue } from "../lib/types"; import { addPatcherNode, deletePortAliases, initConnections, initNodes, removePatcherNode, setPortAliases, updateSetMetaFromRemote, updateSourcePortConnections, updateSystemOrControlPortInfo } from "../actions/graph"; -import { initPatchers } from "../actions/instances"; +import { initPatchers } from "../actions/patchers"; import { initRunnerConfig, updateRunnerConfig } from "../actions/settings"; import { initSets, setGraphSetLatest, initSetPresets, setGraphSetPresetLatest } from "../actions/sets"; import { initDataFiles } from "../actions/datafiles"; @@ -21,7 +21,7 @@ import { removeInstanceParameterByPath, removeInstanceMessageInportByPath, removeInstanceMessageOutportByPath -} from "../actions/instances"; +} from "../actions/patchers"; import { ConnectionType, PortDirection } from "../models/graph"; import { showNotification } from "../actions/notifications"; import { NotificationLevel } from "../models/notification"; diff --git a/src/models/patcher.ts b/src/models/patcher.ts index 401487ac..85a954e9 100644 --- a/src/models/patcher.ts +++ b/src/models/patcher.ts @@ -3,20 +3,20 @@ import { OSCQueryRNBOPatcher } from "../lib/types"; export const UNLOAD_PATCHER_NAME = ""; -export type PatcherRecordProps = { +export type PatcherExportRecordProps = { name: string; io: [number, number, number, number]; } -export class PatcherRecord extends ImmuRecord({ +export class PatcherExportRecord extends ImmuRecord({ name: "", io: [0, 0, 0, 0] }) { - static fromDescription(name: string, desc: OSCQueryRNBOPatcher): PatcherRecord { - return new PatcherRecord({ + static fromDescription(name: string, desc: OSCQueryRNBOPatcher): PatcherExportRecord { + return new PatcherExportRecord({ name, io: desc.CONTENTS.io.VALUE }); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 037d2158..5f3664e0 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -2,7 +2,7 @@ import { Button, Group, Stack, Text, Tooltip } from "@mantine/core"; import { FunctionComponent, useCallback, useEffect } from "react"; import { useAppDispatch, useAppSelector } from "../hooks/useAppDispatch"; import { RootStateType } from "../lib/store"; -import { getPatchersSortedByName } from "../selectors/instances"; +import { getPatchersSortedByName } from "../selectors/patchers"; import { getConnections, getNodes } from "../selectors/graph"; import GraphEditor from "../components/editor"; import PresetDrawer from "../components/presets"; @@ -19,12 +19,12 @@ import { } from "../actions/editor"; import SetsDrawer from "../components/sets"; import { destroySetPresetOnRemote, loadSetPresetOnRemote, saveSetPresetToRemote, renameSetPresetOnRemote, clearGraphSetOnRemote, destroyGraphSetOnRemote, loadGraphSetOnRemote, renameGraphSetOnRemote, saveGraphSetOnRemote } from "../actions/sets"; -import { destroyPatcherOnRemote, renamePatcherOnRemote } from "../actions/instances"; +import { destroyPatcherOnRemote, renamePatcherOnRemote } from "../actions/patchers"; import { PresetRecord } from "../models/preset"; import { getGraphSetPresetsSortedByName, getGraphSetsSortedByName } from "../selectors/sets"; import { useDisclosure } from "@mantine/hooks"; import PatcherDrawer from "../components/patchers"; -import { PatcherRecord } from "../models/patcher"; +import { PatcherExportRecord } from "../models/patcher"; import { SortOrder } from "../lib/constants"; import { GraphSetRecord } from "../models/set"; import { modals } from "@mantine/modals"; @@ -58,7 +58,7 @@ const Index: FunctionComponent> = () => { const [presetDrawerIsOpen, { close: closePresetDrawer, toggle: togglePresetDrawer }] = useDisclosure(); // Instances - const onAddInstance = useCallback((patcher: PatcherRecord) => { + const onAddInstance = useCallback((patcher: PatcherExportRecord) => { dispatch(loadPatcherNodeOnRemote(patcher)); closePatcherDrawer(); }, [dispatch, closePatcherDrawer]); @@ -181,11 +181,11 @@ const Index: FunctionComponent> = () => { dispatch(renameSetPresetOnRemote(preset, name)); }, [dispatch]); - const onDeletePatcher = useCallback((p: PatcherRecord) => { + const onDeletePatcher = useCallback((p: PatcherExportRecord) => { dispatch(destroyPatcherOnRemote(p)); }, [dispatch]); - const onRenamePatcher = useCallback((p: PatcherRecord, name: string) => { + const onRenamePatcher = useCallback((p: PatcherExportRecord, name: string) => { dispatch(renamePatcherOnRemote(p, name)); }, [dispatch]); diff --git a/src/pages/instances/[index].tsx b/src/pages/instances/[index].tsx index 622831aa..c4096055 100644 --- a/src/pages/instances/[index].tsx +++ b/src/pages/instances/[index].tsx @@ -8,13 +8,13 @@ import classes from "../../components/instance/instance.module.css"; import { getAppStatus } from "../../selectors/appStatus"; import { AppStatus, SortOrder } from "../../lib/constants"; import Link from "next/link"; -import { getInstanceByIndex, getInstanceMessageInports, getInstanceMessageOutports, getInstanceParameters, getInstances } from "../../selectors/instances"; +import { getPatcherInstanceByIndex, getPatcherInstanceParametersByInstanceIndex, getPatcherInstances, getPatcherInstanceMessageInportsByInstanceIndex, getPatcherInstanceMesssageOutportsByInstanceIndex } from "../../selectors/patchers"; import { unloadPatcherNodeByIndexOnRemote } from "../../actions/graph"; import { getAppSetting } from "../../selectors/settings"; import { AppSetting } from "../../models/settings"; import PresetDrawer from "../../components/presets"; import { PresetRecord } from "../../models/preset"; -import { destroyPresetOnRemoteInstance, renamePresetOnRemoteInstance, setInitialPresetOnRemoteInstance, loadPresetOnRemoteInstance, savePresetToRemoteInstance } from "../../actions/instances"; +import { destroyPresetOnRemoteInstance, renamePresetOnRemoteInstance, setInitialPresetOnRemoteInstance, loadPresetOnRemoteInstance, savePresetToRemoteInstance } from "../../actions/patchers"; import { useDisclosure } from "@mantine/hooks"; import { getDataFilesSortedByName } from "../../selectors/datafiles"; import InstanceKeyboardModal from "../../components/keyroll/modal"; @@ -47,15 +47,15 @@ export default function Instance() { sortAttr, sortOrder ] = useAppSelector((state: RootStateType) => { - const currentInstance = getInstanceByIndex(state, instanceIndex); + const currentInstance = getPatcherInstanceByIndex(state, instanceIndex); return [ currentInstance, - currentInstance ? getInstanceParameters(state, currentInstance.index) : undefined, - currentInstance ? getInstanceMessageInports(state, currentInstance.index) : undefined, - currentInstance ? getInstanceMessageOutports(state, currentInstance.index) : undefined, + currentInstance ? getPatcherInstanceParametersByInstanceIndex(state, currentInstance.index) : undefined, + currentInstance ? getPatcherInstanceMessageInportsByInstanceIndex(state, currentInstance.index) : undefined, + currentInstance ? getPatcherInstanceMesssageOutportsByInstanceIndex(state, currentInstance.index) : undefined, getAppStatus(state), - getInstances(state), + getPatcherInstances(state), getDataFilesSortedByName(state, SortOrder.Asc), getAppSetting(state, AppSetting.debugMessageOutput), getAppSetting(state, AppSetting.keyboardMIDIInput), diff --git a/src/reducers/index.ts b/src/reducers/index.ts index 5281ed1e..c9d76a9e 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -3,7 +3,7 @@ import { combineReducers } from "redux"; import { appStatus } from "./appStatus"; import { datafiles } from "./datafiles"; import { editor } from "./editor"; -import { instances } from "./instances"; +import { patchers } from "./patchers"; import { graph } from "./graph"; import { nofitications } from "./notifications"; import { settings } from "./settings"; @@ -14,9 +14,9 @@ export const rootReducer = combineReducers({ appStatus, datafiles, editor, - instances, graph, nofitications, + patchers, settings, sets, transport diff --git a/src/reducers/instances.ts b/src/reducers/instances.ts deleted file mode 100644 index 77a1fe52..00000000 --- a/src/reducers/instances.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { Map as ImmuMap } from "immutable"; -import { PatcherInstanceRecord } from "../models/instance"; -import { InstanceAction, InstanceActionType } from "../actions/instances"; -import { ParameterRecord } from "../models/parameter"; -import { MessagePortRecord } from "../models/messageport"; -import { PatcherRecord } from "../models/patcher"; - -export interface InstanceInstancesState { - instances: ImmuMap; - messageInports: ImmuMap; - messageOutports: ImmuMap; - parameters: ImmuMap; - patchers: ImmuMap; -} - -export const instances = (state: InstanceInstancesState = { - - instances: ImmuMap(), - messageInports: ImmuMap(), - messageOutports: ImmuMap(), - parameters: ImmuMap(), - patchers: ImmuMap() - -}, action: InstanceAction): InstanceInstancesState => { - - switch(action.type) { - - case InstanceActionType.INIT_PATCHERS: { - const { patchers } = action.payload; - - return { - ...state, - patchers: ImmuMap(patchers.map(p => [p.id, p])) - }; - } - - case InstanceActionType.SET_INSTANCE: { - const { instance } = action.payload; - - return { - ...state, - instances: state.instances.set(instance.id, instance) - }; - } - - case InstanceActionType.SET_INSTANCES: { - const { instances } = action.payload; - - return { - ...state, - instances: state.instances.withMutations(map => { - for (const instance of instances) { - map.set(instance.id, instance); - } - }) - }; - } - - case InstanceActionType.DELETE_INSTANCE: { - const { instance } = action.payload; - - return { - ...state, - instances: state.instances.delete(instance.id), - parameters: state.parameters.filter(param => param.instanceIndex !== instance.index), - messageInports: state.messageInports.filter(port => port.instanceIndex !== instance.index), - messageOutports: state.messageOutports.filter(port => port.instanceIndex !== instance.index) - }; - } - - case InstanceActionType.DELETE_INSTANCES: { - const { instances } = action.payload; - const indexSet = new Set(instances.map(i => i.index)); - - return { - ...state, - instances: state.instances.deleteAll(instances.map(d => d.id)), - parameters: state.parameters.filter(param => !indexSet.has(param.instanceIndex)), - messageInports: state.messageInports.filter(port => !indexSet.has(port.instanceIndex)), - messageOutports: state.messageOutports.filter(port => !indexSet.has(port.instanceIndex)) - }; - } - - case InstanceActionType.SET_PARAMETER: { - const { parameter } = action.payload; - - return { - ...state, - parameters: state.parameters.set(parameter.id, parameter) - }; - } - - case InstanceActionType.SET_PARAMETERS: { - const { parameters } = action.payload; - - return { - ...state, - parameters: state.parameters.withMutations(map => { - for (const param of parameters) { - map.set(param.id, param); - } - }) - }; - } - - case InstanceActionType.DELETE_PARAMETER: { - const { parameter } = action.payload; - - return { - ...state, - parameters: state.parameters.delete(parameter.id) - }; - } - - case InstanceActionType.DELETE_PARAMETERS: { - const { parameters } = action.payload; - - return { - ...state, - parameters: state.parameters.deleteAll(parameters.map(d => d.id)) - }; - } - - case InstanceActionType.SET_MESSAGE_INPORT: { - const { port } = action.payload; - - return { - ...state, - messageInports: state.messageInports.set(port.id, port) - }; - } - - case InstanceActionType.SET_MESSAGE_INPORTS: { - const { ports } = action.payload; - - return { - ...state, - messageInports: state.messageInports.withMutations(map => { - for (const port of ports) { - map.set(port.id, port); - } - }) - }; - } - - case InstanceActionType.DELETE_MESSAGE_INPORT: { - const { port } = action.payload; - - return { - ...state, - messageInports: state.messageInports.delete(port.id) - }; - } - - case InstanceActionType.DELETE_MESSAGE_INPORTS: { - const { ports } = action.payload; - - return { - ...state, - messageInports: state.messageInports.deleteAll(ports.map(d => d.id)) - }; - } - - case InstanceActionType.SET_MESSAGE_OUTPORT: { - const { port } = action.payload; - - return { - ...state, - messageOutports: state.messageOutports.set(port.id, port) - }; - } - - case InstanceActionType.SET_MESSAGE_OUTPORTS: { - const { ports } = action.payload; - - return { - ...state, - messageOutports: state.messageOutports.withMutations(map => { - for (const port of ports) { - map.set(port.id, port); - } - }) - }; - } - - case InstanceActionType.DELETE_MESSAGE_OUTPORT: { - const { port } = action.payload; - - return { - ...state, - messageOutports: state.messageOutports.delete(port.id) - }; - } - - case InstanceActionType.DELETE_MESSAGE_OUTPORTS: { - const { ports } = action.payload; - - return { - ...state, - messageOutports: state.messageOutports.deleteAll(ports.map(d => d.id)) - }; - } - - default: - return state; - } -}; diff --git a/src/selectors/instances.ts b/src/selectors/instances.ts deleted file mode 100644 index aa182792..00000000 --- a/src/selectors/instances.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { Map as ImmuMap, Seq } from "immutable"; -import { RootStateType } from "../lib/store"; -import { PatcherInstanceRecord } from "../models/instance"; -import { createSelector } from "reselect"; -import { getPatcherIdsByIndex } from "./graph"; -import { ParameterRecord } from "../models/parameter"; -import { MessagePortRecord } from "../models/messageport"; -import { PatcherRecord } from "../models/patcher"; -import { SortOrder } from "../lib/constants"; - -export const getPatchers = (state: RootStateType): ImmuMap => { - return state.instances.patchers; -}; - -export const getPatcher = createSelector( - [ - getPatchers, - (state: RootStateType, name: string): string => name - ], - (patchers, name): PatcherRecord | undefined => { - return patchers.get(name); - } -); - -const collator = new Intl.Collator("en-US"); -export const getPatchersSortedByName = createSelector( - [ - getPatchers, - (state: RootStateType, order: SortOrder): SortOrder => order - ], - (patchers, order): Seq.Indexed => { - return patchers.valueSeq().sort((pA, pB) => { - return collator.compare(pA.name.toLowerCase(), pB.name.toLowerCase()) * (order === SortOrder.Asc ? 1 : -1); - }); - } -); - - -export const getInstances = (state: RootStateType): ImmuMap => state.instances.instances; - -export const getInstance = createSelector( - [ - getInstances, - (state: RootStateType, id: PatcherInstanceRecord["id"]): PatcherInstanceRecord["id"] => id - ], - (instances, id): PatcherInstanceRecord | undefined => instances.get(id) -); - -export const getInstanceByIndex = createSelector( - [ - getInstances, - (state: RootStateType, index: PatcherInstanceRecord["index"]): PatcherInstanceRecord["id"] | undefined => state.graph.patcherNodeIdByIndex.get(index) - ], - (instances, id): PatcherInstanceRecord | undefined => id ? instances.get(id) : undefined -); - -export const getInstancesByIndex = createSelector( - [ - getInstances, - getPatcherIdsByIndex - ], - (instances, idsByIndex): ImmuMap => { - return ImmuMap().withMutations(map => { - idsByIndex.forEach((id, index) => { - const node = instances.get(id); - if (node) map.set(index, node); - }); - }); - } -); - -export const getParameters = (state: RootStateType): ImmuMap => state.instances.parameters; - -export const getParameter = createSelector( - [ - getParameters, - (state: RootStateType, id: ParameterRecord["id"]): ParameterRecord["id"] => id - ], - (parameters, id): ParameterRecord | undefined => { - return parameters.get(id); - } -); - -export const getParameterByPath = createSelector( - [ - getParameters, - (state: RootStateType, path: ParameterRecord["path"]): ParameterRecord["path"] => path - ], - (parameters, path): ParameterRecord | undefined => { - return parameters.find(p => p.path === path); - } -); - -export const getInstanceParameters = createSelector( - [ - getParameters, - (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex - ], - (parameters, instanceIndex): ImmuMap => { - return parameters.filter(p => { - return p.instanceIndex === instanceIndex; - }); - } -); - - -export const getInstanceParameterByName = createSelector( - [ - getParameters, - (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex, - (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"], name: ParameterRecord["name"]): ParameterRecord["name"] => name - ], - (parameters, instanceIndex, name): ParameterRecord | undefined => { - return parameters.find(p => p.instanceIndex === instanceIndex && p.name === name); - } -); - -export const getMessageInports = (state: RootStateType): ImmuMap => state.instances.messageInports; - -export const getMessageInport = createSelector( - [ - getMessageInports, - (state: RootStateType, id: MessagePortRecord["id"]): MessagePortRecord["id"] => id - ], - (ports, id): MessagePortRecord | undefined => { - return ports.get(id); - } -); - -export const getMessageInportByPath = createSelector( - [ - getMessageInports, - (state: RootStateType, path: MessagePortRecord["path"]): MessagePortRecord["path"] => path - ], - (ports, path): MessagePortRecord | undefined => { - return ports.find(p => p.path === path); - } -); - -export const getInstanceMessageInports = createSelector( - [ - getMessageInports, - (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex - ], - (ports, instanceIndex): ImmuMap => { - return ports.filter(p => { - return p.instanceIndex === instanceIndex; - }); - } -); - -export const getInstanceMessageInportByTag = createSelector( - [ - getMessageInports, - (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex, - (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"], tag: MessagePortRecord["tag"]): MessagePortRecord["tag"] => tag - ], - (ports, instanceIndex, tag): MessagePortRecord | undefined => { - return ports.find(p => p.instanceIndex === instanceIndex && p.tag === tag); - } -); - -export const getMessageOutports = (state: RootStateType): ImmuMap => state.instances.messageOutports; - -export const getMessageOutport = createSelector( - [ - getMessageOutports, - (state: RootStateType, id: MessagePortRecord["id"]): MessagePortRecord["id"] => id - ], - (ports, id): MessagePortRecord | undefined => { - return ports.get(id); - } -); - -export const getMessageOutportByPath = createSelector( - [ - getMessageOutports, - (state: RootStateType, path: MessagePortRecord["path"]): MessagePortRecord["path"] => path - ], - (ports, path): MessagePortRecord | undefined => { - return ports.find(p => p.path === path); - } -); - -export const getInstanceMessageOutports = createSelector( - [ - getMessageOutports, - (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex - ], - (ports, instanceIndex): ImmuMap => { - return ports.filter(p => { - return p.instanceIndex === instanceIndex; - }); - } -); - -export const getInstanceMessageOutportByTag = createSelector( - [ - getMessageOutports, - (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex, - (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"], tag: MessagePortRecord["tag"]): MessagePortRecord["tag"] => tag - ], - (ports, instanceIndex, tag): MessagePortRecord | undefined => { - return ports.find(p => p.instanceIndex === instanceIndex && p.tag === tag); - } -); diff --git a/src/selectors/patchers.ts b/src/selectors/patchers.ts index 9bd3d73f..a0b45778 100644 --- a/src/selectors/patchers.ts +++ b/src/selectors/patchers.ts @@ -1,19 +1,23 @@ import { Map as ImmuMap, Seq } from "immutable"; import { RootStateType } from "../lib/store"; -import { PatcherRecord } from "../models/patcher"; +import { PatcherInstanceRecord } from "../models/instance"; import { createSelector } from "reselect"; +import { getPatcherIdsByIndex } from "./graph"; +import { ParameterRecord } from "../models/parameter"; +import { MessagePortRecord } from "../models/messageport"; +import { PatcherExportRecord } from "../models/patcher"; import { SortOrder } from "../lib/constants"; -export const getPatchers = (state: RootStateType): ImmuMap => { - return state.patchers.patchers; +export const getPatcherExports = (state: RootStateType): ImmuMap => { + return state.patchers.exports; }; -export const getPatcher = createSelector( +export const getPatcherExport = createSelector( [ - getPatchers, + getPatcherExports, (state: RootStateType, name: string): string => name ], - (patchers, name): PatcherRecord | undefined => { + (patchers, name): PatcherExportRecord | undefined => { return patchers.get(name); } ); @@ -21,12 +25,183 @@ export const getPatcher = createSelector( const collator = new Intl.Collator("en-US"); export const getPatchersSortedByName = createSelector( [ - getPatchers, + getPatcherExports, (state: RootStateType, order: SortOrder): SortOrder => order ], - (patchers, order): Seq.Indexed => { + (patchers, order): Seq.Indexed => { return patchers.valueSeq().sort((pA, pB) => { return collator.compare(pA.name.toLowerCase(), pB.name.toLowerCase()) * (order === SortOrder.Asc ? 1 : -1); }); } ); + + +export const getPatcherInstances = (state: RootStateType): ImmuMap => state.patchers.instances; + +export const getPatcherInstance = createSelector( + [ + getPatcherInstances, + (state: RootStateType, id: PatcherInstanceRecord["id"]): PatcherInstanceRecord["id"] => id + ], + (instances, id): PatcherInstanceRecord | undefined => instances.get(id) +); + +export const getPatcherInstanceByIndex = createSelector( + [ + getPatcherInstances, + (state: RootStateType, index: PatcherInstanceRecord["index"]): PatcherInstanceRecord["id"] | undefined => state.graph.patcherNodeIdByIndex.get(index) + ], + (instances, id): PatcherInstanceRecord | undefined => id ? instances.get(id) : undefined +); + +export const getPatcherInstancesByIndex = createSelector( + [ + getPatcherInstances, + getPatcherIdsByIndex + ], + (instances, idsByIndex): ImmuMap => { + return ImmuMap().withMutations(map => { + idsByIndex.forEach((id, index) => { + const node = instances.get(id); + if (node) map.set(index, node); + }); + }); + } +); + +export const getPatcherInstanceParameters = (state: RootStateType): ImmuMap => state.patchers.instanceParameters; + +export const getPatcherInstanceParameter = createSelector( + [ + getPatcherInstanceParameters, + (state: RootStateType, id: ParameterRecord["id"]): ParameterRecord["id"] => id + ], + (parameters, id): ParameterRecord | undefined => { + return parameters.get(id); + } +); + +export const getPatcherInstanceParameterByPath = createSelector( + [ + getPatcherInstanceParameters, + (state: RootStateType, path: ParameterRecord["path"]): ParameterRecord["path"] => path + ], + (parameters, path): ParameterRecord | undefined => { + return parameters.find(p => p.path === path); + } +); + +export const getPatcherInstanceParametersByInstanceIndex = createSelector( + [ + getPatcherInstanceParameters, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex + ], + (parameters, instanceIndex): ImmuMap => { + return parameters.filter(p => { + return p.instanceIndex === instanceIndex; + }); + } +); + + +export const getPatcherInstanceParametersByInstanceIndexAndName = createSelector( + [ + getPatcherInstanceParameters, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"], name: ParameterRecord["name"]): ParameterRecord["name"] => name + ], + (parameters, instanceIndex, name): ParameterRecord | undefined => { + return parameters.find(p => p.instanceIndex === instanceIndex && p.name === name); + } +); + +export const getPatcherInstanceMessageInports = (state: RootStateType): ImmuMap => state.patchers.instanceMessageInports; + +export const getPatcherInstanceMessageInport = createSelector( + [ + getPatcherInstanceMessageInports, + (state: RootStateType, id: MessagePortRecord["id"]): MessagePortRecord["id"] => id + ], + (ports, id): MessagePortRecord | undefined => { + return ports.get(id); + } +); + +export const getPatcherInstanceMessageInportByPath = createSelector( + [ + getPatcherInstanceMessageInports, + (state: RootStateType, path: MessagePortRecord["path"]): MessagePortRecord["path"] => path + ], + (ports, path): MessagePortRecord | undefined => { + return ports.find(p => p.path === path); + } +); + +export const getPatcherInstanceMessageInportsByInstanceIndex = createSelector( + [ + getPatcherInstanceMessageInports, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex + ], + (ports, instanceIndex): ImmuMap => { + console.log(ports.valueSeq().toArray().map(p => p.toJSON())); + return ports.filter(p => { + return p.instanceIndex === instanceIndex; + }); + } +); + +export const getPatcherInstanceMessageInportsByInstanceIndexAndTag = createSelector( + [ + getPatcherInstanceMessageInports, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"], tag: MessagePortRecord["tag"]): MessagePortRecord["tag"] => tag + ], + (ports, instanceIndex, tag): MessagePortRecord | undefined => { + return ports.find(p => p.instanceIndex === instanceIndex && p.tag === tag); + } +); + +export const getPatcherInstanceMessageOutports = (state: RootStateType): ImmuMap => state.patchers.instanceMessageOutports; + +export const getPatcherInstanceMessageOutport = createSelector( + [ + getPatcherInstanceMessageOutports, + (state: RootStateType, id: MessagePortRecord["id"]): MessagePortRecord["id"] => id + ], + (ports, id): MessagePortRecord | undefined => { + return ports.get(id); + } +); + +export const getPatcherInstanceMessageOutportByPath = createSelector( + [ + getPatcherInstanceMessageOutports, + (state: RootStateType, path: MessagePortRecord["path"]): MessagePortRecord["path"] => path + ], + (ports, path): MessagePortRecord | undefined => { + return ports.find(p => p.path === path); + } +); + +export const getPatcherInstanceMesssageOutportsByInstanceIndex = createSelector( + [ + getPatcherInstanceMessageOutports, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex + ], + (ports, instanceIndex): ImmuMap => { + return ports.filter(p => { + return p.instanceIndex === instanceIndex; + }); + } +); + +export const getPatcherInstanceMesssageOutportsByInstanceIndexAndTag = createSelector( + [ + getPatcherInstanceMessageOutports, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"]): PatcherInstanceRecord["index"] => instanceIndex, + (state: RootStateType, instanceIndex: PatcherInstanceRecord["index"], tag: MessagePortRecord["tag"]): MessagePortRecord["tag"] => tag + ], + (ports, instanceIndex, tag): MessagePortRecord | undefined => { + return ports.find(p => p.instanceIndex === instanceIndex && p.tag === tag); + } +);