mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add copy buttons for string DrawerItem's
- Changes <LocaleDate> to localizeDate() so that the string check passes - Changes the rendering of booleans in CRDs to manually stringify them Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
169f443c3b
commit
deefdc9af2
@ -155,8 +155,11 @@ export function isMetricsEmpty(metrics: Record<string, IMetrics>) {
|
|||||||
return Object.values(metrics).every(metric => !metric?.data?.result?.length);
|
return Object.values(metrics).every(metric => !metric?.data?.result?.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getItemMetrics(metrics: Record<string, IMetrics>, itemName: string): Record<string, IMetrics> | void {
|
export function getItemMetrics(metrics: Record<string, IMetrics>, itemName: string): Record<string, IMetrics> | undefined {
|
||||||
if (!metrics) return;
|
if (!metrics) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const itemMetrics = { ...metrics };
|
const itemMetrics = { ...metrics };
|
||||||
|
|
||||||
for (const metric in metrics) {
|
for (const metric in metrics) {
|
||||||
|
|||||||
@ -56,6 +56,10 @@ function convertSpecValue(value: any): any {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof value === "boolean") {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +67,7 @@ function convertSpecValue(value: any): any {
|
|||||||
export class CrdResourceDetails extends React.Component<Props> {
|
export class CrdResourceDetails extends React.Component<Props> {
|
||||||
renderAdditionalColumns(resource: KubeObject, columns: AdditionalPrinterColumnsV1[]) {
|
renderAdditionalColumns(resource: KubeObject, columns: AdditionalPrinterColumnsV1[]) {
|
||||||
return columns.map(({ name, jsonPath: jp }) => (
|
return columns.map(({ name, jsonPath: jp }) => (
|
||||||
<DrawerItem key={name} name={name} renderBoolean>
|
<DrawerItem key={name} name={name}>
|
||||||
{convertSpecValue(jsonPath.value(resource, parseJsonPath(jp.slice(1))))}
|
{convertSpecValue(jsonPath.value(resource, parseJsonPath(jp.slice(1))))}
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
));
|
));
|
||||||
|
|||||||
@ -100,32 +100,25 @@ export class CrdResources extends React.Component<Props> {
|
|||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||||
isNamespaced && { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
isNamespaced && { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||||
...extraColumns.map(column => {
|
...extraColumns.map(({ name }) => ({
|
||||||
const { name } = column;
|
title: name,
|
||||||
|
className: name.toLowerCase(),
|
||||||
return {
|
sortBy: name,
|
||||||
title: name,
|
id: name
|
||||||
className: name.toLowerCase(),
|
})),
|
||||||
sortBy: name,
|
|
||||||
id: name
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={crdInstance => [
|
renderTableContents={crdInstance => [
|
||||||
crdInstance.getName(),
|
crdInstance.getName(),
|
||||||
isNamespaced && crdInstance.getNs(),
|
isNamespaced && crdInstance.getNs(),
|
||||||
...extraColumns.map((column) => {
|
...extraColumns.map((column) => {
|
||||||
let value = jsonPath.value(crdInstance, parseJsonPath(column.jsonPath.slice(1)));
|
const value = jsonPath.value(crdInstance, parseJsonPath(column.jsonPath.slice(1)));
|
||||||
|
|
||||||
if (Array.isArray(value) || typeof value === "object") {
|
if (typeof value === "object") {
|
||||||
value = JSON.stringify(value);
|
return JSON.stringify(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return value.toString();
|
||||||
renderBoolean: true,
|
|
||||||
children: value,
|
|
||||||
};
|
|
||||||
}),
|
}),
|
||||||
crdInstance.getAge(),
|
crdInstance.getAge(),
|
||||||
]}
|
]}
|
||||||
|
|||||||
@ -30,10 +30,10 @@ import type { KubeObjectDetailsProps } from "../kube-object-details";
|
|||||||
import { KubeEvent } from "../../../common/k8s-api/endpoints/events.api";
|
import { KubeEvent } from "../../../common/k8s-api/endpoints/events.api";
|
||||||
import { KubeObjectMeta } from "../kube-object-meta";
|
import { KubeObjectMeta } from "../kube-object-meta";
|
||||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||||
import { LocaleDate } from "../locale-date";
|
|
||||||
import { getDetailsUrl } from "../kube-detail-params";
|
import { getDetailsUrl } from "../kube-detail-params";
|
||||||
import { apiManager } from "../../../common/k8s-api/api-manager";
|
import { apiManager } from "../../../common/k8s-api/api-manager";
|
||||||
import logger from "../../../common/logger";
|
import logger from "../../../common/logger";
|
||||||
|
import { localizeDate } from "../../utils";
|
||||||
|
|
||||||
interface Props extends KubeObjectDetailsProps<KubeEvent> {
|
interface Props extends KubeObjectDetailsProps<KubeEvent> {
|
||||||
}
|
}
|
||||||
@ -70,10 +70,10 @@ export class EventDetails extends React.Component<Props> {
|
|||||||
{event.getSource()}
|
{event.getSource()}
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
<DrawerItem name="First seen">
|
<DrawerItem name="First seen">
|
||||||
{event.getFirstSeenTime()} ago (<LocaleDate date={event.firstTimestamp} />)
|
{event.getFirstSeenTime()} ago ({localizeDate(event.firstTimestamp)})
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
<DrawerItem name="Last seen">
|
<DrawerItem name="Last seen">
|
||||||
{event.getLastSeenTime()} ago (<LocaleDate date={event.lastTimestamp} />)
|
{event.getLastSeenTime()} ago ({localizeDate(event.lastTimestamp)})
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
<DrawerItem name="Count">
|
<DrawerItem name="Count">
|
||||||
{count}
|
{count}
|
||||||
|
|||||||
@ -25,8 +25,7 @@ import React from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||||
import { DrawerItem, DrawerTitle } from "../drawer";
|
import { DrawerItem, DrawerTitle } from "../drawer";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames, localizeDate } from "../../utils";
|
||||||
import { LocaleDate } from "../locale-date";
|
|
||||||
import { eventStore } from "./event.store";
|
import { eventStore } from "./event.store";
|
||||||
import logger from "../../../common/logger";
|
import logger from "../../../common/logger";
|
||||||
|
|
||||||
@ -87,7 +86,7 @@ export class KubeEventDetails extends React.Component<KubeEventDetailsProps> {
|
|||||||
{involvedObject.fieldPath}
|
{involvedObject.fieldPath}
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
<DrawerItem name="Last seen">
|
<DrawerItem name="Last seen">
|
||||||
<LocaleDate date={lastTimestamp} />
|
{localizeDate(lastTimestamp)}
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import "./pod-details-container.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import type { IPodContainer, IPodContainerStatus, Pod } from "../../../common/k8s-api/endpoints";
|
import type { IPodContainer, IPodContainerStatus, Pod } from "../../../common/k8s-api/endpoints";
|
||||||
import { DrawerItem } from "../drawer";
|
import { DrawerItem } from "../drawer";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames, localizeDate } from "../../utils";
|
||||||
import { StatusBrick } from "../status-brick";
|
import { StatusBrick } from "../status-brick";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
import { ContainerEnvironment } from "./pod-container-env";
|
import { ContainerEnvironment } from "./pod-container-env";
|
||||||
@ -32,7 +32,6 @@ import { PodContainerPort } from "./pod-container-port";
|
|||||||
import { ResourceMetrics } from "../resource-metrics";
|
import { ResourceMetrics } from "../resource-metrics";
|
||||||
import type { IMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
|
import type { IMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
|
||||||
import { ContainerCharts } from "./container-charts";
|
import { ContainerCharts } from "./container-charts";
|
||||||
import { LocaleDate } from "../locale-date";
|
|
||||||
import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
|
import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
|
||||||
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
||||||
import { portForwardStore } from "../../port-forward/port-forward.store";
|
import { portForwardStore } from "../../port-forward/port-forward.store";
|
||||||
@ -70,8 +69,8 @@ export class PodDetailsContainer extends React.Component<Props> {
|
|||||||
<span>
|
<span>
|
||||||
{lastState}<br/>
|
{lastState}<br/>
|
||||||
Reason: {status.lastState.terminated.reason} - exit code: {status.lastState.terminated.exitCode}<br/>
|
Reason: {status.lastState.terminated.reason} - exit code: {status.lastState.terminated.exitCode}<br/>
|
||||||
Started at: {<LocaleDate date={status.lastState.terminated.startedAt} />}<br/>
|
Started at: {localizeDate(status.lastState.terminated.startedAt)}<br/>
|
||||||
Finished at: {<LocaleDate date={status.lastState.terminated.finishedAt} />}<br/>
|
Finished at: {localizeDate(status.lastState.terminated.finishedAt)}<br/>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,17 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
color: $drawerItemNameColor;
|
color: $drawerItemNameColor;
|
||||||
|
|
||||||
|
.Icon {
|
||||||
|
opacity: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .Icon {
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 250ms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .value {
|
> .value {
|
||||||
@ -40,6 +51,10 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
|
&.noCopy {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
&:empty:after {
|
&:empty:after {
|
||||||
content: '—'
|
content: '—'
|
||||||
}
|
}
|
||||||
@ -93,4 +108,4 @@
|
|||||||
font-weight: $font-weight-bold;
|
font-weight: $font-weight-bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,9 @@
|
|||||||
|
|
||||||
import "./drawer-item.scss";
|
import "./drawer-item.scss";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { cssNames, displayBooleans } from "../../utils";
|
import { clipboard } from "electron";
|
||||||
|
import { cssNames } from "../../utils";
|
||||||
|
import { Icon } from "../icon";
|
||||||
|
|
||||||
export interface DrawerItemProps extends React.HTMLAttributes<any> {
|
export interface DrawerItemProps extends React.HTMLAttributes<any> {
|
||||||
name: React.ReactNode;
|
name: React.ReactNode;
|
||||||
@ -29,22 +31,49 @@ export interface DrawerItemProps extends React.HTMLAttributes<any> {
|
|||||||
title?: string;
|
title?: string;
|
||||||
labelsOnly?: boolean;
|
labelsOnly?: boolean;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
renderBoolean?: boolean; // show "true" or "false" for all of the children elements are "typeof boolean"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DrawerItem extends React.Component<DrawerItemProps> {
|
interface State {
|
||||||
|
isCopied: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DrawerItem extends React.Component<DrawerItemProps, State> {
|
||||||
|
state = {
|
||||||
|
isCopied: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
copyValue = (content: string) => {
|
||||||
|
clipboard.writeText(content);
|
||||||
|
this.setState({ isCopied: true });
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setState({ isCopied: false });
|
||||||
|
}, 3000);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { name, title, labelsOnly, children, hidden, className, renderBoolean, ...elemProps } = this.props;
|
const { name, title, labelsOnly, children, hidden, className, ...elemProps } = this.props;
|
||||||
|
const { isCopied } = this.state;
|
||||||
|
|
||||||
if (hidden) return null;
|
if (hidden) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const classNames = cssNames("DrawerItem", className, { labelsOnly });
|
const stringChildren = React.Children.toArray(children).filter(child => typeof child === "string");
|
||||||
const content = displayBooleans(renderBoolean, children);
|
const canCopy = stringChildren.length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...elemProps} className={classNames} title={title}>
|
<div {...elemProps} className={cssNames("DrawerItem", className, { labelsOnly })} title={title}>
|
||||||
<span className="name">{name}</span>
|
<span className="name">
|
||||||
<span className="value">{content}</span>
|
{name}
|
||||||
|
{canCopy && (
|
||||||
|
<Icon
|
||||||
|
material={isCopied ? "done" : "content_copy"}
|
||||||
|
tooltip={isCopied ? "Copied!" : "Copy"}
|
||||||
|
onClick={() => this.copyValue(stringChildren.join(""))}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span className={cssNames("value", { noCopy: canCopy })}>{children}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,9 +25,9 @@ import { DrawerItem, DrawerItemLabels } from "../drawer";
|
|||||||
import { apiManager } from "../../../common/k8s-api/api-manager";
|
import { apiManager } from "../../../common/k8s-api/api-manager";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||||
import { LocaleDate } from "../locale-date";
|
|
||||||
import { getDetailsUrl } from "../kube-detail-params";
|
import { getDetailsUrl } from "../kube-detail-params";
|
||||||
import logger from "../../../common/logger";
|
import logger from "../../../common/logger";
|
||||||
|
import { localizeDate } from "../../utils";
|
||||||
|
|
||||||
export interface KubeObjectMetaProps {
|
export interface KubeObjectMetaProps {
|
||||||
object: KubeObject;
|
object: KubeObject;
|
||||||
@ -67,7 +67,7 @@ export class KubeObjectMeta extends React.Component<KubeObjectMetaProps> {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DrawerItem name="Created" hidden={this.isHidden("creationTimestamp")}>
|
<DrawerItem name="Created" hidden={this.isHidden("creationTimestamp")}>
|
||||||
{getAge(true, false)} ago ({<LocaleDate date={creationTimestamp} />})
|
{getAge(true, false)} ago ({localizeDate(creationTimestamp)})
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
<DrawerItem name="Name" hidden={this.isHidden("name")}>
|
<DrawerItem name="Name" hidden={this.isHidden("name")}>
|
||||||
{getName()}
|
{getName()}
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2021 OpenLens Authors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to
|
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import moment from "moment-timezone";
|
|
||||||
import { UserStore } from "../../../common/user-store";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
date: string
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export class LocaleDate extends React.Component<Props> {
|
|
||||||
render() {
|
|
||||||
const { date } = this.props;
|
|
||||||
|
|
||||||
return moment.tz(date, UserStore.getInstance().localeTimezone).format();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -23,7 +23,7 @@ import "./table-cell.scss";
|
|||||||
import type { TableSortBy, TableSortParams } from "./table";
|
import type { TableSortBy, TableSortParams } from "./table";
|
||||||
|
|
||||||
import React, { ReactNode } from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { boundMethod, cssNames, displayBooleans } from "../../utils";
|
import { boundMethod, cssNames } from "../../utils";
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { Checkbox } from "../checkbox";
|
import { Checkbox } from "../checkbox";
|
||||||
|
|
||||||
@ -35,7 +35,6 @@ export interface TableCellProps extends React.DOMAttributes<HTMLDivElement> {
|
|||||||
title?: ReactNode;
|
title?: ReactNode;
|
||||||
checkbox?: boolean; // render cell with a checkbox
|
checkbox?: boolean; // render cell with a checkbox
|
||||||
isChecked?: boolean; // mark checkbox as checked or not
|
isChecked?: boolean; // mark checkbox as checked or not
|
||||||
renderBoolean?: boolean; // show "true" or "false" for all of the children elements are "typeof boolean"
|
|
||||||
sortBy?: TableSortBy; // column name, must be same as key in sortable object <Table sortable={}/>
|
sortBy?: TableSortBy; // column name, must be same as key in sortable object <Table sortable={}/>
|
||||||
showWithColumn?: string // id of the column which follow same visibility rules
|
showWithColumn?: string // id of the column which follow same visibility rules
|
||||||
_sorting?: Partial<TableSortParams>; // <Table> sorting state, don't use this prop outside (!)
|
_sorting?: Partial<TableSortParams>; // <Table> sorting state, don't use this prop outside (!)
|
||||||
@ -88,13 +87,13 @@ export class TableCell extends React.Component<TableCellProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className, checkbox, isChecked, sortBy, _sort, _sorting, _nowrap, children, title, renderBoolean: displayBoolean, showWithColumn, ...cellProps } = this.props;
|
const { className, checkbox, isChecked, sortBy, _sort, _sorting, _nowrap, children, title, showWithColumn, ...cellProps } = this.props;
|
||||||
const classNames = cssNames("TableCell", className, {
|
const classNames = cssNames("TableCell", className, {
|
||||||
checkbox,
|
checkbox,
|
||||||
nowrap: _nowrap,
|
nowrap: _nowrap,
|
||||||
sorting: this.isSortable,
|
sorting: this.isSortable,
|
||||||
});
|
});
|
||||||
const content = displayBooleans(displayBoolean, title || children);
|
const content = title || children;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...cellProps} className={classNames} onClick={this.onClick}>
|
<div {...cellProps} className={classNames} onClick={this.onClick}>
|
||||||
|
|||||||
@ -37,3 +37,4 @@ export * from "./name-parts";
|
|||||||
export * from "./prevDefault";
|
export * from "./prevDefault";
|
||||||
export * from "./saveFile";
|
export * from "./saveFile";
|
||||||
export * from "./storageHelper";
|
export * from "./storageHelper";
|
||||||
|
export * from "./locale-date";
|
||||||
|
|||||||
@ -19,4 +19,13 @@
|
|||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from "./locale-date";
|
import moment from "moment";
|
||||||
|
import { UserStore } from "../../common/user-store";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse `date` and reformat it based upon the settings from `UserStore`
|
||||||
|
* @param date The date string
|
||||||
|
*/
|
||||||
|
export function localizeDate(date: string): string {
|
||||||
|
return moment.tz(date, UserStore.getInstance().localeTimezone).format();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user