import { ipcRenderer } from "electron";
import React from "react";
import {
observable,
computed,
runInAction,
action,
makeObservable
} from "mobx";
import { observer } from "mobx-react";
import { keys } from "lodash";
import { humanize } from "eez-studio-shared/string";
import {
showGenericDialog,
FieldComponent
} from "eez-studio-ui/generic-dialog";
import { Tree } from "eez-studio-ui/tree";
import { Button } from "eez-studio-ui/button";
import {
EezObject,
getProperty,
PropertyProps
} from "project-editor/core/object";
import { getObjectFromPath, getProjectStore } from "project-editor/store";
import {
SearchCallbackMessage,
importDirectiveUsageWithImportAs,
importDirectiveUsage
} from "project-editor/core/search";
import type { ImportDirective } from "project-editor/project/project";
export const ImportDirectiveCustomUI = observer((props: PropertyProps) => {
return (
);
});
class UsageTreeNode {
id: string;
label: string;
children: UsageTreeNode[];
selected: boolean;
expanded: boolean;
constructor(label: string, children?: (string | UsageTreeNode)[]) {
makeObservable(this, {
selected: observable,
expanded: observable
});
this.id = label;
this.label = label;
this.children = children
? children.map(child =>
typeof child == "string"
? new UsageTreeNode(child, [])
: child
)
: [];
this.selected = false;
this.expanded = children ? children.length > 0 : false;
}
}
interface IAssetsUsage {
assets: {
[path: string]: string;
};
selectedAsset: string | undefined;
}
const UsageTreeField = observer(
class UsageTreeField extends FieldComponent {
selectedNode: UsageTreeNode | undefined;
constructor(props: any) {
super(props);
makeObservable(this, {
selectedNode: observable,
rootNode: computed
});
}
get rootNode() {
let assetsUsage: IAssetsUsage =
this.props.values[this.props.fieldProperties.name];
return new UsageTreeNode(
"",
keys(assetsUsage.assets)
.sort()
.map(key => {
return new UsageTreeNode(
humanize(key),
assetsUsage.assets[key].split(", ")
);
})
);
}
selectNode = action((node: UsageTreeNode) => {
if (this.selectedNode) {
this.selectedNode.selected = false;
}
this.selectedNode = node;
let assetsUsage: IAssetsUsage =
this.props.values[this.props.fieldProperties.name];
if (this.selectedNode && this.selectedNode.children.length === 0) {
assetsUsage.selectedAsset = this.selectedNode.id;
} else {
assetsUsage.selectedAsset = undefined;
}
if (this.selectedNode) {
this.selectedNode.selected = true;
}
});
render() {
return (
);
}
}
);
class BuildAssetsUssage {
assets: {
[path: string]: Set;
} = {};
assetsUsage: IAssetsUsage = {
assets: {},
selectedAsset: undefined
};
constructor(private importDirective: ImportDirective) {
makeObservable(this, {
assetsUsage: observable
});
}
onMessage(message: SearchCallbackMessage) {
if (this.importDirective.importAs) {
if (message.type == "value") {
let path = message.valueObject.propertyInfo
.referencedObjectCollectionPath! as any;
const assetName = message.valueObject.value;
const prefix = this.importDirective.importAs + ".";
let assetNameWithoutLibraryNamePrefix = assetName.startsWith(
prefix
)
? assetName.slice(prefix.length)
: assetName;
const importedProject = this.importDirective.project!;
let assetsMap = importedProject._assets.maps[
"name"
].allAssetsMaps.find(assetMap => assetMap.path == path);
if (!assetsMap && path == "userWidgets") {
path = "pages";
assetsMap = importedProject._assets.maps[
"name"
].allAssetsMaps.find(assetMap => assetMap.path == path);
}
if (assetsMap) {
const asset = assetsMap.map.get(
assetNameWithoutLibraryNamePrefix
);
if (asset) {
if (path == "variables/globalVariables") {
path = "variables";
}
const set = this.assets[path] ?? new Set();
set.add(assetName);
this.assets[path] = set;
runInAction(
() =>
(this.assetsUsage.assets[path] =
Array.from(set).join(", "))
);
}
}
}
} else {
if (message.type == "value") {
let path =
message.valueObject.propertyInfo
.referencedObjectCollectionPath!;
const importedProject = this.importDirective.project!;
const assetName = message.valueObject.value;
if (
importedProject._assets.maps[
"name"
].assetCollectionPaths.has(path)
) {
let collection = getObjectFromPath(
importedProject,
path.split("/")
) as EezObject[];
if (!collection) {
if (path == "allStyles" || path == "allLvglStyles") {
collection = importedProject[path];
path = "styles";
}
}
const object =
collection &&
collection.find(
object => assetName == getProperty(object, "name")
);
if (object) {
if (path == "variables/globalVariables") {
path = "variables";
}
const set = this.assets[path] ?? new Set();
set.add(assetName);
this.assets[path] = set;
runInAction(
() =>
(this.assetsUsage.assets[path] =
Array.from(set).join(", "))
);
}
}
}
}
return true;
}
}
function showUsage(importDirective: ImportDirective) {
const buildAssetsUsage = new BuildAssetsUssage(importDirective);
const projectStore = getProjectStore(importDirective);
if (importDirective.importAs) {
importDirectiveUsageWithImportAs(
projectStore,
importDirective,
message => buildAssetsUsage.onMessage(message)
);
} else {
importDirectiveUsage(projectStore, message =>
buildAssetsUsage.onMessage(message)
);
}
showGenericDialog({
dialogDefinition: {
title: "Imported Project Assets Usage",
fields: [
{
name: "assetsUsage",
fullLine: true,
type: UsageTreeField
}
]
},
values: {
assetsUsage: buildAssetsUsage.assetsUsage
},
okButtonText: "Search",
okEnabled: result => {
const assetsUsage: IAssetsUsage = result.values.assetsUsage;
return !!assetsUsage.selectedAsset;
}
})
.then(
action(result => {
const assetsUsage: IAssetsUsage = result.values.assetsUsage;
if (assetsUsage.selectedAsset) {
projectStore.uiStateStore.searchPattern =
assetsUsage.selectedAsset;
projectStore.uiStateStore.searchMatchCase = true;
projectStore.uiStateStore.searchMatchWholeWord = true;
projectStore.uiStateStore.replaceEnabled = false;
projectStore.startSearch();
}
})
)
.catch(() => {});
}
function openProject(importDirective: ImportDirective) {
const projectStore = getProjectStore(importDirective);
ipcRenderer.send(
"open-file",
projectStore.getAbsoluteFilePath(importDirective.projectFilePath)
);
}