Skip to content

Commit

Permalink
feat: Add more controls (#787)
Browse files Browse the repository at this point in the history
* wasm: Add special casing for webp vs gif.

This commit adds special casing around if the image returned is a webp
or a gif. For the wasm based editor, we only support gifs.

* cms: Allow for opening and downloading config.

This commit allows for opening a config.json file to use as a starting
point. It can also be saved using save config.
  • Loading branch information
betterengineering authored May 23, 2023
1 parent d0c74e3 commit 71a6f77
Show file tree
Hide file tree
Showing 14 changed files with 126 additions and 24 deletions.
10 changes: 10 additions & 0 deletions src/features/config/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { update, clear } from './configSlice';
import store from '../../store';

export function setConfig(data) {
store.dispatch(update(data));
}

export function resetConfig() {
store.dispatch(clear());
}
10 changes: 9 additions & 1 deletion src/features/config/configSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ export const configSlice = createSlice({
config[action.payload.id] = action.payload
return state;
},
update: (state = initialState, action) => {
state = action.payload;
return state;
},
clear: (state = initialState, action) => {
state = {};
return state;
},
remove: (state = initialState, action) => {
let config = state;
if (action.payload in config) {
Expand All @@ -21,5 +29,5 @@ export const configSlice = createSlice({
},
});

export const { set, remove } = configSlice.actions;
export const { set, remove, update, clear } = configSlice.actions;
export default configSlice.reducer;
80 changes: 72 additions & 8 deletions src/features/controls/Controls.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { useSelector, useDispatch } from 'react-redux';

import { Button, Stack, Grid } from '@mui/material';
import { Button, Stack } from '@mui/material';
import { resetConfig, setConfig } from '../config/actions';
import { set } from '../config/configSlice';

export default function Controls() {
const preview = useSelector(state => state.preview);
const config = useSelector(state => state.config);
const schema = useSelector(state => state.schema);
const dispatch = useDispatch();

let imageType = 'webp';
if (PIXLET_WASM) {
imageType = 'gif';
}

function downloadPreview() {
const date = new Date().getTime();
Expand All @@ -24,22 +34,76 @@ export default function Controls() {
uint8Array[i] = byteCharacters.charCodeAt(i);
}

const file = new Blob([uint8Array], { type: 'image/webp' });
const file = new Blob([uint8Array], { type: 'image/' + imageType });
element.href = URL.createObjectURL(file);
element.download = `tidbyt-preview-${date}.${imageType}`;
document.body.appendChild(element); // Required for this to work in FireFox
element.click();
}

function downloadConfig() {
const date = new Date().getTime();
const element = document.createElement("a");
const jsonData = config;

// Use Blob object for JSON
const file = new Blob([JSON.stringify(jsonData)], { type: 'application/json' });
element.href = URL.createObjectURL(file);
element.download = `tidbyt-preview-${date}.webp`;
element.download = `config-${date}.json`;
document.body.appendChild(element); // Required for this to work in FireFox
element.click();
}

function selectConfig() {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'application/json';

input.onchange = function (event) {
const file = event.target.files[0];
if (file.type !== "application/json") {
return;
}

const reader = new FileReader();

reader.onload = function () {
let contents = reader.result;
let json = JSON.parse(contents);
console.log(json);
setConfig(json);
};

reader.onerror = function () {
console.log(reader.error);
};

reader.readAsText(file);
};

input.click();
}


function resetSchema() {
history.replaceState(null, '', location.pathname);
window.location.reload();
}
resetConfig();
schema.value.schema.forEach((field) => {
if (field.default) {
dispatch(set({
id: field.id,
value: field.default,
}));
};
});
};

return (
<Stack sx={{ marginTop: '32px' }} spacing={2} direction="row">
<Button variant="contained" onClick={() => downloadPreview()}>Save</Button>
<Button variant="contained" onClick={() => resetSchema()}>Reset</Button>
<Button variant="outlined" onClick={() => selectConfig()}>Open Config</Button>
<Button variant="outlined" onClick={() => downloadConfig()}>Save Config</Button>
<Button variant="outlined" onClick={() => resetSchema()}>Reset</Button>
<Button variant="contained" onClick={() => downloadPreview()}>Export Image</Button>
</Stack>
);
}
7 changes: 6 additions & 1 deletion src/features/preview/Preview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ import styles from './styles.css';
export default function Preview() {
const preview = useSelector(state => state.preview);

let displayType = 'data:image/webp;base64,';
if (PIXLET_WASM) {
displayType = 'data:image/gif;base64,';
}

let webp = 'UklGRhoAAABXRUJQVlA4TA4AAAAvP8AHAAcQEf0PRET/Aw==';
if (preview.value.webp) {
webp = preview.value.webp;
}

return (
<Paper sx={{ bgcolor: "black" }}>
<img src={"data:image/webp;base64," + webp} className={styles.image} />
<img src={displayType + webp} className={styles.image} />
</Paper>
);
}
2 changes: 1 addition & 1 deletion src/features/schema/fields/Color.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function Color({ field }) {
value: field.default,
}));
}
}, [])
}, [config])

