Skip to content

Commit

Permalink
composite action + loadaction fix
Browse files Browse the repository at this point in the history
  • Loading branch information
DamianoNaraku committed Dec 11, 2023
1 parent 4aeb796 commit a83b96e
Show file tree
Hide file tree
Showing 15 changed files with 155 additions and 20 deletions.
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function App(props: AllProps) {
})();
})

if(DUser.current) {
if(user) {
return(<div className={'d-flex flex-column h-100 p-1 REACT-ROOT' + (props.debug ? ' debug' : '')}
onClick={e => statehistory.globalcanundostate = true}>
{isLoading && <Loader />}
Expand Down
2 changes: 2 additions & 0 deletions src/components/navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import LogoImage from '../../static/img/logo.png';
import {FakeStateProps} from '../../joiner/types';
import PersistanceApi from "../../api/persistance";
import {SaveManager} from "../topbar/SaveManager";
import Examples from "./tabs/Examples";

let clickTimestamps: number[] = [];
const clicksRequired = 2;
Expand Down Expand Up @@ -50,6 +51,7 @@ function NavbarComponent(props: AllProps) {
<li tabIndex={-1} className={'dropdown-item'} onClick={(e) => SaveManager.load()}>
Load
</li>
<Examples />
</ul>
</li>
}
Expand Down
96 changes: 96 additions & 0 deletions src/components/navbar/tabs/Examples.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React, {Dispatch, ReactElement} from 'react';
import {DState} from '../../../redux/store';
import {connect} from 'react-redux';
import {LoadAction, TRANSACTION} from '../../../redux/action/action';
import stateExamples from '../../../examples';
import { statechartplus } from '../../../examples/statechartplus';
import { viewAsEdge } from '../../../examples/viewAsEdge';
import { sequence } from '../../../examples/sequence';
import { conflictsimulation } from '../../../examples/conflictsimulation';
import {DProject, DUser, GObject, LPointerTargetable, LProject, LUser} from '../../../joiner';

// only for states before User and Project management
function loadOldState(obj: GObject, name: string = "oldSave"): void {
let project: LProject;
let dproject: DProject;
let user: LUser;
TRANSACTION(()=>{
LoadAction.new(obj);
let duser = DUser.new('adminOffline', "Pointer_adminOffline");
DUser.current = duser.id;
// user = LPointerTargetable.fromPointer(DUser.current);
user = LPointerTargetable.fromD(duser);
dproject = DProject.new(name, duser.id);
project = LPointerTargetable.fromD(dproject);
user.projects = [...user.projects, project];
user.project = project;
})
setTimeout(()=> TRANSACTION(()=>{
if (!project) return // only if first transaction failed
console.log("loadOldState", {g: obj.graphs, m: obj.models, v: obj.viewelements, project, dproject, user});
project.graphs = obj.graphs;
project.models = obj.models;
project.views = obj.viewelements;
}), 1);

}
// for new saves
function loadState(obj: GObject, name: string = "oldSave"): void { LoadAction.new(obj); }

function ExamplesComponent(props: AllProps) {

const load = (state: string) => {
return LoadAction.new(JSON.parse(state));
}
const setExample = (example: number) => {
switch(example) {
case 1:
return load(stateExamples.first);
case 2:
return load(stateExamples.second);
default:
return;
}

}

return (
<li tabIndex={-1} className={'dropdown-item'}>Examples
<i className={'ms-auto bi bi-caret-right-fill'} />
<ul className={'submenu dropdown-menu'}>
<li tabIndex={-1} onClick={e => setExample(1)} className={'dropdown-item'}>Simplified Class Diagram</li>
<li tabIndex={-1} onClick={e => setExample(2)} className={'dropdown-item'}>Nodes & Edges</li>
<li tabIndex={-1} onClick={e => loadOldState(statechartplus, "Statechart+")} className={'dropdown-item'}>Student statechart++</li>
<li tabIndex={-1} onClick={e => loadOldState(viewAsEdge, "View as edge")} className={'dropdown-item'}>View Object as Edge</li>
<li tabIndex={-1} onClick={e => loadOldState(conflictsimulation, "Conflict simulation")} className={'dropdown-item'}>Conflict simulation</li>
{false && <li tabIndex={-1} onClick={e => loadOldState(sequence, "Sequence diagram")} className={'dropdown-item'}>Sequence</li>}
</ul>
</li>)
}

interface OwnProps {}
interface StateProps {}
interface DispatchProps {}
type AllProps = OwnProps & StateProps & DispatchProps;


function mapStateToProps(state: DState, ownProps: OwnProps): StateProps {
return {};
}

function mapDispatchToProps(dispatch: Dispatch<any>): DispatchProps {
const ret: DispatchProps = {};
return ret;
}


export const ExamplesConnected = connect<StateProps, DispatchProps, OwnProps, DState>(
mapStateToProps,
mapDispatchToProps
)(ExamplesComponent);

export const Examples = (props: OwnProps, children: (string | React.Component)[] = []): ReactElement => {
return <ExamplesConnected {...{...props, children}} />;
}

export default Examples;
2 changes: 2 additions & 0 deletions src/components/navbar/tabs/File.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {DState, DUser, LGraph, LProject, LUser, Selectors, U} from "../../../joi
import DockManager from "../../abstract/DockManager";
import {connect} from "react-redux";
import {FakeStateProps} from "../../../joiner/types";
import Examples from './Examples';

function FileComponent(props: AllProps) {
const user = props.user;
Expand Down Expand Up @@ -89,6 +90,7 @@ function FileComponent(props: AllProps) {
</li>}
<li tabIndex={-1} onClick={save} className={'dropdown-item'}>Save</li>
<li tabIndex={-1} onClick={load} className={'dropdown-item'}>Load</li>
<Examples />
<li tabIndex={-1} className={'dropdown-item'}>Export
<i className={'ms-auto bi bi-caret-right-fill'} />
<ul className={'submenu dropdown-menu'}>
Expand Down
1 change: 1 addition & 0 deletions src/examples/conflictsimulation.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/examples/examples/first.ts

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/examples/examples/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import first from './first';
import {statechartplus} from "./statechartplus";
import {sequence} from "./sequence";
import second from './second';

export default class stateExamples {
static first = first;
static second = second;
static stateChartPlus = statechartplus;
static sequence = sequence;
}
2 changes: 2 additions & 0 deletions src/examples/examples/second.ts

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/examples/examples/sequence.ts

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/examples/examples/statechartplus.ts

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/examples/viewAsEdge.ts

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion src/joiner/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1769,7 +1769,6 @@ export class DProject extends DPointerTargetable {
.DPointerTargetable().DProject(name, author).end();
}
}

@RuntimeAccessible
export class LProject<Context extends LogicContext<DProject> = any, D extends DProject = DProject> extends LPointerTargetable {
public static cname: string = 'LProject';
Expand Down
11 changes: 8 additions & 3 deletions src/model/logicWrapper/LModelElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3534,18 +3534,22 @@ export class LModel<Context extends LogicContext<DModel> = any, C extends Contex
private static otherObectsAccessedKeys: DocString<"className">[] = [];
// public otherObjectsSetup(){ LModel.otherObjectsTemp = undefined; LModel.otherObectsAccessedKeys = []; }
otherObjects!: LObject[];
__info_of__otherObjects: Info = {type:"LObject[]", txt:<div>Read this.instancesOf documentation first.
otherInstances!: LObject[];
__info_of__otherObjects: Info = {type:"LObject[]", txt:<div>Alias for this.otherInstances.</div>};
__info_of__otherInstances: Info = {type:"LObject[]", txt:<div>Read this.instancesOf documentation first.
<br/>Retrieves all the objects not obtained between previous calls of this.instancesOf and the last call of this method.
<br/>Meaning calling it twice without any instancesOf in between, it will return all objects.</div>};

public get_otherObjects(c: Context): ()=>LObject[]{
public get_otherObjects(c: Context): ()=>LObject[]{ return this.get_otherInstances(c); }
public get_otherInstances(c: Context): ()=>LObject[]{
return ()=>{
let ret: LObject[];
if (!LModel.otherObjectsTemp) { ret = this.get_allSubObjects(c); }
else {
let dict = {...LModel.otherObjectsTemp};
for (let key in LModel.otherObectsAccessedKeys) delete dict[key];
for (let key of LModel.otherObectsAccessedKeys) delete dict[key];
delete (LModel as any).otherObjectsTemp;
delete (LModel as any).otherObectsAccessedKeys;
ret = Object.values(dict).flat();
}
return ret;
Expand All @@ -3564,6 +3568,7 @@ export class LModel<Context extends LogicContext<DModel> = any, C extends Contex
// make it more general, first make a dictionary holding all selected types as keys, including "_other"
// then a SEPARATE (split this) function to return only the selected keys, merging the subarrays in the global naming instance map.
LModel.otherObjectsTemp = {};
LModel.otherObectsAccessedKeys = [];
// part 1: i add empty arrays for all instances, but not include shapeless objects.
for (let name in namemap) { LModel.otherObjectsTemp[name] = []; } //LPointerTargetable.fromPointer(namemap[name].instances); }
// part 2: for shapeless objs too
Expand Down
6 changes: 3 additions & 3 deletions src/redux/action/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,10 @@ export class Action extends RuntimeAccessibleClass {
export class LoadAction extends Action {
public static cname: string = "LoadAction";
static type = 'LOAD';
static new(state: DState): boolean { return state && new LoadAction(state).fire(); }
static create(state: DState): LoadAction { return state && new LoadAction(state); }
static new(state: DState | GObject): boolean { return state && new LoadAction(state).fire(); }
static create(state: DState | GObject): LoadAction { return state && new LoadAction(state); }

constructor(state: DState, fire: boolean = true) {
constructor(state: DState | GObject, fire: boolean = true) {
super('', state, '');
this.className = (this.constructor as typeof RuntimeAccessibleClass).cname || this.constructor.name;
if (fire) this.fire();
Expand Down
36 changes: 24 additions & 12 deletions src/redux/reducer/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ let windoww = window as any;
let U: typeof UType = windoww.U;


function deepCopyButOnlyFollowingPath(oldStateDoNotModify: DState, action: ParsedAction, prevAction: ParsedAction, newVal: any): DState {
function deepCopyButOnlyFollowingPath(oldStateDoNotModify: DState, action: ParsedAction, prevAction: ParsedAction, newVal: any): DState | false{
let newRoot: DState = {...oldStateDoNotModify} as DState;
let current: any = newRoot;
if (!action.path?.length) throw new MyError("path length must be at least 1", {action});
Expand Down Expand Up @@ -71,6 +71,13 @@ function deepCopyButOnlyFollowingPath(oldStateDoNotModify: DState, action: Parse
let oldValue: any;
// let unpointedElement: DPointerTargetable | undefined;
// perform final assignment
if (action.type === CreateElementAction.type && current[key]) {
oldValue = current[key];
gotChanged = false;
Log.ee("rejected CreateElementAction, rollback occurring:", {action,
preexistingValue: current[key], isShallowEqual: current[key] === action.value });
return false; // warning: use return only when you want to abort and skip subsequent CompositeAction sub-actions like now.
}
if (isArrayAppend) {
gotChanged = true;
if (!Array.isArray(current[key])) { current[key] = []; }
Expand Down Expand Up @@ -117,10 +124,11 @@ function deepCopyButOnlyFollowingPath(oldStateDoNotModify: DState, action: Parse
*/
}
} else
// value not changed
if (action.type === DeleteElementAction.type ? !(key in current) : current[key] === newVal) {
gotChanged = false;
// value not changed
} else {
// value changed
// todo: caso in cui setto manualmente classes.1 = pointer;
// the latest element is array and not DPointerTargetable, so might need to buffer upper level in the tree? or instead of "current" keep an array of sub-objects encountered navigating the path in state.
oldValue = current[key];
Expand Down Expand Up @@ -190,27 +198,29 @@ function CompositeActionReducer(oldState: DState, actionBatch: CompositeAction):

// estraggo le azioni derivate
let derivedActions: ParsedAction[] = [];
let newState = oldState;
for (let action of actions) {
switch (action.type){
default: break;
case LoadAction.type: return action.value;
case LoadAction.type: newState = action.value; break;
case CreateElementAction.type:
const elem: DPointerTargetable = action.value;
delete DPointerTargetable.pendingCreation[elem.id];
/*
if (oldState.idlookup[elem.id]) {
console.error("rejected CreateElementAction, roolback occurring:", {action, elem:{...elem},
preexistingValue: {...oldState.idlookup[elem.id]}, isEqual: elem === oldState.idlookup[elem.id] });
return oldState;
/*
Log.ee("rejected CreateElementAction, rollback occurring:", {action, elem:{...elem},
preexistingValue: {...oldState.idlookup[elem.id]}, isEqual: elem === oldState.idlookup[elem.id] });
return oldState; // warning: use return only when you want to abort and skip subsequent CompositeAction sub-actions like now.
action.value = "An element with that id already existed.";
action.path = action.field = "CreateActionRejected";
action.className = SetRootFieldAction.name;
action.type = SetRootFieldAction.type;
action.pathArray = [action.path]; //a
action.isPointer = false;
// just to log it in undo-redo action list and have a feedback*/
break;
}
// just to log it in undo-redo action list and have a feedback
return oldState;}*/

elem.className = elem.className || (elem.constructor as typeof RuntimeAccessibleClass).cname || elem.constructor.name;
let statefoldername = elem.className.substring(1).toLowerCase() + 's';
derivedActions.push(
Expand Down Expand Up @@ -243,7 +253,7 @@ function CompositeActionReducer(oldState: DState, actionBatch: CompositeAction):
actions = actions.sort( (a1, a2) => U.stringCompare(a1.path, a2.path));

// destrutturo solo i nodi intermedi e solo la prima volta che li incontro (richiede le azioni ordinate in base al path)
let newState = oldState;

for (let i = 0; i < actions.length; i++) {
const prevAction: ParsedAction = actions[i-1];
const action: ParsedAction = actions[i];
Expand All @@ -267,7 +277,9 @@ function CompositeActionReducer(oldState: DState, actionBatch: CompositeAction):
case SetRootFieldAction.type:
case DeleteElementAction.type:
case SetFieldAction.type:
newState = deepCopyButOnlyFollowingPath(newState, action, prevAction, action.value);
let tmp: false | DState = deepCopyButOnlyFollowingPath(newState, action, prevAction, action.value);
if (!tmp) return oldState; // rollback due to invalid action in transaction
newState = tmp;
break;
}

Expand Down

0 comments on commit a83b96e

Please sign in to comment.