From cc2a69e7aea5dede0527ea562c1c8ca25b4c9957 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Sat, 15 May 2021 12:20:19 +0100 Subject: [PATCH] feat: add copy build version link in footer (#1007) --- ui/package-lock.json | 30 +++++++++++ ui/package.json | 2 + ui/src/providers/QuestProvider.tsx | 28 ++++++++++ ui/src/providers/index.tsx | 1 + ui/src/scenes/Editor/Ace/index.tsx | 16 ++++-- ui/src/scenes/Footer/BuildVersion/index.tsx | 54 +++++++++++++++++++ ui/src/scenes/Footer/BuildVersion/services.ts | 7 +++ .../{ => Footer}/GithubBanner/index.tsx | 0 ui/src/scenes/Footer/index.tsx | 23 +++++--- ui/src/scenes/Layout/index.tsx | 5 +- 10 files changed, 151 insertions(+), 15 deletions(-) create mode 100644 ui/src/providers/QuestProvider.tsx create mode 100644 ui/src/providers/index.tsx create mode 100644 ui/src/scenes/Footer/BuildVersion/index.tsx create mode 100644 ui/src/scenes/Footer/BuildVersion/services.ts rename ui/src/scenes/{ => Footer}/GithubBanner/index.tsx (100%) diff --git a/ui/package-lock.json b/ui/package-lock.json index c43942e0b..1aee21dbd 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -18879,6 +18879,14 @@ "csstype": "^2.2.0" } }, + "@types/react-copy-to-clipboard": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.0.tgz", + "integrity": "sha512-faUg6Kx3Dfv0MBIcs+xzIptlRtjEVSaNjqyC14YAp4UwSiTHghnKtBOt9ERRTZZJfoJgnw10tomVaqG86GzdAw==", + "requires": { + "@types/react": "*" + } + }, "@types/react-dom": { "version": "16.9.8", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz", @@ -21151,6 +21159,14 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, "copy-webpack-plugin": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.0.2.tgz", @@ -27801,6 +27817,15 @@ "prop-types": "^15.7.2" } }, + "react-copy-to-clipboard": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.3.tgz", + "integrity": "sha512-9S3j+m+UxDZOM0Qb8mhnT/rMR0NGSrj9A/073yz2DSxPMYhmYFBMYIdI2X4o8AjOjyFsSNxDRnCX6s/gRxpriw==", + "requires": { + "copy-to-clipboard": "^3", + "prop-types": "^15.5.8" + } + }, "react-dom": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", @@ -30414,6 +30439,11 @@ "repeat-string": "^1.6.1" } }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", diff --git a/ui/package.json b/ui/package.json index 097618e82..a192e68c0 100644 --- a/ui/package.json +++ b/ui/package.json @@ -26,6 +26,7 @@ "@popperjs/core": "2.4.2", "@questdb/sql-grammar": "1.0.7", "@types/react": "16.9.35", + "@types/react-copy-to-clipboard": "5.0.0", "@types/react-dom": "16.9.8", "@types/react-redux": "7.1.9", "@types/react-transition-group": "4.4.0", @@ -43,6 +44,7 @@ "jquery": "3.5.1", "react": "16.13.1", "react-ace": "9.0.0", + "react-copy-to-clipboard": "5.0.3", "react-dom": "16.13.1", "react-popper": "2.2.3", "react-redux": "7.2.0", diff --git a/ui/src/providers/QuestProvider.tsx b/ui/src/providers/QuestProvider.tsx new file mode 100644 index 000000000..ad8d28ebf --- /dev/null +++ b/ui/src/providers/QuestProvider.tsx @@ -0,0 +1,28 @@ +import React, { createContext, PropsWithChildren } from "react" +import * as QuestDB from "utils/questdb" + +const questClient = new QuestDB.Client() + +type Props = {} + +type ContextProps = { + quest: QuestDB.Client +} + +const defaultValues = { + quest: questClient, +} + +export const QuestContext = createContext(defaultValues) + +export const QuestProvider = ({ children }: PropsWithChildren) => { + return ( + + {children} + + ) +} diff --git a/ui/src/providers/index.tsx b/ui/src/providers/index.tsx new file mode 100644 index 000000000..56794dafc --- /dev/null +++ b/ui/src/providers/index.tsx @@ -0,0 +1 @@ +export * from "./QuestProvider" diff --git a/ui/src/scenes/Editor/Ace/index.tsx b/ui/src/scenes/Editor/Ace/index.tsx index 6cbacfdbf..70588428f 100644 --- a/ui/src/scenes/Editor/Ace/index.tsx +++ b/ui/src/scenes/Editor/Ace/index.tsx @@ -23,7 +23,13 @@ ******************************************************************************/ import { Range } from "ace-builds" -import React, { useCallback, useEffect, useRef, useState } from "react" +import React, { + useCallback, + useContext, + useEffect, + useRef, + useState, +} from "react" import ReactAce from "react-ace" import { useDispatch, useSelector } from "react-redux" import ResizeObserver from "resize-observer-polyfill" @@ -48,8 +54,7 @@ import { savePreferences, toTextPosition, } from "./utils" - -const quest = new QuestDB.Client() +import { QuestContext } from "providers" const Content = styled(PaneContent)` position: relative; @@ -76,6 +81,7 @@ enum Command { } const Ace = () => { + const { quest } = useContext(QuestContext) const [request, setRequest] = useState() const [value, setValue] = useState("") const aceEditor = useRef(null) @@ -93,7 +99,7 @@ const Ace = () => { dispatch(actions.query.stopRunning()) setRequest(undefined) } - }, [dispatch, request, running]) + }, [request, quest, dispatch, running]) useEffect(() => { if (!aceEditor.current) { @@ -192,7 +198,7 @@ const Ace = () => { dispatch(actions.query.stopRunning()) } } - }, [dispatch, running]) + }, [quest, dispatch, running]) useEffect(() => { if (!aceEditor.current) { diff --git a/ui/src/scenes/Footer/BuildVersion/index.tsx b/ui/src/scenes/Footer/BuildVersion/index.tsx new file mode 100644 index 000000000..e744734c8 --- /dev/null +++ b/ui/src/scenes/Footer/BuildVersion/index.tsx @@ -0,0 +1,54 @@ +import { QuestContext } from "providers" +import React, { useContext, useEffect, useState } from "react" +import styled from "styled-components" +import * as QuestDB from "utils/questdb" +import { CopyToClipboard } from "react-copy-to-clipboard" +import { ClipboardCopy } from "@styled-icons/heroicons-outline/ClipboardCopy" +import { SecondaryButton } from "components" +import { formatVersion } from "./services" + +const Wrapper = styled.div` + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + + & > :not(:last-child) { + margin-right: 1rem; + } +` +const CopyButton = styled(SecondaryButton)` + & > :not(:last-child) { + margin-right: 1rem; + } +` + +const BuildVersion = () => { + const { quest } = useContext(QuestContext) + const [buildVersion, setBuildVersion] = useState("") + + useEffect(() => { + void quest.queryRaw("select build", { limit: "0,1000" }).then((result) => { + if (result.type === QuestDB.Type.DQL) { + if (result.count === 1) { + setBuildVersion(result.dataset[0][0]) + } + } + }) + }) + + const version = formatVersion(buildVersion) + + return ( + + + + {version} + + + + + ) +} + +export default BuildVersion diff --git a/ui/src/scenes/Footer/BuildVersion/services.ts b/ui/src/scenes/Footer/BuildVersion/services.ts new file mode 100644 index 000000000..51265cf90 --- /dev/null +++ b/ui/src/scenes/Footer/BuildVersion/services.ts @@ -0,0 +1,7 @@ +const buildVersionRegex = /Build Information: (QuestDB [0-9A-Za-z.]*),/ + +export const formatVersion = (value: string) => { + const matches = buildVersionRegex.exec(value) + + return matches ? matches[1] : "" +} diff --git a/ui/src/scenes/GithubBanner/index.tsx b/ui/src/scenes/Footer/GithubBanner/index.tsx similarity index 100% rename from ui/src/scenes/GithubBanner/index.tsx rename to ui/src/scenes/Footer/GithubBanner/index.tsx diff --git a/ui/src/scenes/Footer/index.tsx b/ui/src/scenes/Footer/index.tsx index 30f0985d7..48c34b623 100644 --- a/ui/src/scenes/Footer/index.tsx +++ b/ui/src/scenes/Footer/index.tsx @@ -31,7 +31,8 @@ import { Github } from "@styled-icons/remix-fill/Github" import { Link, Text, TransitionDuration } from "components" import { selectors } from "store" -import GithubBanner from "../GithubBanner" +import GithubBanner from "./GithubBanner" +import BuildVersion from "./BuildVersion" const Wrapper = styled.div` position: absolute; @@ -43,17 +44,21 @@ const Wrapper = styled.div` padding-left: 45px; ` -const Copyright = styled.div` +const LeftContainer = styled.div` display: flex; padding-left: 1rem; align-items: center; flex: 1; ` -const Icons = styled.div` +const RightContainer = styled.div` display: flex; padding-right: 1rem; align-items: center; + + & > *:not(:last-child) { + margin-right: 1rem; + } ` const GithubBannerTransition = createGlobalStyle` @@ -92,13 +97,13 @@ const Footer = () => { return ( - - + Copyright © 2014-{new Date().getFullYear()} QuestDB - - + + + { > - + + + { }, []) return ( - <> +