const onChange = (value) => {
setColor(value);
Expand Down
2 changes: 1 addition & 1 deletion src/features/schema/fields/DateTime.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function DateTime({ field }) {
if (field.id in config) {
setDateTime(new Date(config[field.id].value));
}
}, []);
}, [config]);

const onChange = (timestamp) => {
if (!timestamp) {
Expand Down
2 changes: 1 addition & 1 deletion src/features/schema/fields/Dropdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default function Dropdown({ field }) {
value: field.default,
}));
}
}, [])
}, [config])

const onChange = (event) => {
setValue(event.target.value);
Expand Down
22 changes: 16 additions & 6 deletions src/features/schema/fields/TextInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@ export default function TextInput({ field }) {

useEffect(() => {
if (field.id in config) {
setValue(config[field.id].value);
if (config[field.id].value != value) {
setValue(config[field.id].value);
}
} else if (field.default) {
setValue(field.default);
if (field.default != value) {
setValue(field.default);
}
console.log("dispatch");
dispatch(set({
id: field.id,
value: field.default,
}));
}
}, [])
}, [config])

const debounce = (callback, wait) => {
let timeoutId = null;
Expand All @@ -39,9 +44,14 @@ export default function TextInput({ field }) {
};
};

const onChange = useCallback(
const onChange = (event) => {
setValue(event.target.value);
debounceConfig(event);
}

const debounceConfig = useCallback(
debounce((event) => {
setValue(event.target.value);
console.log("debounce");
dispatch(set({
id: field.id,
value: event.target.value,
Expand All @@ -51,6 +61,6 @@ export default function TextInput({ field }) {
);

return (
<TextField fullWidth defaultValue={value} label={field.name} variant="outlined" onChange={onChange} />
<TextField fullWidth value={value} label={field.name} variant="outlined" onChange={onChange} />
)
}
2 changes: 1 addition & 1 deletion src/features/schema/fields/Toggle.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function Toggle({ field }) {
value: field.default,
}));
}
}, [])
}, [config])

const onChange = (event) => {
setToggle(event.target.checked);
Expand Down
2 changes: 1 addition & 1 deletion src/features/schema/fields/Typeahead.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function Typeahead({ field }) {
if (field.id in config) {
setValue(JSON.parse(config[field.id].value));
}
}, [])
}, [config])

const onChange = (event, newValue) => {
if (newValue) {
Expand Down
2 changes: 1 addition & 1 deletion src/features/schema/fields/location/LocationBased.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function LocationBased({ field }) {
}));
}
callHandler(field.id, field.handler, JSON.stringify(locationValue));
}, [])
}, [config])

const setPart = (partName, partValue) => {
let newLocationValue = { ...locationValue };
Expand Down
2 changes: 1 addition & 1 deletion src/features/schema/fields/location/LocationForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default function LocationForm({ field }) {
value: field.default,
}));
}
}, [])
}, [config])

const setPart = (partName, partValue) => {
let newValue = { ...value };
Expand Down
2 changes: 1 addition & 1 deletion src/features/schema/fields/oauth2/OAuth2.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function OAuth2({ field }) {
if (field.id in config) {
setLoggedIn(config[field.id].value);
}
}, [])
}, [config])

const onSuccess = (response) => {
if (!response.code) {
Expand Down
5 changes: 5 additions & 0 deletions src/features/schema/fields/photoselect/PhotoSelect.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export default function PhotoSelect({ field }) {
id: field.id,
value: croppedImage,
}))
} else if (field.default) {
dispatch(set({
id: field.id,
value: field.default,
}));
} else {
dispatch(remove(field.id));
}
Expand Down

0 comments on commit 71a6f77

Please sign in to comment.