diff --git a/data/main.jsx b/data/main.jsx index 2f16181e8..404e57b9a 100644 --- a/data/main.jsx +++ b/data/main.jsx @@ -138,12 +138,15 @@ const mainAppStyle = theme => ({ "align-items": "center" }, content: { - flexGrow: 1, padding: theme.spacing.unit * 10, transition: theme.transitions.create('padding-left', { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, - }) + }), + display: "flex", + flexDirection: "row", + flexWrap: "wrap", + rowGap: "10px" }, contentShrinked: { "padding-left": drawerWidth + 10, @@ -178,6 +181,45 @@ const mainAppStyle = theme => ({ hidden: { display: "none" } +});const designStyle = theme => ({ + root: { + "display": "flex", + "flex-direction": "column", + border: "green solid 2px", + borderRadius: "15px", + padding: "10px", + }, + hidden: { + display: "none" + }, + effectsHeader: { + display: "flex", + flexDirection: "row", + borderBottom: "green solid 1px", + columnGap: "5px", + }, + effectsHeaderValue: { + display: "flex", + flexDirection: "row", + columnGap: "3px", + alignItems: "center", + }, + effect: { + display: "flex", + flexDirection: "row", + alignItems: "center", + }, + timeremaining: { + width: "50px" + }, + effectAttribute: { + display: "flex", + flexDirection: "row", + alignItems: "center", + }, + unselected: { + opacity: "30%" + } });const statsStyle = theme => ({ root: { "display": "flex", @@ -458,8 +500,8 @@ const MainApp = withStyles(mainAppStyle)(props => { const { classes } = props; const [drawerOpened, setDrawerOpened] = useState(false); const [mode, setMode] = useState('dark'); - const [stats, setStats] = useState(true); - const [designer, setDesigner] = useState(false); + const [stats, setStats] = useState(false); + const [designer, setDesigner] = useState(true); const [config, setConfig] = useState(false); const [statsRefreshRate, setStatsRefreshRate ] = useState(3); const [maxSamples, setMaxSamples ] = useState(50); @@ -504,9 +546,9 @@ const MainApp = withStyles(mainAppStyle)(props => { Night Driver Strip - - notifications - + + notifications + @@ -535,7 +577,8 @@ const MainApp = withStyles(mainAppStyle)(props => { } - + + {setConfig(false)}} /> @@ -643,6 +686,135 @@ const ConfigDialog = withStyles(configStyle)(props => { ); +});const DesignerPanel = withStyles(designStyle)(props => { + const { classes, siteConfig, open } = props; + const [ effects, setEffects ] = useState(undefined); + const [ abortControler, setAbortControler ] = useState(undefined); + const [ nextRefreshDate, setNextRefreshDate] = useState(undefined); + const [ editing, setEditing ] = useState(false); + const [ pendingInterval, setPendingInterval ] = useState(undefined); + const [ timeRemaining, setTimeRemaining ] = useState(undefined); + + useEffect(() => { + if (abortControler) { + abortControler.abort(); + } + + if (open) { + const aborter = new AbortController(); + setAbortControler(aborter); + + fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/getEffectList`,{signal:aborter.signal}) + .then(resp => resp.json()) + .then(setEffects) + .catch(console.error); + + return () => { + abortControler && abortControler.abort(); + } + } + },[open,nextRefreshDate]); + + useEffect(() => { + if (effects) { + const timeReference = Date.now()+effects.millisecondsRemaining; + var requestSent = false; + const interval = setInterval(()=>{ + const remaining = timeReference-Date.now(); + if (remaining >= 0) { + setTimeRemaining(remaining); + } + if ((remaining <= 100) && !requestSent) { + setNextRefreshDate(Date.now()); + requestSent=true; + } + },50); + return ()=>clearInterval(interval); + } + },[effects]); + + const navigate = (up)=>{ + fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/${up ? "nextEffect" : "previousEffect"}`,{method:"POST"}) + .then(setNextRefreshDate(Date.now())) + .catch(console.error); + } + + const navigateTo = (idx)=>{ + fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/setCurrentEffectIndex?`,{method:"POST", body: new URLSearchParams({currentEffectIndex:idx})}) + .then(setNextRefreshDate(Date.now())) + .catch(console.error); + } + + const updatePendingInterval = (interval)=>{ + fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/settings`,{method:"POST", body: new URLSearchParams({effectInterval:interval})}) + .then(setNextRefreshDate(Date.now())) + .catch(console.error); + } + + const effectEnable = (idx,enable)=>{ + fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/${enable?"enable":"disable"}Effect`,{method:"POST", body:new URLSearchParams({effectIndex:idx})}) + .then(setNextRefreshDate(Date.now())) + .catch(console.error); + } + + if (!effects && open){ + return Loading....; + } + + return effects && + + {editing ? + + setPendingInterval(event.target.value) } /> + + { + updatePendingInterval(pendingInterval); + setEditing(false); + }}> + save + + { + setEditing(false); + }}> + cancel + + + : + + Interval: + {effects.effectInterval} + setEditing(true)}>edit + } + + Time Remaining: + {timeRemaining} + + {(effects.Effects.length > 1) && + navigate(false)}>skip_previous + navigate(true)}>skip_next + } + + {effects.Effects.map((effect,idx) => + + {(idx === effects.currentEffect) ? + effects.Effects.length > 1 && arrow_right_alt: + navigateTo(idx)}>{effect.enabled ? "arrow_right_alt":""}} + + effectEnable(idx,!effect.enabled)}>{effect.enabled ? "check" : "close"} + {effect.name} + + )} + });const StaticStatsPanel = withStyles(staticStatStyle)(props => { const { classes, stat, name, detail } = props; @@ -867,28 +1039,32 @@ const ConfigDialog = withStyles(configStyle)(props => { if (abortControler) { abortControler.abort(); } - const aborter = new AbortController(); - setAbortControler(aborter); - getStats(aborter).then(setStatistics) - .catch(console.error); - - if (timer) { - clearTimeout(timer); - setTimer(undefined); - } - - if (statsRefreshRate.value && open) { - setTimer(setTimeout(() => setLastRefreshDate(Date.now()),statsRefreshRate.value*1000)); - } - - return () => { - timer && clearTimeout(timer); - abortControler && abortControler.abort(); + if (open) { + const aborter = new AbortController(); + setAbortControler(aborter); + + getStats(aborter) + .then(setStatistics) + .catch(console.error); + + if (timer) { + clearTimeout(timer); + setTimer(undefined); + } + + if (statsRefreshRate.value && open) { + setTimer(setTimeout(() => setLastRefreshDate(Date.now()),statsRefreshRate.value*1000)); + } + + return () => { + timer && clearTimeout(timer); + abortControler && abortControler.abort(); + } } },[statsRefreshRate.value, lastRefreshDate, open]); - if (!statistics) { + if (!statistics && open) { return Loading... } diff --git a/platformio.ini b/platformio.ini index 2f6160213..68b2fc16f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -263,9 +263,17 @@ board = lolin_d32_pro monitor_speed = 115200 upload_speed = 921600 build_flags = -DLEDSTRIP=1 + -DMESMERIZER=1 + -DUSEMATRIX=1 + -DUSE_PSRAM=1 + -DBOARD_HAS_PSRAM=1 + -mfix-esp32-psram-cache-issue + -D_IR_ENABLE_DEFAULT_=false -std=gnu++17 -Dregister= ; Sinister: redefine 'register' so FastLED can use that keyword under C++17 -Ofast +lib_deps = ${common.lib_deps} + https://github.com/davepl/SmartMatrix.git [env:generic] board = esp32dev diff --git a/site/src/components/home/designer/designer.jsx b/site/src/components/home/designer/designer.jsx new file mode 100644 index 000000000..8e47d4efb --- /dev/null +++ b/site/src/components/home/designer/designer.jsx @@ -0,0 +1,130 @@ +const DesignerPanel = withStyles(designStyle)(props => { + const { classes, siteConfig, open } = props; + const [ effects, setEffects ] = useState(undefined); + const [ abortControler, setAbortControler ] = useState(undefined); + const [ nextRefreshDate, setNextRefreshDate] = useState(undefined); + const [ editing, setEditing ] = useState(false); + const [ pendingInterval, setPendingInterval ] = useState(undefined); + const [ timeRemaining, setTimeRemaining ] = useState(undefined); + + useEffect(() => { + if (abortControler) { + abortControler.abort(); + } + + if (open) { + const aborter = new AbortController(); + setAbortControler(aborter); + + fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/getEffectList`,{signal:aborter.signal}) + .then(resp => resp.json()) + .then(setEffects) + .catch(console.error); + + return () => { + abortControler && abortControler.abort(); + } + } + },[open,nextRefreshDate]); + + useEffect(() => { + if (effects) { + const timeReference = Date.now()+effects.millisecondsRemaining; + var requestSent = false; + const interval = setInterval(()=>{ + const remaining = timeReference-Date.now(); + if (remaining >= 0) { + setTimeRemaining(remaining); + } + if ((remaining <= 100) && !requestSent) { + setNextRefreshDate(Date.now()); + requestSent=true; + } + },50); + return ()=>clearInterval(interval); + } + },[effects]); + + const navigate = (up)=>{ + fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/${up ? "nextEffect" : "previousEffect"}`,{method:"POST"}) + .then(setNextRefreshDate(Date.now())) + .catch(console.error); + } + + const navigateTo = (idx)=>{ + fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/setCurrentEffectIndex?`,{method:"POST", body: new URLSearchParams({currentEffectIndex:idx})}) + .then(setNextRefreshDate(Date.now())) + .catch(console.error); + } + + const updatePendingInterval = (interval)=>{ + fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/settings`,{method:"POST", body: new URLSearchParams({effectInterval:interval})}) + .then(setNextRefreshDate(Date.now())) + .catch(console.error); + } + + const effectEnable = (idx,enable)=>{ + fetch(`${httpPrefix !== undefined ? httpPrefix : ""}/${enable?"enable":"disable"}Effect`,{method:"POST", body:new URLSearchParams({effectIndex:idx})}) + .then(setNextRefreshDate(Date.now())) + .catch(console.error); + } + + if (!effects && open){ + return Loading....; + } + + return effects && + + {editing ? + + setPendingInterval(event.target.value) } /> + + { + updatePendingInterval(pendingInterval); + setEditing(false); + }}> + save + + { + setEditing(false); + }}> + cancel + + + : + + Interval: + {effects.effectInterval} + setEditing(true)}>edit + } + + Time Remaining: + {timeRemaining} + + {(effects.Effects.length > 1) && + navigate(false)}>skip_previous + navigate(true)}>skip_next + } + + {effects.Effects.map((effect,idx) => + + {(idx === effects.currentEffect) ? + effects.Effects.length > 1 && arrow_right_alt: + navigateTo(idx)}>{effect.enabled ? "arrow_right_alt":""}} + + effectEnable(idx,!effect.enabled)}>{effect.enabled ? "check" : "close"} + {effect.name} + + )} + +}); \ No newline at end of file diff --git a/site/src/components/home/designer/style.jsx b/site/src/components/home/designer/style.jsx new file mode 100644 index 000000000..d8ea3deba --- /dev/null +++ b/site/src/components/home/designer/style.jsx @@ -0,0 +1,40 @@ +const designStyle = theme => ({ + root: { + "display": "flex", + "flex-direction": "column", + border: "green solid 2px", + borderRadius: "15px", + padding: "10px", + }, + hidden: { + display: "none" + }, + effectsHeader: { + display: "flex", + flexDirection: "row", + borderBottom: "green solid 1px", + columnGap: "5px", + }, + effectsHeaderValue: { + display: "flex", + flexDirection: "row", + columnGap: "3px", + alignItems: "center", + }, + effect: { + display: "flex", + flexDirection: "row", + alignItems: "center", + }, + timeremaining: { + width: "50px" + }, + effectAttribute: { + display: "flex", + flexDirection: "row", + alignItems: "center", + }, + unselected: { + opacity: "30%" + } +}); \ No newline at end of file diff --git a/site/src/components/home/home.jsx b/site/src/components/home/home.jsx index 0243cc9ad..9af241ebc 100644 --- a/site/src/components/home/home.jsx +++ b/site/src/components/home/home.jsx @@ -2,8 +2,8 @@ const MainApp = withStyles(mainAppStyle)(props => { const { classes } = props; const [drawerOpened, setDrawerOpened] = useState(false); const [mode, setMode] = useState('dark'); - const [stats, setStats] = useState(true); - const [designer, setDesigner] = useState(false); + const [stats, setStats] = useState(false); + const [designer, setDesigner] = useState(true); const [config, setConfig] = useState(false); const [statsRefreshRate, setStatsRefreshRate ] = useState(3); const [maxSamples, setMaxSamples ] = useState(50); @@ -48,9 +48,9 @@ const MainApp = withStyles(mainAppStyle)(props => { Night Driver Strip - - notifications - + + notifications + @@ -79,7 +79,8 @@ const MainApp = withStyles(mainAppStyle)(props => { } - + + {setConfig(false)}} /> diff --git a/site/src/components/home/statistics/stats.jsx b/site/src/components/home/statistics/stats.jsx index 095b76d8a..51efc03a4 100644 --- a/site/src/components/home/statistics/stats.jsx +++ b/site/src/components/home/statistics/stats.jsx @@ -101,28 +101,32 @@ const StatsPanel = withStyles(statsStyle)(props => { if (abortControler) { abortControler.abort(); } - const aborter = new AbortController(); - setAbortControler(aborter); - getStats(aborter).then(setStatistics) - .catch(console.error); - - if (timer) { - clearTimeout(timer); - setTimer(undefined); - } - - if (statsRefreshRate.value && open) { - setTimer(setTimeout(() => setLastRefreshDate(Date.now()),statsRefreshRate.value*1000)); - } - - return () => { - timer && clearTimeout(timer); - abortControler && abortControler.abort(); + if (open) { + const aborter = new AbortController(); + setAbortControler(aborter); + + getStats(aborter) + .then(setStatistics) + .catch(console.error); + + if (timer) { + clearTimeout(timer); + setTimer(undefined); + } + + if (statsRefreshRate.value && open) { + setTimer(setTimeout(() => setLastRefreshDate(Date.now()),statsRefreshRate.value*1000)); + } + + return () => { + timer && clearTimeout(timer); + abortControler && abortControler.abort(); + } } },[statsRefreshRate.value, lastRefreshDate, open]); - if (!statistics) { + if (!statistics && open) { return Loading... } diff --git a/site/src/components/home/style.jsx b/site/src/components/home/style.jsx index 056d799b9..9061751e2 100644 --- a/site/src/components/home/style.jsx +++ b/site/src/components/home/style.jsx @@ -65,12 +65,15 @@ const mainAppStyle = theme => ({ "align-items": "center" }, content: { - flexGrow: 1, padding: theme.spacing.unit * 10, transition: theme.transitions.create('padding-left', { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, - }) + }), + display: "flex", + flexDirection: "row", + flexWrap: "wrap", + rowGap: "10px" }, contentShrinked: { "padding-left": drawerWidth + 10,