1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Add ability to configure the locale timezone (#2523)

This commit is contained in:
Arthur Knoepflin 2021-04-20 15:26:52 +02:00 committed by GitHub
parent 8d42d40433
commit 52ebcc4fdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 84 additions and 8 deletions

View File

@ -224,6 +224,7 @@
"mobx-react": "^6.2.2", "mobx-react": "^6.2.2",
"mock-fs": "^4.12.0", "mock-fs": "^4.12.0",
"moment": "^2.26.0", "moment": "^2.26.0",
"moment-timezone": "^0.5.33",
"node-pty": "^0.9.0", "node-pty": "^0.9.0",
"npm": "^6.14.8", "npm": "^6.14.8",
"openid-client": "^3.15.2", "openid-client": "^3.15.2",

View File

@ -3,6 +3,7 @@ import { app, remote } from "electron";
import semver from "semver"; import semver from "semver";
import { readFile } from "fs-extra"; import { readFile } from "fs-extra";
import { action, computed, observable, reaction, toJS } from "mobx"; import { action, computed, observable, reaction, toJS } from "mobx";
import moment from "moment-timezone";
import { BaseStore } from "./base-store"; import { BaseStore } from "./base-store";
import migrations from "../migrations/user-store"; import migrations from "../migrations/user-store";
import { getAppVersion } from "./utils/app-version"; import { getAppVersion } from "./utils/app-version";
@ -23,6 +24,7 @@ export interface UserPreferences {
httpsProxy?: string; httpsProxy?: string;
shell?: string; shell?: string;
colorTheme?: string; colorTheme?: string;
localeTimezone?: string;
allowUntrustedCAs?: boolean; allowUntrustedCAs?: boolean;
allowTelemetry?: boolean; allowTelemetry?: boolean;
downloadMirror?: string | "default"; downloadMirror?: string | "default";
@ -54,6 +56,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
allowTelemetry: true, allowTelemetry: true,
allowUntrustedCAs: false, allowUntrustedCAs: false,
colorTheme: UserStore.defaultTheme, colorTheme: UserStore.defaultTheme,
localeTimezone: moment.tz.guess(true) || "UTC",
downloadMirror: "default", downloadMirror: "default",
downloadKubectlBinaries: true, // Download kubectl binaries matching cluster version downloadKubectlBinaries: true, // Download kubectl binaries matching cluster version
openAtLogin: false, openAtLogin: false,
@ -130,6 +133,11 @@ export class UserStore extends BaseStore<UserStoreModel> {
this.lastSeenAppVersion = getAppVersion(); this.lastSeenAppVersion = getAppVersion();
} }
@action
setLocaleTimezone(tz: string) {
this.preferences.localeTimezone = tz;
}
protected refreshNewContexts = async () => { protected refreshNewContexts = async () => {
try { try {
const kubeConfig = await readFile(this.kubeConfigPath, "utf8"); const kubeConfig = await readFile(this.kubeConfigPath, "utf8");

View File

@ -11,6 +11,7 @@ import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { lookupApiLink } from "../../api/kube-api"; import { lookupApiLink } from "../../api/kube-api";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry"; import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
import { LocaleDate } from "../locale-date";
interface Props extends KubeObjectDetailsProps<KubeEvent> { interface Props extends KubeObjectDetailsProps<KubeEvent> {
} }
@ -38,10 +39,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 {event.firstTimestamp} {event.getFirstSeenTime()} ago (<LocaleDate date={event.firstTimestamp} />)
</DrawerItem> </DrawerItem>
<DrawerItem name="Last seen"> <DrawerItem name="Last seen">
{event.getLastSeenTime()} ago {event.lastTimestamp} {event.getLastSeenTime()} ago (<LocaleDate date={event.lastTimestamp} />)
</DrawerItem> </DrawerItem>
<DrawerItem name="Count"> <DrawerItem name="Count">
{count} {count}

View File

@ -1,6 +1,7 @@
import "./preferences.scss"; import "./preferences.scss";
import React from "react"; import React from "react";
import moment from "moment-timezone";
import { computed, observable, reaction } from "mobx"; import { computed, observable, reaction } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
@ -40,6 +41,11 @@ export class Preferences extends React.Component {
})); }));
} }
timezoneOptions: SelectOption<string>[] = moment.tz.names().map(zone => ({
label: zone,
value: zone,
}));
componentDidMount() { componentDidMount() {
disposeOnUnmount(this, [ disposeOnUnmount(this, [
reaction(() => navigation.location.hash, hash => { reaction(() => navigation.location.hash, hash => {
@ -95,7 +101,7 @@ export class Preferences extends React.Component {
const { preferences } = userStore; const { preferences } = userStore;
const extensions = appPreferenceRegistry.getItems(); const extensions = appPreferenceRegistry.getItems();
const telemetryExtensions = extensions.filter(e => e.showInPreferencesTab == Pages.Telemetry); const telemetryExtensions = extensions.filter(e => e.showInPreferencesTab == Pages.Telemetry);
let defaultShell = process.env.SHELL || process.env.PTYSHELL; let defaultShell = process.env.SHELL || process.env.PTYSHELL;
if (!defaultShell) { if (!defaultShell) {
if (isWindows) { if (isWindows) {
@ -153,6 +159,18 @@ export class Preferences extends React.Component {
label="Automatically start Lens on login" label="Automatically start Lens on login"
/> />
</section> </section>
<hr />
<section id="locale">
<SubTitle title="Locale Timezone" />
<Select
options={this.timezoneOptions}
value={preferences.localeTimezone}
onChange={({ value }: SelectOption) => userStore.setLocaleTimezone(value)}
themeName="lens"
/>
</section>
</section> </section>
)} )}
{this.activeTab == Pages.Proxy && ( {this.activeTab == Pages.Proxy && (

View File

@ -13,6 +13,7 @@ import { IMetrics } from "../../api/endpoints/metrics.api";
import { ContainerCharts } from "./container-charts"; import { ContainerCharts } from "./container-charts";
import { ResourceType } from "../cluster-settings/components/cluster-metrics-setting"; import { ResourceType } from "../cluster-settings/components/cluster-metrics-setting";
import { clusterStore } from "../../../common/cluster-store"; import { clusterStore } from "../../../common/cluster-store";
import { LocaleDate } from "../locale-date";
interface Props { interface Props {
pod: Pod; pod: Pod;
@ -39,8 +40,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: {status.lastState.terminated.startedAt}<br/> Started at: {<LocaleDate date={status.lastState.terminated.startedAt} />}<br/>
Finished at: {status.lastState.terminated.finishedAt}<br/> Finished at: {<LocaleDate date={status.lastState.terminated.finishedAt} />}<br/>
</span> </span>
); );
} }

View File

@ -6,9 +6,11 @@ import DOMPurify from "dompurify";
import debounce from "lodash/debounce"; import debounce from "lodash/debounce";
import { action, computed, observable } from "mobx"; import { action, computed, observable } from "mobx";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import moment from "moment-timezone";
import { Align, ListOnScrollProps } from "react-window"; import { Align, ListOnScrollProps } from "react-window";
import { SearchStore, searchStore } from "../../../common/search-store"; import { SearchStore, searchStore } from "../../../common/search-store";
import { userStore } from "../../../common/user-store";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import { Button } from "../button"; import { Button } from "../button";
import { Icon } from "../icon"; import { Icon } from "../icon";
@ -79,12 +81,15 @@ export class LogList extends React.Component<Props> {
@computed @computed
get logs() { get logs() {
const showTimestamps = logTabStore.getData(this.props.id).showTimestamps; const showTimestamps = logTabStore.getData(this.props.id).showTimestamps;
const { preferences } = userStore;
if (!showTimestamps) { if (!showTimestamps) {
return logStore.logsWithoutTimestamps; return logStore.logsWithoutTimestamps;
} }
return this.props.logs; return this.props.logs
.map(log => logStore.splitOutTimestamp(log))
.map(([logTimestamp, log]) => (`${moment.tz(logTimestamp, preferences.localeTimezone).format()}${log}`));
} }
/** /**

View File

@ -146,6 +146,16 @@ export class LogStore {
return stamp.toISOString(); return stamp.toISOString();
} }
splitOutTimestamp(logs: string): [string, string] {
const extraction = /^(\d+\S+)(.*)/m.exec(logs);
if (!extraction || extraction.length < 3) {
return ["", ""];
}
return [extraction[1], extraction[2]];
}
getTimestamps(logs: string) { getTimestamps(logs: string) {
return logs.match(/^\d+\S+/gm); return logs.match(/^\d+\S+/gm);
} }

View File

@ -4,6 +4,7 @@ import { DrawerItem, DrawerItemLabels } from "../drawer";
import { lookupApiLink } from "../../api/kube-api"; import { lookupApiLink } from "../../api/kube-api";
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-object-details"; import { getDetailsUrl } from "./kube-object-details";
export interface KubeObjectMetaProps { export interface KubeObjectMetaProps {
@ -35,7 +36,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 ({creationTimestamp}) {getAge(true, false)} ago ({<LocaleDate date={creationTimestamp} />})
</DrawerItem> </DrawerItem>
<DrawerItem name="Name" hidden={this.isHidden("name")}> <DrawerItem name="Name" hidden={this.isHidden("name")}>
{getName()} <KubeObjectStatusIcon key="icon" object={object} /> {getName()} <KubeObjectStatusIcon key="icon" object={object} />

View File

@ -0,0 +1 @@
export * from "./locale-date";

View File

@ -0,0 +1,18 @@
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 { preferences } = userStore;
const { date } = this.props;
return <>{moment.tz(date, preferences.localeTimezone).format()}</>;
}
}

View File

@ -9538,6 +9538,18 @@ mock-fs@^4.12.0:
resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.12.0.tgz#a5d50b12d2d75e5bec9dac3b67ffe3c41d31ade4" resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.12.0.tgz#a5d50b12d2d75e5bec9dac3b67ffe3c41d31ade4"
integrity sha512-/P/HtrlvBxY4o/PzXY9cCNBrdylDNxg7gnrv2sMNxj+UJ2m8jSpl0/A6fuJeNAWr99ZvGWH8XCbE0vmnM5KupQ== integrity sha512-/P/HtrlvBxY4o/PzXY9cCNBrdylDNxg7gnrv2sMNxj+UJ2m8jSpl0/A6fuJeNAWr99ZvGWH8XCbE0vmnM5KupQ==
moment-timezone@^0.5.33:
version "0.5.33"
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.33.tgz#b252fd6bb57f341c9b59a5ab61a8e51a73bbd22c"
integrity sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==
dependencies:
moment ">= 2.9.0"
"moment@>= 2.9.0":
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
moment@^2.10.2, moment@^2.26.0: moment@^2.10.2, moment@^2.26.0:
version "2.26.0" version "2.26.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a" resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"