Skip to content

Commit

Permalink
Adds gating to the commit graph
Browse files Browse the repository at this point in the history
  • Loading branch information
d13 committed Aug 25, 2022
1 parent 13ae92d commit d40056c
Show file tree
Hide file tree
Showing 6 changed files with 329 additions and 33 deletions.
46 changes: 41 additions & 5 deletions src/plus/webviews/graph/graphWebview.ts
Expand Up @@ -7,6 +7,7 @@ import { configuration } from '../../../configuration';
import { Commands, ContextKeys } from '../../../constants';
import type { Container } from '../../../container';
import { setContext } from '../../../context';
import { PlusFeatures } from '../../../features';
import type { GitCommit } from '../../../git/models/commit';
import type { GitGraph } from '../../../git/models/graph';
import type { Repository, RepositoryChangeEvent } from '../../../git/models/repository';
Expand All @@ -22,12 +23,14 @@ import { RepositoryFolderNode } from '../../../views/nodes/viewNode';
import type { IpcMessage } from '../../../webviews/protocol';
import { onIpc } from '../../../webviews/protocol';
import { WebviewBase } from '../../../webviews/webviewBase';
import type { SubscriptionChangeEvent } from '../../subscription/subscriptionService';
import { ensurePlusFeaturesEnabled } from '../../subscription/utils';
import type { GraphCompositeConfig, GraphRepository, State } from './protocol';
import {
DidChangeCommitsNotificationType,
DidChangeGraphConfigurationNotificationType,
DidChangeNotificationType,
DidChangeSubscriptionNotificationType,
DismissPreviewCommandType,
GetMoreCommitsCommandType,
UpdateColumnCommandType,
Expand Down Expand Up @@ -69,6 +72,7 @@ export class GraphWebview extends WebviewBase<State> {
return this._selection;
}

private _etagSubscription?: number;
private _etagRepository?: number;
private _repositoryEventsDisposable: Disposable | undefined;
private _repositoryGraph?: GitGraph;
Expand All @@ -88,12 +92,16 @@ export class GraphWebview extends WebviewBase<State> {
'graphWebview',
Commands.ShowGraphPage,
);
this.disposables.push(configuration.onDidChange(this.onConfigurationChanged, this), {
dispose: () => {
this._statusBarItem?.dispose();
void this._repositoryEventsDisposable?.dispose();
this.disposables.push(
configuration.onDidChange(this.onConfigurationChanged, this),
{
dispose: () => {
this._statusBarItem?.dispose();
void this._repositoryEventsDisposable?.dispose();
},
},
});
this.container.subscription.onDidChange(this.onSubscriptionChanged, this),
);

this.onConfigurationChanged();
}
Expand Down Expand Up @@ -232,6 +240,13 @@ export class GraphWebview extends WebviewBase<State> {
this.updateState();
}

private onSubscriptionChanged(e: SubscriptionChangeEvent) {
if (e.etag === this._etagSubscription) return;

this._etagSubscription = e.etag;
void this.notifyDidChangeSubscription();
}

private onThemeChanged(theme: ColorTheme) {
if (this._theme != null) {
if (
Expand Down Expand Up @@ -340,6 +355,17 @@ export class GraphWebview extends WebviewBase<State> {
});
}

@debug()
private async notifyDidChangeSubscription() {
if (!this.isReady || !this.visible) return false;

const access = await this.container.git.access(PlusFeatures.Graph, this.repository?.path);
return this.notify(DidChangeSubscriptionNotificationType, {
subscription: access.subscription.current,
allowed: access.allowed,
});
}

@debug()
private async notifyDidChangeCommits() {
if (!this.isReady || !this.visible) return false;
Expand Down Expand Up @@ -385,6 +411,13 @@ export class GraphWebview extends WebviewBase<State> {
// If we have a set of data refresh to the same set
const limit = this._repositoryGraph?.paging?.limit ?? config.defaultItemLimit;

// only check on private
const access = await this.container.git.access(PlusFeatures.Graph, this.repository?.path);
// TODO: probably not the right place to set this
if (this._etagSubscription == null) {
this._etagSubscription = this.container.subscription.etag;
}

const data = await this.container.git.getCommitsForGraph(
this.repository.path,
this._panel!.webview.asWebviewUri,
Expand All @@ -396,6 +429,9 @@ export class GraphWebview extends WebviewBase<State> {
previewBanner: this.previewBanner,
repositories: formatRepositories(this.container.git.openRepositories),
selectedRepository: this.repository.path,
selectedVisibility: access.visibility,
subscription: access.subscription.current,
allowed: access.allowed,
rows: data.rows,
paging: {
startingCursor: data.paging?.startingCursor,
Expand Down
13 changes: 13 additions & 0 deletions src/plus/webviews/graph/protocol.ts
@@ -1,10 +1,15 @@
import type { CommitType, GraphRow, Remote } from '@gitkraken/gitkraken-components';
import type { GraphColumnConfig, GraphConfig } from '../../../config';
import type { RepositoryVisibility } from '../../../git/gitProvider';
import type { Subscription } from '../../../subscription';
import { IpcCommandType, IpcNotificationType } from '../../../webviews/protocol';

export interface State {
repositories?: GraphRepository[];
selectedRepository?: string;
selectedVisibility?: RepositoryVisibility;
subscription?: Subscription;
allowed?: boolean;
rows?: GraphRow[];
paging?: GraphPaging;
config?: GraphCompositeConfig;
Expand Down Expand Up @@ -94,6 +99,14 @@ export const DidChangeGraphConfigurationNotificationType = new IpcNotificationTy
'graph/configuration/didChange',
);

export interface DidChangeSubscriptionParams {
subscription: Subscription;
allowed: boolean;
}
export const DidChangeSubscriptionNotificationType = new IpcNotificationType<DidChangeSubscriptionParams>(
'graph/subscription/didChange',
);

export interface DidChangeCommitsParams {
rows: GraphRow[];
paging?: GraphPaging;
Expand Down
161 changes: 150 additions & 11 deletions src/webviews/apps/plus/graph/GraphWrapper.tsx
Expand Up @@ -14,6 +14,9 @@ import type {
GraphRepository,
State,
} from '../../../../plus/webviews/graph/protocol';
import type { Subscription } from '../../../../subscription';
import { SubscriptionState } from '../../../../subscription';
import { fromNow } from '../../shared/date';

export interface GraphWrapperProps extends State {
nonce?: string;
Expand Down Expand Up @@ -114,6 +117,8 @@ export function GraphWrapper({
repositories = [],
rows = [],
selectedRepository,
subscription,
allowed,
config,
paging,
onSelectRepository,
Expand All @@ -139,7 +144,12 @@ export function GraphWrapper({
const [mainWidth, setMainWidth] = useState<number>();
const [mainHeight, setMainHeight] = useState<number>();
const mainRef = useRef<HTMLElement>(null);
const [showBanner, setShowBanner] = useState(previewBanner);
// banner
const [showPreview, setShowPreview] = useState(previewBanner);
// account
const [showAccount, setShowAccount] = useState(true);
const [isAllowed, setIsAllowed] = useState(allowed ?? false);
const [subscriptionSnapshot, setSubscriptionSnapshot] = useState<Subscription | undefined>(subscription);
// repo selection UI
const [repoExpanded, setRepoExpanded] = useState(false);

Expand Down Expand Up @@ -173,6 +183,8 @@ export function GraphWrapper({
setPagingState(state.paging);
setIsLoading(false);
setStyleProps(getStyleProps(state.mixedColumnColors));
setIsAllowed(state.allowed ?? false);
setSubscriptionSnapshot(state.subscription);
}

useEffect(() => {
Expand Down Expand Up @@ -207,15 +219,135 @@ export function GraphWrapper({
onSelectionChange?.(graphRows.map(r => r.sha));
};

const handleDismissBanner = () => {
setShowBanner(false);
const handleDismissPreview = () => {
setShowPreview(false);
onDismissPreview?.();
};

const handleDismissAccount = () => {
setShowAccount(false);
};

const renderAlertContent = () => {
if (subscriptionSnapshot == null) return;

let icon = 'account';
let modifier = '';
let content;
let actions;
switch (subscriptionSnapshot.state) {
case SubscriptionState.Free:
case SubscriptionState.Paid:
return;
case SubscriptionState.FreeInPreview:
icon = 'calendar';
modifier = 'neutral';
content = (
<>
<p className="alert__title">Trial Preview</p>
<p className="alert__message">
You're able to view the Commit Graph with any repository until your preview expires
{subscriptionSnapshot.previewTrial
? ` ${fromNow(new Date(subscriptionSnapshot.previewTrial.expiresOn))}`
: ''}
.
</p>
</>
);
break;
case SubscriptionState.FreePreviewExpired:
icon = 'warning';
modifier = 'warning';
content = (
<>
<p className="alert__title">Extend Your Trial</p>
<p className="alert__message">Sign in to extend your free trial an additional 7-days.</p>
</>
);
actions = (
<>
<a className="alert-action" href="command:gitlens.plus.loginOrSignUp">
Try for 7-days
</a>{' '}
<a className="alert-action" href="command:gitlens.plus.purchase">
View Plans
</a>
</>
);
break;
case SubscriptionState.FreePlusInTrial:
icon = 'calendar';
modifier = 'neutral';
content = (
<>
<p className="alert__title">Extended Trial</p>
<p className="alert__message">
You're able to view the Commit Graph with any repository until your trial expires
{subscriptionSnapshot.previewTrial
? ` ${fromNow(new Date(subscriptionSnapshot.previewTrial.expiresOn))}`
: ''}
.
</p>
</>
);
break;
case SubscriptionState.FreePlusTrialExpired:
icon = 'warning';
modifier = 'warning';
content = (
<>
<p className="alert__title">Trial Expired</p>
<p className="alert__message">
Upgrade your account to use the Commit Graph and other GitLens+ features on private repos.
</p>
<p>
<a className="alert-action" href="command:gitlens.plus.purchase">
Upgrade Your Account
</a>
</p>
</>
);
break;
case SubscriptionState.VerificationRequired:
icon = 'unverified';
modifier = 'warning';
content = (
<>
<p className="alert__title">Please verify your email</p>
<p className="alert__message">Please verify the email for the account you created.</p>
</>
);
actions = (
<>
<a className="alert-action" href="command:gitlens.plus.resendVerification">
Resend Verification Email
</a>
<a className="alert-action" href="command:gitlens.plus.validate">
Refresh Verification Status
</a>
</>
);
break;
}

return (
<div className={`alert${modifier !== '' ? ` alert--${modifier}` : ''}`}>
<span className={`alert__icon codicon codicon-${icon}`}></span>
<div className="alert__content">{content}</div>
{actions && <div className="alert__actions">{actions}</div>}
{isAllowed && (
<button className="alert__dismiss" type="button" onClick={() => handleDismissAccount()}>
<span className="codicon codicon-chrome-close"></span>
</button>
)}
</div>
);
};

return (
<>
{showBanner && (
<section className="graph-app__banner">
<section className="graph-app__banners">
{showPreview && (
<div className="alert">
<span className="alert__icon codicon codicon-search"></span>
<div className="alert__content">
Expand All @@ -230,13 +362,20 @@ export function GraphWrapper({
.
</p>
</div>
<button className="alert__action" type="button" onClick={() => handleDismissBanner()}>
<button className="alert__dismiss" type="button" onClick={() => handleDismissPreview()}>
<span className="codicon codicon-chrome-close"></span>
</button>
</div>
</section>
)}
<main ref={mainRef} id="main" className="graph-app__main">
)}
{showAccount && renderAlertContent()}
</section>
<main
ref={mainRef}
id="main"
className={`graph-app__main${!isAllowed ? ' is-gated' : ''}`}
aria-hidden={!isAllowed}
>
{!isAllowed && <div className="graph-app__cover"></div>}
{currentRepository !== undefined ? (
<>
{mainWidth !== undefined && mainHeight !== undefined && (
Expand All @@ -263,7 +402,7 @@ export function GraphWrapper({
<p>No repository is selected</p>
)}
</main>
<footer className="actionbar graph-app__footer">
<footer className={`actionbar graph-app__footer${!isAllowed ? ' is-gated' : ''}`} aria-hidden={!isAllowed}>
<div className="actionbar__group">
<div className="actioncombo">
<button
Expand Down Expand Up @@ -321,7 +460,7 @@ export function GraphWrapper({
)}
</div>
</div>
{graphList.length > 0 && (
{isAllowed && graphList.length > 0 && (
<span className="actionbar__details">
showing {graphList.length} item{graphList.length ? 's' : ''}
</span>
Expand Down

0 comments on commit d40056c

Please sign in to comment.