Browse Source

updates home view status popover

- opens popover on subscription changes
- adds button to dismiss popover
main
Keith Daulton 2 years ago
parent
commit
876ae0f12e
6 changed files with 94 additions and 20 deletions
  1. +3
    -0
      src/storage.ts
  2. +38
    -3
      src/webviews/apps/home/components/header-card.ts
  3. +17
    -2
      src/webviews/apps/home/home.ts
  4. +7
    -13
      src/webviews/apps/shared/components/overlays/pop-over.ts
  5. +25
    -2
      src/webviews/home/homeWebviewView.ts
  6. +4
    -0
      src/webviews/home/protocol.ts

+ 3
- 0
src/storage.ts View File

@ -139,6 +139,9 @@ export interface GlobalStorage {
sections: {
dismissed?: string[];
};
status: {
pinned?: boolean;
};
};
pendingWelcomeOnFocus?: boolean;
pendingWhatsNewOnFocus?: boolean;

+ 38
- 3
src/webviews/apps/home/components/header-card.ts View File

@ -13,8 +13,23 @@ const template = html`
</h1>
<p class="header-card__account">
<span class="status">
<span class="repo-access${x => (x.isPro ? ' is-pro' : '')}"></span>${x => x.planName}
<pop-over>
<span ${ref('statusNode')} tabindex="-1" class="status-label"
><span class="repo-access${x => (x.isPro ? ' is-pro' : '')}"></span>${x => x.planName}</span
>
<pop-over class="${x => (x.pinStatus ? 'is-pinned' : null)}">
${when(
x => x.pinStatus,
html<HeaderCard>`
<span slot="type">status update</span>
<a
href="#"
class="action is-icon"
slot="actions"
@click="${(x, c) => x.dismissStatus(c.event as MouseEvent)}"
><code-icon icon="close"></code-icon
></a>
`,
)}
You have access to GitLens+ features on ${x => (x.isPro ? 'any repo' : 'local & public repos')}, and all
other GitLens features on any repo.
</pop-over>
@ -188,6 +203,9 @@ const styles = css`
}
.status {
color: var(--color-foreground--65);
}
.status-label {
cursor: help;
}
@ -195,7 +213,7 @@ const styles = css`
top: 1.6em;
left: 0;
}
.status:not(:hover) pop-over {
.status-label:not(:hover) + pop-over:not(.is-pinned) {
display: none;
}
@ -251,6 +269,10 @@ const styles = css`
:host-context(.vscode-light) .action:hover {
background-color: var(--color-background--darken-10);
}
pop-over .action {
margin-right: -0.2rem;
}
`;
@customElement({ name: 'header-card', template: template, styles: styles })
@ -276,7 +298,11 @@ export class HeaderCard extends FASTElement {
@attr
plan = '';
@attr({ attribute: 'pin-status', mode: 'boolean' })
pinStatus = true;
progressNode!: HTMLElement;
statusNode!: HTMLElement;
override attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
super.attributeChangedCallback(name, oldValue, newValue);
@ -348,4 +374,13 @@ export class HeaderCard extends FASTElement {
updateProgressWidth() {
this.progressNode.style.width = this.progress;
}
dismissStatus(e: MouseEvent) {
this.pinStatus = false;
this.$emit('dismiss-status');
window.requestAnimationFrame(() => {
this.statusNode?.focus();
});
}
}

+ 17
- 2
src/webviews/apps/home/home.ts View File

@ -11,12 +11,14 @@ import {
DidChangeLayoutType,
DidChangeSubscriptionNotificationType,
DismissSectionCommandType,
DismissStatusCommandType,
} from '../../home/protocol';
import type { IpcMessage } from '../../protocol';
import { ExecuteCommandType, onIpc } from '../../protocol';
import { App } from '../shared/appBase';
import { DOM } from '../shared/dom';
import type { CardSection } from './components/card-section';
import type { HeaderCard } from './components/header-card';
import type { PlusBanner } from './components/plus-banner';
import type { SteppedSection } from './components/stepped-section';
import '../shared/components/code-icon';
@ -65,6 +67,11 @@ export class HomeApp extends App {
this.onCardDismissed(e, target),
),
);
disposables.push(
DOM.on<HeaderCard, undefined>('header-card', 'dismiss-status', (e, target: HTMLElement) =>
this.onStatusDismissed(e, target),
),
);
return disposables;
}
@ -80,6 +87,7 @@ export class HomeApp extends App {
this.state.subscription = params.subscription;
this.state.completedActions = params.completedActions;
this.state.avatar = params.avatar;
this.state.pinStatus = params.pinStatus;
this.updateState();
});
break;
@ -129,6 +137,12 @@ export class HomeApp extends App {
this.updateState();
}
private onStatusDismissed(e: CustomEvent<undefined>, target: HTMLElement) {
this.state.pinStatus = false;
this.sendCommand(DismissStatusCommandType, undefined);
this.updateHeader();
}
private onDataActionClicked(e: MouseEvent, target: HTMLElement) {
const action = target.dataset.action;
this.onActionClickedCore(action);
@ -165,9 +179,9 @@ export class HomeApp extends App {
}
private updateHeader(days = this.getDaysRemaining(), forceShowPlus = this.forceShowPlus()) {
const { subscription, completedSteps, avatar } = this.state;
const { subscription, completedSteps, avatar, pinStatus } = this.state;
const $headerContent = document.getElementById('header-card');
const $headerContent = document.getElementById('header-card') as HeaderCard;
if ($headerContent) {
if (avatar) {
$headerContent.setAttribute('image', avatar);
@ -191,6 +205,7 @@ export class HomeApp extends App {
$headerContent.setAttribute('state', subscription.state.toString());
$headerContent.setAttribute('plan', subscription.plan.effective.name);
$headerContent.setAttribute('days', days.toString());
$headerContent.pinStatus = pinStatus;
}
}

+ 7
- 13
src/webviews/apps/shared/components/overlays/pop-over.ts View File

@ -14,19 +14,13 @@ import { elementBase } from '../styles/base';
const template = html<PopOver>`
<template>
${when(
x => x.hasTopNodes,
html<PopOver>`
<div class="top">
<slot ${slotted('typeNodes')} name="type"></slot>
<slot ${slotted('actionsNodes')} name="actions"></slot>
</div>
`,
)}
${when(
x => x.hasHeadingNodes,
html<PopOver>`<div class="heading"><slot ${slotted('headingNodes')} name="heading"></slot></div>`,
)}
<div class="top" ?hidden="${x => !x.hasTopNodes}">
<slot ${slotted('typeNodes')} name="type"></slot>
<slot ${slotted('actionsNodes')} name="actions"></slot>
</div>
<div class="heading" ?hidden="${x => !x.hasHeadingNodes}">
<slot ${slotted('headingNodes')} name="heading"></slot>
</div>
<div class="content"><slot></slot></div>
${when(x => x.caret, html<PopOver>`<span class="caret"></span>`)}
</template>

