From 04962ba56a8ef20021c4783b58518ef7a0eb2443 Mon Sep 17 00:00:00 2001 From: NlightN22 Date: Wed, 13 Mar 2024 00:54:43 +0700 Subject: [PATCH] finish config edit page --- Dockerfile | 2 +- src/pages/HostConfigPage.tsx | 120 +++++++++++++------- src/pages/TestPage.tsx | 127 ++++++++++++++++++---- src/services/frigate.proxy/frigate.api.ts | 1 + yarn.lock | 7 +- 5 files changed, 195 insertions(+), 62 deletions(-) diff --git a/Dockerfile b/Dockerfile index 137568d..1119fb2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 # Build commands: -# - $VERSION=0.9 +# - $VERSION=1.0 # - rm build -r -Force ; rm ./node_modules/.cache/babel-loader -r -Force ; yarn build # - docker build --pull --rm -t oncharterliz/multi-frigate:latest -t oncharterliz/multi-frigate:$VERSION "." # - docker image push --all-tags oncharterliz/multi-frigate diff --git a/src/pages/HostConfigPage.tsx b/src/pages/HostConfigPage.tsx index 4a49442..febf1fe 100644 --- a/src/pages/HostConfigPage.tsx +++ b/src/pages/HostConfigPage.tsx @@ -1,28 +1,61 @@ -import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'; +import { Button, Flex, Text, useMantineTheme } from '@mantine/core'; +import { useClipboard } from '@mantine/hooks'; +import Editor, { Monaco } from '@monaco-editor/react'; +import { useMutation, useQuery } from '@tanstack/react-query'; +import { observer } from 'mobx-react-lite'; +import * as monaco from "monaco-editor"; +import { SchemasSettings, configureMonacoYaml } from 'monaco-yaml'; +import { useCallback, useContext, useEffect, useRef, useState } from 'react'; import { useParams } from 'react-router-dom'; import { Context } from '..'; -import { useMutation, useQuery } from '@tanstack/react-query'; -import { frigateApi, frigateQueryKeys, mapHostToHostname, proxyApi } from '../services/frigate.proxy/frigate.api'; -import { Button, Flex, useMantineTheme, Text } from '@mantine/core'; -import { useClipboard } from '@mantine/hooks'; -import Editor, { Monaco } from '@monaco-editor/react' -import * as monaco from "monaco-editor"; -import CenterLoader from '../shared/components/loaders/CenterLoader'; -import RetryErrorPage from './RetryErrorPage'; import { useAdminRole } from '../hooks/useAdminRole'; -import Forbidden from './403'; -import { observer } from 'mobx-react-lite'; +import { frigateApi, frigateQueryKeys, mapHostToHostname, proxyApi } from '../services/frigate.proxy/frigate.api'; +import { GetFrigateHost } from '../services/frigate.proxy/frigate.schema'; +import CenterLoader from '../shared/components/loaders/CenterLoader'; import { isProduction } from '../shared/env.const'; import { SaveOption } from '../types/saveConfig'; -import { GetFrigateHost } from '../services/frigate.proxy/frigate.schema'; -import { error } from 'console'; +import Forbidden from './403'; +import RetryErrorPage from './RetryErrorPage'; +import { notifications } from '@mantine/notifications'; +import { IconAlertCircle, IconCircleCheck } from '@tabler/icons-react'; +window.MonacoEnvironment = { + getWorker(moduleId, label) { + switch (label) { + case 'editorWorkerService': + return new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker', import.meta.url)) + case 'css': + case 'less': + case 'scss': + return new Worker(new URL('monaco-editor/esm/vs/language/css/css.worker', import.meta.url)) + case 'handlebars': + case 'html': + case 'razor': + return new Worker( + new URL('monaco-editor/esm/vs/language/html/html.worker', import.meta.url) + ) + case 'json': + return new Worker( + new URL('monaco-editor/esm/vs/language/json/json.worker', import.meta.url) + ) + case 'javascript': + case 'typescript': + return new Worker( + new URL('monaco-editor/esm/vs/language/typescript/ts.worker', import.meta.url) + ) + case 'yaml': + return new Worker(new URL('monaco-yaml/yaml.worker', import.meta.url)) + default: + throw new Error(`Unknown label ${label}`) + } + } +} + const HostConfigPage = () => { const executed = useRef(false) const host = useRef() const { sideBarsStore } = useContext(Context) - const [saveMessage, setSaveMessage] = useState() let { id } = useParams<'id'>() const { isAdmin, isLoading: adminLoading } = useAdminRole() @@ -51,10 +84,26 @@ const HostConfigPage = () => { }) }, onSuccess: (data) => { - setSaveMessage(data?.message) + notifications.show({ + id: data?.message, + withCloseButton: true, + autoClose: 5000, + title: `Sucess: ${data?.success}`, + message: data?.message, + color: 'green', + icon: + }) }, - onError: (error) => { - setSaveMessage(error.message) + onError: (e) => { + notifications.show({ + id: e.message, + withCloseButton: true, + autoClose: false, + title: "Error", + message: e.message, + color: 'red', + icon: , + }) } }) @@ -72,28 +121,24 @@ const HostConfigPage = () => { const editorRef = useRef(null) function handleEditorWillMount(monaco: Monaco) { - monaco.languages.typescript.javascriptDefaults.setEagerModelSync(true); - - // TODO add yaml schema - // const modelUri = monaco.Uri.parse("http://localhost:4000/proxy/api/config/schema.json?hostName=localhost:5001") - // configureMonacoYaml(monaco, { - // enableSchemaRequest: true, - // hover: true, - // completion: true, - // validate: true, - // format: true, - // schemas: [ - // { - // uri: `http://localhost:4000/proxy/api/config/schema.json?hostName=localhost:5001`, - // fileMatch: ['**/.schema.*'], - // }, - // ], - // }) + const hostName = mapHostToHostname(host.current) + if (!hostName) return + const schemaURL = proxyApi.configSchemaURL(hostName) + const defaultSchema: SchemasSettings = { + uri: schemaURL, + fileMatch: ['monaco-yaml.yaml'] + } + configureMonacoYaml(monaco, { + enableSchemaRequest: true, + schemas: [defaultSchema] + }) } function handleEditorDidMount(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) { // here is another way to get monaco instance // you can also store it in `useRef` for further usage + const model = monaco.editor.createModel(config, 'yaml', monaco.Uri.parse('monaco-yaml.yaml')) + editor.setModel(model) editorRef.current = editor; } @@ -115,7 +160,7 @@ const HostConfigPage = () => { saveConfig({ saveOption: saveOption, config: editorRef.current.getValue() }) }, [editorRef]) - if (configPending || adminLoading) return + if (configPending || adminLoading ) return if (configError) return if (!isAdmin) return @@ -133,11 +178,6 @@ const HostConfigPage = () => { Save Only - {!saveMessage ? null : - - {saveMessage} - - } { const executed = useRef(false) + const editorRef = useRef(null) + const theme = useMantineTheme(); const { sideBarsStore } = useContext(Context) sideBarsStore.rightVisible = true useEffect(() => { if (!executed.current) { - sideBarsStore.rightVisible = false - sideBarsStore.setLeftChildren(null) - sideBarsStore.setRightChildren(null) - executed.current = true + sideBarsStore.rightVisible = false + sideBarsStore.setLeftChildren(null) + sideBarsStore.setRightChildren(null) + executed.current = true } -}, [sideBarsStore]) + }, [sideBarsStore]) - // const handleClick = () => { - // const startOfDay = new Date(); - // startOfDay.setHours(0, 0, 0, 0); - // setValue([startOfDay, startOfDay]) - // } - const cards = (qty: number) => { - let items = [] - for (let i = 0; i < qty; i++) { - items.push() + + const value = ` + # Property descriptions are displayed when hovering over properties using your cursor + property: This property has a JSON schema description + + + # Titles work too! + titledProperty: Titles work too! + + + # Even markdown descriptions work + markdown: hover me to get a markdown based description 😮 + + + # Enums can be autocompleted by placing the cursor after the colon and pressing Ctrl+Space + enum: + + + # Unused anchors will be reported + unused anchor: &unused anchor + + + # Of course numbers are supported! + number: 12 + + + # As well as booleans! + boolean: true + + + # And strings + string: I am a string + + + # This property is using the JSON schema recursively + reference: + boolean: Not a boolean + + + # Also works in arrays + array: + - string: 12 + enum: Mewtwo + reference: + reference: + boolean: true + + + # JSON referenses can be clicked for navigation + pointer: + $ref: '#/array' + + + # This anchor can be referenced + anchorRef: &anchor can be clicked as well + + + # Press control while hovering over the anchor + anchorPointer: *anchor + + + formatting: Formatting is supported too! Under the hood this is powered by Prettier. Just press Ctrl+Shift+I or right click and press Format to format this document. + + + + + + + `.replace(/:$/m, ': ') + + function handleEditorWillMount(monaco: Monaco) { + const defaultSchema: SchemasSettings = { + uri: 'https://github.com/remcohaszing/monaco-yaml/blob/HEAD/examples/demo/src/schema.json', + fileMatch: ['monaco-yaml.yaml'] } - return items + + configureMonacoYaml(monaco, { + enableSchemaRequest: true, + schemas: [defaultSchema] + }) } + function handleEditorDidMount(editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) { + // here is another way to get monaco instance + // you can also store it in `useRef` for further usage + const model = monaco.editor.createModel(value, 'yaml', monaco.Uri.parse('monaco-yaml.yaml')) + editor.setModel(model) + editorRef.current = editor; + } + + return ( @@ -54,9 +136,14 @@ const Test = () => { - - {cards(60)} - + ); diff --git a/src/services/frigate.proxy/frigate.api.ts b/src/services/frigate.proxy/frigate.api.ts index b03ac99..adf8495 100644 --- a/src/services/frigate.proxy/frigate.api.ts +++ b/src/services/frigate.proxy/frigate.api.ts @@ -143,6 +143,7 @@ export const proxyApi = { getEventsSummary: (hostName: string, cameraName: string) => instanceApi.get(`proxy/${hostName}/api/${cameraName}/events/summary`).then(res => res.data), + configSchemaURL: (hostName: string) => `${proxyPrefix}${hostName}/api/config/schema.json`, cameraWsURL: (hostName: string, cameraName: string) => { const protocol = proxyURL.protocol === 'https:' ? 'wss' : 'ws'; return `${protocol}://${proxyURL.host}/proxy-ws/${hostName}/live/jsmpeg/${cameraName}` diff --git a/yarn.lock b/yarn.lock index 6529b34..6e09f9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10857,7 +10857,12 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.0.0, yaml@^2.3.4: +yaml@^2.0.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.1.tgz#2e57e0b5e995292c25c75d2658f0664765210eed" + integrity sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg== + +yaml@^2.3.4: version "2.4.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.0.tgz#2376db1083d157f4b3a452995803dbcf43b08140" integrity sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==