From b11dcdf03f1632b2975633bdc5a38aec7745c5a1 Mon Sep 17 00:00:00 2001 From: Peter Tandler Date: Wed, 15 Mar 2023 08:45:49 +0100 Subject: [PATCH 1/6] Added directory breadcrumbs to - be able to view the current path one came to the current directory - be able to quickly navigate upwards --- package.json | 1 + src/DirectoryBreadcrumbs.jsx | 28 ++++++++++++++++++++++++++ src/DirectoryItems.jsx | 6 +++--- src/DirectoryObject.jsx | 39 ++++++++++++++++++++---------------- src/SnapshotsTable.jsx | 5 ++++- src/uiutil.jsx | 18 ++++++++--------- 6 files changed, 67 insertions(+), 30 deletions(-) create mode 100644 src/DirectoryBreadcrumbs.jsx diff --git a/package.json b/package.json index 921b32c..7ac82e0 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "license": "Apache-2.0", "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/free-regular-svg-icons": "^6.2.0", "@fortawesome/free-solid-svg-icons": "^6.1.1", "@fortawesome/react-fontawesome": "^0.2.0", diff --git a/src/DirectoryBreadcrumbs.jsx b/src/DirectoryBreadcrumbs.jsx new file mode 100644 index 0000000..8888c5d --- /dev/null +++ b/src/DirectoryBreadcrumbs.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import Button from 'react-bootstrap/Button'; +import { useHistory, useLocation } from 'react-router-dom'; +import { GoBackButton } from './uiutil'; + +export function DirectoryBreadcrumbs() { + const location = useLocation(); + const history = useHistory(); + + const breadcrumbs = [] + for (let state = location.state; state; state = state.prevState) { + breadcrumbs.unshift(state) + } + + return (<> + {breadcrumbs.length >= 1 && } + { + breadcrumbs.map((state, i) => { + const index = breadcrumbs.length - i - 1 // revert index + return ( + + ); + }) + } + + ) +} diff --git a/src/DirectoryItems.jsx b/src/DirectoryItems.jsx index 6653cd4..83d8f2f 100644 --- a/src/DirectoryItems.jsx +++ b/src/DirectoryItems.jsx @@ -23,9 +23,9 @@ function sizeInfo(item) { return 0; } -function directoryLinkOrDownload(x) { +function directoryLinkOrDownload(x, state) { if (x.obj.startsWith("k")) { - return {objectName(x.name, x.type)}; + return {objectName(x.name, x.type)}; } return directoryLinkOrDownload(x), + accessor: x => directoryLinkOrDownload(x, this.props.historyState), }, { id: "mtime", accessor: "mtime", diff --git a/src/DirectoryObject.jsx b/src/DirectoryObject.jsx index f4973d8..c298217 100644 --- a/src/DirectoryObject.jsx +++ b/src/DirectoryObject.jsx @@ -7,7 +7,8 @@ import Row from 'react-bootstrap/Row'; import Col from 'react-bootstrap/Col'; import Spinner from 'react-bootstrap/Spinner'; import { DirectoryItems } from "./DirectoryItems"; -import { CLIEquivalent, GoBackButton } from './uiutil'; +import { CLIEquivalent } from './uiutil'; +import { DirectoryBreadcrumbs } from "./DirectoryBreadcrumbs"; export class DirectoryObject extends Component { constructor() { @@ -120,31 +121,35 @@ export class DirectoryObject extends Component { return <> - +   - {this.state.mountInfo.path ? <> + OID: {this.state.oid} + + +   + + {this.state.mountInfo.path ? <> - {window.kopiaUI && <> -   + {window.kopiaUI && <> +   - } -   + } +   - : <> + : <> - } -   - - - -   - - You can mount/restore all the files & directories that you see below or restore files individually. + } +   + +   + You can mount/restore all the files & directories that you see below or restore files + individually.   - + diff --git a/src/SnapshotsTable.jsx b/src/SnapshotsTable.jsx index 54f0a55..e5b752d 100644 --- a/src/SnapshotsTable.jsx +++ b/src/SnapshotsTable.jsx @@ -334,7 +334,10 @@ export class SnapshotsTable extends Component { id: 'startTime', Header: 'Start time', width: 200, - accessor: x => {rfc3339TimestampForDisplay(x.startTime)}, + accessor: x => { + let timestamp = rfc3339TimestampForDisplay(x.startTime); + return {timestamp}; + }, }, { id: 'description', Header: '', diff --git a/src/uiutil.jsx b/src/uiutil.jsx index 626cb81..36e65e9 100644 --- a/src/uiutil.jsx +++ b/src/uiutil.jsx @@ -87,9 +87,9 @@ export function rfc3339TimestampForDisplay(n) { return t.toLocaleString(); } -export function objectLink(n) { +export function objectLink(n, label, prevState) { if (n.startsWith("k") || n.startsWith("Ik")) { - return "/snapshots/dir/" + n; + return { pathname: "/snapshots/dir/" + n, state: { label, prevState } }; } return "/api/v1/objects/" + n; } @@ -111,7 +111,7 @@ export function redirectIfNotConnected(e) { /** * Convert a number of milliseconds into a string containing multiple units. - * + * * e.g. 90000 --> "1m 30s" or "1 minute 30 seconds" * * @param {number} ms - A duration (as a number of milliseconds). @@ -127,9 +127,9 @@ export function formatMillisecondsUsingMultipleUnits(ms) { * Separate a duration into integer magnitudes of multiple units which, * when combined together, equal the original duration (minus any partial * milliseconds, if the original duration included any partial milliseconds). - * + * * e.g. 100000123.999 --> 1 day 3 hours 46 minutes 40 seconds 123 milliseconds - * + * * @param {number} ms - A duration (as a number of milliseconds). * @returns {object} An object having numeric properties named `days`, `hours`, * `minutes`, `seconds`, and `milliseconds`; whose values, @@ -152,16 +152,16 @@ export function separateMillisecondsIntoMagnitudes(ms) { * Format a duration in terms of the largest unit having a non-zero magnitude, * together with the next largest unit (e.g. hours --> hours and minutes), * disregarding all smaller units (i.e. truncate, as opposed to round). - * + * * There are some exceptions, which are listed below. - * + * * Exceptions: * 1. If the largest unit having a non-zero magnitude is `seconds` and the * magnitude is at least 10, format it as an integer number of seconds. * 2. If the largest unit having a non-zero magnitude is `seconds` and the * magnitude is less than 10, or if the largest unit having a non-zero * magnitude is `milliseconds`, format it as a fractional number of seconds. - * + * * @param {object} magnitudes - An object having numeric properties named * `days`, `hours`, `minutes`, `seconds`, and * `milliseconds`; whose values, when combined @@ -221,7 +221,7 @@ export function formatMagnitudesUsingMultipleUnits(magnitudes, abbreviateUnits = /** * Convert a number of milliseconds into a formatted string, either * using multiple units (e.g. "1m 5s") or using seconds (e.g. "65.0s"). - * + * * @param {number} ms - The number of milliseconds (i.e. some duration). * @param {boolean} useMultipleUnits - Whether you want to use multiple units. * @returns {string} The formatted string. From c62d5acf406fbfcbcdfe306fd1c5907b5bed3b79 Mon Sep 17 00:00:00 2001 From: Peter Tandler Date: Wed, 15 Mar 2023 16:33:46 +0100 Subject: [PATCH 2/6] layout fixes --- src/DirectoryBreadcrumbs.jsx | 2 +- src/DirectoryObject.jsx | 42 ++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/DirectoryBreadcrumbs.jsx b/src/DirectoryBreadcrumbs.jsx index 8888c5d..7cacefd 100644 --- a/src/DirectoryBreadcrumbs.jsx +++ b/src/DirectoryBreadcrumbs.jsx @@ -13,7 +13,7 @@ export function DirectoryBreadcrumbs() { } return (<> - {breadcrumbs.length >= 1 && } + {breadcrumbs.length <= 1 && } { breadcrumbs.map((state, i) => { const index = breadcrumbs.length - i - 1 // revert index diff --git a/src/DirectoryObject.jsx b/src/DirectoryObject.jsx index c298217..a558e64 100644 --- a/src/DirectoryObject.jsx +++ b/src/DirectoryObject.jsx @@ -123,28 +123,34 @@ export class DirectoryObject extends Component {   - OID: {this.state.oid} + OID: {this.state.oid}   - - {this.state.mountInfo.path ? <> - - {window.kopiaUI && <> -   - + + + {this.state.mountInfo.path ? <> + + {window.kopiaUI && <> +   + + } +   + + : <> + } -   - - : <> - - } -   - -   - You can mount/restore all the files & directories that you see below or restore files - individually. +   + +   + + + You can mount/restore all the files & directories that you see below or restore files + individually. +   From d931998160569f0e4c3c3ab2c7485a09f73a2627 Mon Sep 17 00:00:00 2001 From: Peter Tandler Date: Thu, 8 Jun 2023 22:44:55 +0200 Subject: [PATCH 3/6] use breadcrumps for history in DirectoryObject.jsx / DirectoryBreadcrumbs.jsx --- src/DirectoryBreadcrumbs.jsx | 26 +++++++++++++++++++------- src/DirectoryObject.jsx | 9 +-------- src/uiutil.jsx | 2 +- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/DirectoryBreadcrumbs.jsx b/src/DirectoryBreadcrumbs.jsx index 7cacefd..47e5074 100644 --- a/src/DirectoryBreadcrumbs.jsx +++ b/src/DirectoryBreadcrumbs.jsx @@ -1,7 +1,7 @@ import React from 'react'; -import Button from 'react-bootstrap/Button'; +import Breadcrumb from 'react-bootstrap/Breadcrumb'; import { useHistory, useLocation } from 'react-router-dom'; -import { GoBackButton } from './uiutil'; +import { OverlayTrigger, Tooltip } from "react-bootstrap"; export function DirectoryBreadcrumbs() { const location = useLocation(); @@ -12,17 +12,29 @@ export function DirectoryBreadcrumbs() { breadcrumbs.unshift(state) } - return (<> - {breadcrumbs.length <= 1 && } + // TODO: get name of "snapshot"!!?? ##### + // TODO: no tooltip if no OID ##### + // TODO: enable copying of OID to clipboard ##### + // TODO: disable / improve wrapping of tooltip ##### + // TODO: there is some flickering if hovering changes ##### + // TODO: disable clicking on current breadcrumb item ##### + return ( + + Snapshots ##### { breadcrumbs.map((state, i) => { const index = breadcrumbs.length - i - 1 // revert index return ( - + OID: {state.oid}}> + history.go(-index)} + active={!index}> + {state.label} + + ); }) } - + ) } diff --git a/src/DirectoryObject.jsx b/src/DirectoryObject.jsx index a558e64..315591d 100644 --- a/src/DirectoryObject.jsx +++ b/src/DirectoryObject.jsx @@ -119,14 +119,7 @@ export class DirectoryObject extends Component { } return <> - - - -   - OID: {this.state.oid} - - -   + {this.state.mountInfo.path ? <> diff --git a/src/uiutil.jsx b/src/uiutil.jsx index 6be2279..a105145 100644 --- a/src/uiutil.jsx +++ b/src/uiutil.jsx @@ -92,7 +92,7 @@ export function rfc3339TimestampForDisplay(n) { export function objectLink(n, label, prevState) { if (n.startsWith("k") || n.startsWith("Ik")) { - return { pathname: "/snapshots/dir/" + n, state: { label, prevState } }; + return { pathname: "/snapshots/dir/" + n, state: { label, oid: n, prevState } }; } return "/api/v1/objects/" + n; } From 0ff26631d437885fbecee94154c14e00b53d7beb Mon Sep 17 00:00:00 2001 From: Peter Tandler Date: Sun, 11 Jun 2023 15:37:45 +0200 Subject: [PATCH 4/6] - get path of snapshot - no tooltip if no OID - disable / improve wrapping of tooltip - disable clicking on current breadcrumb item --- src/App.css | 6 +++++- src/DirectoryBreadcrumbs.jsx | 25 ++++++++++++------------- src/SnapshotsTable.jsx | 15 +++++++++------ 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/App.css b/src/App.css index e636f69..72c065c 100644 --- a/src/App.css +++ b/src/App.css @@ -212,4 +212,8 @@ nav.navbar { color: #000080; font-weight: bold; padding-right: 10px; -} \ No newline at end of file +} + +.wide-tooltip .tooltip-inner { + max-width: none; +} diff --git a/src/DirectoryBreadcrumbs.jsx b/src/DirectoryBreadcrumbs.jsx index 47e5074..18b1d7d 100644 --- a/src/DirectoryBreadcrumbs.jsx +++ b/src/DirectoryBreadcrumbs.jsx @@ -12,26 +12,25 @@ export function DirectoryBreadcrumbs() { breadcrumbs.unshift(state) } - // TODO: get name of "snapshot"!!?? ##### - // TODO: no tooltip if no OID ##### // TODO: enable copying of OID to clipboard ##### - // TODO: disable / improve wrapping of tooltip ##### - // TODO: there is some flickering if hovering changes ##### - // TODO: disable clicking on current breadcrumb item ##### return ( - Snapshots ##### { breadcrumbs.map((state, i) => { const index = breadcrumbs.length - i - 1 // revert index + let item = { + if (index) history.go(-index); + }} + active={!index}> + {state.label} + ; return ( - OID: {state.oid}}> - history.go(-index)} - active={!index}> - {state.label} - - + state.oid + ? OID: {state.oid}}> + {item} + + : item ); }) } diff --git a/src/SnapshotsTable.jsx b/src/SnapshotsTable.jsx index e5b752d..dbadf69 100644 --- a/src/SnapshotsTable.jsx +++ b/src/SnapshotsTable.jsx @@ -248,8 +248,8 @@ export class SnapshotsTable extends Component { originalSnapshotDescription: x.description, }) }} - title={x.description + " - Click to update snapshot description."} - className={x.description ? "text-warning" : "text-muted"}>; + title={x.description + " - Click to update snapshot description."} + className={x.description ? "text-warning" : "text-muted"}>; } newPinFor(x) { @@ -321,6 +321,9 @@ export class SnapshotsTable extends Component { if (isLoading && !snapshots) { return ; } + const searchParams = new URLSearchParams(window.location.search); + const path = searchParams.get("path"); + snapshots.sort((a, b) => -compare(a.startTime, b.startTime)); @@ -336,7 +339,7 @@ export class SnapshotsTable extends Component { width: 200, accessor: x => { let timestamp = rfc3339TimestampForDisplay(x.startTime); - return {timestamp}; + return {timestamp}; }, }, { id: 'description', @@ -466,9 +469,9 @@ export class SnapshotsTable extends Component { Enter new description this.setState({ updatedSnapshotDescription: e.target.value })} /> + size="sm" + value={this.state.updatedSnapshotDescription} + onChange={e => this.setState({ updatedSnapshotDescription: e.target.value })} /> From abcd6ce343b920b1f23230ae468f0de9984aa6eb Mon Sep 17 00:00:00 2001 From: Peter Tandler Date: Sun, 11 Jun 2023 16:06:09 +0200 Subject: [PATCH 5/6] enable copying of OID to clipboard --- src/DirectoryBreadcrumbs.jsx | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/DirectoryBreadcrumbs.jsx b/src/DirectoryBreadcrumbs.jsx index 18b1d7d..c839558 100644 --- a/src/DirectoryBreadcrumbs.jsx +++ b/src/DirectoryBreadcrumbs.jsx @@ -2,6 +2,8 @@ import React from 'react'; import Breadcrumb from 'react-bootstrap/Breadcrumb'; import { useHistory, useLocation } from 'react-router-dom'; import { OverlayTrigger, Tooltip } from "react-bootstrap"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faInfoCircle } from "@fortawesome/free-solid-svg-icons"; export function DirectoryBreadcrumbs() { const location = useLocation(); @@ -12,26 +14,25 @@ export function DirectoryBreadcrumbs() { breadcrumbs.unshift(state) } - // TODO: enable copying of OID to clipboard ##### return ( { breadcrumbs.map((state, i) => { const index = breadcrumbs.length - i - 1 // revert index - let item = { - if (index) history.go(-index); - }} - active={!index}> + return { + if (index) history.go(-index); + }} + active={!index}> {state.label} + {state.oid && !index && <> OID: {state.oid}} + > + + } ; - return ( - state.oid - ? OID: {state.oid}}> - {item} - - : item - ); }) } From fa2f6b1819415a9b52d537a008c925cf3242ccc1 Mon Sep 17 00:00:00 2001 From: Peter Tandler Date: Sun, 11 Jun 2023 16:06:26 +0200 Subject: [PATCH 6/6] fix warning about className and readOnly form --- src/uiutil.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uiutil.jsx b/src/uiutil.jsx index a105145..65ddd0d 100644 --- a/src/uiutil.jsx +++ b/src/uiutil.jsx @@ -404,8 +404,8 @@ export function CLIEquivalent(props) { return <> - {visible && } - {visible && } + {visible && } + {visible && } ; }