+ 25
- 2
src/webviews/home/homeWebviewView.ts View File

@ -26,6 +26,7 @@ import {
DidChangeLayoutType,
DidChangeSubscriptionNotificationType,
DismissSectionCommandType,
DismissStatusCommandType,
} from './protocol';
export class HomeWebviewView extends WebviewViewBase<State> {
@ -52,7 +53,8 @@ export class HomeWebviewView extends WebviewViewBase {
return super.show(options);
}
private onSubscriptionChanged(e: SubscriptionChangeEvent) {
private async onSubscriptionChanged(e: SubscriptionChangeEvent) {
await this.container.storage.store('home:status:pinned', true);
void this.notifyDidChangeData(e.current);
}
@ -133,6 +135,9 @@ export class HomeWebviewView extends WebviewViewBase {
case DismissSectionCommandType.method:
onIpc(DismissSectionCommandType, e, params => this.dismissSection(params));
break;
case DismissStatusCommandType.method:
onIpc(DismissStatusCommandType, e, _params => this.dismissPinStatus());
break;
}
}
@ -158,6 +163,10 @@ export class HomeWebviewView extends WebviewViewBase {
void this.container.storage.store('home:sections:dismissed', sections);
}
private dismissPinStatus() {
void this.container.storage.store('home:status:pinned', false);
}
protected override async includeBootstrap(): Promise<State> {
return this.getState();
}
@ -194,6 +203,10 @@ export class HomeWebviewView extends WebviewViewBase {
};
}
private getPinStatus() {
return this.container.storage.get('home:status:pinned') ?? true;
}
private async getState(subscription?: Subscription): Promise<State> {
const subscriptionState = await this.getSubscription(subscription);
const steps = this.container.storage.get('home:steps:completed', []);
@ -210,14 +223,24 @@ export class HomeWebviewView extends WebviewViewBase {
dismissedSections: sections,
avatar: subscriptionState.avatar,
layout: this.getLayout(),
pinStatus: this.getPinStatus(),
};
}
private notifyDidChangeData(subscription?: Subscription) {
if (!this.isReady) return false;
const getSub = async () => {
const sub = await this.getSubscription(subscription);
return {
...sub,
pinStatus: this.getPinStatus(),
};
};
return window.withProgress({ location: { viewId: this.id } }, async () =>
this.notify(DidChangeSubscriptionNotificationType, await this.getSubscription(subscription)),
this.notify(DidChangeSubscriptionNotificationType, await getSub()),
);
}

+ 4
- 0
src/webviews/home/protocol.ts View File

@ -19,6 +19,7 @@ export interface State {
visibility: RepositoriesVisibility;
avatar?: string;
layout: ViewsLayout;
pinStatus: boolean;
}
export interface CompleteStepParams {
@ -32,10 +33,13 @@ export interface DismissSectionParams {
}
export const DismissSectionCommandType = new IpcCommandType<DismissSectionParams>('home/section/dismiss');
export const DismissStatusCommandType = new IpcCommandType<undefined>('home/status/dismiss');
export interface DidChangeSubscriptionParams {
subscription: Subscription;
completedActions: CompletedActions[];
avatar?: string;
pinStatus: boolean;
}
export const DidChangeSubscriptionNotificationType = new IpcNotificationType<DidChangeSubscriptionParams>(
'subscription/didChange',

Loading…
Cancel
Save