Cities2Modding / Gooee

Gooee is a UI framework for Cities Skylines 2
GNU General Public License v3.0
3 stars 3 forks source link

Model is not fully populated on first render using in components #3

Closed 89pleasure closed 7 months ago

89pleasure commented 7 months ago

Seems that the Model is not fully populated on first render of the components.

For example, I do have to use the following code to get a Dictionary of translations from the Model:

if (model.Translations === undefined) {
    return <div></div>;
};
const translations = model.Translations;

Another value i can't get at all for some reason, trying to set the initial value of a useState()

react.useEffect(() => {
    if (model.DisplayModeDelay !== undefined) {
        setDelaySliderValue(millisecondsToSliderValue(model.DisplayModeDelay));
    }
});

I do use this inside a custom component called TabSettings after I init the controller with const { model, update, trigger } = controller();

The full component looks like this:

import React from 'react';
import EtSettingsBox from './components/_et_settings_box.jsx';

const TabSettings = ({ react, controller }) => {

    const { Grid, FormGroup, FormCheckBox, Dropdown, Button, Icon, Slider } = window.$_gooee.framework;
    const { model, update, trigger } = controller();

    const [tooltipCategory, setTooltipCategory] = react.useState("general");
    const [delaySliderValue, setDelaySliderValue] = react.useState("50");

    // Slider vars
    const sliderMin = 1;
    const sliderMax = 100;
    const millisecondsMin = 250;
    const millisecondsMax = 5000;
    const millisecondsToSliderValue = (value) => {
        return Math.round(((value - millisecondsMin) / (millisecondsMax - millisecondsMin)) * (sliderMax - sliderMin) + sliderMin);
    };

    react.useEffect(() => {
        if (model.DisplayModeDelay !== undefined) {
            setDelaySliderValue(millisecondsToSliderValue(model.DisplayModeDelay));
        }
    });

    if (model.Translations === undefined) {
        return <div></div>;
    };
    const translations = model.Translations;

    const onCategoryChanged = (value) => {
        setTooltipCategory(value);
    };

    const calculateDelayInMs = (value) => {
        const milliseconds = Math.round(((value - sliderMin) / (sliderMax - sliderMin)) * (millisecondsMax - millisecondsMin) + millisecondsMin);
        return milliseconds;
    };

    const onDisplayModeDelayChanged = (value) => {
        setDelaySliderValue(value);
        var delayInMs = calculateDelayInMs(value);
        update("DisplayModeDelay", delayInMs.toString());
        trigger("DoSave");
    };

    const onDisplayModeChanged = (value) => {
        update("DisplayMode", value);
        trigger("DoSave");
    };
    const displayModeOptions = [
        {
            label: "Instant",
            value: "instant",
        }, {
            label: "Delayed",
            value: "delayed",
        }, {
            label: "Hotkey",
            value: "hotkey",
        },
    ];
    const onDisplayModeHotkeyChanged = (value) => {
        update("DisplayModeHotkey", value);
        trigger("DoSave");
    };
    const displayModeHotkeyOptions = [
        {
            label: "CTRL",
            value: "CTRL",
        }, {
            label: "SHIFT",
            value: "SHIFT",
        }, {
            label: "ALT",
            value: "ALT",
        },
    ];

    const onSettingsToggle = (name, value) => {
        update(name, value)
        trigger("DoSave");
    };

    const menuButton = (id, title, icon) => {
        return <Button size="sm" style="trans" color={tooltipCategory == id ? 'dark' : null} onClick={() => onCategoryChanged(id)}>
            <div className="d-flex flex-row align-items-center">
                <Icon className="mr-1" size="sm" icon={icon} />
                <p>{title}</p>
            </div>
        </Button>
    };

    const tooltipCategoryContent = [
        {
            name: "general",
            content: <EtSettingsBox title="General" description="Manage tooltips which show up on multiple entities of the game." icon="coui://GameUI/Media/Game/Icons/Information.svg">
                <Grid>
                    <div className="col-6">
                        <div className="my-3">
                            <FormCheckBox className="mb-1" checked={model.ShowCompanyOutput} label={translations.companyOutput} onToggle={value => onSettingsToggle("ShowCompanyOutput", value)} disabled={!model.IsEnabled} />
                            <p className="text-muted fs-sm">The amount of resources the company has in stock.</p>
                        </div>
                        <div className="my-3">
                            <FormCheckBox className="mb-1" checked={model.ShowEmployee} label={translations.employee} onToggle={value => onSettingsToggle("ShowEmployee", value)} disabled={!model.IsEnabled} />
                            <p className="text-muted fs-sm">Shows the amount of employees a buildings has.</p>
                        </div>
                    </div>
                    <div className="col-6">
                        <div className="my-3">
                            <FormCheckBox className="mb-1" checked={model.ShowEfficiency} label={translations.efficiency} onToggle={value => onSettingsToggle("ShowEfficiency", value)} disabled={!model.IsEnabled} />
                            <p className="text-muted fs-sm">Shows the efficiency of buliding in %.</p>
                        </div>
                    </div>
                </Grid>
            </EtSettingsBox>
        },
        {
            name: "citizens",
            content: <EtSettingsBox title={translations.citizen} description="Manage tooltips while hover a citizen." icon="coui://GameUI/Media/Game/Icons/Population.svg">
                <Grid>
                    <div className="col-6"> 
                        <div className="my-3">
                            <FormCheckBox className="mb-1" checked={model.ShowCitizenState} label={translations.citizenState} onToggle={value => onSettingsToggle("ShowCitizenState", value)} disabled={!model.IsEnabled} />
                            <p className="text-muted fs-sm">Shows the current state of the selected citizen.</p>
                        </div>
                        <div className="my-3">
                            <FormCheckBox className="mb-1" checked={model.ShowCitizenWealth} label={translations.citizenWealth} onToggle={value => onSettingsToggle("ShowCitizenWealth", value)} disabled={!model.IsEnabled} />
                            <p className="text-muted fs-sm">Shows the current wealth of the selected citizen.</p>
                        </div>
                        <div className="my-3">
                            <FormCheckBox className="mb-1" checked={model.ShowCitizenType} label={translations.citizenType} onToggle={value => onSettingsToggle("ShowCitizenType", value)} disabled={!model.IsEnabled} />
                            <p className="text-muted fs-sm">Shows which type the selected citizen is.</p>
                        </div>
                    </div>
                    <div className="col-6">
                        <div className="my-3">
                            <FormCheckBox className="mb-1" checked={model.ShowCitizenHappiness} label={translations.citizenHappiness} onToggle={value => onSettingsToggle("ShowCitizenHappiness", value)} disabled={!model.IsEnabled} />
                            <p className="text-muted fs-sm">Shows how happy the selected citizen currently is.</p>
                        </div>
                        <div className="my-3">
                            <FormCheckBox className="mb-1" checked={model.ShowCitizenEducation} label={translations.citizenEducation} onToggle={value => onSettingsToggle("ShowCitizenEducation", value)} disabled={!model.IsEnabled} />
                            <p className="text-muted fs-sm">Shows the education level of the selected citizen.</p>
                        </div>
                    </div>
                </Grid>
            </EtSettingsBox>
        },
        {
            name: "education",
            content: <EtSettingsBox title={translations.school} description="Manage tooltips related to educational buildings." icon="coui://GameUI/Media/Game/Icons/Education.svg">
                <Grid>
                    <div class="col-6">
                        <div className="my-3">
                            <FormCheckBox className="mb-1" label={translations.schoolStudentCapacity} checked={model.ShowSchoolStudentCapacity} onToggle={(value) => onSettingsToggle("ShowSchoolStudentCapacity", value)} disabled={!model.IsEnabled} />
                            <p className="text-muted fs-sm">Shows the currently occupied and available places at schools and universities.</p>
                        </div>
                    </div>
                    <div class="col-6">&nbsp;</div>
                </Grid>
            </EtSettingsBox>
        },
        {
            name: "growables",
            content: <EtSettingsBox title={translations.spawnable} description="Enable related tooltips for growables." icon="coui://GameUI/Media/Game/Icons/Zones.svg">
                <Grid>
                    <div className="col-6">
                        <div className="my-3">
                            <FormCheckBox className="mb-2" checked={model.ShowGrowablesHousehold} label={translations.spawnableHousehold} onToggle={value => onSettingsToggle("ShowGrowablesHousehold", value)} disabled={!model.IsEnabled} />
                        </div>
                        <div className="my-3">
                            <FormCheckBox className="mb-2" checked={model.ShowGrowablesHouseholdDetails} label={translations.spawnableHouseholdDetails} onToggle={value => onSettingsToggle("ShowGrowablesHouseholdDetails", value)} disabled={!model.IsEnabled} />
                        </div>
                        <div className="my-3">
                            <FormCheckBox className="mb-2" checked={model.ShowGrowablesHouseholdWealth} label={translations.spawnableHouseholdWealth} onToggle={value => onSettingsToggle("ShowGrowablesHouseholdWealth", value)} disabled={!model.IsEnabled} />
                        </div>
                        <div className="my-3">
                            <FormCheckBox className="mb-2" checked={model.ShowGrowablesLevel} label={translations.spawnableLevel} onToggle={value => onSettingsToggle("ShowGrowablesLevel", value)} disabled={!model.IsEnabled} />
                        </div>
                    </div>
                    <div className="col-6">
                        <div className="my-3">
                            <FormCheckBox className="mb-2" checked={model.ShowGrowablesLevelDetails} label="Show Detailed Level" onToggle={value => onSettingsToggle("ShowGrowablesLevelDetails", value)} disabled={!model.IsEnabled} />
                        </div>
                        <div className="my-3">
                            <FormCheckBox className="mb-2" checked={model.ShowGrowablesRent} label="Show Rent" onToggle={value => onSettingsToggle("ShowGrowablesRent", value)} disabled={!model.IsEnabled} />
                        </div>
                        <div className="my-3">
                            <FormCheckBox className="mb-2" checked={model.ShowGrowablesBalance} label="Show Balance" onToggle={value => onSettingsToggle("ShowGrowablesBalance", value)} disabled={!model.IsEnabled} />
                        </div>
                        <div className="my-3">
                            <FormCheckBox className="mb-2" checked={model.ShowGrowablesZoneInfo} label="Show Zone Info" onToggle={value => onSettingsToggle("ShowGrowablesZoneInfo", value)} disabled={!model.IsEnabled} />
                        </div>
                    </div>
                </Grid>
            </EtSettingsBox>
        },
        {
            name: "nettool",
            content: <EtSettingsBox title={translations.toolSystem} description="Manage tooltips which show up while in placing roads." icon="coui://GameUI/Media/Game/Icons/RoadsServices.svg">
                <Grid>
                    <div className="col-6">
                        <div className="my-3">
                            <FormCheckBox className="mb-2" label="Show Mode" checked={model.ShowNetToolMode} onToggle={(value) => onSettingsToggle("ShowNetToolMode", value)} disabled={!model.IsEnabled} />
                        </div>
                    </div>
                    <div className="col-6">
                        <div className="my-3">
                            <FormCheckBox className="mb-2" label="Show Elevation" checked={model.ShowNetToolElevation} onToggle={(value) => onSettingsToggle("ShowNetToolElevation", value)} disabled={!model.IsEnabled} />
                        </div>
                    </div>
                </Grid>
            </EtSettingsBox>
        },
        {
            name: "parks",
            content: <EtSettingsBox title={translations.park} description="Shows information related to company data" icon="coui://GameUI/Media/Game/Icons/ParksAndRecreation.svg">
                <FormCheckBox className="mb-2" checked={model.ShowParkMaintenance} label="Show Park Maintenance" onToggle={value => onSettingsToggle("ShowParkMaintenance", value)} disabled={!model.IsEnabled} />
            </EtSettingsBox>
        },
        {
            name: "parking",
            content: <EtSettingsBox title="Parking" description="Enable parking related tooltips." icon="coui://GameUI/Media/Game/Icons/Parking.svg">
                <FormCheckBox className="mb-2" checked={model.ShowParkingFees} label="Show Fees" onToggle={value => onSettingsToggle("ShowParkingFees", value)} disabled={!model.IsEnabled} />
                <FormCheckBox className="mb-2" checked={model.ShowParkingCapacity} label="Show Parking Capacity" onToggle={value => onSettingsToggle("ShowParkingCapacity", value)} disabled={!model.IsEnabled} />
            </EtSettingsBox>
        },
        {
            name: "publictransport",
            content: <EtSettingsBox title="Public Transportation" description="Enable parking related tooltips." icon="coui://GameUI/Media/Game/Icons/TransportationOverview.svg">
                <FormCheckBox className="mb-2" checked={model.ShowPublicTransportWaitingPassengers} label="Show Waiting Passengers" onToggle={value => onSettingsToggle("ShowPublicTransportWaitingPassengers", value)} disabled={!model.IsEnabled} />
                <FormCheckBox className="mb-2" checked={model.ShowPublicTransportWaitingTime} label="Show Waiting Time" onToggle={value => onSettingsToggle("ShowPublicTransportWaitingTime", value)} disabled={!model.IsEnabled} />
            </EtSettingsBox>
        },
        {
            name: "roads",
            content: <EtSettingsBox title="Roads" description="Enable tooltips while hover over roads" icon="coui://GameUI/Media/Game/Icons/Roads.svg">
                <FormCheckBox className="mb-2" checked={model.ShowRoadLength} label="Show Length" onToggle={value => onSettingsToggle("ShowRoadLength", value)} disabled={!model.IsEnabled} />
                <FormCheckBox className="mb-2" checked={model.ShowRoadUpkeep} label="Show Upkeep" onToggle={value => onSettingsToggle("ShowRoadUpkeep", value)} disabled={!model.IsEnabled} />
                <FormCheckBox className="mb-2" checked={model.ShowRoadCondition} label="Show Conditions" onToggle={value => onSettingsToggle("ShowRoadCondition", value)} disabled={!model.IsEnabled} />
            </EtSettingsBox>
        },
        {
            name: "vehicles",
            content: <EtSettingsBox title="Vehicles" description="Enable related tooltips for vehicles." icon="coui://GameUI/Media/Game/Icons/Traffic.svg">
                <FormCheckBox className="mb-2" checked={model.ShowVehicleState} label="Show State" onToggle={value => onSettingsToggle("ShowVehicleState", value)} disabled={!model.IsEnabled} />
                <FormCheckBox className="mb-2" checked={model.ShowVehicleDriver} label="Show Driver" onToggle={value => onSettingsToggle("ShowVehicleDriver", value)} disabled={!model.IsEnabled} />
                <FormCheckBox className="mb-2" checked={model.ShowVehiclePostvan} label="Show Postvan" onToggle={value => onSettingsToggle("ShowVehiclePostvan", value)} disabled={!model.IsEnabled} />
                <FormCheckBox className="mb-2" checked={model.ShowVehicleGarbageTruck} label="Show Garbage Truck" onToggle={value => onSettingsToggle("ShowVehicleGarbageTruck", value)} disabled={!model.IsEnabled} />
                <FormCheckBox className="mb-2" checked={model.ShowVehiclePassengerDetails} label="Show Passenger Details" onToggle={value => onSettingsToggle("ShowVehiclePassengerDetails", value)} disabled={!model.IsEnabled} />
            </EtSettingsBox>
        }
    ];

    return <div>
        <Grid className="h-100">
            <div className="col-3 p-4 bg-black-trans-faded rounded-sm">
                <div class="mb-2">
                    <h2 className="mb-2">General</h2>
                </div>
                <div className="bg-black-trans-less-faded rounded-sm mb-4">
                    <div class="p-4">
                        <FormGroup label="Enable Mod">
                            <p className="text-muted mb-2">Enables a wider layout for the tooltips.</p>
                            <FormCheckBox checked={model.IsEnabled} label="On/Off" onToggle={value => onSettingsToggle("IsEnabled", value)} />
                        </FormGroup>
                    </div>
                </div>
                <div className="bg-black-trans-less-faded rounded-sm mb-4">
                    <div class="p-4">
                        <FormGroup label="Display Mode">
                            <p className="text-muted mb-2">Choose how the tooltip is displayed.</p>
                            <Dropdown options={displayModeOptions} selected={model.DisplayMode} onSelectionChanged={onDisplayModeChanged} />
                        </FormGroup>
                        {model.DisplayMode == "hotkey" ? <FormGroup className="mt-3" label="Hotkey">
                            <Dropdown options={displayModeHotkeyOptions} selected={model.DisplayModeHotkey} onSelectionChanged={onDisplayModeHotkeyChanged} disabled={!model.IsEnabled} />
                        </FormGroup> : null}
                        {model.DisplayMode == 'delayed' ? <FormGroup className="mt-3" label="Delay in ms">
                            <Grid>
                                <div className="col-10">
                                    <Slider value={delaySliderValue} onValueChanged={value => onDisplayModeDelayChanged(Number(value))} />
                                </div>
                                <div className="col-2 align-items-center justify-content-center text-muted">
                                    {calculateDelayInMs(delaySliderValue) + "ms"}
                                </div>
                            </Grid>
                            <small className="form-text text-muted">Some example muted text.</small>
                        </FormGroup>: null}
                    </div>
                </div>
                <div className="bg-black-trans-less-faded rounded-sm mb-4">
                    <div class="p-4">
                        <FormGroup label="Extended Layout">
                            <p className="text-muted mb-2">Enables a wider layout for the tooltips.</p>
                            <FormCheckBox checked={model.UseExtendedLayout} label="On/Off" onToggle={value => onSettingsToggle("UseExtendedLayout", value)} disabled={!model.IsEnabled} />
                        </FormGroup>
                    </div>
                </div>
            </div>
            <div className="col-9 p-4 bg-black-trans-faded rounded-sm">
                <div class="d-flex flex-row align-items-center mb-4">
                    <h2 className="mr-2">Tooltips</h2>
                    <p className="text-muted">Which tooltips you want to enable.</p>
                </div>
                <Grid>
                    <div class="col-3 bg-black-trans-less-faded rounded-sm">
                        <div className="btn-group-vertical w-100">
                            {menuButton("general", "General", "coui://GameUI/Media/Game/Icons/Information.svg")}
                            {menuButton("citizens", translations.citizen, "coui://GameUI/Media/Game/Icons/Population.svg")}
                            {menuButton("education", translations.school, "coui://GameUI/Media/Game/Icons/Education.svg")}
                            {menuButton("growables", translations.spawnable, "coui://GameUI/Media/Game/Icons/Zones.svg")}
                            {menuButton("nettool", translations.toolSystem, "coui://GameUI/Media/Game/Icons/RoadsServices.svg")}
                            {menuButton("parks", translations.park, "coui://GameUI/Media/Game/Icons/ParksAndRecreation.svg")}
                            {menuButton("parking", translations.parkingFacility, "coui://GameUI/Media/Game/Icons/Parking.svg")}
                            {menuButton("publictransport", translations.publicTransport, "coui://GameUI/Media/Game/Icons/TransportationOverview.svg")}
                            {menuButton("roads", translations.road, "coui://GameUI/Media/Game/Icons/Roads.svg")}
                            {menuButton("vehicles", translations.vehicle, "coui://GameUI/Media/Game/Icons/Traffic.svg")}
                        </div>
                    </div>
                    <div class="col-9">
                        {!model.IsEnabled && <div className="alert alert-danger mb-2">
                            <div className="d-flex flex-row align-items-center">
                                <Icon fa className="mr-2" icon="solid-circle-exclamation"></Icon>
                                <div>Mod is globally disabled!</div>
                            </div>
                        </div>}
                        {tooltipCategoryContent.find(x => x['name'] === tooltipCategory).content}
                    </div>
                </Grid>
            </div>
        </Grid>
    </div>;
}

export default TabSettings;
optimus-code commented 7 months ago

New versions pre-populate a blank model that is available before load to prevent these errors. This issue is now resolved. We still have to wait for bindings to wire up and comms to client so nothing can really be done about that.