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[];