diff --git a/.eslintcache b/.eslintcache index bbf6e9e..73d3cb2 100644 --- a/.eslintcache +++ b/.eslintcache @@ -1 +1 @@ -[{"/home/nathan/source/inovelli-led-strip-toolbox/src/index.js":"1","/home/nathan/source/inovelli-led-strip-toolbox/src/serviceWorker.js":"2","/home/nathan/source/inovelli-led-strip-toolbox/src/swconfig.js":"3","/home/nathan/source/inovelli-led-strip-toolbox/src/AppWrapper.js":"4","/home/nathan/source/inovelli-led-strip-toolbox/src/App.js":"5","/home/nathan/source/inovelli-led-strip-toolbox/src/ThemeProvider.js":"6","/home/nathan/source/inovelli-led-strip-toolbox/src/Strip.js":"7","/home/nathan/source/inovelli-led-strip-toolbox/src/AppBar.js":"8","/home/nathan/source/inovelli-led-strip-toolbox/src/Options.js":"9","/home/nathan/source/inovelli-led-strip-toolbox/src/CustomStripEffects.js":"10","/home/nathan/source/inovelli-led-strip-toolbox/src/LED.js":"11","/home/nathan/source/inovelli-led-strip-toolbox/src/CustomEffectEditor.js":"12","/home/nathan/source/inovelli-led-strip-toolbox/src/HomeAssistantIcon.js":"13","/home/nathan/source/inovelli-led-strip-toolbox/src/ParameterModal.js":"14","/home/nathan/source/inovelli-led-strip-toolbox/src/SaveAsIcon.js":"15","/home/nathan/source/inovelli-led-strip-toolbox/src/SaveIcon.js":"16","/home/nathan/source/inovelli-led-strip-toolbox/src/SaveDialog.js":"17","/home/nathan/source/inovelli-led-strip-toolbox/src/ImportProgram.js":"18","/home/nathan/source/inovelli-led-strip-toolbox/src/OpenDialog.js":"19","/home/nathan/source/inovelli-led-strip-toolbox/src/Utils.js":"20","/home/nathan/source/inovelli-led-strip-toolbox/src/ClipboardAccess.js":"21","/home/nathan/source/inovelli-led-strip-toolbox/src/AboutDialog.js":"22","/home/nathan/source/inovelli-led-strip-toolbox/src/ShareDialog.js":"23"},{"size":565,"mtime":1609804151374,"results":"24","hashOfConfig":"25"},{"size":5086,"mtime":1609804151375,"results":"26","hashOfConfig":"25"},{"size":268,"mtime":1609804151378,"results":"27","hashOfConfig":"25"},{"size":519,"mtime":1609804875869,"results":"28","hashOfConfig":"25"},{"size":8125,"mtime":1609889361748,"results":"29","hashOfConfig":"25"},{"size":1539,"mtime":1609804151370,"results":"30","hashOfConfig":"25"},{"size":2337,"mtime":1609804151369,"results":"31","hashOfConfig":"25"},{"size":1013,"mtime":1609889386785,"results":"32","hashOfConfig":"25"},{"size":3874,"mtime":1609804252874,"results":"33","hashOfConfig":"25"},{"size":20884,"mtime":1609899698961,"results":"34","hashOfConfig":"25"},{"size":2516,"mtime":1609804151363,"results":"35","hashOfConfig":"25"},{"size":4325,"mtime":1609804151358,"results":"36","hashOfConfig":"25"},{"size":1181,"mtime":1609804151361,"results":"37","hashOfConfig":"25"},{"size":9236,"mtime":1609804151366,"results":"38","hashOfConfig":"25"},{"size":645,"mtime":1609804151367,"results":"39","hashOfConfig":"25"},{"size":422,"mtime":1609804151368,"results":"40","hashOfConfig":"25"},{"size":1013,"mtime":1609804151368,"results":"41","hashOfConfig":"25"},{"size":1514,"mtime":1609804151362,"results":"42","hashOfConfig":"25"},{"size":4402,"mtime":1609804151363,"results":"43","hashOfConfig":"25"},{"size":1598,"mtime":1609804151371,"results":"44","hashOfConfig":"25"},{"size":1095,"mtime":1609804151357,"results":"45","hashOfConfig":"25"},{"size":2175,"mtime":1609889188886,"results":"46","hashOfConfig":"25"},{"size":2872,"mtime":1609899465406,"results":"47","hashOfConfig":"25"},{"filePath":"48","messages":"49","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},"jq0hv9",{"filePath":"51","messages":"52","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"53","messages":"54","errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"55","usedDeprecatedRules":"50"},{"filePath":"56","messages":"57","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"58","messages":"59","errorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"60","usedDeprecatedRules":"50"},{"filePath":"61","messages":"62","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"63","messages":"64","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"65","messages":"66","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"67","messages":"68","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"69","messages":"70","errorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"71","usedDeprecatedRules":"50"},{"filePath":"72","messages":"73","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"74","messages":"75","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"76","messages":"77","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"78","messages":"79","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"80","messages":"81","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"82","messages":"83","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"84","messages":"85","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"86","messages":"87","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"88","messages":"89","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"90","messages":"91","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"92","messages":"93","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"94","messages":"95","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},{"filePath":"96","messages":"97","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"50"},"/home/nathan/source/inovelli-led-strip-toolbox/src/index.js",[],["98","99"],"/home/nathan/source/inovelli-led-strip-toolbox/src/serviceWorker.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/swconfig.js",["100"],"export default {\r\n onUpdate: registration => {\r\n registration.unregister().then(() => {\r\n window.location.reload();\r\n });\r\n },\r\n onSuccess: registration => {\r\n console.info(\"service worker on success state\");\r\n console.log(registration);\r\n }\r\n};\r\n","/home/nathan/source/inovelli-led-strip-toolbox/src/AppWrapper.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/App.js",["101","102","103"],"import React from \"react\";\nimport { CssBaseline, withStyles, Tabs, Tab } from \"@material-ui/core\";\nimport Strip from \"./Strip\";\n\nimport AppBar from \"./AppBar\";\nimport Options from \"./Options\";\nimport CustomStripEffects from \"./CustomStripEffects\";\n\nimport {\n createConnection,\n callService,\n createLongLivedTokenAuth,\n} from \"home-assistant-js-websocket\";\nimport AboutDialog from \"./AboutDialog\";\n\nconst styles = (theme) => ({\n root: {\n //height: \"98vh\",\n display: \"flex\",\n alignItems: \"center\",\n flexDirection: \"column\",\n \"&>:first-child\": {\n //flex: \"0 0 auto\",\n width: \"100%\",\n },\n \"&>:last-child\": {\n flex: \"1 1 100%\",\n display: \"flex\",\n width: \"100%\",\n alignItems: \"stretch\",\n justifyContent: \"center\",\n },\n },\n tabWrapper: {\n width: \"100%\",\n },\n areaWrapper: {\n display: \"flex\",\n alignItems: \"stretch\",\n \"&>:first-child\": {\n flex: \"1 1 100%\",\n //alignItems: \"center\",\n display: \"flex\",\n justifyContent: \"center\",\n maxWidth: \"800px\",\n },\n \"&>:last-child\": {\n //justifyItem: \"start\",\n //minWidth: \"400px\",\n //backgroundColor: theme.palette.background.paper,\n },\n },\n});\n\nclass App extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n customStripEffects: [],\n finishBehavior: 0,\n HA_URL: window.localStorage.getItem(\"HA_URL\"),\n HA_TOKEN: window.localStorage.getItem(\"HA_TOKEN\"),\n HA_NODE: window.localStorage.getItem(\"HA_NODE\"),\n HA_SERVICE: window.localStorage.getItem(\"HA_SERVICE\"),\n valueFormat: window.localStorage.getItem(\"valueFormat\") || 10,\n aboutDialogOpen: false\n };\n\n if (this.state.HA_URL && this.state.HA_TOKEN) {\n (async () => {\n const auth = createLongLivedTokenAuth(\n this.state.HA_URL,\n this.state.HA_TOKEN\n );\n\n const connection = await createConnection({ auth });\n this.setState({ homeAssistant: connection }, () => {\n console.info(\"Home Assistant Connection Saved to State....\");\n });\n })();\n }\n }\n\n handleChangeTab = (e, value) => {\n this.setState({ tab: value });\n };\n\n handleParameter10Changes = (field, value) => {\n this.setState({\n parameter10: { ...this.state.parameter10, [field]: value },\n });\n };\n\n handleCustomStripEffectChange = (effectArray) => {\n this.setState({\n customStripEffects: effectArray,\n });\n };\n\n handleTimeUnitChange = (unit) => {\n this.setState({ timeUnit: unit });\n };\n\n handleFinishBehaviorChange = (behavior) => {\n this.setState({ finishBehavior: behavior });\n };\n\n handleOnPlay = (lightShow) => {\n this.setState({ lightShow });\n };\n\n handleOpenOptions = () => {\n this.setState({ optionsOpen: true });\n };\n\n handleOptionsClosed = () => {\n this.setState({ optionsOpen: false });\n if (this.state.homeAssistant) {\n this.state.homeAssistant.close();\n }\n if (this.state.HA_URL && this.state.HA_TOKEN) {\n (async () => {\n const auth = createLongLivedTokenAuth(\n this.state.HA_URL,\n this.state.HA_TOKEN\n );\n\n const connection = await createConnection({ auth });\n this.setState({ homeAssistant: connection }, () => {\n console.info(\"Home Assistant Connection Saved to State....\");\n });\n })();\n }\n };\n\n handleHomeAssistantURLChange = (value) => {\n this.setState({ HA_URL: value });\n window.localStorage.setItem(\"HA_URL\", value);\n };\n\n handleHomeAssistantTokenChange = (value) => {\n this.setState({ HA_TOKEN: value });\n window.localStorage.setItem(\"HA_TOKEN\", value);\n };\n\n handleHomeAssistantNodeChange = (value) => {\n this.setState({ HA_NODE: value });\n window.localStorage.setItem(\"HA_NODE\", value);\n };\n\n handleHomeAssistantServiceChange = (value) => {\n this.setState({ HA_SERVICE: value });\n window.localStorage.setItem(\"HA_SERVICE\", value);\n };\n\n handleChangeValueFormat = (value) => {\n this.setState({ valueFormat: value });\n window.localStorage.setItem(\"valueFormat\", value);\n };\n\n handleChangeThemeValue = (value) => {\n this.setState({theme:value});\n window.localStorage.setItem(\"theme\",value);\n }\n\n sendProgamThroughHomeAssistant = (program) => {\n const SERVICE_PARTS = this.state.HA_SERVICE.split(\".\");\n console.log(SERVICE_PARTS);\n const HA_SERVICE_DOMAIN = SERVICE_PARTS[0];\n const HA_SERVICE = SERVICE_PARTS[1];\n console.log(SERVICE_PARTS[0], SERVICE_PARTS[1]);\n Promise.all([\n callService(this.state.homeAssistant, HA_SERVICE_DOMAIN, HA_SERVICE, {\n node_id: parseInt(this.state.HA_NODE),\n parameter: 30,\n value: 0,\n }),\n ...program.map(async (parameter) => {\n return callService(\n this.state.homeAssistant,\n HA_SERVICE_DOMAIN,\n HA_SERVICE,\n {\n node_id: parseInt(this.state.HA_NODE),\n parameter: parseInt(parameter.number),\n value: parseInt(parameter.value),\n }\n );\n }),\n ])\n .then(() => {\n console.info(\"Successfully sent to Home Assistant\");\n })\n .catch((ex) => {\n console.error(ex);\n });\n };\n\n handleCloseAboutDialog = () => {\n this.setState({aboutDialogOpen:false})\n }\n\n handleOpenAboutDialog = () => {\n this.setState({aboutDialogOpen: true});\n }\n\n render() {\n return (\n \n \n \n \n
\n
\n {/* \n \n \n \n \n */}\n {/*
\n {this.state.tab === 0 && (\n \n )}\n {this.state.tab === 2 && (\n \n )} \n
*/}\n
\n
\n {/*
\n \n
*/}\n {/*
*/}\n \n \n {/*
*/}\n
\n
\n
\n );\n }\n}\n\nexport default withStyles(styles)(App);\n","/home/nathan/source/inovelli-led-strip-toolbox/src/ThemeProvider.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/Strip.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/AppBar.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/Options.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/CustomStripEffects.js",["104","105","106","107","108","109"],"import React from \"react\";\nimport {\n FormControl,\n IconButton,\n InputLabel,\n Menu,\n MenuItem,\n List,\n ListItem,\n ListItemIcon,\n ListItemText,\n ListItemSecondaryAction,\n Select,\n TextField,\n withStyles,\n Divider,\n InputAdornment,\n Tooltip,\n SvgIcon,\n Button,\n Typography,\n Grid,\n Slider,\n FormHelperText,\n} from \"@material-ui/core\";\nimport Add from \"@material-ui/icons/Add\";\nimport Light from \"@material-ui/icons/EmojiObjects\";\nimport MoreVert from \"@material-ui/icons/MoreVert\";\nimport CustomEffectEditor from \"./CustomEffectEditor\";\nimport Open from \"@material-ui/icons/FolderOpen\";\nimport Import from \"@material-ui/icons/SaveAlt\";\nimport PlayIcon from \"@material-ui/icons/PlayArrow\";\nimport FormatListNumberedIcon from \"@material-ui/icons/FormatListNumbered\";\nimport {\n byteArrayToLong,\n longToByteArray,\n shortToByteArray,\n UNITS,\n COLORS,\n EFFECTS,\n FINISHES,\n} from \"./Utils\";\nimport ParameterModal from \"./ParameterModal\";\nimport InfiniteIcon from \"@material-ui/icons/AllInclusive\";\nimport TimelapseIcon from \"@material-ui/icons/Timelapse\";\nimport SaveDialog from \"./SaveDialog\";\nimport OpenDialog from \"./OpenDialog\";\nimport HomeAssistantIcon from \"./HomeAssistantIcon\";\nimport SaveIcon from \"./SaveIcon\";\nimport SaveAsIcon from \"./SaveAsIcon\";\nimport NewIcon from \"@material-ui/icons/AddBox\";\nimport ShareIcon from '@material-ui/icons/Share';\nimport ImportProgram from \"./ImportProgram\";\nimport qs from 'qs';\nimport ShareDialog from './ShareDialog';\n\nconst styles = (theme) => ({\n root: {\n display: \"flex\",\n flexDirection: \"row-reverse\",\n },\n});\n\nclass CustomStripEffects extends React.Component {\n static defaultProps = {\n timeUnit: 1,\n finishBehavior: 0,\n };\n constructor(props) {\n super(props);\n this.state = {\n editorOpen: false,\n effects: [],\n selectedEffect: undefined,\n effect: {},\n anchorEl: null,\n iterations: 255,\n timeUnit: 1,\n finishBehavior: 0,\n saveDialogOpen: false,\n openDialogOpen: false,\n savedAnimation: { animation: {} },\n indexOfSavedAnimation: null,\n importProgramDialogOpen: false,\n shareDialogOpen: false,\n };\n }\n\n componentDidMount(){\n //Import Program from Share Link\n if(window.location.search){\n let query = qs.parse(window.location.search.replace(/\\?/,\"\"));\n console.log(query);\n let COLORS = longToByteArray(query.p22).map(\n (colorEffect) => shortToByteArray(colorEffect)[1]\n );\n let EFFECTS = longToByteArray(query.p22).map(\n (colorEffect) => shortToByteArray(colorEffect)[0]\n );\n let BRIGHTNESS = longToByteArray(query.p23);\n let DURATIONS = longToByteArray(query.p24);\n let SETTINGS = longToByteArray(query.p30);\n //console.log(COLORS, EFFECTS, BRIGHTNESS, DURATIONS);\n //Drop Empty Effects\n for (let i = COLORS.length - 1; i >= 0; i--) {\n if (DURATIONS[i] !== 0) {\n break;\n }\n COLORS.pop();\n EFFECTS.pop();\n BRIGHTNESS.pop();\n DURATIONS.pop();\n }\n COLORS.reverse();\n EFFECTS.reverse();\n BRIGHTNESS.reverse();\n DURATIONS.reverse();\n //console.log(COLORS, EFFECTS, BRIGHTNESS, DURATIONS);\n const IMPORTED_EFFECTS = COLORS.map((color, index) => ({\n color: color,\n effect: EFFECTS[index],\n brightness: BRIGHTNESS[index],\n duration: DURATIONS[index],\n }));\n \n this.setState({\n effects: IMPORTED_EFFECTS,\n iterations: SETTINGS[0],\n finishBehavior: SETTINGS[1],\n timeUnit: SETTINGS[2],\n });\n }\n \n }\n\n openContextMenu = (selectedEffect) => (e) => {\n this.setState({ selectedEffect, anchorEl: e.currentTarget });\n };\n\n handleCloseContextMenu = () => {\n this.setState({ selectedEffect: undefined, anchorEl: null });\n };\n\n handleEditEffect = (i) => () => {\n this.setState({\n editorOpen: true,\n effect: Object.assign({}, this.state.effects[i]),\n selectedEffect: i,\n });\n };\n\n handleAddEffect = () => {\n this.setState({\n editorOpen: true,\n selectedEffect: undefined,\n effect: {\n color: 0,\n effect: 0,\n brightness: 99,\n duration: 1,\n },\n });\n };\n\n handleFinishBehaviorChange = (e) => {\n const { value } = e.target;\n this.setState({ finishBehavior: value });\n };\n\n onCloseEditor = () => {\n this.setState({ editorOpen: false });\n };\n\n onSave = (effect) => {\n this.setState((lastState) => {\n let effects = JSON.parse(JSON.stringify(lastState.effects));\n if (lastState.selectedEffect !== undefined) {\n effects[lastState.selectedEffect] = effect;\n } else {\n effects.push(effect);\n }\n return { effects, editorOpen: false, selectedEffect: undefined };\n });\n };\n\n handleTimeUnitChange = (e) => {\n const { value } = e.target;\n this.setState({ timeUnit: value });\n };\n\n handleDeleteEffect = () => {\n this.setState((lastState) => {\n let effects = JSON.parse(JSON.stringify(lastState.effects));\n effects.splice(this.state.selectedEffect, 1);\n return {\n anchorEl: null,\n effects,\n selectedEffect: undefined,\n };\n });\n };\n\n get parameter22() {\n return byteArrayToLong(\n Array.from(this.state.effects)\n .reverse()\n .map((effect) => effect.effect + effect.color * 8)\n ).toString(Number(this.props.format || 10));\n }\n\n get parameter23() {\n return byteArrayToLong(\n Array.from(this.state.effects)\n .reverse()\n .map((effect) => effect.brightness)\n ).toString(Number(this.props.format || 10));\n }\n\n get parameter24() {\n return byteArrayToLong(\n Array.from(this.state.effects)\n .reverse()\n .map((effect) => effect.duration)\n ).toString(Number(this.props.format || 10));\n }\n\n get parameter30() {\n return byteArrayToLong([\n this.state.iterations,\n this.state.finishBehavior,\n this.state.timeUnit,\n ]).toString(Number(this.props.format || 10));\n }\n\n toggleParameterModal = () => {\n this.setState((lastState) => ({\n parameterModalOpen: !lastState.parameterModalOpen,\n }));\n };\n\n setIterations = (e, value) => {\n this.setState({ iterations: value });\n };\n\n handleSaveDialog = () => {\n this.setState({ saveDialogOpen: true });\n };\n\n handleCloseSaveDialog = () => {\n this.setState({ saveDialogOpen: false });\n };\n\n handleSaveAnimation = (name) => {\n let animations = JSON.parse(window.localStorage.getItem(\"animations\"));\n if (!animations) {\n animations = [];\n }\n\n const date = new Date();\n\n animations.push({\n date: date.getTime(),\n animation: {\n iterations: this.state.iterations,\n finishBehavior: this.state.finishBehavior || 0,\n timeUnit: this.state.timeUnit,\n effects: this.state.effects,\n },\n name,\n });\n\n window.localStorage.setItem(\"animations\", JSON.stringify(animations));\n\n this.setState({\n saveDialogOpen: false,\n savedAnimation: {\n date: date.getTime(),\n animation: {\n iterations: this.state.iterations,\n finishBehavior: this.state.finishBehavior || 0,\n timeUnit: this.state.timeUnit,\n effects: this.state.effects,\n },\n name,\n },\n indexOfSavedAnimation: animations.length - 1,\n });\n };\n\n handleSaveToCurrentProgram = () => {\n let animations = JSON.parse(window.localStorage.getItem(\"animations\"));\n if (!animations) {\n animations = [];\n }\n\n const date = new Date();\n\n animations[this.state.indexOfSavedAnimation] = {\n date: date.getTime(),\n animation: {\n iterations: this.state.iterations,\n finishBehavior: this.state.finishBehavior || 0,\n timeUnit: this.state.timeUnit,\n effects: this.state.effects,\n },\n name: this.state.savedAnimation.name,\n };\n\n this.setState({\n savedAnimation: {\n date: date.getTime(),\n animation: {\n iterations: this.state.iterations,\n finishBehavior: this.state.finishBehavior || 0,\n timeUnit: this.state.timeUnit,\n effects: this.state.effects,\n },\n name: this.state.savedAnimation.name,\n },\n });\n\n window.localStorage.setItem(\"animations\", JSON.stringify(animations));\n };\n\n handleOpenDialog = () => {\n this.setState({ openDialogOpen: true });\n };\n\n handleCloseOpenDialog = () => {\n this.setState({ openDialogOpen: false });\n };\n\n handleOpenSavedAnimation = (index, entry) => {\n this.setState({\n openDialogOpen: false,\n indexOfSavedAnimation: index,\n savedAnimation: entry,\n iterations: entry.animation.iterations,\n timeUnit: entry.animation.timeUnit,\n effects: entry.animation.effects,\n finishBehavior: entry.animation.finishBehavior,\n });\n };\n\n handlePlayAnimationClick = () => {\n this.props.onPlay(\n JSON.parse(\n JSON.stringify({\n effects: this.state.effects,\n iterations: this.state.iterations,\n timeUnit: this.state.timeUnit,\n })\n )\n );\n };\n\n sendProgramToHomeAssistant = () => {\n this.props.onSendToHomeAssistant([\n { number: 22, value: this.parameter22 },\n { number: 23, value: this.parameter23 },\n { number: 24, value: this.parameter24 },\n { number: 30, value: this.parameter30 },\n ]);\n };\n\n handleStartNew = () => {\n this.setState({\n effects: [],\n selectedEffect: undefined,\n effect: {},\n anchorEl: null,\n iterations: 255,\n timeUnit: 1,\n finishBehavior: 0,\n savedAnimation: { animation: {} },\n indexOfSavedAnimation: null,\n });\n };\n\n handleOpenImportProgramDialog = () => {\n this.setState({\n importProgramDialogOpen: true,\n });\n };\n\n handleCloseImportProgramDialog = () => {\n this.setState({ importProgramDialogOpen: false });\n };\n\n handleImportProgram = (program) => {\n const PARAMETERS = program.split(\",\");\n console.log(longToByteArray(PARAMETERS[0]));\n let COLORS = longToByteArray(PARAMETERS[0]).map(\n (colorEffect) => shortToByteArray(colorEffect)[1]\n );\n let EFFECTS = longToByteArray(PARAMETERS[0]).map(\n (colorEffect) => shortToByteArray(colorEffect)[0]\n );\n let BRIGHTNESS = longToByteArray(PARAMETERS[1]);\n let DURATIONS = longToByteArray(PARAMETERS[2]);\n let SETTINGS = longToByteArray(PARAMETERS[3]);\n //console.log(COLORS, EFFECTS, BRIGHTNESS, DURATIONS);\n //Drop Empty Effects\n for (let i = COLORS.length - 1; i >= 0; i--) {\n if (DURATIONS[i] !== 0) {\n break;\n }\n COLORS.pop();\n EFFECTS.pop();\n BRIGHTNESS.pop();\n DURATIONS.pop();\n }\n COLORS.reverse();\n EFFECTS.reverse();\n BRIGHTNESS.reverse();\n DURATIONS.reverse();\n //console.log(COLORS, EFFECTS, BRIGHTNESS, DURATIONS);\n const IMPORTED_EFFECTS = COLORS.map((color, index) => ({\n color: color,\n effect: EFFECTS[index],\n brightness: BRIGHTNESS[index],\n duration: DURATIONS[index],\n }));\n\n this.setState({\n importProgramDialogOpen: false,\n effects: IMPORTED_EFFECTS,\n iterations: SETTINGS[0],\n finishBehavior: SETTINGS[1],\n timeUnit: SETTINGS[2],\n });\n };\n\n handleOpenShareDialog = () => {\n this.setState({shareDialogOpen: true});\n }\n\n handleCloseShareDialog = () => {\n this.setState({shareDialogOpen: false});\n }\n\n render() {\n const IS_SAVED_PROGRAM_CHANGED =\n JSON.stringify(this.state.savedAnimation.animation.effects) !==\n JSON.stringify(this.state.effects) ||\n this.state.timeUnit !== this.state.savedAnimation.animation.timeUnit ||\n this.state.iterations !==\n this.state.savedAnimation.animation.iterations ||\n this.state.finishBehavior !==\n this.state.savedAnimation.animation.finishBehavior;\n return (\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {this.state.savedAnimation.name && (\n \n \n {this.state.savedAnimation.name}{\" \"}\n {IS_SAVED_PROGRAM_CHANGED && (\n \n (Modified)\n \n )}\n \n \n )}\n \n \n \n Time Unit\n \n {UNITS.map((unit, index) => (\n {unit}\n ))}\n \n \n \n \n {this.state.effects.map((effect, i) => (\n \n \n \n \n \n \n \n \n \n \n Delete\n \n \n \n ))}\n {this.state.effects.length < 4 && (\n \n \n \n \n \n \n )}\n {this.state.effects.length > 0 && (\n \n \n \n
\n \n Iterations\n \n
\n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n \n Finish Behavior\n \n Off\n Previous Color\n Last Color In Sequence\n \n {this.state.iterations === 255 && (\n \n No Finish Behavior for Infinite Iterations\n \n )}\n \n \n \n {/* \n \n \n \n \n */}\n \n \n \n \n \n \n {this.props.isHomeAssistantConfigured && \n \n \n \n \n } \n
\n )}\n
\n \n \n \n \n \n \n
\n );\n }\n}\n\nexport default withStyles(styles)(CustomStripEffects);\n","/home/nathan/source/inovelli-led-strip-toolbox/src/LED.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/CustomEffectEditor.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/HomeAssistantIcon.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/ParameterModal.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/SaveAsIcon.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/SaveIcon.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/SaveDialog.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/ImportProgram.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/OpenDialog.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/Utils.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/ClipboardAccess.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/AboutDialog.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/ShareDialog.js",[],{"ruleId":"110","replacedBy":"111"},{"ruleId":"112","replacedBy":"113"},{"ruleId":"114","severity":1,"message":"115","line":1,"column":1,"nodeType":"116","endLine":11,"endColumn":3},{"ruleId":"117","severity":1,"message":"118","line":2,"column":35,"nodeType":"119","messageId":"120","endLine":2,"endColumn":39},{"ruleId":"117","severity":1,"message":"121","line":2,"column":41,"nodeType":"119","messageId":"120","endLine":2,"endColumn":44},{"ruleId":"117","severity":1,"message":"122","line":3,"column":8,"nodeType":"119","messageId":"120","endLine":3,"endColumn":13},{"ruleId":"117","severity":1,"message":"123","line":14,"column":3,"nodeType":"119","messageId":"120","endLine":14,"endColumn":12},{"ruleId":"117","severity":1,"message":"124","line":17,"column":3,"nodeType":"119","messageId":"120","endLine":17,"endColumn":17},{"ruleId":"117","severity":1,"message":"125","line":19,"column":3,"nodeType":"119","messageId":"120","endLine":19,"endColumn":10},{"ruleId":"117","severity":1,"message":"126","line":20,"column":3,"nodeType":"119","messageId":"120","endLine":20,"endColumn":9},{"ruleId":"117","severity":1,"message":"127","line":32,"column":8,"nodeType":"119","messageId":"120","endLine":32,"endColumn":16},{"ruleId":"117","severity":1,"message":"128","line":41,"column":3,"nodeType":"119","messageId":"120","endLine":41,"endColumn":11},"no-native-reassign",["129"],"no-negated-in-lhs",["130"],"import/no-anonymous-default-export","Assign object to a variable before exporting as module default","ExportDefaultDeclaration","no-unused-vars","'Tabs' is defined but never used.","Identifier","unusedVar","'Tab' is defined but never used.","'Strip' is defined but never used.","'TextField' is defined but never used.","'InputAdornment' is defined but never used.","'SvgIcon' is defined but never used.","'Button' is defined but never used.","'PlayIcon' is defined but never used.","'FINISHES' is defined but never used.","no-global-assign","no-unsafe-negation"] \ No newline at end of file +[{"/home/nathan/source/inovelli-led-strip-toolbox/src/index.js":"1","/home/nathan/source/inovelli-led-strip-toolbox/src/swconfig.js":"2","/home/nathan/source/inovelli-led-strip-toolbox/src/AppWrapper.js":"3","/home/nathan/source/inovelli-led-strip-toolbox/src/App.js":"4","/home/nathan/source/inovelli-led-strip-toolbox/src/ThemeProvider.js":"5","/home/nathan/source/inovelli-led-strip-toolbox/src/Strip.js":"6","/home/nathan/source/inovelli-led-strip-toolbox/src/AppBar.js":"7","/home/nathan/source/inovelli-led-strip-toolbox/src/Options.js":"8","/home/nathan/source/inovelli-led-strip-toolbox/src/CustomStripEffects.js":"9","/home/nathan/source/inovelli-led-strip-toolbox/src/LED.js":"10","/home/nathan/source/inovelli-led-strip-toolbox/src/CustomEffectEditor.js":"11","/home/nathan/source/inovelli-led-strip-toolbox/src/HomeAssistantIcon.js":"12","/home/nathan/source/inovelli-led-strip-toolbox/src/ParameterModal.js":"13","/home/nathan/source/inovelli-led-strip-toolbox/src/SaveAsIcon.js":"14","/home/nathan/source/inovelli-led-strip-toolbox/src/SaveIcon.js":"15","/home/nathan/source/inovelli-led-strip-toolbox/src/SaveDialog.js":"16","/home/nathan/source/inovelli-led-strip-toolbox/src/ImportProgram.js":"17","/home/nathan/source/inovelli-led-strip-toolbox/src/OpenDialog.js":"18","/home/nathan/source/inovelli-led-strip-toolbox/src/Utils.js":"19","/home/nathan/source/inovelli-led-strip-toolbox/src/ClipboardAccess.js":"20","/home/nathan/source/inovelli-led-strip-toolbox/src/AboutDialog.js":"21","/home/nathan/source/inovelli-led-strip-toolbox/src/ShareDialog.js":"22","/home/nathan/source/inovelli-led-strip-toolbox/src/service-worker.js":"23","/home/nathan/source/inovelli-led-strip-toolbox/src/serviceWorkerRegistration.js":"24","/home/nathan/source/inovelli-led-strip-toolbox/src/reportWebVitals.js":"25"},{"size":673,"mtime":1609907230223,"results":"26","hashOfConfig":"27"},{"size":268,"mtime":1609804151378,"results":"28","hashOfConfig":"27"},{"size":519,"mtime":1609804875869,"results":"29","hashOfConfig":"27"},{"size":8125,"mtime":1609889361748,"results":"30","hashOfConfig":"27"},{"size":1539,"mtime":1609804151370,"results":"31","hashOfConfig":"27"},{"size":2337,"mtime":1609804151369,"results":"32","hashOfConfig":"27"},{"size":1013,"mtime":1609889386785,"results":"33","hashOfConfig":"27"},{"size":3874,"mtime":1609804252874,"results":"34","hashOfConfig":"27"},{"size":20884,"mtime":1609899698961,"results":"35","hashOfConfig":"27"},{"size":2516,"mtime":1609804151363,"results":"36","hashOfConfig":"27"},{"size":4325,"mtime":1609804151358,"results":"37","hashOfConfig":"27"},{"size":1181,"mtime":1609804151361,"results":"38","hashOfConfig":"27"},{"size":9236,"mtime":1609804151366,"results":"39","hashOfConfig":"27"},{"size":645,"mtime":1609804151367,"results":"40","hashOfConfig":"27"},{"size":422,"mtime":1609804151368,"results":"41","hashOfConfig":"27"},{"size":1013,"mtime":1609804151368,"results":"42","hashOfConfig":"27"},{"size":1514,"mtime":1609804151362,"results":"43","hashOfConfig":"27"},{"size":4402,"mtime":1609804151363,"results":"44","hashOfConfig":"27"},{"size":1598,"mtime":1609804151371,"results":"45","hashOfConfig":"27"},{"size":1095,"mtime":1609804151357,"results":"46","hashOfConfig":"27"},{"size":2175,"mtime":1609889188886,"results":"47","hashOfConfig":"27"},{"size":2872,"mtime":1609899465406,"results":"48","hashOfConfig":"27"},{"size":2837,"mtime":1609907109824,"results":"49","hashOfConfig":"50"},{"size":5064,"mtime":1609907080375,"results":"51","hashOfConfig":"27"},{"size":364,"mtime":1609907068474,"results":"52","hashOfConfig":"50"},{"filePath":"53","messages":"54","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},"jq0hv9",{"filePath":"56","messages":"57","errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"58","usedDeprecatedRules":"55"},{"filePath":"59","messages":"60","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"61","messages":"62","errorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"63","usedDeprecatedRules":"55"},{"filePath":"64","messages":"65","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"66","messages":"67","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"68","messages":"69","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"70","messages":"71","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"72","messages":"73","errorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"74","usedDeprecatedRules":"55"},{"filePath":"75","messages":"76","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"77","messages":"78","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"79","messages":"80","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"81","messages":"82","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"83","messages":"84","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"85","messages":"86","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"87","messages":"88","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"89","messages":"90","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"91","messages":"92","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"93","messages":"94","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"95","messages":"96","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"97","messages":"98","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"99","messages":"100","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"101","messages":"102","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"8stn2s",{"filePath":"103","messages":"104","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"55"},{"filePath":"105","messages":"106","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"/home/nathan/source/inovelli-led-strip-toolbox/src/index.js",[],["107","108"],"/home/nathan/source/inovelli-led-strip-toolbox/src/swconfig.js",["109"],"export default {\r\n onUpdate: registration => {\r\n registration.unregister().then(() => {\r\n window.location.reload();\r\n });\r\n },\r\n onSuccess: registration => {\r\n console.info(\"service worker on success state\");\r\n console.log(registration);\r\n }\r\n};\r\n","/home/nathan/source/inovelli-led-strip-toolbox/src/AppWrapper.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/App.js",["110","111","112"],"import React from \"react\";\nimport { CssBaseline, withStyles, Tabs, Tab } from \"@material-ui/core\";\nimport Strip from \"./Strip\";\n\nimport AppBar from \"./AppBar\";\nimport Options from \"./Options\";\nimport CustomStripEffects from \"./CustomStripEffects\";\n\nimport {\n createConnection,\n callService,\n createLongLivedTokenAuth,\n} from \"home-assistant-js-websocket\";\nimport AboutDialog from \"./AboutDialog\";\n\nconst styles = (theme) => ({\n root: {\n //height: \"98vh\",\n display: \"flex\",\n alignItems: \"center\",\n flexDirection: \"column\",\n \"&>:first-child\": {\n //flex: \"0 0 auto\",\n width: \"100%\",\n },\n \"&>:last-child\": {\n flex: \"1 1 100%\",\n display: \"flex\",\n width: \"100%\",\n alignItems: \"stretch\",\n justifyContent: \"center\",\n },\n },\n tabWrapper: {\n width: \"100%\",\n },\n areaWrapper: {\n display: \"flex\",\n alignItems: \"stretch\",\n \"&>:first-child\": {\n flex: \"1 1 100%\",\n //alignItems: \"center\",\n display: \"flex\",\n justifyContent: \"center\",\n maxWidth: \"800px\",\n },\n \"&>:last-child\": {\n //justifyItem: \"start\",\n //minWidth: \"400px\",\n //backgroundColor: theme.palette.background.paper,\n },\n },\n});\n\nclass App extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n customStripEffects: [],\n finishBehavior: 0,\n HA_URL: window.localStorage.getItem(\"HA_URL\"),\n HA_TOKEN: window.localStorage.getItem(\"HA_TOKEN\"),\n HA_NODE: window.localStorage.getItem(\"HA_NODE\"),\n HA_SERVICE: window.localStorage.getItem(\"HA_SERVICE\"),\n valueFormat: window.localStorage.getItem(\"valueFormat\") || 10,\n aboutDialogOpen: false\n };\n\n if (this.state.HA_URL && this.state.HA_TOKEN) {\n (async () => {\n const auth = createLongLivedTokenAuth(\n this.state.HA_URL,\n this.state.HA_TOKEN\n );\n\n const connection = await createConnection({ auth });\n this.setState({ homeAssistant: connection }, () => {\n console.info(\"Home Assistant Connection Saved to State....\");\n });\n })();\n }\n }\n\n handleChangeTab = (e, value) => {\n this.setState({ tab: value });\n };\n\n handleParameter10Changes = (field, value) => {\n this.setState({\n parameter10: { ...this.state.parameter10, [field]: value },\n });\n };\n\n handleCustomStripEffectChange = (effectArray) => {\n this.setState({\n customStripEffects: effectArray,\n });\n };\n\n handleTimeUnitChange = (unit) => {\n this.setState({ timeUnit: unit });\n };\n\n handleFinishBehaviorChange = (behavior) => {\n this.setState({ finishBehavior: behavior });\n };\n\n handleOnPlay = (lightShow) => {\n this.setState({ lightShow });\n };\n\n handleOpenOptions = () => {\n this.setState({ optionsOpen: true });\n };\n\n handleOptionsClosed = () => {\n this.setState({ optionsOpen: false });\n if (this.state.homeAssistant) {\n this.state.homeAssistant.close();\n }\n if (this.state.HA_URL && this.state.HA_TOKEN) {\n (async () => {\n const auth = createLongLivedTokenAuth(\n this.state.HA_URL,\n this.state.HA_TOKEN\n );\n\n const connection = await createConnection({ auth });\n this.setState({ homeAssistant: connection }, () => {\n console.info(\"Home Assistant Connection Saved to State....\");\n });\n })();\n }\n };\n\n handleHomeAssistantURLChange = (value) => {\n this.setState({ HA_URL: value });\n window.localStorage.setItem(\"HA_URL\", value);\n };\n\n handleHomeAssistantTokenChange = (value) => {\n this.setState({ HA_TOKEN: value });\n window.localStorage.setItem(\"HA_TOKEN\", value);\n };\n\n handleHomeAssistantNodeChange = (value) => {\n this.setState({ HA_NODE: value });\n window.localStorage.setItem(\"HA_NODE\", value);\n };\n\n handleHomeAssistantServiceChange = (value) => {\n this.setState({ HA_SERVICE: value });\n window.localStorage.setItem(\"HA_SERVICE\", value);\n };\n\n handleChangeValueFormat = (value) => {\n this.setState({ valueFormat: value });\n window.localStorage.setItem(\"valueFormat\", value);\n };\n\n handleChangeThemeValue = (value) => {\n this.setState({theme:value});\n window.localStorage.setItem(\"theme\",value);\n }\n\n sendProgamThroughHomeAssistant = (program) => {\n const SERVICE_PARTS = this.state.HA_SERVICE.split(\".\");\n console.log(SERVICE_PARTS);\n const HA_SERVICE_DOMAIN = SERVICE_PARTS[0];\n const HA_SERVICE = SERVICE_PARTS[1];\n console.log(SERVICE_PARTS[0], SERVICE_PARTS[1]);\n Promise.all([\n callService(this.state.homeAssistant, HA_SERVICE_DOMAIN, HA_SERVICE, {\n node_id: parseInt(this.state.HA_NODE),\n parameter: 30,\n value: 0,\n }),\n ...program.map(async (parameter) => {\n return callService(\n this.state.homeAssistant,\n HA_SERVICE_DOMAIN,\n HA_SERVICE,\n {\n node_id: parseInt(this.state.HA_NODE),\n parameter: parseInt(parameter.number),\n value: parseInt(parameter.value),\n }\n );\n }),\n ])\n .then(() => {\n console.info(\"Successfully sent to Home Assistant\");\n })\n .catch((ex) => {\n console.error(ex);\n });\n };\n\n handleCloseAboutDialog = () => {\n this.setState({aboutDialogOpen:false})\n }\n\n handleOpenAboutDialog = () => {\n this.setState({aboutDialogOpen: true});\n }\n\n render() {\n return (\n \n \n \n \n
\n
\n {/* \n \n \n \n \n */}\n {/*
\n {this.state.tab === 0 && (\n \n )}\n {this.state.tab === 2 && (\n \n )} \n
*/}\n
\n
\n {/*
\n \n
*/}\n {/*
*/}\n \n \n {/*
*/}\n
\n
\n
\n );\n }\n}\n\nexport default withStyles(styles)(App);\n","/home/nathan/source/inovelli-led-strip-toolbox/src/ThemeProvider.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/Strip.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/AppBar.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/Options.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/CustomStripEffects.js",["113","114","115","116","117","118"],"import React from \"react\";\nimport {\n FormControl,\n IconButton,\n InputLabel,\n Menu,\n MenuItem,\n List,\n ListItem,\n ListItemIcon,\n ListItemText,\n ListItemSecondaryAction,\n Select,\n TextField,\n withStyles,\n Divider,\n InputAdornment,\n Tooltip,\n SvgIcon,\n Button,\n Typography,\n Grid,\n Slider,\n FormHelperText,\n} from \"@material-ui/core\";\nimport Add from \"@material-ui/icons/Add\";\nimport Light from \"@material-ui/icons/EmojiObjects\";\nimport MoreVert from \"@material-ui/icons/MoreVert\";\nimport CustomEffectEditor from \"./CustomEffectEditor\";\nimport Open from \"@material-ui/icons/FolderOpen\";\nimport Import from \"@material-ui/icons/SaveAlt\";\nimport PlayIcon from \"@material-ui/icons/PlayArrow\";\nimport FormatListNumberedIcon from \"@material-ui/icons/FormatListNumbered\";\nimport {\n byteArrayToLong,\n longToByteArray,\n shortToByteArray,\n UNITS,\n COLORS,\n EFFECTS,\n FINISHES,\n} from \"./Utils\";\nimport ParameterModal from \"./ParameterModal\";\nimport InfiniteIcon from \"@material-ui/icons/AllInclusive\";\nimport TimelapseIcon from \"@material-ui/icons/Timelapse\";\nimport SaveDialog from \"./SaveDialog\";\nimport OpenDialog from \"./OpenDialog\";\nimport HomeAssistantIcon from \"./HomeAssistantIcon\";\nimport SaveIcon from \"./SaveIcon\";\nimport SaveAsIcon from \"./SaveAsIcon\";\nimport NewIcon from \"@material-ui/icons/AddBox\";\nimport ShareIcon from '@material-ui/icons/Share';\nimport ImportProgram from \"./ImportProgram\";\nimport qs from 'qs';\nimport ShareDialog from './ShareDialog';\n\nconst styles = (theme) => ({\n root: {\n display: \"flex\",\n flexDirection: \"row-reverse\",\n },\n});\n\nclass CustomStripEffects extends React.Component {\n static defaultProps = {\n timeUnit: 1,\n finishBehavior: 0,\n };\n constructor(props) {\n super(props);\n this.state = {\n editorOpen: false,\n effects: [],\n selectedEffect: undefined,\n effect: {},\n anchorEl: null,\n iterations: 255,\n timeUnit: 1,\n finishBehavior: 0,\n saveDialogOpen: false,\n openDialogOpen: false,\n savedAnimation: { animation: {} },\n indexOfSavedAnimation: null,\n importProgramDialogOpen: false,\n shareDialogOpen: false,\n };\n }\n\n componentDidMount(){\n //Import Program from Share Link\n if(window.location.search){\n let query = qs.parse(window.location.search.replace(/\\?/,\"\"));\n console.log(query);\n let COLORS = longToByteArray(query.p22).map(\n (colorEffect) => shortToByteArray(colorEffect)[1]\n );\n let EFFECTS = longToByteArray(query.p22).map(\n (colorEffect) => shortToByteArray(colorEffect)[0]\n );\n let BRIGHTNESS = longToByteArray(query.p23);\n let DURATIONS = longToByteArray(query.p24);\n let SETTINGS = longToByteArray(query.p30);\n //console.log(COLORS, EFFECTS, BRIGHTNESS, DURATIONS);\n //Drop Empty Effects\n for (let i = COLORS.length - 1; i >= 0; i--) {\n if (DURATIONS[i] !== 0) {\n break;\n }\n COLORS.pop();\n EFFECTS.pop();\n BRIGHTNESS.pop();\n DURATIONS.pop();\n }\n COLORS.reverse();\n EFFECTS.reverse();\n BRIGHTNESS.reverse();\n DURATIONS.reverse();\n //console.log(COLORS, EFFECTS, BRIGHTNESS, DURATIONS);\n const IMPORTED_EFFECTS = COLORS.map((color, index) => ({\n color: color,\n effect: EFFECTS[index],\n brightness: BRIGHTNESS[index],\n duration: DURATIONS[index],\n }));\n \n this.setState({\n effects: IMPORTED_EFFECTS,\n iterations: SETTINGS[0],\n finishBehavior: SETTINGS[1],\n timeUnit: SETTINGS[2],\n });\n }\n \n }\n\n openContextMenu = (selectedEffect) => (e) => {\n this.setState({ selectedEffect, anchorEl: e.currentTarget });\n };\n\n handleCloseContextMenu = () => {\n this.setState({ selectedEffect: undefined, anchorEl: null });\n };\n\n handleEditEffect = (i) => () => {\n this.setState({\n editorOpen: true,\n effect: Object.assign({}, this.state.effects[i]),\n selectedEffect: i,\n });\n };\n\n handleAddEffect = () => {\n this.setState({\n editorOpen: true,\n selectedEffect: undefined,\n effect: {\n color: 0,\n effect: 0,\n brightness: 99,\n duration: 1,\n },\n });\n };\n\n handleFinishBehaviorChange = (e) => {\n const { value } = e.target;\n this.setState({ finishBehavior: value });\n };\n\n onCloseEditor = () => {\n this.setState({ editorOpen: false });\n };\n\n onSave = (effect) => {\n this.setState((lastState) => {\n let effects = JSON.parse(JSON.stringify(lastState.effects));\n if (lastState.selectedEffect !== undefined) {\n effects[lastState.selectedEffect] = effect;\n } else {\n effects.push(effect);\n }\n return { effects, editorOpen: false, selectedEffect: undefined };\n });\n };\n\n handleTimeUnitChange = (e) => {\n const { value } = e.target;\n this.setState({ timeUnit: value });\n };\n\n handleDeleteEffect = () => {\n this.setState((lastState) => {\n let effects = JSON.parse(JSON.stringify(lastState.effects));\n effects.splice(this.state.selectedEffect, 1);\n return {\n anchorEl: null,\n effects,\n selectedEffect: undefined,\n };\n });\n };\n\n get parameter22() {\n return byteArrayToLong(\n Array.from(this.state.effects)\n .reverse()\n .map((effect) => effect.effect + effect.color * 8)\n ).toString(Number(this.props.format || 10));\n }\n\n get parameter23() {\n return byteArrayToLong(\n Array.from(this.state.effects)\n .reverse()\n .map((effect) => effect.brightness)\n ).toString(Number(this.props.format || 10));\n }\n\n get parameter24() {\n return byteArrayToLong(\n Array.from(this.state.effects)\n .reverse()\n .map((effect) => effect.duration)\n ).toString(Number(this.props.format || 10));\n }\n\n get parameter30() {\n return byteArrayToLong([\n this.state.iterations,\n this.state.finishBehavior,\n this.state.timeUnit,\n ]).toString(Number(this.props.format || 10));\n }\n\n toggleParameterModal = () => {\n this.setState((lastState) => ({\n parameterModalOpen: !lastState.parameterModalOpen,\n }));\n };\n\n setIterations = (e, value) => {\n this.setState({ iterations: value });\n };\n\n handleSaveDialog = () => {\n this.setState({ saveDialogOpen: true });\n };\n\n handleCloseSaveDialog = () => {\n this.setState({ saveDialogOpen: false });\n };\n\n handleSaveAnimation = (name) => {\n let animations = JSON.parse(window.localStorage.getItem(\"animations\"));\n if (!animations) {\n animations = [];\n }\n\n const date = new Date();\n\n animations.push({\n date: date.getTime(),\n animation: {\n iterations: this.state.iterations,\n finishBehavior: this.state.finishBehavior || 0,\n timeUnit: this.state.timeUnit,\n effects: this.state.effects,\n },\n name,\n });\n\n window.localStorage.setItem(\"animations\", JSON.stringify(animations));\n\n this.setState({\n saveDialogOpen: false,\n savedAnimation: {\n date: date.getTime(),\n animation: {\n iterations: this.state.iterations,\n finishBehavior: this.state.finishBehavior || 0,\n timeUnit: this.state.timeUnit,\n effects: this.state.effects,\n },\n name,\n },\n indexOfSavedAnimation: animations.length - 1,\n });\n };\n\n handleSaveToCurrentProgram = () => {\n let animations = JSON.parse(window.localStorage.getItem(\"animations\"));\n if (!animations) {\n animations = [];\n }\n\n const date = new Date();\n\n animations[this.state.indexOfSavedAnimation] = {\n date: date.getTime(),\n animation: {\n iterations: this.state.iterations,\n finishBehavior: this.state.finishBehavior || 0,\n timeUnit: this.state.timeUnit,\n effects: this.state.effects,\n },\n name: this.state.savedAnimation.name,\n };\n\n this.setState({\n savedAnimation: {\n date: date.getTime(),\n animation: {\n iterations: this.state.iterations,\n finishBehavior: this.state.finishBehavior || 0,\n timeUnit: this.state.timeUnit,\n effects: this.state.effects,\n },\n name: this.state.savedAnimation.name,\n },\n });\n\n window.localStorage.setItem(\"animations\", JSON.stringify(animations));\n };\n\n handleOpenDialog = () => {\n this.setState({ openDialogOpen: true });\n };\n\n handleCloseOpenDialog = () => {\n this.setState({ openDialogOpen: false });\n };\n\n handleOpenSavedAnimation = (index, entry) => {\n this.setState({\n openDialogOpen: false,\n indexOfSavedAnimation: index,\n savedAnimation: entry,\n iterations: entry.animation.iterations,\n timeUnit: entry.animation.timeUnit,\n effects: entry.animation.effects,\n finishBehavior: entry.animation.finishBehavior,\n });\n };\n\n handlePlayAnimationClick = () => {\n this.props.onPlay(\n JSON.parse(\n JSON.stringify({\n effects: this.state.effects,\n iterations: this.state.iterations,\n timeUnit: this.state.timeUnit,\n })\n )\n );\n };\n\n sendProgramToHomeAssistant = () => {\n this.props.onSendToHomeAssistant([\n { number: 22, value: this.parameter22 },\n { number: 23, value: this.parameter23 },\n { number: 24, value: this.parameter24 },\n { number: 30, value: this.parameter30 },\n ]);\n };\n\n handleStartNew = () => {\n this.setState({\n effects: [],\n selectedEffect: undefined,\n effect: {},\n anchorEl: null,\n iterations: 255,\n timeUnit: 1,\n finishBehavior: 0,\n savedAnimation: { animation: {} },\n indexOfSavedAnimation: null,\n });\n };\n\n handleOpenImportProgramDialog = () => {\n this.setState({\n importProgramDialogOpen: true,\n });\n };\n\n handleCloseImportProgramDialog = () => {\n this.setState({ importProgramDialogOpen: false });\n };\n\n handleImportProgram = (program) => {\n const PARAMETERS = program.split(\",\");\n console.log(longToByteArray(PARAMETERS[0]));\n let COLORS = longToByteArray(PARAMETERS[0]).map(\n (colorEffect) => shortToByteArray(colorEffect)[1]\n );\n let EFFECTS = longToByteArray(PARAMETERS[0]).map(\n (colorEffect) => shortToByteArray(colorEffect)[0]\n );\n let BRIGHTNESS = longToByteArray(PARAMETERS[1]);\n let DURATIONS = longToByteArray(PARAMETERS[2]);\n let SETTINGS = longToByteArray(PARAMETERS[3]);\n //console.log(COLORS, EFFECTS, BRIGHTNESS, DURATIONS);\n //Drop Empty Effects\n for (let i = COLORS.length - 1; i >= 0; i--) {\n if (DURATIONS[i] !== 0) {\n break;\n }\n COLORS.pop();\n EFFECTS.pop();\n BRIGHTNESS.pop();\n DURATIONS.pop();\n }\n COLORS.reverse();\n EFFECTS.reverse();\n BRIGHTNESS.reverse();\n DURATIONS.reverse();\n //console.log(COLORS, EFFECTS, BRIGHTNESS, DURATIONS);\n const IMPORTED_EFFECTS = COLORS.map((color, index) => ({\n color: color,\n effect: EFFECTS[index],\n brightness: BRIGHTNESS[index],\n duration: DURATIONS[index],\n }));\n\n this.setState({\n importProgramDialogOpen: false,\n effects: IMPORTED_EFFECTS,\n iterations: SETTINGS[0],\n finishBehavior: SETTINGS[1],\n timeUnit: SETTINGS[2],\n });\n };\n\n handleOpenShareDialog = () => {\n this.setState({shareDialogOpen: true});\n }\n\n handleCloseShareDialog = () => {\n this.setState({shareDialogOpen: false});\n }\n\n render() {\n const IS_SAVED_PROGRAM_CHANGED =\n JSON.stringify(this.state.savedAnimation.animation.effects) !==\n JSON.stringify(this.state.effects) ||\n this.state.timeUnit !== this.state.savedAnimation.animation.timeUnit ||\n this.state.iterations !==\n this.state.savedAnimation.animation.iterations ||\n this.state.finishBehavior !==\n this.state.savedAnimation.animation.finishBehavior;\n return (\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {this.state.savedAnimation.name && (\n \n \n {this.state.savedAnimation.name}{\" \"}\n {IS_SAVED_PROGRAM_CHANGED && (\n \n (Modified)\n \n )}\n \n \n )}\n \n \n \n Time Unit\n \n {UNITS.map((unit, index) => (\n {unit}\n ))}\n \n \n \n \n {this.state.effects.map((effect, i) => (\n \n \n \n \n \n \n \n \n \n \n Delete\n \n \n \n ))}\n {this.state.effects.length < 4 && (\n \n \n \n \n \n \n )}\n {this.state.effects.length > 0 && (\n \n \n \n
\n \n Iterations\n \n
\n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n \n Finish Behavior\n \n Off\n Previous Color\n Last Color In Sequence\n \n {this.state.iterations === 255 && (\n \n No Finish Behavior for Infinite Iterations\n \n )}\n \n \n \n {/* \n \n \n \n \n */}\n \n \n \n \n \n \n {this.props.isHomeAssistantConfigured && \n \n \n \n \n } \n
\n )}\n
\n \n \n \n \n \n \n
\n );\n }\n}\n\nexport default withStyles(styles)(CustomStripEffects);\n","/home/nathan/source/inovelli-led-strip-toolbox/src/LED.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/CustomEffectEditor.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/HomeAssistantIcon.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/ParameterModal.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/SaveAsIcon.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/SaveIcon.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/SaveDialog.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/ImportProgram.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/OpenDialog.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/Utils.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/ClipboardAccess.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/AboutDialog.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/ShareDialog.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/service-worker.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/serviceWorkerRegistration.js",[],"/home/nathan/source/inovelli-led-strip-toolbox/src/reportWebVitals.js",[],{"ruleId":"119","replacedBy":"120"},{"ruleId":"121","replacedBy":"122"},{"ruleId":"123","severity":1,"message":"124","line":1,"column":1,"nodeType":"125","endLine":11,"endColumn":3},{"ruleId":"126","severity":1,"message":"127","line":2,"column":35,"nodeType":"128","messageId":"129","endLine":2,"endColumn":39},{"ruleId":"126","severity":1,"message":"130","line":2,"column":41,"nodeType":"128","messageId":"129","endLine":2,"endColumn":44},{"ruleId":"126","severity":1,"message":"131","line":3,"column":8,"nodeType":"128","messageId":"129","endLine":3,"endColumn":13},{"ruleId":"126","severity":1,"message":"132","line":14,"column":3,"nodeType":"128","messageId":"129","endLine":14,"endColumn":12},{"ruleId":"126","severity":1,"message":"133","line":17,"column":3,"nodeType":"128","messageId":"129","endLine":17,"endColumn":17},{"ruleId":"126","severity":1,"message":"134","line":19,"column":3,"nodeType":"128","messageId":"129","endLine":19,"endColumn":10},{"ruleId":"126","severity":1,"message":"135","line":20,"column":3,"nodeType":"128","messageId":"129","endLine":20,"endColumn":9},{"ruleId":"126","severity":1,"message":"136","line":32,"column":8,"nodeType":"128","messageId":"129","endLine":32,"endColumn":16},{"ruleId":"126","severity":1,"message":"137","line":41,"column":3,"nodeType":"128","messageId":"129","endLine":41,"endColumn":11},"no-native-reassign",["138"],"no-negated-in-lhs",["139"],"import/no-anonymous-default-export","Assign object to a variable before exporting as module default","ExportDefaultDeclaration","no-unused-vars","'Tabs' is defined but never used.","Identifier","unusedVar","'Tab' is defined but never used.","'Strip' is defined but never used.","'TextField' is defined but never used.","'InputAdornment' is defined but never used.","'SvgIcon' is defined but never used.","'Button' is defined but never used.","'PlayIcon' is defined but never used.","'FINISHES' is defined but never used.","no-global-assign","no-unsafe-negation"] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d0a5611..642749d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16835,6 +16835,11 @@ "minimalistic-assert": "^1.0.0" } }, + "web-vitals": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-0.2.4.tgz", + "integrity": "sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg==" + }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", diff --git a/package.json b/package.json index caecccd..4fe777c 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,20 @@ "qs": "^6.9.4", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-scripts": "4.0.1" + "react-scripts": "4.0.1", + "web-vitals": "^0.2.4", + "workbox-background-sync": "^5.1.4", + "workbox-broadcast-update": "^5.1.4", + "workbox-cacheable-response": "^5.1.4", + "workbox-core": "^5.1.4", + "workbox-expiration": "^5.1.4", + "workbox-google-analytics": "^5.1.4", + "workbox-navigation-preload": "^5.1.4", + "workbox-precaching": "^5.1.4", + "workbox-range-requests": "^5.1.4", + "workbox-routing": "^5.1.4", + "workbox-strategies": "^5.1.4", + "workbox-streams": "^5.1.4" }, "scripts": { "start": "react-scripts start", diff --git a/src/index.js b/src/index.js index afb3eb0..04790e4 100644 --- a/src/index.js +++ b/src/index.js @@ -2,7 +2,8 @@ import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; import AppWrapper from "./AppWrapper"; -import * as serviceWorker from "./serviceWorker"; +import * as serviceWorkerRegistration from './serviceWorkerRegistration'; +//import reportWebVitals from './reportWebVitals'; import swConfig from "./swconfig"; ReactDOM.render( @@ -15,4 +16,6 @@ ReactDOM.render( // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA -serviceWorker.register(swConfig); +serviceWorkerRegistration.register(swConfig); + +//reportWebVitals(); \ No newline at end of file diff --git a/src/reportWebVitals.js b/src/reportWebVitals.js new file mode 100644 index 0000000..532f29b --- /dev/null +++ b/src/reportWebVitals.js @@ -0,0 +1,13 @@ +const reportWebVitals = (onPerfEntry) => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/src/service-worker.js b/src/service-worker.js new file mode 100644 index 0000000..0f1e0ce --- /dev/null +++ b/src/service-worker.js @@ -0,0 +1,72 @@ +/* eslint-disable no-restricted-globals */ + +// This service worker can be customized! +// See https://developers.google.com/web/tools/workbox/modules +// for the list of available Workbox modules, or add any other +// code you'd like. +// You can also remove this file if you'd prefer not to use a +// service worker, and the Workbox build step will be skipped. + +import { clientsClaim } from 'workbox-core'; +import { ExpirationPlugin } from 'workbox-expiration'; +import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching'; +import { registerRoute } from 'workbox-routing'; +import { StaleWhileRevalidate } from 'workbox-strategies'; + +clientsClaim(); + +// Precache all of the assets generated by your build process. +// Their URLs are injected into the manifest variable below. +// This variable must be present somewhere in your service worker file, +// even if you decide not to use precaching. See https://cra.link/PWA +precacheAndRoute(self.__WB_MANIFEST); + +// Set up App Shell-style routing, so that all navigation requests +// are fulfilled with your index.html shell. Learn more at +// https://developers.google.com/web/fundamentals/architecture/app-shell +const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$'); +registerRoute( + // Return false to exempt requests from being fulfilled by index.html. + ({ request, url }) => { + // If this isn't a navigation, skip. + if (request.mode !== 'navigate') { + return false; + } // If this is a URL that starts with /_, skip. + + if (url.pathname.startsWith('/_')) { + return false; + } // If this looks like a URL for a resource, because it contains // a file extension, skip. + + if (url.pathname.match(fileExtensionRegexp)) { + return false; + } // Return true to signal that we want to use the handler. + + return true; + }, + createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html') +); + +// An example runtime caching route for requests that aren't handled by the +// precache, in this case same-origin .png requests like those from in public/ +registerRoute( + // Add in any other file extensions or routing criteria as needed. + ({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'), // Customize this strategy as needed, e.g., by changing to CacheFirst. + new StaleWhileRevalidate({ + cacheName: 'images', + plugins: [ + // Ensure that once this runtime cache reaches a maximum size the + // least-recently used images are removed. + new ExpirationPlugin({ maxEntries: 50 }), + ], + }) +); + +// This allows the web app to trigger skipWaiting via +// registration.waiting.postMessage({type: 'SKIP_WAITING'}) +self.addEventListener('message', (event) => { + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } +}); + +// Any other custom service worker logic can go here. diff --git a/src/serviceWorker.js b/src/serviceWorkerRegistration.js similarity index 88% rename from src/serviceWorker.js rename to src/serviceWorkerRegistration.js index b04b771..2262ecd 100644 --- a/src/serviceWorker.js +++ b/src/serviceWorkerRegistration.js @@ -8,16 +8,14 @@ // resources are updated in the background. // To learn more about the benefits of this model and instructions on how to -// opt-in, read https://bit.ly/CRA-PWA +// opt-in, read https://cra.link/PWA const isLocalhost = Boolean( window.location.hostname === 'localhost' || // [::1] is the IPv6 localhost address. window.location.hostname === '[::1]' || // 127.0.0.0/8 are considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) + window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) ); export function register(config) { @@ -43,7 +41,7 @@ export function register(config) { navigator.serviceWorker.ready.then(() => { console.log( 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit https://bit.ly/CRA-PWA' + 'worker. To learn more, visit https://cra.link/PWA' ); }); } else { @@ -57,7 +55,7 @@ export function register(config) { function registerValidSW(swUrl, config) { navigator.serviceWorker .register(swUrl) - .then(registration => { + .then((registration) => { registration.onupdatefound = () => { const installingWorker = registration.installing; if (installingWorker == null) { @@ -71,7 +69,7 @@ function registerValidSW(swUrl, config) { // content until all client tabs are closed. console.log( 'New content is available and will be used when all ' + - 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' + 'tabs for this page are closed. See https://cra.link/PWA.' ); // Execute callback @@ -93,7 +91,7 @@ function registerValidSW(swUrl, config) { }; }; }) - .catch(error => { + .catch((error) => { console.error('Error during service worker registration:', error); }); } @@ -103,7 +101,7 @@ function checkValidServiceWorker(swUrl, config) { fetch(swUrl, { headers: { 'Service-Worker': 'script' }, }) - .then(response => { + .then((response) => { // Ensure service worker exists, and that we really are getting a JS file. const contentType = response.headers.get('content-type'); if ( @@ -111,7 +109,7 @@ function checkValidServiceWorker(swUrl, config) { (contentType != null && contentType.indexOf('javascript') === -1) ) { // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { + navigator.serviceWorker.ready.then((registration) => { registration.unregister().then(() => { window.location.reload(); }); @@ -122,19 +120,17 @@ function checkValidServiceWorker(swUrl, config) { } }) .catch(() => { - console.log( - 'No internet connection found. App is running in offline mode.' - ); + console.log('No internet connection found. App is running in offline mode.'); }); } export function unregister() { if ('serviceWorker' in navigator) { navigator.serviceWorker.ready - .then(registration => { + .then((registration) => { registration.unregister(); }) - .catch(error => { + .catch((error) => { console.error(error.message); }); }