Path: blob/main/src/vs/sessions/contrib/accountMenu/browser/updateHoverWidget.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js';6import { localize } from '../../../../nls.js';7import { IHoverService } from '../../../../platform/hover/browser/hover.js';8import { IProductService } from '../../../../platform/product/common/productService.js';9import { Downloading, IUpdate, IUpdateService, State, StateType, Updating } from '../../../../platform/update/common/update.js';10import './media/updateHoverWidget.css';1112export class UpdateHoverWidget {1314constructor(15private readonly updateService: IUpdateService,16private readonly productService: IProductService,17private readonly hoverService: IHoverService,18private readonly stateProvider?: () => State,19) { }2021attachTo(target: HTMLElement) {22return this.hoverService.setupDelayedHover(23target,24() => ({25content: this.createHoverContent(),26position: { hoverPosition: HoverPosition.RIGHT },27appearance: { showPointer: true }28}),29{ groupId: 'sessions-account-update' }30);31}3233createHoverContent(state: State = this.stateProvider?.() ?? this.updateService.state): HTMLElement {34const update = this.getUpdateFromState(state);35const currentVersion = this.productService.version ?? localize('unknownVersion', "Unknown");36const targetVersion = update?.productVersion ?? update?.version ?? localize('unknownVersion', "Unknown");37const currentCommit = this.productService.commit;38const targetCommit = update?.version;39const progressPercent = this.getUpdateProgressPercent(state);4041const container = document.createElement('div');42container.classList.add('sessions-update-hover');4344// Header: e.g. "Downloading VS Code Insiders"45const header = document.createElement('div');46header.classList.add('sessions-update-hover-header');47header.textContent = this.getUpdateHeaderLabel(state.type);48container.appendChild(header);4950// Progress bar51if (progressPercent !== undefined) {52const progressTrack = document.createElement('div');53progressTrack.classList.add('sessions-update-hover-progress-track');54const progressFill = document.createElement('div');55progressFill.classList.add('sessions-update-hover-progress-fill');56progressFill.style.width = `${progressPercent}%`;57progressTrack.appendChild(progressFill);58container.appendChild(progressTrack);59}6061// Version info grid62const detailsGrid = document.createElement('div');63detailsGrid.classList.add('sessions-update-hover-grid');6465const currentDate = this.productService.date ? new Date(this.productService.date) : undefined;66const currentAge = currentDate ? this.formatCompactAge(currentDate.getTime()) : undefined;67const newAge = update?.timestamp ? this.formatCompactAge(update.timestamp) : undefined;6869this.appendGridRow(detailsGrid, localize('updateHoverCurrentVersionLabel', "Current"), currentVersion, currentAge, currentCommit);70this.appendGridRow(detailsGrid, localize('updateHoverNewVersionLabel', "New"), targetVersion, newAge, targetCommit);7172container.appendChild(detailsGrid);7374return container;75}7677private appendGridRow(grid: HTMLElement, label: string, version: string, age?: string, commit?: string): void {78const labelEl = document.createElement('span');79labelEl.classList.add('sessions-update-hover-label');80labelEl.textContent = label;81grid.appendChild(labelEl);8283const versionEl = document.createElement('span');84versionEl.classList.add('sessions-update-hover-version');85versionEl.textContent = version;86grid.appendChild(versionEl);8788const ageEl = document.createElement('span');89ageEl.classList.add('sessions-update-hover-age');90ageEl.textContent = age ?? '';91grid.appendChild(ageEl);9293const commitEl = document.createElement('span');94commitEl.classList.add('sessions-update-hover-commit');95commitEl.textContent = commit ? commit.substring(0, 7) : '';96grid.appendChild(commitEl);97}9899private formatCompactAge(timestamp: number): string {100const seconds = Math.round((Date.now() - timestamp) / 1000);101if (seconds < 60) {102return localize('compactAgeNow', "now");103}104const minutes = Math.round(seconds / 60);105if (minutes < 60) {106return localize('compactAgeMinutes', "{0}m ago", minutes);107}108const hours = Math.round(seconds / 3600);109if (hours < 24) {110return localize('compactAgeHours', "{0}h ago", hours);111}112const days = Math.round(seconds / 86400);113if (days < 7) {114return localize('compactAgeDays', "{0}d ago", days);115}116const weeks = Math.round(days / 7);117if (weeks < 5) {118return localize('compactAgeWeeks', "{0}w ago", weeks);119}120const months = Math.round(days / 30);121return localize('compactAgeMonths', "{0}mo ago", months);122}123124private getUpdateFromState(state: State): IUpdate | undefined {125switch (state.type) {126case StateType.AvailableForDownload:127case StateType.Downloaded:128case StateType.Ready:129case StateType.Overwriting:130case StateType.Updating:131return state.update;132case StateType.Downloading:133return state.update;134default:135return undefined;136}137}138139/**140* Returns progress as a percentage (0-100), or undefined if progress is not applicable.141*/142private getUpdateProgressPercent(state: State): number | undefined {143switch (state.type) {144case StateType.Downloading: {145const downloadingState = state as Downloading;146if (downloadingState.downloadedBytes !== undefined && downloadingState.totalBytes && downloadingState.totalBytes > 0) {147return Math.min(100, Math.round((downloadingState.downloadedBytes / downloadingState.totalBytes) * 100));148}149return 0;150}151case StateType.Updating: {152const updatingState = state as Updating;153if (updatingState.currentProgress !== undefined && updatingState.maxProgress && updatingState.maxProgress > 0) {154return Math.min(100, Math.round((updatingState.currentProgress / updatingState.maxProgress) * 100));155}156return 0;157}158case StateType.Downloaded:159case StateType.Ready:160return 100;161case StateType.AvailableForDownload:162case StateType.Overwriting:163return 0;164default:165return undefined;166}167}168169private getUpdateHeaderLabel(type: StateType): string {170const productName = this.productService.nameShort;171switch (type) {172case StateType.Ready:173return localize('updateReady', "{0} Update Ready", productName);174case StateType.AvailableForDownload:175return localize('downloadAvailable', "{0} Update Available", productName);176case StateType.Downloading:177case StateType.Overwriting:178return localize('downloadingUpdate', "Downloading {0}", productName);179case StateType.Downloaded:180return localize('installingUpdate', "Installing {0}", productName);181case StateType.Updating:182return localize('updatingApp', "Updating {0}", productName);183default:184return localize('updating', "Updating {0}", productName);185}186}187}188189190