From da3ed45758dd526ecc87247a5678bfffe22ac7e2 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Sun, 18 Sep 2022 23:48:33 -0400 Subject: [PATCH] Adds new graph settings & settings UI --- package.json | 64 ++++++-- src/config.ts | 4 + src/plus/webviews/graph/graphWebview.ts | 43 ++++-- src/plus/webviews/graph/protocol.ts | 15 +- src/system/date.ts | 4 +- src/webviews/apps/plus/graph/GraphWrapper.tsx | 37 ++++- .../apps/settings/partials/commit-graph.html | 145 ++++++++++++++++++ .../apps/settings/partials/rebase-editor.html | 24 ++- src/webviews/apps/settings/settings.html | 10 ++ src/webviews/apps/settings/settings.scss | 1 + src/webviews/apps/shared/appWithConfigBase.ts | 6 + 11 files changed, 307 insertions(+), 46 deletions(-) create mode 100644 src/webviews/apps/settings/partials/commit-graph.html diff --git a/package.json b/package.json index 8ffb39a51d534..f5930cd31e4ee 100644 --- a/package.json +++ b/package.json @@ -2092,6 +2092,34 @@ "title": "Commit Graph", "order": 105, "properties": { + "gitlens.graph.avatars": { + "type": "boolean", + "default": true, + "markdownDescription": "Specifies whether to show avatar images instead of author initials in the _Commit Graph_", + "scope": "window", + "order": 10 + }, + "gitlens.graph.highlightRowsOnRefHover": { + "type": "boolean", + "default": true, + "markdownDescription": "Specifies whether to highlight rows associated with the ref when hovering over it in the _Commit Graph_", + "scope": "window", + "order": 11 + }, + "gitlens.graph.defaultItemLimit": { + "type": "number", + "default": 500, + "markdownDescription": "Specifies the default number of items to show in the _Commit Graph_. Use 0 to specify no limit", + "scope": "window", + "order": 20 + }, + "gitlens.graph.pageItemLimit": { + "type": "number", + "default": 200, + "markdownDescription": "Specifies the number of additional items to fetch when paginating in the _Commit Graph_. Use 0 to specify no limit", + "scope": "window", + "order": 21 + }, "gitlens.graph.commitOrdering": { "type": "string", "default": "date", @@ -2107,21 +2135,35 @@ ], "markdownDescription": "Specifies the order by which commits will be shown on the _Commit Graph_", "scope": "window", - "order": 10 + "order": 30 }, - "gitlens.graph.defaultItemLimit": { - "type": "number", - "default": 500, - "markdownDescription": "Specifies the default number of items to show in the _Commit Graph_. Use 0 to specify no limit", + "gitlens.graph.dateStyle": { + "type": [ + "string", + "null" + ], + "default": "relative", + "enum": [ + "relative", + "absolute" + ], + "enumDescriptions": [ + "e.g. 1 day ago", + "e.g. July 25th, 2018 7:18pm" + ], + "markdownDescription": "Specifies how dates will be displayed in the _Commit Graph_", "scope": "window", - "order": 20 + "order": 40 }, - "gitlens.graph.pageItemLimit": { - "type": "number", - "default": 200, - "markdownDescription": "Specifies the number of additional items to fetch when paginating in the _Commit Graph_. Use 0 to specify no limit", + "gitlens.graph.dateFormat": { + "type": [ + "string", + "null" + ], + "default": null, + "markdownDescription": "Specifies how absolute dates will be formatted in the _Commit Graph_. See the [Moment.js docs](https://momentjs.com/docs/#/displaying/format/) for supported formats", "scope": "window", - "order": 21 + "order": 41 }, "gitlens.graph.statusBar.enabled": { "type": "boolean", diff --git a/src/config.ts b/src/config.ts index 4c47f9bb71b5b..35f8de163fc30 100644 --- a/src/config.ts +++ b/src/config.ts @@ -378,8 +378,12 @@ export interface GraphColumnConfig { } export interface GraphConfig { + avatars: boolean; commitOrdering: 'date' | 'author-date' | 'topo'; + dateFormat: DateTimeFormat | string | null; + dateStyle: DateStyle | null; defaultItemLimit: number; + highlightRowsOnRefHover: boolean; pageItemLimit: number; statusBar: { enabled: boolean; diff --git a/src/plus/webviews/graph/graphWebview.ts b/src/plus/webviews/graph/graphWebview.ts index d03c758f5c138..b8411b7c9e904 100644 --- a/src/plus/webviews/graph/graphWebview.ts +++ b/src/plus/webviews/graph/graphWebview.ts @@ -27,7 +27,7 @@ import { onIpc } from '../../../webviews/protocol'; import { WebviewBase } from '../../../webviews/webviewBase'; import type { SubscriptionChangeEvent } from '../../subscription/subscriptionService'; import { ensurePlusFeaturesEnabled } from '../../subscription/utils'; -import type { DismissBannerParams, GraphCompositeConfig, GraphRepository, State } from './protocol'; +import type { DismissBannerParams, GraphComponentConfig, GraphRepository, State } from './protocol'; import { DidChangeAvatarsNotificationType, DidChangeCommitsNotificationType, @@ -254,6 +254,24 @@ export class GraphWebview extends WebviewBase { this._statusBarItem = undefined; } } + + if (configuration.changed(e, 'graph.commitOrdering')) { + this.updateState(); + + return; + } + + if ( + configuration.changed(e, 'defaultDateFormat') || + configuration.changed(e, 'defaultDateStyle') || + configuration.changed(e, 'advanced.abbreviatedShaLength') || + configuration.changed(e, 'graph.avatars') || + configuration.changed(e, 'graph.dateFormat') || + configuration.changed(e, 'graph.dateStyle') || + configuration.changed(e, 'graph.highlightRowsOnRefHover') + ) { + void this.notifyDidChangeGraphConfiguration(); + } } private onRepositoryChanged(e: RepositoryChangeEvent) { @@ -351,7 +369,7 @@ export class GraphWebview extends WebviewBase { return; } - const { defaultItemLimit, pageItemLimit } = this.getConfig(); + const { defaultItemLimit, pageItemLimit } = configuration.get('graph'); const newGraph = await this._graph.more(pageItemLimit ?? defaultItemLimit); if (newGraph != null) { this.setGraph(newGraph); @@ -444,7 +462,7 @@ export class GraphWebview extends WebviewBase { if (!this.isReady || !this.visible) return false; return this.notify(DidChangeGraphConfigurationNotificationType, { - config: this.getConfig(), + config: this.getComponentConfig(), }); } @@ -498,11 +516,16 @@ export class GraphWebview extends WebviewBase { return success; } - private getConfig(): GraphCompositeConfig { - const settings = configuration.get('graph'); - const config: GraphCompositeConfig = { - ...settings, + private getComponentConfig(): GraphComponentConfig { + const config: GraphComponentConfig = { + avatars: configuration.get('graph.avatars'), columns: this.container.storage.getWorkspace('graph:columns'), + dateFormat: + configuration.get('graph.dateFormat') ?? configuration.get('defaultDateFormat') ?? 'short+short', + dateStyle: configuration.get('graph.dateStyle') ?? configuration.get('defaultDateStyle'), + enableMultiSelection: false, + highlightRowsOnRefHover: configuration.get('graph.highlightRowsOnRefHover'), + shaLength: configuration.get('advanced.abbreviatedShaLength'), }; return config; } @@ -528,10 +551,10 @@ export class GraphWebview extends WebviewBase { this._etagRepository = this.repository?.etag; this.title = `${this.originalTitle}: ${this.repository.formattedName}`; - const config = this.getConfig(); + const { defaultItemLimit } = configuration.get('graph'); // If we have a set of data refresh to the same set - const limit = Math.max(config.defaultItemLimit, this._graph?.ids.size ?? config.defaultItemLimit); + const limit = Math.max(defaultItemLimit, this._graph?.ids.size ?? defaultItemLimit); // Check for GitLens+ access const access = await this.getGraphAccess(); @@ -578,7 +601,7 @@ export class GraphWebview extends WebviewBase { more: data.paging?.more ?? false, } : undefined, - config: config, + config: this.getComponentConfig(), nonce: this.cspNonce, }; } diff --git a/src/plus/webviews/graph/protocol.ts b/src/plus/webviews/graph/protocol.ts index 8fa1d00e49344..391eed5940bbf 100644 --- a/src/plus/webviews/graph/protocol.ts +++ b/src/plus/webviews/graph/protocol.ts @@ -1,8 +1,9 @@ import type { GraphRow, Remote } from '@gitkraken/gitkraken-components'; -import type { GraphColumnConfig, GraphConfig } from '../../../config'; +import type { DateStyle, GraphColumnConfig } from '../../../config'; import type { RepositoryVisibility } from '../../../git/gitProvider'; import type { GitGraphRowType } from '../../../git/models/graph'; import type { Subscription } from '../../../subscription'; +import type { DateTimeFormat } from '../../../system/date'; import { IpcCommandType, IpcNotificationType } from '../../../webviews/protocol'; export interface State { @@ -16,7 +17,7 @@ export interface State { loading?: boolean; rows?: GraphRow[]; paging?: GraphPaging; - config?: GraphCompositeConfig; + config?: GraphComponentConfig; nonce?: string; previewBanner?: boolean; trialBanner?: boolean; @@ -56,8 +57,14 @@ export type GraphRemote = Remote; export type GraphTag = Record; export type GraphBranch = Record; -export interface GraphCompositeConfig extends GraphConfig { +export interface GraphComponentConfig { + avatars?: boolean; columns?: Record; + dateFormat: DateTimeFormat | string; + dateStyle: DateStyle; + enableMultiSelection?: boolean; + highlightRowsOnRefHover?: boolean; + shaLength?: number; } export interface UpdateStateCallback { @@ -102,7 +109,7 @@ export interface DidChangeParams { export const DidChangeNotificationType = new IpcNotificationType('graph/didChange'); export interface DidChangeGraphConfigurationParams { - config: GraphCompositeConfig; + config: GraphComponentConfig; } export const DidChangeGraphConfigurationNotificationType = new IpcNotificationType( 'graph/configuration/didChange', diff --git a/src/system/date.ts b/src/system/date.ts index 0bbf4312142d1..1e88a90a13c9c 100644 --- a/src/system/date.ts +++ b/src/system/date.ts @@ -72,8 +72,8 @@ export function createFromDateDelta( return d; } -export function fromNow(date: Date, short?: boolean): string { - const elapsed = date.getTime() - new Date().getTime(); +export function fromNow(date: Date | number, short?: boolean): string { + const elapsed = (typeof date === 'number' ? date : date.getTime()) - new Date().getTime(); for (const [unit, threshold, divisor, shortUnit] of relativeUnitThresholds) { const elapsedABS = Math.abs(elapsed); diff --git a/src/webviews/apps/plus/graph/GraphWrapper.tsx b/src/webviews/apps/plus/graph/GraphWrapper.tsx index cf9ef816815ca..82b5e6c73347d 100644 --- a/src/webviews/apps/plus/graph/GraphWrapper.tsx +++ b/src/webviews/apps/plus/graph/GraphWrapper.tsx @@ -1,3 +1,4 @@ +import type { OnFormatCommitDateTime } from '@gitkraken/gitkraken-components'; import GraphContainer, { type CssVariables, type GraphColumnSetting as GKGraphColumnSetting, @@ -9,12 +10,13 @@ import GraphContainer, { import type { ReactElement } from 'react'; import React, { createElement, useEffect, useRef, useState } from 'react'; import { getPlatform } from '@env/platform'; +import { DateStyle } from '../../../../config'; import type { GraphColumnConfig } from '../../../../config'; import { RepositoryVisibility } from '../../../../git/gitProvider'; import type { GitGraphRowType } from '../../../../git/models/graph'; import type { DismissBannerParams, - GraphCompositeConfig, + GraphComponentConfig, GraphRepository, State, UpdateStateCallback, @@ -22,6 +24,8 @@ import type { import type { Subscription } from '../../../../subscription'; import { getSubscriptionTimeRemaining, SubscriptionState } from '../../../../subscription'; import { pluralize } from '../../../../system/string'; +import type { DateTimeFormat } from '../../shared/date'; +import { formatDate, fromNow } from '../../shared/date'; export interface GraphWrapperProps extends State { nonce?: string; @@ -65,7 +69,7 @@ const defaultGraphColumnsSettings: GKGraphColumnsSettings = { refZone: { width: 150 }, }; -const getGraphColSettingsModel = (config?: GraphCompositeConfig): GKGraphColumnsSettings => { +const getGraphColSettingsModel = (config?: GraphComponentConfig): GKGraphColumnsSettings => { const columnsSettings: GKGraphColumnsSettings = { ...defaultGraphColumnsSettings }; if (config?.columns !== undefined) { for (const column of Object.keys(config.columns)) { @@ -77,6 +81,10 @@ const getGraphColSettingsModel = (config?: GraphCompositeConfig): GKGraphColumns return columnsSettings; }; +const getGraphDateFormatter = (config?: GraphComponentConfig): OnFormatCommitDateTime => { + return (commitDateTime: number) => formatCommitDateTime(commitDateTime, config?.dateStyle, config?.dateFormat); +}; + type DebouncableFn = (...args: any) => void; type DebouncedFn = (...args: any) => void; const debounceFrame = (func: DebouncableFn): DebouncedFn => { @@ -163,6 +171,8 @@ export function GraphWrapper({ reposList.find(item => item.path === selectedRepository), ); const [graphSelectedRows, setSelectedRows] = useState(selectedRows); + const [graphConfig, setGraphConfig] = useState(config); + // const [graphDateFormatter, setGraphDateFormatter] = useState(getGraphDateFormatter(config)); const [graphColSettings, setGraphColSettings] = useState(getGraphColSettingsModel(config)); const [pagingState, setPagingState] = useState(paging); const [isLoading, setIsLoading] = useState(loading); @@ -204,6 +214,8 @@ export function GraphWrapper({ setReposList(state.repositories ?? []); setCurrentRepository(reposList.find(item => item.path === state.selectedRepository)); setSelectedRows(state.selectedRows); + setGraphConfig(state.config); + // setGraphDateFormatter(getGraphDateFormatter(config)); setGraphColSettings(getGraphColSettingsModel(state.config)); setPagingState(state.paging); setStyleProps(getStyleProps(state.mixedColumnColors)); @@ -429,23 +441,28 @@ export function GraphWrapper({ <> {mainWidth !== undefined && mainHeight !== undefined && ( )} @@ -540,3 +557,11 @@ export function GraphWrapper({ ); } + +function formatCommitDateTime( + commitDateTime: number, + style: DateStyle = DateStyle.Absolute, + format: DateTimeFormat | string = 'short+short', +): string { + return style === DateStyle.Relative ? fromNow(commitDateTime) : formatDate(commitDateTime, format); +} diff --git a/src/webviews/apps/settings/partials/commit-graph.html b/src/webviews/apps/settings/partials/commit-graph.html new file mode 100644 index 0000000000000..ba67ff0281d51 --- /dev/null +++ b/src/webviews/apps/settings/partials/commit-graph.html @@ -0,0 +1,145 @@ +
+
+

+ Commit Graph + + + +

+ +

+ Adds a + Commit Graph + to visualize, explore, and manage a Git repository ✨ +

+
+ +
+
+
+
+
+
+ + + +
+
+ +
+
+ + + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+ + +
+ +
+
+ + + + + +
+ Example date: + + +
+
+
+ +
+ +
+
+
+
diff --git a/src/webviews/apps/settings/partials/rebase-editor.html b/src/webviews/apps/settings/partials/rebase-editor.html index 9dabcc490770a..9bfb961c4c872 100644 --- a/src/webviews/apps/settings/partials/rebase-editor.html +++ b/src/webviews/apps/settings/partials/rebase-editor.html @@ -23,20 +23,18 @@

-
-
-
+
+ -
- -
+
+
diff --git a/src/webviews/apps/settings/settings.html b/src/webviews/apps/settings/settings.html index bdf13bb976673..b61bbe6845519 100644 --- a/src/webviews/apps/settings/settings.html +++ b/src/webviews/apps/settings/settings.html @@ -81,6 +81,7 @@

Settings

<%= require('html-loader?{"esModule":false}!./partials/blame.html') %> <%= require('html-loader?{"esModule":false}!./partials/changes.html') %> <%= require('html-loader?{"esModule":false}!./partials/heatmap.html') %> + <%= require('html-loader?{"esModule":false}!./partials/commit-graph.html') %> <%= require('html-loader?{"esModule":false}!./partials/rebase-editor.html') %> <%= require('html-loader?{"esModule":false}!./partials/autolinks.html') %> <%= require('html-loader?{"esModule":false}!./partials/dates.html') %> @@ -268,6 +269,15 @@

Jump to

  • + Commit Graph +
  • +
  • extends Ap if (!value) { value = el.dataset.settingPreviewDefault; + if (value == null) { + const lookup = el.dataset.settingPreviewDefaultLookup; + if (lookup != null) { + value = this.getSettingValue(lookup); + } + } } el.innerText = value == null ? '' : formatDate(date, value, undefined, false);