diff --git a/src/storage.ts b/src/storage.ts index 288852d..f43b447 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -142,6 +142,9 @@ export interface GlobalStorage { status: { pinned?: boolean; }; + banners: { + dismissed?: string[]; + }; }; pendingWelcomeOnFocus?: boolean; pendingWhatsNewOnFocus?: boolean; diff --git a/src/webviews/apps/home/home.html b/src/webviews/apps/home/home.html index 22529fc..7c6aff1 100644 --- a/src/webviews/apps/home/home.html +++ b/src/webviews/apps/home/home.html @@ -83,7 +83,32 @@ .gl-plus-banner { background-image: url(#{webroot}/media/gitlens-backdrop-opacity.webp); } + .vscode-high-contrast .ribbon-banner, + .vscode-dark .ribbon-banner { + background-image: url(#{webroot}/media/cyber-week-banner-dark.webp); + } + .vscode-high-contrast-light .ribbon-banner, + .vscode-light .ribbon-banner { + background-image: url(#{webroot}/media/cyber-week-banner-light.webp); + } +
Welcome to GitLens 13 diff --git a/src/webviews/apps/home/home.scss b/src/webviews/apps/home/home.scss index c59aeb4..119e74e 100644 --- a/src/webviews/apps/home/home.scss +++ b/src/webviews/apps/home/home.scss @@ -34,6 +34,10 @@ visibility: hidden; } +[hidden] { + display: none !important; +} + html { height: 100%; font-size: 62.5%; @@ -605,6 +609,81 @@ vscode-button { } } +.ribbon-banner { + &-container { + position: relative; + } + + &-dismiss { + position: absolute; + top: 0; + right: 0; + + display: inline-flex; + justify-content: center; + align-items: center; + width: 2.2rem; + height: 2.2rem; + color: var(--color-foreground--75); + &:hover { + color: var(--color-foreground--75); + text-decoration: none; + } + } + + .vscode-high-contrast &, + .vscode-dark & { + background-color: var(--color-background--lighten-10); + background-position: right -7rem center; + background-size: auto 140px; + } + .vscode-high-contrast-light &, + .vscode-light & { + background-color: var(--color-background--darken-10); + background-position: right 2.5rem center; + background-size: auto 28px; + } + + background-repeat: no-repeat; + display: block; + text-align: left; + color: inherit; + padding: 1.2rem 9.3rem 1.2rem 1.4rem; + line-height: 1.1; + margin-top: -2rem; + margin-bottom: 1rem; + border-radius: 0.4rem; + mix-blend-mode: luminosity; + opacity: 0.8; + transition: opacity ease 250ms, mix-blend-mode ease 250ms; + + &:focus, + &:hover { + color: inherit; + text-decoration: none; + mix-blend-mode: normal; + opacity: 1; + } + + h2 { + font-size: 1.2rem; + font-weight: inherit; + margin: 0 0 0.8rem 0; + } + p { + font-size: 1rem; + margin: 0; + opacity: 0.8; + } + strong { + font-weight: 800; + opacity: 1; + } + &__subtext { + opacity: 0.8; + } +} + @media (max-width: 280px) { .not-small { display: none; diff --git a/src/webviews/apps/home/home.ts b/src/webviews/apps/home/home.ts index d71a75c..aabc7aa 100644 --- a/src/webviews/apps/home/home.ts +++ b/src/webviews/apps/home/home.ts @@ -10,6 +10,7 @@ import { DidChangeExtensionEnabledType, DidChangeLayoutType, DidChangeSubscriptionNotificationType, + DismissBannerCommandType, DismissSectionCommandType, DismissStatusCommandType, } from '../../home/protocol'; @@ -72,6 +73,9 @@ export class HomeApp extends App { this.onStatusDismissed(e, target), ), ); + disposables.push( + DOM.on('[data-banner-dismiss]', 'click', (e, target: HTMLElement) => this.onBannerDismissed(e, target)), + ); return disposables; } @@ -143,6 +147,17 @@ export class HomeApp extends App { this.updateHeader(); } + private onBannerDismissed(_e: MouseEvent, target: HTMLElement) { + const key = target.getAttribute('data-banner-dismiss'); + if (key == null || this.state.dismissedBanners?.includes(key)) { + return; + } + this.state.dismissedBanners = this.state.dismissedBanners ?? []; + this.state.dismissedBanners.push(key); + this.sendCommand(DismissBannerCommandType, { id: key }); + this.updateBanners(); + } + private onDataActionClicked(_e: MouseEvent, target: HTMLElement) { const action = target.dataset.action; this.onActionClickedCore(action); @@ -209,6 +224,28 @@ export class HomeApp extends App { } } + private updateBanners() { + const $banners = [...document.querySelectorAll('[data-banner]')]; + if (!$banners.length) { + return; + } + + const { subscription, dismissedBanners } = this.state; + const isPaid = subscription.state === SubscriptionState.Paid; + $banners.forEach($el => { + const key = $el.getAttribute('data-banner'); + if ( + isPaid || + (key !== null && dismissedBanners?.includes(key)) || + (key === 'cyberweek2022' && !showCyberWeek()) + ) { + $el.setAttribute('hidden', 'true'); + } else { + $el.removeAttribute('hidden'); + } + }); + } + private updateNoRepo() { const { extensionEnabled } = this.state; @@ -305,6 +342,7 @@ export class HomeApp extends App { this.updateSteps(forceShowPlus); this.updateSections(); + this.updateBanners(); } } @@ -319,4 +357,9 @@ function toggleArrayItem(list: string[] = [], item: string, add = true) { return list; } +const cyberweekEnding = Date.parse('2022-12-06T00:00:00.000-08:00'); +function showCyberWeek() { + return Date.now() < cyberweekEnding; +} + new HomeApp(); diff --git a/src/webviews/apps/media/cyber-week-banner-dark.png b/src/webviews/apps/media/cyber-week-banner-dark.png new file mode 100644 index 0000000..fee08e3 Binary files /dev/null and b/src/webviews/apps/media/cyber-week-banner-dark.png differ diff --git a/src/webviews/apps/media/cyber-week-banner-light.png b/src/webviews/apps/media/cyber-week-banner-light.png new file mode 100644 index 0000000..289fcc9 Binary files /dev/null and b/src/webviews/apps/media/cyber-week-banner-light.png differ diff --git a/src/webviews/home/homeWebviewView.ts b/src/webviews/home/homeWebviewView.ts index 13cfdee..f9d499d 100644 --- a/src/webviews/home/homeWebviewView.ts +++ b/src/webviews/home/homeWebviewView.ts @@ -17,7 +17,7 @@ import { debounce } from '../../system/function'; import type { IpcMessage } from '../protocol'; import { onIpc } from '../protocol'; import { WebviewViewBase } from '../webviewViewBase'; -import type { CompleteStepParams, DismissSectionParams, State } from './protocol'; +import type { CompleteStepParams, DismissBannerParams, DismissSectionParams, State } from './protocol'; import { CompletedActions, CompleteStepCommandType, @@ -25,6 +25,7 @@ import { DidChangeExtensionEnabledType, DidChangeLayoutType, DidChangeSubscriptionNotificationType, + DismissBannerCommandType, DismissSectionCommandType, DismissStatusCommandType, } from './protocol'; @@ -136,6 +137,9 @@ export class HomeWebviewView extends WebviewViewBase { case DismissStatusCommandType.method: onIpc(DismissStatusCommandType, e, _params => this.dismissPinStatus()); break; + case DismissBannerCommandType.method: + onIpc(DismissBannerCommandType, e, params => this.dismissBanner(params)); + break; } } @@ -153,14 +157,24 @@ export class HomeWebviewView extends WebviewViewBase { private dismissSection(params: DismissSectionParams) { const sections = this.container.storage.get('home:sections:dismissed', []); - - if (!sections.includes(params.id)) { - sections.push(params.id); + if (sections.includes(params.id)) { + return; } + sections.push(params.id); void this.container.storage.store('home:sections:dismissed', sections); } + private dismissBanner(params: DismissBannerParams) { + const banners = this.container.storage.get('home:banners:dismissed', []); + + if (!banners.includes(params.id)) { + banners.push(params.id); + } + + void this.container.storage.store('home:banners:dismissed', banners); + } + private dismissPinStatus() { void this.container.storage.store('home:status:pinned', false); } @@ -209,6 +223,7 @@ export class HomeWebviewView extends WebviewViewBase { const subscriptionState = await this.getSubscription(subscription); const steps = this.container.storage.get('home:steps:completed', []); const sections = this.container.storage.get('home:sections:dismissed', []); + const dismissedBanners = this.container.storage.get('home:banners:dismissed', []); return { extensionEnabled: this.getExtensionEnabled(), @@ -222,6 +237,7 @@ export class HomeWebviewView extends WebviewViewBase { avatar: subscriptionState.avatar, layout: this.getLayout(), pinStatus: this.getPinStatus(), + dismissedBanners: dismissedBanners, }; } diff --git a/src/webviews/home/protocol.ts b/src/webviews/home/protocol.ts index d7dd3a0..422f3fa 100644 --- a/src/webviews/home/protocol.ts +++ b/src/webviews/home/protocol.ts @@ -14,6 +14,7 @@ export interface State { subscription: Subscription; completedActions: CompletedActions[]; completedSteps?: string[]; + dismissedBanners?: string[]; dismissedSections?: string[]; plusEnabled: boolean; visibility: RepositoriesVisibility; @@ -35,6 +36,11 @@ export const DismissSectionCommandType = new IpcCommandType('home/status/dismiss'); +export interface DismissBannerParams { + id: string; +} +export const DismissBannerCommandType = new IpcCommandType('home/banner/dismiss'); + export interface DidChangeSubscriptionParams { subscription: Subscription; completedActions: CompletedActions[];