196 lines
6.3 KiB

import type { PullRequestShape } from '../../../../git/models/pullRequest';
import type { State } from '../../../../plus/webviews/focus/protocol';
import {
DidChangeNotificationType,
OpenWorktreeCommandType,
SwitchToBranchCommandType,
} from '../../../../plus/webviews/focus/protocol';
import type { IpcMessage } from '../../../protocol';
import { onIpc } from '../../../protocol';
import { App } from '../../shared/appBase';
import type { AccountBadge } from '../../shared/components/account/account-badge';
import type { FeatureGate } from '../../shared/components/feature-gate';
import { DOM } from '../../shared/dom';
import type { IssueRow } from './components/issue-row';
import type { PullRequestRow } from './components/pull-request-row';
import '../../shared/components/button';
import '../../shared/components/code-icon';
import '../../shared/components/avatars/avatar-item';
import '../../shared/components/avatars/avatar-stack';
import '../../shared/components/table/table-container';
import '../../shared/components/table/table-row';
import '../../shared/components/table/table-cell';
import '../../shared/components/account/account-badge';
import './components/issue-row';
import './components/pull-request-row';
import '../../shared/components/feature-gate';
import './focus.scss';
export class FocusApp extends App<State> {
constructor() {
super('FocusApp');
}
private _prFilter?: string;
private _issueFilter?: string;
override onInitialize() {
this.renderContent();
}
protected override onBind() {
const disposables = super.onBind?.() ?? [];
disposables.push(
DOM.on('#pr-filter [data-tab]', 'click', e =>
this.onSelectTab(e, val => {
this._prFilter = val;
this.renderPullRequests();
}),
),
DOM.on('#issue-filter [data-tab]', 'click', e =>
this.onSelectTab(e, val => {
this._issueFilter = val;
this.renderIssues();
}),
),
DOM.on<PullRequestRow, PullRequestShape>('pull-request-row', 'open-worktree', (e, target: HTMLElement) =>
this.onOpenWorktree(e, target),
),
DOM.on<PullRequestRow, PullRequestShape>('pull-request-row', 'switch-branch', (e, target: HTMLElement) =>
this.onSwitchBranch(e, target),
),
);
return disposables;
}
private onSwitchBranch(e: CustomEvent<PullRequestShape>, _target: HTMLElement) {
if (e.detail?.refs?.head == null) return;
this.sendCommand(SwitchToBranchCommandType, { pullRequest: e.detail });
}
private onOpenWorktree(e: CustomEvent<PullRequestShape>, _target: HTMLElement) {
if (e.detail?.refs?.head == null) return;
this.sendCommand(OpenWorktreeCommandType, { pullRequest: e.detail });
}
protected override onMessageReceived(e: MessageEvent) {
const msg = e.data as IpcMessage;
this.log(`onMessageReceived(${msg.id}): name=${msg.method}`);
switch (msg.method) {
case DidChangeNotificationType.method:
onIpc(DidChangeNotificationType, msg, params => {
this.state = { ...this.state, ...params.state };
this.setState(this.state);
this.renderContent();
});
break;
}
}
renderContent() {
let $gate = document.getElementById('subscription-gate')! as FeatureGate;
if ($gate != null) {
$gate.state = this.state.access.subscription.current.state;
$gate.visible = this.state.access.allowed !== true;
}
$gate = document.getElementById('connection-gate')! as FeatureGate;
if ($gate != null) {
$gate.visible =
this.state.access.allowed === true && !(this.state.repos?.some(r => r.isConnected) ?? false);
}
const badgeEl = document.getElementById('account-badge')! as AccountBadge;
badgeEl.subscription = this.state.access.subscription.current;
if (this.state.access.allowed === true) {
this.renderPullRequests();
this.renderIssues();
}
}
renderPullRequests() {
const tableEl = document.getElementById('pull-requests');
if (tableEl == null) return;
const rowEls = tableEl.querySelectorAll('pull-request-row');
rowEls.forEach(el => el.remove());
const noneEl = document.getElementById('no-pull-requests')!;
const loadingEl = document.getElementById('loading-pull-requests')!;
if (this.state.pullRequests == null) {
noneEl.setAttribute('hidden', 'true');
loadingEl.removeAttribute('hidden');
} else if (this.state.pullRequests.length === 0) {
noneEl.removeAttribute('hidden');
loadingEl.setAttribute('hidden', 'true');
} else {
noneEl.setAttribute('hidden', 'true');
loadingEl.setAttribute('hidden', 'true');
this.state.pullRequests.forEach(
({ pullRequest, reasons, isCurrentBranch, isCurrentWorktree, hasWorktree, hasLocalBranch }) => {
if (this._prFilter == null || this._prFilter === '' || reasons.includes(this._prFilter)) {
const rowEl = document.createElement('pull-request-row') as PullRequestRow;
rowEl.pullRequest = pullRequest;
rowEl.reasons = reasons;
rowEl.isCurrentBranch = isCurrentBranch;
rowEl.isCurrentWorktree = isCurrentWorktree;
rowEl.hasWorktree = hasWorktree;
rowEl.hasLocalBranch = hasLocalBranch;
tableEl.append(rowEl);
}
},
);
}
}
renderIssues() {
const tableEl = document.getElementById('issues')!;
const rowEls = tableEl.querySelectorAll('issue-row');
rowEls.forEach(el => el.remove());
const noneEl = document.getElementById('no-issues')!;
const loadingEl = document.getElementById('loading-issues')!;
if (this.state.issues == null) {
noneEl.setAttribute('hidden', 'true');
loadingEl.removeAttribute('hidden');
} else if (this.state.issues.length === 0) {
noneEl.removeAttribute('hidden');
loadingEl.setAttribute('hidden', 'true');
} else {
noneEl.setAttribute('hidden', 'true');
loadingEl.setAttribute('hidden', 'true');
this.state.issues.forEach(({ issue, reasons }) => {
if (this._issueFilter == null || this._issueFilter === '' || reasons.includes(this._issueFilter)) {
const rowEl = document.createElement('issue-row') as IssueRow;
rowEl.issue = issue;
rowEl.reasons = reasons;
tableEl.append(rowEl);
}
});
}
}
onSelectTab(e: Event, callback?: (val?: string) => void) {
const thisEl = e.target as HTMLElement;
const tab = thisEl.dataset.tab!;
thisEl.parentElement?.querySelectorAll('[data-tab]')?.forEach(el => {
if ((el as HTMLElement).dataset.tab !== tab) {
el.classList.remove('is-active');
} else {
el.classList.add('is-active');
}
});
callback?.(tab);
}
}
new FocusApp();