mirror of
https://github.com/desktop/desktop
synced 2024-09-13 21:31:32 +00:00
Merge branch 'development' into jc-appearance-menu-underline
This commit is contained in:
commit
7b3c649edb
44
.github/workflows/close-single-word-issues.yml
vendored
Normal file
44
.github/workflows/close-single-word-issues.yml
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
name: Close Single-Word Issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
close-issue:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Close Single-Word Issue
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const issueTitle = context.payload.issue.title.trim();
|
||||
const isSingleWord = /^\S+$/.test(issueTitle);
|
||||
|
||||
if (isSingleWord) {
|
||||
const issueNumber = context.payload.issue.number;
|
||||
const repo = context.repo.repo;
|
||||
|
||||
// Close the issue and add the invalid label
|
||||
github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: repo,
|
||||
issue_number: issueNumber,
|
||||
labels: ['invalid'],
|
||||
state: 'closed'
|
||||
});
|
||||
|
||||
// Comment on the issue
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: repo,
|
||||
issue_number: issueNumber,
|
||||
body: `This issue may have been opened accidentally. I'm going to close it now, but feel free to open a new issue with a more descriptive title.`
|
||||
});
|
||||
}
|
2
.github/workflows/release-pr.yml
vendored
2
.github/workflows/release-pr.yml
vendored
|
@ -37,7 +37,7 @@ jobs:
|
|||
private_key: ${{ secrets.DESKTOP_RELEASES_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Create Release Pull Request
|
||||
uses: peter-evans/create-pull-request@v5.0.2
|
||||
uses: peter-evans/create-pull-request@v6.0.0
|
||||
if: |
|
||||
startsWith(github.ref, 'refs/heads/releases/') && !contains(github.ref, 'test')
|
||||
with:
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"productName": "GitHub Desktop",
|
||||
"bundleID": "com.github.GitHubClient",
|
||||
"companyName": "GitHub, Inc.",
|
||||
"version": "3.3.7",
|
||||
"version": "3.3.9",
|
||||
"main": "./main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -4,7 +4,7 @@ import { TitleBar } from '../ui/window/title-bar'
|
|||
import { encodePathAsUrl } from '../lib/path'
|
||||
import { WindowState } from '../lib/window-state'
|
||||
import { Octicon } from '../ui/octicons'
|
||||
import * as OcticonSymbol from '../ui/octicons/octicons.generated'
|
||||
import * as octicons from '../ui/octicons/octicons.generated'
|
||||
import { Button } from '../ui/lib/button'
|
||||
import { LinkButton } from '../ui/lib/link-button'
|
||||
import { getVersion } from '../ui/lib/app-proxy'
|
||||
|
@ -141,7 +141,7 @@ export class CrashApp extends React.Component<ICrashAppProps, ICrashAppState> {
|
|||
|
||||
return (
|
||||
<header>
|
||||
<Octicon symbol={OcticonSymbol.stop} className="error-icon" />
|
||||
<Octicon symbol={octicons.stop} className="error-icon" />
|
||||
<h1>{message}</h1>
|
||||
</header>
|
||||
)
|
||||
|
|
|
@ -104,6 +104,7 @@ const extensionModes: ReadonlyArray<IModeDefinition> = [
|
|||
mappings: {
|
||||
'.markdown': 'text/x-markdown',
|
||||
'.md': 'text/x-markdown',
|
||||
'.mdx': 'text/x-markdown',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -118,6 +119,7 @@ const extensionModes: ReadonlyArray<IModeDefinition> = [
|
|||
mappings: {
|
||||
'.xml': 'text/xml',
|
||||
'.xaml': 'text/xml',
|
||||
'.xsd': 'text/xml',
|
||||
'.csproj': 'text/xml',
|
||||
'.fsproj': 'text/xml',
|
||||
'.vcxproj': 'text/xml',
|
||||
|
@ -149,6 +151,9 @@ const extensionModes: ReadonlyArray<IModeDefinition> = [
|
|||
'.cpp': 'text/x-c++src',
|
||||
'.hpp': 'text/x-c++src',
|
||||
'.cc': 'text/x-c++src',
|
||||
'.hh': 'text/x-c++src',
|
||||
'.hxx': 'text/x-c++src',
|
||||
'.cxx': 'text/x-c++src',
|
||||
'.ino': 'text/x-c++src',
|
||||
'.kt': 'text/x-kotlin',
|
||||
},
|
||||
|
@ -208,6 +213,7 @@ const extensionModes: ReadonlyArray<IModeDefinition> = [
|
|||
install: () => import('codemirror/mode/python/python'),
|
||||
mappings: {
|
||||
'.py': 'text/x-python',
|
||||
'.pyi': 'text/x-python',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -425,6 +431,12 @@ const extensionModes: ReadonlyArray<IModeDefinition> = [
|
|||
'.dart': 'application/dart',
|
||||
},
|
||||
},
|
||||
{
|
||||
install: () => import('codemirror/mode/cmake/cmake'),
|
||||
mappings: {
|
||||
'.cmake': 'text/x-cmake',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
|
|
|
@ -332,6 +332,9 @@ export interface IAppState {
|
|||
| PullRequestSuggestedNextAction
|
||||
| undefined
|
||||
|
||||
/** Whether or not the user will see check marks indicating a line is included in the check in the diff */
|
||||
readonly showDiffCheckMarks: boolean
|
||||
|
||||
/**
|
||||
* Cached repo rulesets. Used to prevent repeatedly querying the same
|
||||
* rulesets to check their bypass status.
|
||||
|
|
|
@ -311,7 +311,6 @@ export class DiffParser {
|
|||
let diffLineNumber = linesConsumed
|
||||
while ((c = this.parseLinePrefix(this.peek()))) {
|
||||
const line = this.readLine()
|
||||
diffLineNumber++
|
||||
|
||||
if (!line) {
|
||||
throw new Error('Expected unified diff line but reached end of diff')
|
||||
|
@ -338,6 +337,12 @@ export class DiffParser {
|
|||
continue
|
||||
}
|
||||
|
||||
// We must increase `diffLineNumber` only when we're certain that the line
|
||||
// is not a "no newline" marker. Otherwise, we'll end up with a wrong
|
||||
// `diffLineNumber` for the next line. This could happen if the last line
|
||||
// in the file doesn't have a newline before the change.
|
||||
diffLineNumber++
|
||||
|
||||
let diffLine: DiffLine
|
||||
|
||||
if (c === DiffPrefixAdd) {
|
||||
|
|
|
@ -120,3 +120,24 @@ export const isAttributableEmailFor = (account: Account, email: string) => {
|
|||
getLegacyStealthEmailForUser(login, endpoint).toLowerCase() === needle
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A regular expression meant to match both the legacy format GitHub.com
|
||||
* stealth email address and the modern format (login@ vs id+login@).
|
||||
*
|
||||
* Yields two capture groups, the first being an optional capture of the
|
||||
* user id and the second being the mandatory login.
|
||||
*/
|
||||
const StealthEmailRegexp = /^(?:(\d+)\+)?(.+?)@(users\.noreply\..+)$/i
|
||||
|
||||
export const parseStealthEmail = (email: string, endpoint: string) => {
|
||||
const stealthEmailHost = getStealthEmailHostForEndpoint(endpoint)
|
||||
const match = StealthEmailRegexp.exec(email)
|
||||
|
||||
if (!match || stealthEmailHost !== match[3]) {
|
||||
return null
|
||||
}
|
||||
|
||||
const [, id, login] = match
|
||||
return { id: id ? parseInt(id, 10) : undefined, login }
|
||||
}
|
||||
|
|
|
@ -42,12 +42,7 @@ const versionCache = new Map<string, semver.SemVer | null>()
|
|||
const endpointVersionKey = (ep: string) => `endpoint-version:${ep}`
|
||||
|
||||
/**
|
||||
* Whether or not the given endpoint URI matches GitHub.com's
|
||||
*
|
||||
* I.e. https://api.github.com/
|
||||
*
|
||||
* Most often used to check if an endpoint _isn't_ GitHub.com meaning it's
|
||||
* either GitHub Enterprise Server or GitHub AE
|
||||
* Whether or not the given endpoint belong's to GitHub.com
|
||||
*/
|
||||
export const isDotCom = (ep: string) => ep === getDotComAPIEndpoint()
|
||||
|
||||
|
@ -149,9 +144,6 @@ export const supportsRetrieveActionWorkflowByCheckSuiteId = endpointSatisfies({
|
|||
dotcom: true,
|
||||
})
|
||||
|
||||
export const supportsAliveSessions = endpointSatisfies({
|
||||
dotcom: true,
|
||||
ghe: false,
|
||||
})
|
||||
export const supportsAliveSessions = endpointSatisfies({ dotcom: true })
|
||||
|
||||
export const supportsRepoRules = endpointSatisfies({ dotcom: true })
|
||||
|
|
|
@ -101,3 +101,8 @@ export function enableSectionList(): boolean {
|
|||
export const enableRepoRulesBeta = () => true
|
||||
|
||||
export const enableCommitDetailsHeaderExpansion = () => true
|
||||
|
||||
export const enableDiffCheckMarksAndLinkUnderlines = enableBetaFeatures
|
||||
|
||||
export const enableDiffCheckMarks = enableDiffCheckMarksAndLinkUnderlines
|
||||
export const enableGroupDiffCheckmarks = enableDevelopmentFeatures
|
||||
|
|
|
@ -410,6 +410,9 @@ const pullRequestSuggestedNextActionKey =
|
|||
const underlineLinksKey = 'underline-links'
|
||||
const underlineLinksDefault = true
|
||||
|
||||
const showDiffCheckMarksDefault = true
|
||||
const showDiffCheckMarksKey = 'diff-check-marks-visible'
|
||||
|
||||
export class AppStore extends TypedBaseStore<IAppState> {
|
||||
private readonly gitStoreCache: GitStoreCache
|
||||
|
||||
|
@ -540,6 +543,8 @@ export class AppStore extends TypedBaseStore<IAppState> {
|
|||
| PullRequestSuggestedNextAction
|
||||
| undefined = undefined
|
||||
|
||||
private showDiffCheckMarks: boolean = showDiffCheckMarksDefault
|
||||
|
||||
private cachedRepoRulesets = new Map<number, IAPIRepoRuleset>()
|
||||
|
||||
private underlineLinks: boolean = underlineLinksDefault
|
||||
|
@ -1024,6 +1029,7 @@ export class AppStore extends TypedBaseStore<IAppState> {
|
|||
resizablePaneActive: this.resizablePaneActive,
|
||||
cachedRepoRulesets: this.cachedRepoRulesets,
|
||||
underlineLinks: this.underlineLinks,
|
||||
showDiffCheckMarks: this.showDiffCheckMarks,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2220,6 +2226,11 @@ export class AppStore extends TypedBaseStore<IAppState> {
|
|||
|
||||
this.underlineLinks = getBoolean(underlineLinksKey, underlineLinksDefault)
|
||||
|
||||
this.showDiffCheckMarks = getBoolean(
|
||||
showDiffCheckMarksKey,
|
||||
showDiffCheckMarksDefault
|
||||
)
|
||||
|
||||
this.emitUpdateNow()
|
||||
|
||||
this.accountsStore.refresh()
|
||||
|
@ -4874,7 +4885,7 @@ export class AppStore extends TypedBaseStore<IAppState> {
|
|||
)
|
||||
|
||||
const gitStore = this.gitStoreCache.get(repository)
|
||||
await gitStore.restoreCoAuthorsFromCommit(commit)
|
||||
await gitStore.prepareToAmendCommit(commit)
|
||||
|
||||
this.setRepositoryCommitToAmend(repository, commit)
|
||||
|
||||
|
|
|
@ -716,7 +716,20 @@ export class GitStore extends BaseStore {
|
|||
this.emitUpdate()
|
||||
}
|
||||
|
||||
public async restoreCoAuthorsFromCommit(commit: Commit) {
|
||||
public async prepareToAmendCommit(commit: Commit) {
|
||||
const coAuthorsRestored = await this.restoreCoAuthorsFromCommit(commit)
|
||||
if (coAuthorsRestored) {
|
||||
return
|
||||
}
|
||||
|
||||
this._commitMessage = {
|
||||
summary: commit.summary,
|
||||
description: commit.body,
|
||||
}
|
||||
this.emitUpdate()
|
||||
}
|
||||
|
||||
private async restoreCoAuthorsFromCommit(commit: Commit) {
|
||||
// Let's be safe about this since it's untried waters.
|
||||
// If we can restore co-authors then that's fantastic
|
||||
// but if we can't we shouldn't be throwing an error,
|
||||
|
|
|
@ -8,19 +8,26 @@ export function installAliveOriginFilter(orderedWebRequest: OrderedWebRequest) {
|
|||
orderedWebRequest.onBeforeSendHeaders.addEventListener(async details => {
|
||||
const { protocol, host } = new URL(details.url)
|
||||
|
||||
// If it's a WebSocket Secure request directed to a github.com subdomain,
|
||||
// probably related to the Alive server, we need to override the `Origin`
|
||||
// header with a valid value.
|
||||
if (protocol === 'wss:' && /(^|\.)github\.com$/.test(host)) {
|
||||
return {
|
||||
requestHeaders: {
|
||||
...details.requestHeaders,
|
||||
// TODO: discuss with Alive team a good Origin value to use here
|
||||
Origin: 'https://desktop.github.com',
|
||||
},
|
||||
}
|
||||
// Here we're only interested in WebSockets
|
||||
if (protocol !== 'wss:') {
|
||||
return {}
|
||||
}
|
||||
|
||||
return {}
|
||||
// Alive URLs are supposed to be prefixed by "alive" and then the hostname
|
||||
if (
|
||||
!/^alive\.github\.com$/.test(host) &&
|
||||
!/^alive\.(.*)\.ghe\.com$/.test(host)
|
||||
) {
|
||||
return {}
|
||||
}
|
||||
|
||||
// We will just replace the `alive` prefix (which indicates the service)
|
||||
// with `desktop`.
|
||||
return {
|
||||
requestHeaders: {
|
||||
...details.requestHeaders,
|
||||
Origin: `https://${host.replace('alive.', 'desktop.')}`,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -135,6 +135,54 @@ export class DiffSelection {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the range is all selected, partially
|
||||
* selected, or not selected.
|
||||
*
|
||||
* @param from The line index (inclusive) from where to checking the range.
|
||||
*
|
||||
* @param length The number of lines to check from the start point of
|
||||
* 'from', Assumes positive number, returns None if length is <= 0.
|
||||
*/
|
||||
public isRangeSelected(from: number, length: number): DiffSelectionType {
|
||||
if (length <= 0) {
|
||||
// This shouldn't happen? But if it does we'll log it and return None.
|
||||
return DiffSelectionType.None
|
||||
}
|
||||
|
||||
const computedSelectionType = this.getSelectionType()
|
||||
if (computedSelectionType !== DiffSelectionType.Partial) {
|
||||
// Nothing for us to do here. If all lines are selected or none, then any
|
||||
// range of lines will be the same.
|
||||
return computedSelectionType
|
||||
}
|
||||
|
||||
if (length === 1) {
|
||||
return this.isSelected(from)
|
||||
? DiffSelectionType.All
|
||||
: DiffSelectionType.None
|
||||
}
|
||||
|
||||
const to = from + length
|
||||
let foundSelected = false
|
||||
let foundDeselected = false
|
||||
for (let i = from; i < to; i++) {
|
||||
if (this.isSelected(i)) {
|
||||
foundSelected = true
|
||||
}
|
||||
|
||||
if (!this.isSelected(i)) {
|
||||
foundDeselected = true
|
||||
}
|
||||
|
||||
if (foundSelected && foundDeselected) {
|
||||
return DiffSelectionType.Partial
|
||||
}
|
||||
}
|
||||
|
||||
return foundSelected ? DiffSelectionType.All : DiffSelectionType.None
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating wether the given line number is selectable.
|
||||
* A line not being selectable usually means it's a hunk header or a context
|
||||
|
|
|
@ -24,7 +24,7 @@ import { writeGitAttributes } from './git-attributes'
|
|||
import { getDefaultDir, setDefaultDir } from '../lib/default-dir'
|
||||
import { Dialog, DialogContent, DialogFooter, DialogError } from '../dialog'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
import { PopupType } from '../../models/popup'
|
||||
import { Ref } from '../lib/ref'
|
||||
|
@ -443,7 +443,7 @@ export class CreateRepository extends React.Component<
|
|||
|
||||
return (
|
||||
<Row className="warning-helper-text">
|
||||
<Octicon symbol={OcticonSymbol.alert} />
|
||||
<Octicon symbol={octicons.alert} />
|
||||
Will be created as {sanitizedName}
|
||||
</Row>
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react'
|
|||
import classNames from 'classnames'
|
||||
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { MenuItem } from '../../models/app-menu'
|
||||
import { AccessText } from '../lib/access-text'
|
||||
import { getPlatformSpecificNameOrSymbolForModifier } from '../../lib/menu-item'
|
||||
|
@ -95,9 +95,9 @@ export class MenuListItem extends React.Component<IMenuListItemProps, {}> {
|
|||
|
||||
private getIcon(item: MenuItem): JSX.Element | null {
|
||||
if (item.type === 'checkbox' && item.checked) {
|
||||
return <Octicon className="icon" symbol={OcticonSymbol.check} />
|
||||
return <Octicon className="icon" symbol={octicons.check} />
|
||||
} else if (item.type === 'radio' && item.checked) {
|
||||
return <Octicon className="icon" symbol={OcticonSymbol.dotFill} />
|
||||
return <Octicon className="icon" symbol={octicons.dotFill} />
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -153,10 +153,7 @@ export class MenuListItem extends React.Component<IMenuListItemProps, {}> {
|
|||
|
||||
const arrow =
|
||||
item.type === 'submenuItem' && this.props.renderSubMenuArrow !== false ? (
|
||||
<Octicon
|
||||
className="submenu-arrow"
|
||||
symbol={OcticonSymbol.triangleRight}
|
||||
/>
|
||||
<Octicon className="submenu-arrow" symbol={octicons.triangleRight} />
|
||||
) : null
|
||||
|
||||
const accelerator =
|
||||
|
|
|
@ -51,8 +51,8 @@ import {
|
|||
BranchDropdown,
|
||||
RevertProgress,
|
||||
} from './toolbar'
|
||||
import { iconForRepository, OcticonSymbolType } from './octicons'
|
||||
import * as OcticonSymbol from './octicons/octicons.generated'
|
||||
import { iconForRepository, OcticonSymbol } from './octicons'
|
||||
import * as octicons from './octicons/octicons.generated'
|
||||
import {
|
||||
showCertificateTrustDialog,
|
||||
sendReady,
|
||||
|
@ -2886,17 +2886,17 @@ export class App extends React.Component<IAppProps, IAppState> {
|
|||
|
||||
const repository = selection ? selection.repository : null
|
||||
|
||||
let icon: OcticonSymbolType
|
||||
let icon: OcticonSymbol
|
||||
let title: string
|
||||
if (repository) {
|
||||
const alias = repository instanceof Repository ? repository.alias : null
|
||||
icon = iconForRepository(repository)
|
||||
title = alias ?? repository.name
|
||||
} else if (this.state.repositories.length > 0) {
|
||||
icon = OcticonSymbol.repo
|
||||
icon = octicons.repo
|
||||
title = __DARWIN__ ? 'Select a Repository' : 'Select a repository'
|
||||
} else {
|
||||
icon = OcticonSymbol.repo
|
||||
icon = octicons.repo
|
||||
title = __DARWIN__ ? 'No Repositories' : 'No repositories'
|
||||
}
|
||||
|
||||
|
@ -3293,6 +3293,7 @@ export class App extends React.Component<IAppProps, IAppState> {
|
|||
imageDiffType={state.imageDiffType}
|
||||
hideWhitespaceInChangesDiff={state.hideWhitespaceInChangesDiff}
|
||||
hideWhitespaceInHistoryDiff={state.hideWhitespaceInHistoryDiff}
|
||||
showDiffCheckMarks={state.showDiffCheckMarks}
|
||||
showSideBySideDiff={state.showSideBySideDiff}
|
||||
focusCommitMessage={state.focusCommitMessage}
|
||||
askForConfirmationOnDiscardChanges={
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
|
||||
interface IBannerProps {
|
||||
readonly id?: string
|
||||
|
@ -37,7 +37,7 @@ export class Banner extends React.Component<IBannerProps, {}> {
|
|||
return (
|
||||
<div className="close">
|
||||
<button onClick={onDismissed} aria-label="Dismiss this message">
|
||||
<Octicon symbol={OcticonSymbol.x} />
|
||||
<Octicon symbol={octicons.x} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Banner } from './banner'
|
||||
|
||||
export function BranchAlreadyUpToDate({
|
||||
|
@ -29,7 +29,7 @@ export function BranchAlreadyUpToDate({
|
|||
return (
|
||||
<Banner id="successful-merge" timeout={5000} onDismissed={onDismissed}>
|
||||
<div className="green-circle">
|
||||
<Octicon className="check-icon" symbol={OcticonSymbol.check} />
|
||||
<Octicon className="check-icon" symbol={octicons.check} />
|
||||
</div>
|
||||
<div className="banner-message">{message}</div>
|
||||
</Banner>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Banner } from './banner'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
|
||||
|
@ -35,7 +35,7 @@ export class CherryPickConflictsBanner extends React.Component<
|
|||
dismissable={false}
|
||||
onDismissed={this.onDismissed}
|
||||
>
|
||||
<Octicon className="alert-icon" symbol={OcticonSymbol.alert} />
|
||||
<Octicon className="alert-icon" symbol={octicons.alert} />
|
||||
<div className="banner-message">
|
||||
<span>
|
||||
Resolve conflicts to continue cherry-picking onto{' '}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Banner } from './banner'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
|
||||
|
@ -41,7 +41,7 @@ export class ConflictsFoundBanner extends React.Component<
|
|||
dismissable={false}
|
||||
onDismissed={this.onDismissed}
|
||||
>
|
||||
<Octicon className="alert-icon" symbol={OcticonSymbol.alert} />
|
||||
<Octicon className="alert-icon" symbol={octicons.alert} />
|
||||
<div className="banner-message">
|
||||
<span>
|
||||
Resolve conflicts to continue {this.props.operationDescription}.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Banner } from './banner'
|
||||
import { Dispatcher } from '../dispatcher'
|
||||
import { Popup } from '../../models/popup'
|
||||
|
@ -31,7 +31,7 @@ export class MergeConflictsBanner extends React.Component<
|
|||
dismissable={false}
|
||||
onDismissed={this.props.onDismissed}
|
||||
>
|
||||
<Octicon className="alert-icon" symbol={OcticonSymbol.alert} />
|
||||
<Octicon className="alert-icon" symbol={octicons.alert} />
|
||||
<div className="banner-message">
|
||||
<span>
|
||||
Resolve conflicts and commit to merge into{' '}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Banner } from './banner'
|
||||
import { Dispatcher } from '../dispatcher'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
|
@ -38,7 +38,7 @@ export class RebaseConflictsBanner extends React.Component<
|
|||
dismissable={false}
|
||||
onDismissed={this.onDismissed}
|
||||
>
|
||||
<Octicon className="alert-icon" symbol={OcticonSymbol.alert} />
|
||||
<Octicon className="alert-icon" symbol={octicons.alert} />
|
||||
<div className="banner-message">
|
||||
<span>
|
||||
Resolve conflicts to continue rebasing{' '}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Banner } from './banner'
|
||||
|
||||
interface ISuccessBannerProps {
|
||||
|
@ -36,7 +36,7 @@ export class SuccessBanner extends React.Component<ISuccessBannerProps, {}> {
|
|||
onDismissed={this.props.onDismissed}
|
||||
>
|
||||
<div className="green-circle">
|
||||
<Octicon className="check-icon" symbol={OcticonSymbol.check} />
|
||||
<Octicon className="check-icon" symbol={octicons.checkCircleFill} />
|
||||
</div>
|
||||
<div className="banner-message">
|
||||
<span className="success-contents">{this.props.children}</span>
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
updateStore,
|
||||
} from '../lib/update-store'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { PopupType } from '../../models/popup'
|
||||
import { shell } from '../../lib/app-shell'
|
||||
|
||||
|
@ -39,7 +39,7 @@ export class UpdateAvailable extends React.Component<
|
|||
{!this.props.isUpdateShowcaseVisible && (
|
||||
<Octicon
|
||||
className="download-icon"
|
||||
symbol={OcticonSymbol.desktopDownload}
|
||||
symbol={octicons.desktopDownload}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Banner } from './banner'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
import { setNumber } from '../../lib/local-storage'
|
||||
|
@ -23,7 +23,7 @@ export class WindowsVersionNoLongerSupportedBanner extends React.Component<{
|
|||
dismissable={true}
|
||||
onDismissed={this.onDismissed}
|
||||
>
|
||||
<Octicon className="alert-icon" symbol={OcticonSymbol.alert} />
|
||||
<Octicon className="alert-icon" symbol={octicons.alert} />
|
||||
<div className="banner-message">
|
||||
<span>
|
||||
This operating system is no longer supported. Software updates have
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as React from 'react'
|
|||
import { IMatches } from '../../lib/fuzzy-find'
|
||||
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { HighlightText } from '../lib/highlight-text'
|
||||
import { dragAndDropManager } from '../../lib/drag-and-drop-manager'
|
||||
import { DragType, DropTargetType } from '../../models/drag-drop'
|
||||
|
@ -92,7 +92,7 @@ export class BranchListItem extends React.Component<
|
|||
|
||||
public render() {
|
||||
const { lastCommitDate, isCurrentBranch, name } = this.props
|
||||
const icon = isCurrentBranch ? OcticonSymbol.check : OcticonSymbol.gitBranch
|
||||
const icon = isCurrentBranch ? octicons.check : octicons.gitBranch
|
||||
const className = classNames('branches-list-item', {
|
||||
'drop-target': this.state.isDragInProgress,
|
||||
})
|
||||
|
|
|
@ -17,7 +17,7 @@ import { TabBar } from '../tab-bar'
|
|||
|
||||
import { Row } from '../lib/row'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Button } from '../lib/button'
|
||||
|
||||
import { BranchList } from './branch-list'
|
||||
|
@ -172,7 +172,7 @@ export class BranchesContainer extends React.Component<
|
|||
onClick={this.onMergeClick}
|
||||
tooltip={`Choose a branch to merge into ${currentBranch.name}`}
|
||||
>
|
||||
<Octicon className="icon" symbol={OcticonSymbol.gitMerge} />
|
||||
<Octicon className="icon" symbol={octicons.gitMerge} />
|
||||
Choose a branch to merge into <strong>{currentBranch.name}</strong>
|
||||
</Button>
|
||||
</Row>
|
||||
|
@ -297,7 +297,7 @@ export class BranchesContainer extends React.Component<
|
|||
onMouseLeave={this.onMouseLeaveNewBranchDrop}
|
||||
onMouseUp={this.onMouseUpNewBranchDrop}
|
||||
>
|
||||
<Octicon className="icon" symbol={OcticonSymbol.plus} />
|
||||
<Octicon className="icon" symbol={octicons.plus} />
|
||||
<div className="name">{label}</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon, OcticonSymbolType } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import { Octicon, OcticonSymbol } from '../octicons'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import classNames from 'classnames'
|
||||
import { GitHubRepository } from '../../models/github-repository'
|
||||
import { DisposableLike } from 'event-kit'
|
||||
|
@ -123,28 +123,28 @@ export class CIStatus extends React.PureComponent<
|
|||
|
||||
export function getSymbolForCheck(
|
||||
check: ICombinedRefCheck | IRefCheck | IAPIWorkflowJobStep
|
||||
): OcticonSymbolType {
|
||||
): OcticonSymbol {
|
||||
switch (check.conclusion) {
|
||||
case 'timed_out':
|
||||
return OcticonSymbol.x
|
||||
return octicons.x
|
||||
case 'failure':
|
||||
return OcticonSymbol.x
|
||||
return octicons.x
|
||||
case 'neutral':
|
||||
return OcticonSymbol.squareFill
|
||||
return octicons.squareFill
|
||||
case 'success':
|
||||
return OcticonSymbol.check
|
||||
return octicons.check
|
||||
case 'cancelled':
|
||||
return OcticonSymbol.stop
|
||||
return octicons.stop
|
||||
case 'action_required':
|
||||
return OcticonSymbol.alert
|
||||
return octicons.alert
|
||||
case 'skipped':
|
||||
return OcticonSymbol.skip
|
||||
return octicons.skip
|
||||
case 'stale':
|
||||
return OcticonSymbol.issueReopened
|
||||
return octicons.issueReopened
|
||||
}
|
||||
|
||||
// Pending
|
||||
return OcticonSymbol.dotFill
|
||||
return octicons.dotFill
|
||||
}
|
||||
|
||||
export function getClassNameForCheck(
|
||||
|
@ -170,12 +170,12 @@ export function getClassNameForCheck(
|
|||
|
||||
export function getSymbolForLogStep(
|
||||
logStep: IAPIWorkflowJobStep
|
||||
): OcticonSymbolType {
|
||||
): OcticonSymbol {
|
||||
switch (logStep.conclusion) {
|
||||
case 'success':
|
||||
return OcticonSymbol.checkCircleFill
|
||||
return octicons.checkCircleFill
|
||||
case 'failure':
|
||||
return OcticonSymbol.xCircleFill
|
||||
return octicons.xCircleFill
|
||||
}
|
||||
|
||||
return getSymbolForCheck(logStep)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import classNames from 'classnames'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { CIStatus } from './ci-status'
|
||||
import { HighlightText } from '../lib/highlight-text'
|
||||
import { IMatches } from '../../lib/fuzzy-find'
|
||||
|
@ -139,8 +139,8 @@ export class PullRequestListItem extends React.Component<
|
|||
className="icon"
|
||||
symbol={
|
||||
this.props.draft
|
||||
? OcticonSymbol.gitPullRequestDraft
|
||||
: OcticonSymbol.gitPullRequest
|
||||
? octicons.gitPullRequestDraft
|
||||
: octicons.gitPullRequest
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -44,8 +44,8 @@ import {
|
|||
Foldout,
|
||||
} from '../../lib/app-state'
|
||||
import { ContinueRebase } from './continue-rebase'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import { Octicon, OcticonSymbolVariant } from '../octicons'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { IStashEntry } from '../../models/stash-entry'
|
||||
import classNames from 'classnames'
|
||||
import { hasWritePermission } from '../../models/github-repository'
|
||||
|
@ -59,19 +59,19 @@ import { RepoRulesInfo } from '../../models/repo-rules'
|
|||
import { IAheadBehind } from '../../models/branch'
|
||||
|
||||
const RowHeight = 29
|
||||
const StashIcon: OcticonSymbol.OcticonSymbolType = {
|
||||
const StashIcon: OcticonSymbolVariant = {
|
||||
w: 16,
|
||||
h: 16,
|
||||
d:
|
||||
p: [
|
||||
'M10.5 1.286h-9a.214.214 0 0 0-.214.214v9a.214.214 0 0 0 .214.214h9a.214.214 0 0 0 ' +
|
||||
'.214-.214v-9a.214.214 0 0 0-.214-.214zM1.5 0h9A1.5 1.5 0 0 1 12 1.5v9a1.5 1.5 0 0 1-1.5 ' +
|
||||
'1.5h-9A1.5 1.5 0 0 1 0 10.5v-9A1.5 1.5 0 0 1 1.5 0zm5.712 7.212a1.714 1.714 0 1 ' +
|
||||
'1-2.424-2.424 1.714 1.714 0 0 1 2.424 2.424zM2.015 12.71c.102.729.728 1.29 1.485 ' +
|
||||
'1.29h9a1.5 1.5 0 0 0 1.5-1.5v-9a1.5 1.5 0 0 0-1.29-1.485v1.442a.216.216 0 0 1 ' +
|
||||
'.004.043v9a.214.214 0 0 1-.214.214h-9a.216.216 0 0 1-.043-.004H2.015zm2 2c.102.729.728 ' +
|
||||
'1.29 1.485 1.29h9a1.5 1.5 0 0 0 1.5-1.5v-9a1.5 1.5 0 0 0-1.29-1.485v1.442a.216.216 0 0 1 ' +
|
||||
'.004.043v9a.214.214 0 0 1-.214.214h-9a.216.216 0 0 1-.043-.004H4.015z',
|
||||
fr: 'evenodd',
|
||||
'.214-.214v-9a.214.214 0 0 0-.214-.214zM1.5 0h9A1.5 1.5 0 0 1 12 1.5v9a1.5 1.5 0 0 1-1.5 ' +
|
||||
'1.5h-9A1.5 1.5 0 0 1 0 10.5v-9A1.5 1.5 0 0 1 1.5 0zm5.712 7.212a1.714 1.714 0 1 ' +
|
||||
'1-2.424-2.424 1.714 1.714 0 0 1 2.424 2.424zM2.015 12.71c.102.729.728 1.29 1.485 ' +
|
||||
'1.29h9a1.5 1.5 0 0 0 1.5-1.5v-9a1.5 1.5 0 0 0-1.29-1.485v1.442a.216.216 0 0 1 ' +
|
||||
'.004.043v9a.214.214 0 0 1-.214.214h-9a.216.216 0 0 1-.043-.004H2.015zm2 2c.102.729.728 ' +
|
||||
'1.29 1.485 1.29h9a1.5 1.5 0 0 0 1.5-1.5v-9a1.5 1.5 0 0 0-1.29-1.485v1.442a.216.216 0 0 1 ' +
|
||||
'.004.043v9a.214.214 0 0 1-.214.214h-9a.216.216 0 0 1-.043-.004H4.015z',
|
||||
],
|
||||
}
|
||||
|
||||
const GitIgnoreFileName = '.gitignore'
|
||||
|
@ -935,7 +935,7 @@ export class ChangesList extends React.Component<
|
|||
>
|
||||
<Octicon className="stack-icon" symbol={StashIcon} />
|
||||
<div className="text">Stashed Changes</div>
|
||||
<Octicon symbol={OcticonSymbol.chevronRight} />
|
||||
<Octicon symbol={octicons.chevronRight} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -49,6 +49,9 @@ interface IChangesProps {
|
|||
*/
|
||||
readonly showSideBySideDiff: boolean
|
||||
|
||||
/** Whether or not to show the diff check marks indicating inclusion in a commit */
|
||||
readonly showDiffCheckMarks: boolean
|
||||
|
||||
/** Called when the user opens the diff options popover */
|
||||
readonly onDiffOptionsOpened: () => void
|
||||
}
|
||||
|
@ -120,6 +123,7 @@ export class Changes extends React.Component<IChangesProps, {}> {
|
|||
diff={this.props.diff}
|
||||
hideWhitespaceInDiff={this.props.hideWhitespaceInDiff}
|
||||
showSideBySideDiff={this.props.showSideBySideDiff}
|
||||
showDiffCheckMarks={this.props.showDiffCheckMarks}
|
||||
askForConfirmationOnDiscardChanges={
|
||||
this.props.askForConfirmationOnDiscardChanges
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
import { IAvatarUser } from '../../models/avatar'
|
||||
import { Avatar } from '../lib/avatar'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
import { OkCancelButtonGroup } from '../dialog'
|
||||
import { getConfigValue } from '../../lib/git/config'
|
||||
|
@ -190,7 +190,7 @@ export class CommitMessageAvatar extends React.Component<
|
|||
error: isError,
|
||||
warning: !isError,
|
||||
})
|
||||
const symbol = isError ? OcticonSymbol.stop : OcticonSymbol.alert
|
||||
const symbol = isError ? octicons.stop : octicons.alert
|
||||
|
||||
return (
|
||||
<div className={classes} ref={this.warningBadgeRef}>
|
||||
|
|
|
@ -13,8 +13,8 @@ import { Button } from '../lib/button'
|
|||
import { Loading } from '../lib/loading'
|
||||
import { AuthorInput } from '../lib/author-input/author-input'
|
||||
import { FocusContainer } from '../lib/focus-container'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import { Octicon, OcticonSymbolVariant } from '../octicons'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Author, UnknownAuthor, isKnownAuthor } from '../../models/author'
|
||||
import { IMenuItem } from '../../lib/menu-item'
|
||||
import { Commit, ICommitContext } from '../../models/commit'
|
||||
|
@ -57,16 +57,17 @@ import { Dispatcher } from '../dispatcher'
|
|||
import { formatCommitMessage } from '../../lib/format-commit-message'
|
||||
import { useRepoRulesLogic } from '../../lib/helpers/repo-rules'
|
||||
|
||||
const addAuthorIcon = {
|
||||
const addAuthorIcon: OcticonSymbolVariant = {
|
||||
w: 18,
|
||||
h: 13,
|
||||
d:
|
||||
p: [
|
||||
'M14 6V4.25a.75.75 0 0 1 1.5 0V6h1.75a.75.75 0 1 1 0 1.5H15.5v1.75a.75.75 0 0 ' +
|
||||
'1-1.5 0V7.5h-1.75a.75.75 0 1 1 0-1.5H14zM8.5 4a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 ' +
|
||||
'0zm.063 3.064a3.995 3.995 0 0 0 1.2-4.429A3.996 3.996 0 0 0 8.298.725a4.01 4.01 0 0 ' +
|
||||
'0-6.064 1.91 3.987 3.987 0 0 0 1.2 4.43A5.988 5.988 0 0 0 0 12.2a.748.748 0 0 0 ' +
|
||||
'.716.766.751.751 0 0 0 .784-.697 4.49 4.49 0 0 1 1.39-3.04 4.51 4.51 0 0 1 6.218 ' +
|
||||
'0 4.49 4.49 0 0 1 1.39 3.04.748.748 0 0 0 .786.73.75.75 0 0 0 .714-.8 5.989 5.989 0 0 0-3.435-5.136z',
|
||||
'1-1.5 0V7.5h-1.75a.75.75 0 1 1 0-1.5H14zM8.5 4a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 ' +
|
||||
'0zm.063 3.064a3.995 3.995 0 0 0 1.2-4.429A3.996 3.996 0 0 0 8.298.725a4.01 4.01 0 0 ' +
|
||||
'0-6.064 1.91 3.987 3.987 0 0 0 1.2 4.43A5.988 5.988 0 0 0 0 12.2a.748.748 0 0 0 ' +
|
||||
'.716.766.751.751 0 0 0 .784-.697 4.49 4.49 0 0 1 1.39-3.04 4.51 4.51 0 0 1 6.218 ' +
|
||||
'0 4.49 4.49 0 0 1 1.39 3.04.748.748 0 0 0 .786.73.75.75 0 0 0 .714-.8 5.989 5.989 0 0 0-3.435-5.136z',
|
||||
],
|
||||
}
|
||||
|
||||
interface ICommitMessageProps {
|
||||
|
@ -1279,7 +1280,7 @@ export class CommitMessage extends React.Component<
|
|||
tooltipClassName="length-hint-tooltip"
|
||||
ariaLabel="Open Summary Length Info"
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.lightBulb} />
|
||||
<Octicon symbol={octicons.lightBulb} />
|
||||
</ToggledtippedContent>
|
||||
)
|
||||
}
|
||||
|
@ -1313,7 +1314,7 @@ export class CommitMessage extends React.Component<
|
|||
onClick={this.toggleRuleFailurePopover}
|
||||
>
|
||||
<Octicon
|
||||
symbol={canBypass ? OcticonSymbol.alert : OcticonSymbol.stop}
|
||||
symbol={canBypass ? octicons.alert : octicons.stop}
|
||||
className={canBypass ? 'warning-icon' : 'error-icon'}
|
||||
/>
|
||||
</button>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { assertNever } from '../../lib/fatal-error'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
|
||||
export enum CommitWarningIcon {
|
||||
Warning,
|
||||
|
@ -11,20 +11,20 @@ export enum CommitWarningIcon {
|
|||
|
||||
const renderIcon = (icon: CommitWarningIcon) => {
|
||||
let className = ''
|
||||
let symbol = OcticonSymbol.alert
|
||||
let symbol = octicons.alert
|
||||
|
||||
switch (icon) {
|
||||
case CommitWarningIcon.Warning:
|
||||
className = 'warning-icon'
|
||||
symbol = OcticonSymbol.alert
|
||||
symbol = octicons.alert
|
||||
break
|
||||
case CommitWarningIcon.Information:
|
||||
className = 'information-icon'
|
||||
symbol = OcticonSymbol.info
|
||||
symbol = octicons.info
|
||||
break
|
||||
case CommitWarningIcon.Error:
|
||||
className = 'error-icon'
|
||||
symbol = OcticonSymbol.stop
|
||||
symbol = octicons.stop
|
||||
break
|
||||
default:
|
||||
assertNever(icon, `Unexpected icon value ${icon}`)
|
||||
|
|
|
@ -4,7 +4,7 @@ import { IRefCheck } from '../../lib/ci-checks/ci-checks'
|
|||
import { IMenuItem, showContextualMenu } from '../../lib/menu-item'
|
||||
import { Button } from '../lib/button'
|
||||
import { Octicon, syncClockwise } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
|
||||
interface ICICheckReRunButtonProps {
|
||||
readonly disabled: boolean
|
||||
|
@ -44,7 +44,7 @@ export class CICheckReRunButton extends React.PureComponent<ICICheckReRunButtonP
|
|||
const text =
|
||||
this.props.canReRunFailed && this.failedChecksExist ? (
|
||||
<>
|
||||
Re-run <Octicon symbol={OcticonSymbol.triangleDown} />
|
||||
Re-run <Octicon symbol={octicons.triangleDown} />
|
||||
</>
|
||||
) : (
|
||||
'Re-run Checks'
|
||||
|
|
|
@ -5,7 +5,7 @@ import { IRefCheck } from '../../lib/ci-checks/ci-checks'
|
|||
import { Octicon } from '../octicons'
|
||||
import { getClassNameForCheck, getSymbolForCheck } from '../branches/ci-status'
|
||||
import classNames from 'classnames'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { TooltippedContent } from '../lib/tooltipped-content'
|
||||
import { CICheckRunActionsJobStepList } from './ci-check-run-actions-job-step-list'
|
||||
import { IAPIWorkflowJobStep } from '../../lib/api'
|
||||
|
@ -123,9 +123,7 @@ export class CICheckRunListItem extends React.PureComponent<
|
|||
<div className="job-step-toggled-indicator">
|
||||
<Octicon
|
||||
symbol={
|
||||
isCheckRunExpanded
|
||||
? OcticonSymbol.chevronUp
|
||||
: OcticonSymbol.chevronDown
|
||||
isCheckRunExpanded ? octicons.chevronUp : octicons.chevronDown
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
@ -181,7 +179,7 @@ export class CICheckRunListItem extends React.PureComponent<
|
|||
return (
|
||||
<div className={classes} onClick={this.rerunJob}>
|
||||
<TooltippedContent tooltip={tooltip}>
|
||||
<Octicon symbol={OcticonSymbol.sync} />
|
||||
<Octicon symbol={octicons.sync} />
|
||||
</TooltippedContent>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
import { CICheckRunList } from './ci-check-run-list'
|
||||
import { encodePathAsUrl } from '../../lib/path'
|
||||
import { PopupType } from '../../models/popup'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Donut } from '../donut'
|
||||
import {
|
||||
supportsRerunningChecks,
|
||||
|
@ -253,14 +253,14 @@ export class CICheckRunPopover extends React.PureComponent<
|
|||
return (
|
||||
<Octicon
|
||||
className={'completeness-indicator-success'}
|
||||
symbol={OcticonSymbol.checkCircleFill}
|
||||
symbol={octicons.checkCircleFill}
|
||||
/>
|
||||
)
|
||||
case allFailure: {
|
||||
return (
|
||||
<Octicon
|
||||
className={'completeness-indicator-error'}
|
||||
symbol={OcticonSymbol.xCircleFill}
|
||||
symbol={octicons.xCircleFill}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
IAPICheckSuite,
|
||||
} from '../../lib/api'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from './../octicons/octicons.generated'
|
||||
import * as octicons from './../octicons/octicons.generated'
|
||||
import { encodePathAsUrl } from '../../lib/path'
|
||||
import { offsetFromNow } from '../../lib/offset-from'
|
||||
|
||||
|
@ -188,7 +188,7 @@ export class CICheckRunRerunDialog extends React.Component<
|
|||
}${pluralize} that cannot be re-run`
|
||||
return (
|
||||
<div className="non-re-run-info warning-helper-text">
|
||||
<Octicon symbol={OcticonSymbol.alert} />
|
||||
<Octicon symbol={octicons.alert} />
|
||||
|
||||
{`${warningPrefix}. A check run cannot be re-run if the check is more than one month old,
|
||||
the check or its dependent has not completed, or the check is not configured to be
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { IAPIRepository } from '../../lib/api'
|
||||
import { IFilterListGroup, IFilterListItem } from '../lib/filter-list'
|
||||
import { OcticonSymbolType } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import { OcticonSymbol } from '../octicons'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import entries from 'lodash/entries'
|
||||
import groupBy from 'lodash/groupBy'
|
||||
import { caseInsensitiveEquals, compare } from '../../lib/compare'
|
||||
|
@ -20,7 +20,7 @@ export interface ICloneableRepositoryListItem extends IFilterListItem {
|
|||
readonly name: string
|
||||
|
||||
/** The icon for the repo. */
|
||||
readonly icon: OcticonSymbolType
|
||||
readonly icon: OcticonSymbol
|
||||
|
||||
/** The clone URL. */
|
||||
readonly url: string
|
||||
|
@ -29,15 +29,15 @@ export interface ICloneableRepositoryListItem extends IFilterListItem {
|
|||
readonly archived?: boolean
|
||||
}
|
||||
|
||||
function getIcon(gitHubRepo: IAPIRepository): OcticonSymbolType {
|
||||
function getIcon(gitHubRepo: IAPIRepository): OcticonSymbol {
|
||||
if (gitHubRepo.private) {
|
||||
return OcticonSymbol.lock
|
||||
return octicons.lock
|
||||
}
|
||||
if (gitHubRepo.fork) {
|
||||
return OcticonSymbol.repoForked
|
||||
return octicons.repoForked
|
||||
}
|
||||
|
||||
return OcticonSymbol.repo
|
||||
return octicons.repo
|
||||
}
|
||||
|
||||
const toListItems = (repositories: ReadonlyArray<IAPIRepository>) =>
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as React from 'react'
|
|||
import { CloningRepository } from '../models/cloning-repository'
|
||||
import { ICloneProgress } from '../models/progress'
|
||||
import { Octicon } from './octicons'
|
||||
import * as OcticonSymbol from './octicons/octicons.generated'
|
||||
import * as octicons from './octicons/octicons.generated'
|
||||
import { UiView } from './ui-view'
|
||||
import { TooltippedContent } from './lib/tooltipped-content'
|
||||
|
||||
|
@ -25,7 +25,7 @@ export class CloningRepositoryView extends React.Component<
|
|||
return (
|
||||
<UiView id="cloning-repository-view">
|
||||
<div className="title-container">
|
||||
<Octicon symbol={OcticonSymbol.desktopDownload} />
|
||||
<Octicon symbol={octicons.desktopDownload} />
|
||||
<div className="title">Cloning {this.props.repository.name}</div>
|
||||
</div>
|
||||
<progress value={progressValue} />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { clipboard } from 'electron'
|
||||
import React from 'react'
|
||||
import * as OcticonSymbol from './octicons/octicons.generated'
|
||||
import * as octicons from './octicons/octicons.generated'
|
||||
import { Octicon } from './octicons'
|
||||
import { sleep } from '../lib/promise'
|
||||
import { Button } from './lib/button'
|
||||
|
@ -41,9 +41,9 @@ export class CopyButton extends React.Component<
|
|||
public renderSymbol() {
|
||||
const { showCopied } = this.state
|
||||
|
||||
const symbol = showCopied ? OcticonSymbol.check : OcticonSymbol.copy
|
||||
const symbol = showCopied ? octicons.check : octicons.copy
|
||||
|
||||
return <Octicon symbol={symbol} />
|
||||
return <Octicon symbol={symbol} height={24} />
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
|
||||
/**
|
||||
* A component used for displaying short error messages inline
|
||||
|
@ -17,7 +17,7 @@ export class DialogError extends React.Component {
|
|||
public render() {
|
||||
return (
|
||||
<div className="dialog-banner dialog-error" role="alert">
|
||||
<Octicon symbol={OcticonSymbol.stop} />
|
||||
<Octicon symbol={octicons.stop} />
|
||||
<div>{this.props.children}</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon, syncClockwise } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
|
||||
interface IDialogHeaderProps {
|
||||
/**
|
||||
|
@ -67,7 +67,7 @@ export class DialogHeader extends React.Component<IDialogHeaderProps, {}> {
|
|||
onClick={this.onCloseButtonClick}
|
||||
aria-label="Close"
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.x} />
|
||||
<Octicon symbol={octicons.x} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
|
||||
/**
|
||||
* A component used for displaying short success messages inline
|
||||
|
@ -17,7 +17,7 @@ export class DialogSuccess extends React.Component {
|
|||
public render() {
|
||||
return (
|
||||
<div className="dialog-banner dialog-success" role="alert">
|
||||
<Octicon symbol={OcticonSymbol.check} />
|
||||
<Octicon symbol={octicons.check} />
|
||||
<div>{this.props.children}</div>
|
||||
</div>
|
||||
)
|
||||
|
|
90
app/src/ui/diff/diff-contents-warning.tsx
Normal file
90
app/src/ui/diff/diff-contents-warning.tsx
Normal file
|
@ -0,0 +1,90 @@
|
|||
import React from 'react'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
import { ITextDiff, LineEndingsChange } from '../../models/diff'
|
||||
|
||||
enum DiffContentsWarningType {
|
||||
UnicodeBidiCharacters,
|
||||
LineEndingsChange,
|
||||
}
|
||||
|
||||
type DiffContentsWarningItem =
|
||||
| {
|
||||
readonly type: DiffContentsWarningType.UnicodeBidiCharacters
|
||||
}
|
||||
| {
|
||||
readonly type: DiffContentsWarningType.LineEndingsChange
|
||||
readonly lineEndingsChange: LineEndingsChange
|
||||
}
|
||||
|
||||
interface IDiffContentsWarningProps {
|
||||
readonly diff: ITextDiff
|
||||
}
|
||||
|
||||
export class DiffContentsWarning extends React.Component<IDiffContentsWarningProps> {
|
||||
public render() {
|
||||
const items = this.getTextDiffWarningItems()
|
||||
|
||||
if (items.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="diff-contents-warning-container">
|
||||
{items.map((item, i) => (
|
||||
<div className="diff-contents-warning" key={i}>
|
||||
<Octicon symbol={octicons.alert} />
|
||||
{this.getWarningMessageForItem(item)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private getTextDiffWarningItems(): ReadonlyArray<DiffContentsWarningItem> {
|
||||
const items = new Array<DiffContentsWarningItem>()
|
||||
const { diff } = this.props
|
||||
|
||||
if (diff.hasHiddenBidiChars) {
|
||||
items.push({
|
||||
type: DiffContentsWarningType.UnicodeBidiCharacters,
|
||||
})
|
||||
}
|
||||
|
||||
if (diff.lineEndingsChange) {
|
||||
items.push({
|
||||
type: DiffContentsWarningType.LineEndingsChange,
|
||||
lineEndingsChange: diff.lineEndingsChange,
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
private getWarningMessageForItem(item: DiffContentsWarningItem) {
|
||||
switch (item.type) {
|
||||
case DiffContentsWarningType.UnicodeBidiCharacters:
|
||||
return (
|
||||
<>
|
||||
This diff contains bidirectional Unicode text that may be
|
||||
interpreted or compiled differently than what appears below. To
|
||||
review, open the file in an editor that reveals hidden Unicode
|
||||
characters.{' '}
|
||||
<LinkButton uri="https://github.co/hiddenchars">
|
||||
Learn more about bidirectional Unicode characters
|
||||
</LinkButton>
|
||||
</>
|
||||
)
|
||||
|
||||
case DiffContentsWarningType.LineEndingsChange:
|
||||
const { lineEndingsChange } = item
|
||||
return (
|
||||
<>
|
||||
This diff contains a change in line endings from '
|
||||
{lineEndingsChange.from}' to '{lineEndingsChange.to}'.
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ import { PathLabel } from '../lib/path-label'
|
|||
import { AppFileStatus } from '../../models/status'
|
||||
import { IDiff, DiffType } from '../../models/diff'
|
||||
import { Octicon, iconForStatus } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import { mapStatus } from '../../lib/status'
|
||||
import { DiffOptions } from './diff-options'
|
||||
|
||||
|
@ -37,7 +36,6 @@ export class DiffHeader extends React.Component<IDiffHeaderProps, {}> {
|
|||
return (
|
||||
<div className="header">
|
||||
<PathLabel path={this.props.path} status={this.props.status} />
|
||||
{this.renderDecorator()}
|
||||
|
||||
{this.renderDiffOptions()}
|
||||
|
||||
|
@ -68,25 +66,4 @@ export class DiffHeader extends React.Component<IDiffHeaderProps, {}> {
|
|||
/>
|
||||
)
|
||||
}
|
||||
|
||||
private renderDecorator() {
|
||||
const diff = this.props.diff
|
||||
|
||||
if (diff === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (diff.kind === DiffType.Text && diff.lineEndingsChange) {
|
||||
const message = `Warning: line endings will be changed from '${diff.lineEndingsChange.from}' to '${diff.lineEndingsChange.to}'.`
|
||||
return (
|
||||
<Octicon
|
||||
symbol={OcticonSymbol.alert}
|
||||
className={'line-endings'}
|
||||
title={message}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { Checkbox, CheckboxValue } from '../lib/checkbox'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { RadioButton } from '../lib/radio-button'
|
||||
import {
|
||||
Popover,
|
||||
|
@ -99,9 +99,9 @@ export class DiffOptions extends React.Component<
|
|||
{buttonLabel}
|
||||
</Tooltip>
|
||||
<span ref={this.gearIconRef}>
|
||||
<Octicon symbol={OcticonSymbol.gear} />
|
||||
<Octicon symbol={octicons.gear} />
|
||||
</span>
|
||||
<Octicon symbol={OcticonSymbol.triangleDown} />
|
||||
<Octicon symbol={octicons.triangleDown} />
|
||||
</button>
|
||||
{this.state.isPopoverOpen && this.renderPopover()}
|
||||
</div>
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import React from 'react'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
|
||||
export class HiddenBidiCharsWarning extends React.Component {
|
||||
public render() {
|
||||
return (
|
||||
<div className="hidden-bidi-chars-warning">
|
||||
<Octicon symbol={OcticonSymbol.alert} />
|
||||
This diff contains bidirectional Unicode text that may be interpreted or
|
||||
compiled differently than what appears below. To review, open the file
|
||||
in an editor that reveals hidden Unicode characters.{' '}
|
||||
<LinkButton uri="https://github.co/hiddenchars">
|
||||
Learn more about bidirectional Unicode characters
|
||||
</LinkButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -76,6 +76,9 @@ interface IDiffProps {
|
|||
/** Whether we should show a confirmation dialog when the user discards changes */
|
||||
readonly askForConfirmationOnDiscardChanges?: boolean
|
||||
|
||||
/** Whether or not to show the diff check marks indicating inclusion in a commit */
|
||||
readonly showDiffCheckMarks: boolean
|
||||
|
||||
/**
|
||||
* Called when the user requests to open a binary file in an the
|
||||
* system-assigned application for said file type.
|
||||
|
@ -287,6 +290,7 @@ export class Diff extends React.Component<IDiffProps, IDiffState> {
|
|||
onHideWhitespaceInDiffChanged={
|
||||
this.props.onHideWhitespaceInDiffChanged
|
||||
}
|
||||
showDiffCheckMarks={this.props.showDiffCheckMarks}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -68,6 +68,11 @@ interface ISeamlessDiffSwitcherProps {
|
|||
// eslint-disable-next-line react/no-unused-prop-types
|
||||
readonly showSideBySideDiff: boolean
|
||||
|
||||
/** Whether or not to show the diff check marks indicating inclusion in a commit */
|
||||
// Used in getDerivedStateFromProps, no-unused-prop-types doesn't know that
|
||||
// eslint-disable-next-line react/no-unused-prop-types
|
||||
readonly showDiffCheckMarks: boolean
|
||||
|
||||
/** Whether we should show a confirmation dialog when the user discards changes */
|
||||
readonly askForConfirmationOnDiscardChanges?: boolean
|
||||
|
||||
|
@ -327,6 +332,7 @@ export class SeamlessDiffSwitcher extends React.Component<
|
|||
readOnly,
|
||||
hideWhitespaceInDiff,
|
||||
showSideBySideDiff,
|
||||
showDiffCheckMarks,
|
||||
onIncludeChanged,
|
||||
onDiscardChanges,
|
||||
file,
|
||||
|
@ -363,6 +369,7 @@ export class SeamlessDiffSwitcher extends React.Component<
|
|||
askForConfirmationOnDiscardChanges={
|
||||
this.props.askForConfirmationOnDiscardChanges
|
||||
}
|
||||
showDiffCheckMarks={showDiffCheckMarks}
|
||||
onIncludeChanged={isLoadingDiff ? noop : onIncludeChanged}
|
||||
onDiscardChanges={isLoadingDiff ? noop : onDiscardChanges}
|
||||
onOpenBinaryFile={isLoadingDiff ? noop : onOpenBinaryFile}
|
||||
|
|
|
@ -10,14 +10,19 @@ import {
|
|||
import { ILineTokens } from '../../lib/highlighter/types'
|
||||
import classNames from 'classnames'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { narrowNoNewlineSymbol } from './text-diff'
|
||||
import { shallowEquals, structuralEquals } from '../../lib/equality'
|
||||
import { DiffHunkExpansionType } from '../../models/diff'
|
||||
import { DiffHunkExpansionType, DiffSelectionType } from '../../models/diff'
|
||||
import { PopoverAnchorPosition } from '../lib/popover'
|
||||
import { WhitespaceHintPopover } from './whitespace-hint-popover'
|
||||
import { TooltipDirection } from '../lib/tooltip'
|
||||
import { Button } from '../lib/button'
|
||||
import { diffCheck } from '../octicons/diff-check'
|
||||
import {
|
||||
enableDiffCheckMarks,
|
||||
enableGroupDiffCheckmarks,
|
||||
} from '../../lib/feature-flag'
|
||||
|
||||
enum DiffRowPrefix {
|
||||
Added = '+',
|
||||
|
@ -25,6 +30,44 @@ enum DiffRowPrefix {
|
|||
Nothing = '\u{A0}',
|
||||
}
|
||||
|
||||
/**
|
||||
* This interface is used to pass information about a continuous group of
|
||||
* selectable rows to the row component as it pertains to a given row.
|
||||
*
|
||||
* Primarily used for the styling of the row and it's check all control.
|
||||
*/
|
||||
export interface IRowSelectableGroup {
|
||||
/**
|
||||
* Whether or not the row is the first in the selectable group
|
||||
*/
|
||||
isFirst: boolean
|
||||
|
||||
/**
|
||||
* Whether or not the row is the last in the selectable group
|
||||
*/
|
||||
isLast: boolean
|
||||
|
||||
/**
|
||||
* Whether or not the group is hovered by the mouse
|
||||
*/
|
||||
isHovered: boolean
|
||||
|
||||
/**
|
||||
* Whether or not the group is focused by the keyboard
|
||||
*/
|
||||
isFocused: boolean
|
||||
|
||||
/**
|
||||
* The selection state of the group - 'All', 'Partial', or 'None'
|
||||
*/
|
||||
selectionState: DiffSelectionType | null
|
||||
|
||||
/**
|
||||
* The height of the rows in the group
|
||||
*/
|
||||
height: number
|
||||
}
|
||||
|
||||
interface ISideBySideDiffRowProps {
|
||||
/**
|
||||
* The row data. This contains most of the information used to render the row.
|
||||
|
@ -71,13 +114,6 @@ interface ISideBySideDiffRowProps {
|
|||
select: boolean
|
||||
) => void
|
||||
|
||||
/**
|
||||
* Called when a line selection is updated. Called with the
|
||||
* row and column of the hovered line.
|
||||
* (only relevant when isDiffSelectable is true)
|
||||
*/
|
||||
readonly onUpdateSelection: (row: number, column: DiffColumn) => void
|
||||
|
||||
/**
|
||||
* Called when the user hovers the hunk handle. Called with the start
|
||||
* line of the hunk.
|
||||
|
@ -112,6 +148,15 @@ interface ISideBySideDiffRowProps {
|
|||
*/
|
||||
readonly onContextMenuLine: (diffLineNumber: number) => void
|
||||
|
||||
/**
|
||||
* Called when the user toggles the inclusion of line
|
||||
*/
|
||||
readonly onLineNumberCheckedChanged: (
|
||||
row: number,
|
||||
column: DiffColumn,
|
||||
select: boolean
|
||||
) => void
|
||||
|
||||
/**
|
||||
* Called when the user right-clicks a hunk handle. Called with the start
|
||||
* line of the hunk.
|
||||
|
@ -147,6 +192,12 @@ interface ISideBySideDiffRowProps {
|
|||
expansionType: DiffHunkExpansionType,
|
||||
element: HTMLButtonElement | null
|
||||
) => void
|
||||
|
||||
/** Whether or not to show the diff check marks indicating inclusion in a commit */
|
||||
readonly showDiffCheckMarks: boolean
|
||||
|
||||
/** The selectable group details */
|
||||
readonly rowSelectableGroup: IRowSelectableGroup | null
|
||||
}
|
||||
|
||||
interface ISideBySideDiffRowState {
|
||||
|
@ -161,31 +212,33 @@ export class SideBySideDiffRow extends React.Component<
|
|||
super(props)
|
||||
this.state = { showWhitespaceHint: undefined }
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { row, showSideBySideDiff, beforeClassNames, afterClassNames } =
|
||||
this.props
|
||||
|
||||
const baseRowClasses = classNames('row', {
|
||||
'has-check-all-control': enableGroupDiffCheckmarks(),
|
||||
})
|
||||
const beforeClasses = classNames('before', ...beforeClassNames)
|
||||
const afterClasses = classNames('after', ...afterClassNames)
|
||||
switch (row.type) {
|
||||
case DiffRowType.Hunk: {
|
||||
const className = ['row', 'hunk-info']
|
||||
if (row.expansionType === DiffHunkExpansionType.Both) {
|
||||
className.push('expandable-both')
|
||||
}
|
||||
|
||||
const rowClasses = classNames('hunk-info', baseRowClasses, {
|
||||
'expandable-both': row.expansionType === DiffHunkExpansionType.Both,
|
||||
})
|
||||
return (
|
||||
<div className={classNames(className)}>
|
||||
<div className={rowClasses}>
|
||||
{this.renderHunkHeaderGutter(row.hunkIndex, row.expansionType)}
|
||||
{this.renderContentFromString(row.content)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
case DiffRowType.Context:
|
||||
const rowClasses = classNames('context', baseRowClasses)
|
||||
const { beforeLineNumber, afterLineNumber } = row
|
||||
if (!showSideBySideDiff) {
|
||||
return (
|
||||
<div className="row context">
|
||||
<div className={rowClasses}>
|
||||
<div className="before">
|
||||
{this.renderLineNumbers(
|
||||
[beforeLineNumber, afterLineNumber],
|
||||
|
@ -198,7 +251,7 @@ export class SideBySideDiffRow extends React.Component<
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="row context">
|
||||
<div className={rowClasses}>
|
||||
<div className="before">
|
||||
{this.renderLineNumber(beforeLineNumber, DiffColumn.Before)}
|
||||
{this.renderContentFromString(row.content, row.beforeTokens)}
|
||||
|
@ -212,19 +265,17 @@ export class SideBySideDiffRow extends React.Component<
|
|||
|
||||
case DiffRowType.Added: {
|
||||
const { lineNumber, isSelected } = row.data
|
||||
const rowClasses = classNames('added', baseRowClasses)
|
||||
if (!showSideBySideDiff) {
|
||||
return (
|
||||
<div
|
||||
className="row added"
|
||||
onMouseEnter={this.onMouseEnterLineNumber}
|
||||
>
|
||||
<div className={rowClasses}>
|
||||
{this.renderHunkHandle()}
|
||||
<div className={afterClasses}>
|
||||
{this.renderLineNumbers(
|
||||
[undefined, lineNumber],
|
||||
DiffColumn.After,
|
||||
isSelected
|
||||
)}
|
||||
{this.renderHunkHandle()}
|
||||
{this.renderContent(row.data, DiffRowPrefix.Added)}
|
||||
{this.renderWhitespaceHintPopover(DiffColumn.After)}
|
||||
</div>
|
||||
|
@ -233,36 +284,34 @@ export class SideBySideDiffRow extends React.Component<
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="row added" onMouseEnter={this.onMouseEnterLineNumber}>
|
||||
<div className={rowClasses}>
|
||||
<div className={beforeClasses}>
|
||||
{this.renderLineNumber(undefined, DiffColumn.Before)}
|
||||
{this.renderContentFromString('')}
|
||||
{this.renderWhitespaceHintPopover(DiffColumn.Before)}
|
||||
</div>
|
||||
{this.renderHunkHandle()}
|
||||
<div className={afterClasses}>
|
||||
{this.renderLineNumber(lineNumber, DiffColumn.After, isSelected)}
|
||||
{this.renderContent(row.data, DiffRowPrefix.Added)}
|
||||
{this.renderWhitespaceHintPopover(DiffColumn.After)}
|
||||
</div>
|
||||
{this.renderHunkHandle()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
case DiffRowType.Deleted: {
|
||||
const { lineNumber, isSelected } = row.data
|
||||
const rowClasses = classNames('deleted', baseRowClasses)
|
||||
if (!showSideBySideDiff) {
|
||||
return (
|
||||
<div
|
||||
className="row deleted"
|
||||
onMouseEnter={this.onMouseEnterLineNumber}
|
||||
>
|
||||
<div className={rowClasses}>
|
||||
{this.renderHunkHandle()}
|
||||
<div className={beforeClasses}>
|
||||
{this.renderLineNumbers(
|
||||
[lineNumber, undefined],
|
||||
DiffColumn.Before,
|
||||
isSelected
|
||||
)}
|
||||
{this.renderHunkHandle()}
|
||||
{this.renderContent(row.data, DiffRowPrefix.Deleted)}
|
||||
{this.renderWhitespaceHintPopover(DiffColumn.Before)}
|
||||
</div>
|
||||
|
@ -271,32 +320,27 @@ export class SideBySideDiffRow extends React.Component<
|
|||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="row deleted"
|
||||
onMouseEnter={this.onMouseEnterLineNumber}
|
||||
>
|
||||
<div className={rowClasses}>
|
||||
<div className={beforeClasses}>
|
||||
{this.renderLineNumber(lineNumber, DiffColumn.Before, isSelected)}
|
||||
{this.renderContent(row.data, DiffRowPrefix.Deleted)}
|
||||
{this.renderWhitespaceHintPopover(DiffColumn.Before)}
|
||||
</div>
|
||||
{this.renderHunkHandle()}
|
||||
<div className={afterClasses}>
|
||||
{this.renderLineNumber(undefined, DiffColumn.After)}
|
||||
{this.renderContentFromString('', [])}
|
||||
{this.renderWhitespaceHintPopover(DiffColumn.After)}
|
||||
</div>
|
||||
{this.renderHunkHandle()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
case DiffRowType.Modified: {
|
||||
const { beforeData: before, afterData: after } = row
|
||||
const rowClasses = classNames('modified', baseRowClasses)
|
||||
return (
|
||||
<div className="row modified">
|
||||
<div
|
||||
className={beforeClasses}
|
||||
onMouseEnter={this.onMouseEnterLineNumber}
|
||||
>
|
||||
<div className={rowClasses}>
|
||||
<div className={beforeClasses}>
|
||||
{this.renderLineNumber(
|
||||
before.lineNumber,
|
||||
DiffColumn.Before,
|
||||
|
@ -305,10 +349,8 @@ export class SideBySideDiffRow extends React.Component<
|
|||
{this.renderContent(before, DiffRowPrefix.Deleted)}
|
||||
{this.renderWhitespaceHintPopover(DiffColumn.Before)}
|
||||
</div>
|
||||
<div
|
||||
className={afterClasses}
|
||||
onMouseEnter={this.onMouseEnterLineNumber}
|
||||
>
|
||||
{this.renderHunkHandle()}
|
||||
<div className={afterClasses}>
|
||||
{this.renderLineNumber(
|
||||
after.lineNumber,
|
||||
DiffColumn.After,
|
||||
|
@ -317,7 +359,6 @@ export class SideBySideDiffRow extends React.Component<
|
|||
{this.renderContent(after, DiffRowPrefix.Added)}
|
||||
{this.renderWhitespaceHintPopover(DiffColumn.After)}
|
||||
</div>
|
||||
{this.renderHunkHandle()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -381,7 +422,7 @@ export class SideBySideDiffRow extends React.Component<
|
|||
// This can only be the first hunk
|
||||
case DiffHunkExpansionType.Up:
|
||||
return {
|
||||
icon: OcticonSymbol.foldUp,
|
||||
icon: octicons.foldUp,
|
||||
title: 'Expand Up',
|
||||
handler: this.onExpandHunk(hunkIndex, expansionType),
|
||||
}
|
||||
|
@ -389,13 +430,13 @@ export class SideBySideDiffRow extends React.Component<
|
|||
// second to last hunk down.
|
||||
case DiffHunkExpansionType.Down:
|
||||
return {
|
||||
icon: OcticonSymbol.foldDown,
|
||||
icon: octicons.foldDown,
|
||||
title: 'Expand Down',
|
||||
handler: this.onExpandHunk(hunkIndex - 1, expansionType),
|
||||
}
|
||||
case DiffHunkExpansionType.Short:
|
||||
return {
|
||||
icon: OcticonSymbol.fold,
|
||||
icon: octicons.fold,
|
||||
title: 'Expand All',
|
||||
handler: this.onExpandHunk(hunkIndex, expansionType),
|
||||
}
|
||||
|
@ -410,20 +451,28 @@ export class SideBySideDiffRow extends React.Component<
|
|||
* for side-by-side diffs the gutter contains the line number of only one side.
|
||||
*/
|
||||
private get lineGutterWidth() {
|
||||
const { showSideBySideDiff, lineNumberWidth } = this.props
|
||||
return showSideBySideDiff ? lineNumberWidth : lineNumberWidth * 2
|
||||
const {
|
||||
showSideBySideDiff,
|
||||
lineNumberWidth,
|
||||
isDiffSelectable,
|
||||
showDiffCheckMarks,
|
||||
} = this.props
|
||||
return (
|
||||
(showSideBySideDiff ? lineNumberWidth : lineNumberWidth * 2) +
|
||||
(isDiffSelectable && showDiffCheckMarks && enableDiffCheckMarks()
|
||||
? 14
|
||||
: 0)
|
||||
)
|
||||
}
|
||||
|
||||
private renderHunkExpansionHandle(
|
||||
hunkIndex: number,
|
||||
expansionType: DiffHunkExpansionType
|
||||
) {
|
||||
const width = this.lineGutterWidth
|
||||
if (expansionType === DiffHunkExpansionType.None) {
|
||||
return (
|
||||
<div
|
||||
className="hunk-expansion-handle"
|
||||
style={{ width: this.lineGutterWidth }}
|
||||
>
|
||||
<div className="hunk-expansion-handle" style={{ width }}>
|
||||
<div className="hunk-expansion-placeholder" />
|
||||
</div>
|
||||
)
|
||||
|
@ -437,7 +486,7 @@ export class SideBySideDiffRow extends React.Component<
|
|||
return (
|
||||
<div
|
||||
className="hunk-expansion-handle selectable hoverable"
|
||||
style={{ width: this.lineGutterWidth }}
|
||||
style={{ width }}
|
||||
>
|
||||
<Button
|
||||
onClick={elementInfo.handler}
|
||||
|
@ -479,17 +528,39 @@ export class SideBySideDiffRow extends React.Component<
|
|||
}
|
||||
|
||||
private renderHunkHandle() {
|
||||
if (!this.props.isDiffSelectable) {
|
||||
const { isDiffSelectable, rowSelectableGroup } = this.props
|
||||
if (!isDiffSelectable || rowSelectableGroup === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
// In unified mode, the hunk handle left position depends on the line gutter
|
||||
// width.
|
||||
const style: React.CSSProperties = this.props.showSideBySideDiff
|
||||
? {}
|
||||
: { left: this.lineGutterWidth }
|
||||
const placeHolder = <div className="hunk-handle-place-holder"></div>
|
||||
|
||||
return (
|
||||
if (!rowSelectableGroup.isFirst) {
|
||||
return placeHolder
|
||||
}
|
||||
|
||||
const { height } = rowSelectableGroup
|
||||
const style = { height }
|
||||
|
||||
/* The hunk-handle is a a single element with a calculated height of all the
|
||||
rows in the selectable group (See `getRowSelectableGroupHeight` in
|
||||
`side-by-side-diff.tsx`). This gives us a single element to be our control
|
||||
of the check all functionality. It is positioned absolutely over the
|
||||
hunk-handle-place-holders in each row in order to provide one element that
|
||||
is interactive.
|
||||
|
||||
Other notes: I originally attempted to just use a single hunk-handle
|
||||
for the first row in a group as the heights of the rows are calculated and
|
||||
the rows do not clip overflow. However, that messes with the initial
|
||||
measurement for cache of the first row's height as the cell measurer will
|
||||
include the hunk handles initially calculated height (num rows times
|
||||
default row height) in it's measurement. (Resulting in the first row in a
|
||||
group heights = to however many lines in the group x 20) Thus, I decided to
|
||||
use the place holder element in each row to define the width of the hunk
|
||||
handle in the row and just position the hunk handle over them. A bit on the
|
||||
hacky side.
|
||||
*/
|
||||
const hunkHandle = (
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
<div
|
||||
className="hunk-handle hoverable"
|
||||
|
@ -498,7 +569,17 @@ export class SideBySideDiffRow extends React.Component<
|
|||
onClick={this.onClickHunk}
|
||||
onContextMenu={this.onContextMenuHunk}
|
||||
style={style}
|
||||
></div>
|
||||
>
|
||||
{!enableGroupDiffCheckmarks() && (
|
||||
<div className="increased-hover-surface" style={{ height }} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<>
|
||||
{placeHolder}
|
||||
{hunkHandle}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -522,39 +603,67 @@ export class SideBySideDiffRow extends React.Component<
|
|||
) {
|
||||
const wrapperID =
|
||||
column === undefined ? undefined : this.getLineNumbersContainerID(column)
|
||||
if (!this.props.isDiffSelectable || isSelected === undefined) {
|
||||
return (
|
||||
<div
|
||||
id={wrapperID}
|
||||
className="line-number"
|
||||
style={{ width: this.lineGutterWidth }}
|
||||
>
|
||||
{lineNumbers.map((lineNumber, index) => (
|
||||
<span key={index}>{lineNumber}</span>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
const isSelectable = this.props.isDiffSelectable && isSelected !== undefined
|
||||
const classes = classNames('line-number', {
|
||||
selectable: isSelectable,
|
||||
hoverable: isSelectable,
|
||||
'line-selected': isSelected,
|
||||
hover: this.props.isHunkHovered,
|
||||
})
|
||||
const checkboxId = wrapperID ? wrapperID + '-checkbox' : undefined
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||
<div
|
||||
id={wrapperID}
|
||||
className={classNames('line-number', 'selectable', 'hoverable', {
|
||||
'line-selected': isSelected,
|
||||
hover: this.props.isHunkHovered,
|
||||
})}
|
||||
className={classes}
|
||||
style={{ width: this.lineGutterWidth }}
|
||||
onMouseDown={this.onMouseDownLineNumber}
|
||||
onContextMenu={this.onContextMenuLineNumber}
|
||||
>
|
||||
{lineNumbers.map((lineNumber, index) => (
|
||||
<span key={index}>{lineNumber}</span>
|
||||
))}
|
||||
{isSelectable &&
|
||||
this.renderLineNumberCheckbox(checkboxId, isSelected === true)}
|
||||
<label htmlFor={checkboxId}>
|
||||
{this.renderLineNumberCheck(isSelected)}
|
||||
{lineNumbers.map((lineNumber, index) => (
|
||||
<span key={index}>{lineNumber}</span>
|
||||
))}
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private renderLineNumberCheck(isSelected?: boolean) {
|
||||
if (
|
||||
!this.props.isDiffSelectable ||
|
||||
!enableDiffCheckMarks() ||
|
||||
!this.props.showDiffCheckMarks
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="line-number-check">
|
||||
{isSelected ? <Octicon symbol={diffCheck} /> : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private renderLineNumberCheckbox(
|
||||
checkboxId: string | undefined,
|
||||
isSelected: boolean
|
||||
) {
|
||||
return (
|
||||
<input
|
||||
className="sr-only"
|
||||
id={checkboxId}
|
||||
type="checkbox"
|
||||
onChange={this.onLineNumberCheckboxChange}
|
||||
checked={isSelected}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
private renderWhitespaceHintPopover(column: DiffColumn) {
|
||||
if (this.state.showWhitespaceHint !== column) {
|
||||
return
|
||||
|
@ -644,6 +753,29 @@ export class SideBySideDiffRow extends React.Component<
|
|||
return null
|
||||
}
|
||||
|
||||
private onLineNumberCheckboxChange = ({
|
||||
currentTarget: checkbox,
|
||||
}: {
|
||||
currentTarget: HTMLInputElement
|
||||
}) => {
|
||||
const column = this.getDiffColumn(checkbox)
|
||||
|
||||
if (column === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.props.hideWhitespaceInDiff) {
|
||||
this.setState({ showWhitespaceHint: column })
|
||||
return
|
||||
}
|
||||
|
||||
this.props.onLineNumberCheckedChanged(
|
||||
this.props.numRow,
|
||||
column,
|
||||
checkbox.checked
|
||||
)
|
||||
}
|
||||
|
||||
private onMouseDownLineNumber = (evt: React.MouseEvent) => {
|
||||
if (evt.buttons === 2) {
|
||||
return
|
||||
|
@ -652,27 +784,20 @@ export class SideBySideDiffRow extends React.Component<
|
|||
const column = this.getDiffColumn(evt.currentTarget)
|
||||
const data = this.getDiffData(evt.currentTarget)
|
||||
|
||||
if (data !== null && column !== null) {
|
||||
if (this.props.hideWhitespaceInDiff) {
|
||||
this.setState({ showWhitespaceHint: column })
|
||||
return
|
||||
}
|
||||
|
||||
this.props.onStartSelection(this.props.numRow, column, !data.isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
private onMouseEnterLineNumber = (evt: React.MouseEvent) => {
|
||||
if (this.props.hideWhitespaceInDiff) {
|
||||
if (column === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const data = this.getDiffData(evt.currentTarget)
|
||||
const column = this.getDiffColumn(evt.currentTarget)
|
||||
|
||||
if (data !== null && column !== null) {
|
||||
this.props.onUpdateSelection(this.props.numRow, column)
|
||||
if (this.props.hideWhitespaceInDiff) {
|
||||
this.setState({ showWhitespaceHint: column })
|
||||
return
|
||||
}
|
||||
|
||||
if (data === null) {
|
||||
return
|
||||
}
|
||||
|
||||
this.props.onStartSelection(this.props.numRow, column, !data.isSelected)
|
||||
}
|
||||
|
||||
private onMouseEnterHunk = () => {
|
||||
|
|
|
@ -29,7 +29,7 @@ import {
|
|||
OverscanIndicesGetterParams,
|
||||
defaultOverscanIndicesGetter,
|
||||
} from 'react-virtualized'
|
||||
import { SideBySideDiffRow } from './side-by-side-diff-row'
|
||||
import { IRowSelectableGroup, SideBySideDiffRow } from './side-by-side-diff-row'
|
||||
import memoize from 'memoize-one'
|
||||
import {
|
||||
findInteractiveOriginalDiffRange,
|
||||
|
@ -60,20 +60,20 @@ import {
|
|||
expandWholeTextDiff,
|
||||
} from './text-diff-expansion'
|
||||
import { IMenuItem } from '../../lib/menu-item'
|
||||
import { HiddenBidiCharsWarning } from './hidden-bidi-chars-warning'
|
||||
import { DiffContentsWarning } from './diff-contents-warning'
|
||||
import { findDOMNode } from 'react-dom'
|
||||
import escapeRegExp from 'lodash/escapeRegExp'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const DefaultRowHeight = 20
|
||||
|
||||
export interface ISelectionPoint {
|
||||
readonly column: DiffColumn
|
||||
readonly row: number
|
||||
}
|
||||
|
||||
export interface ISelection {
|
||||
readonly from: ISelectionPoint
|
||||
readonly to: ISelectionPoint
|
||||
/// Initial diff line number in the selection
|
||||
readonly from: number
|
||||
|
||||
/// Last diff line number in the selection
|
||||
readonly to: number
|
||||
|
||||
readonly isSelected: boolean
|
||||
}
|
||||
|
||||
|
@ -135,6 +135,9 @@ interface ISideBySideDiffProps {
|
|||
*/
|
||||
readonly showSideBySideDiff: boolean
|
||||
|
||||
/** Whether or not to show the diff check marks indicating inclusion in a commit */
|
||||
readonly showDiffCheckMarks: boolean
|
||||
|
||||
/** Called when the user changes the hide whitespace in diffs setting. */
|
||||
readonly onHideWhitespaceInDiffChanged: (checked: boolean) => void
|
||||
}
|
||||
|
@ -372,6 +375,7 @@ export class SideBySideDiff extends React.Component<
|
|||
'selectionchange',
|
||||
this.onDocumentSelectionChange
|
||||
)
|
||||
document.removeEventListener('mousemove', this.onUpdateSelection)
|
||||
}
|
||||
|
||||
public componentDidUpdate(
|
||||
|
@ -514,14 +518,20 @@ export class SideBySideDiff extends React.Component<
|
|||
this.diffContainer = ref
|
||||
}
|
||||
|
||||
public render() {
|
||||
private getCurrentDiffRows() {
|
||||
const { diff } = this.state
|
||||
|
||||
const rows = getDiffRows(
|
||||
return getDiffRows(
|
||||
diff,
|
||||
this.props.showSideBySideDiff,
|
||||
this.canExpandDiff()
|
||||
)
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { diff } = this.state
|
||||
|
||||
const rows = this.getCurrentDiffRows()
|
||||
const containerClassName = classNames('side-by-side-diff-container', {
|
||||
'unified-diff': !this.props.showSideBySideDiff,
|
||||
[`selecting-${this.state.selectingTextInRow}`]:
|
||||
|
@ -537,7 +547,7 @@ export class SideBySideDiff extends React.Component<
|
|||
onMouseDown={this.onMouseDown}
|
||||
onKeyDown={this.onKeyDown}
|
||||
>
|
||||
{diff.hasHiddenBidiChars && <HiddenBidiCharsWarning />}
|
||||
<DiffContentsWarning diff={diff} />
|
||||
{this.state.isSearching && (
|
||||
<DiffSearchInput
|
||||
onSearch={this.onSearch}
|
||||
|
@ -599,6 +609,79 @@ export class SideBySideDiff extends React.Component<
|
|||
return defaultOverscanIndicesGetter({ ...params, startIndex, stopIndex })
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers information about if the row is in a selectable group. This
|
||||
* information is used to facilitate the use of check all feature for the
|
||||
* selectable group.
|
||||
*
|
||||
* This will return null if the row is not in a selectable group. A group is
|
||||
* more than one row.
|
||||
*/
|
||||
private getRowSelectableGroupDetails(
|
||||
row: SimplifiedDiffRow,
|
||||
prev: SimplifiedDiffRow,
|
||||
next: SimplifiedDiffRow
|
||||
): IRowSelectableGroup | null {
|
||||
if (!('hunkStartLine' in row)) {
|
||||
// can't be a selection hunk without a hunkStartLine
|
||||
return null
|
||||
}
|
||||
|
||||
const { diff, hoveredHunk } = this.state
|
||||
|
||||
const selectableType = [
|
||||
DiffRowType.Added,
|
||||
DiffRowType.Deleted,
|
||||
DiffRowType.Modified,
|
||||
]
|
||||
|
||||
if (!selectableType.includes(row.type)) {
|
||||
// We only care about selectable rows
|
||||
return null
|
||||
}
|
||||
|
||||
const range = findInteractiveOriginalDiffRange(
|
||||
diff.hunks,
|
||||
row.hunkStartLine
|
||||
)
|
||||
if (range === null) {
|
||||
// We only care about ranges with more than one line
|
||||
return null
|
||||
}
|
||||
|
||||
const selection = this.getSelection()
|
||||
if (selection === undefined) {
|
||||
// We only care about selectable rows.. so if no selection, no selectable rows
|
||||
return null
|
||||
}
|
||||
|
||||
const { from, to } = range
|
||||
|
||||
return {
|
||||
isFirst: prev === undefined || !selectableType.includes(prev.type),
|
||||
isLast: next === undefined || !selectableType.includes(next.type),
|
||||
isHovered: hoveredHunk === row.hunkStartLine,
|
||||
isFocused: true, // focusedHunk === row.hunkStartLine, - To be added in later PR
|
||||
selectionState: selection.isRangeSelected(from, to - from + 1),
|
||||
height: this.getRowSelectableGroupHeight(row.hunkStartLine),
|
||||
}
|
||||
}
|
||||
|
||||
private getRowSelectableGroupHeight = (hunkStartLine: number) => {
|
||||
const rows = getDiffRows(
|
||||
this.state.diff,
|
||||
this.props.showSideBySideDiff,
|
||||
this.canExpandDiff()
|
||||
)
|
||||
|
||||
return rows.reduce((acc, r, index) => {
|
||||
if (!('hunkStartLine' in r) || r.hunkStartLine !== hunkStartLine) {
|
||||
return acc
|
||||
}
|
||||
return acc + this.getRowHeight({ index })
|
||||
}, 0)
|
||||
}
|
||||
|
||||
private renderRow = ({ index, parent, style, key }: ListRowProps) => {
|
||||
const { diff } = this.state
|
||||
const rows = getDiffRows(
|
||||
|
@ -634,8 +717,14 @@ export class SideBySideDiff extends React.Component<
|
|||
|
||||
const rowWithTokens = this.createFullRow(row, index)
|
||||
|
||||
const isHunkHovered =
|
||||
'hunkStartLine' in row && this.state.hoveredHunk === row.hunkStartLine
|
||||
const rowSelectableGroupDetails = this.getRowSelectableGroupDetails(
|
||||
row,
|
||||
prev,
|
||||
next
|
||||
)
|
||||
|
||||
// Just temporary until pass the whole row group data down.
|
||||
const isHunkHovered = !!rowSelectableGroupDetails?.isHovered
|
||||
|
||||
return (
|
||||
<CellMeasurer
|
||||
|
@ -652,10 +741,11 @@ export class SideBySideDiff extends React.Component<
|
|||
numRow={index}
|
||||
isDiffSelectable={canSelect(this.props.file)}
|
||||
isHunkHovered={isHunkHovered}
|
||||
rowSelectableGroup={rowSelectableGroupDetails}
|
||||
showSideBySideDiff={this.props.showSideBySideDiff}
|
||||
hideWhitespaceInDiff={this.props.hideWhitespaceInDiff}
|
||||
showDiffCheckMarks={this.props.showDiffCheckMarks}
|
||||
onStartSelection={this.onStartSelection}
|
||||
onUpdateSelection={this.onUpdateSelection}
|
||||
onMouseEnterHunk={this.onMouseEnterHunk}
|
||||
onMouseLeaveHunk={this.onMouseLeaveHunk}
|
||||
onExpandHunk={this.onExpandHunk}
|
||||
|
@ -670,12 +760,41 @@ export class SideBySideDiff extends React.Component<
|
|||
beforeClassNames={beforeClassNames}
|
||||
afterClassNames={afterClassNames}
|
||||
onHunkExpansionRef={this.onHunkExpansionRef}
|
||||
onLineNumberCheckedChanged={this.onLineNumberCheckedChanged}
|
||||
/>
|
||||
</div>
|
||||
</CellMeasurer>
|
||||
)
|
||||
}
|
||||
|
||||
private onLineNumberCheckedChanged = (
|
||||
row: number,
|
||||
column: DiffColumn,
|
||||
isSelected: boolean
|
||||
) => {
|
||||
if (this.props.onIncludeChanged === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
let selection = this.getSelection()
|
||||
if (selection === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const lineBefore = this.getDiffLineNumber(row, column)
|
||||
const lineAfter = this.getDiffLineNumber(row, column)
|
||||
|
||||
if (lineBefore !== null) {
|
||||
selection = selection.withLineSelection(lineBefore, isSelected)
|
||||
}
|
||||
|
||||
if (lineAfter !== null) {
|
||||
selection = selection.withLineSelection(lineAfter, isSelected)
|
||||
}
|
||||
|
||||
this.props.onIncludeChanged(selection)
|
||||
}
|
||||
|
||||
private onHunkExpansionRef = (
|
||||
hunkIndex: number,
|
||||
expansionType: DiffHunkExpansionType,
|
||||
|
@ -745,7 +864,7 @@ export class SideBySideDiff extends React.Component<
|
|||
data: this.getRowDataPopulated(
|
||||
row.data,
|
||||
numRow,
|
||||
this.props.showSideBySideDiff ? DiffColumn.After : DiffColumn.Before,
|
||||
DiffColumn.After,
|
||||
this.state.afterTokens
|
||||
),
|
||||
}
|
||||
|
@ -834,8 +953,6 @@ export class SideBySideDiff extends React.Component<
|
|||
data.diffLineNumber !== null &&
|
||||
isInSelection(
|
||||
data.diffLineNumber,
|
||||
row,
|
||||
column,
|
||||
this.getSelection(),
|
||||
this.state.temporarySelection
|
||||
),
|
||||
|
@ -888,6 +1005,10 @@ export class SideBySideDiff extends React.Component<
|
|||
return null
|
||||
}
|
||||
|
||||
return this.getDiffRowLineNumber(row, column)
|
||||
}
|
||||
|
||||
private getDiffRowLineNumber(row: SimplifiedDiffRow, column: DiffColumn) {
|
||||
if (row.type === DiffRowType.Added || row.type === DiffRowType.Deleted) {
|
||||
return row.data.diffLineNumber
|
||||
}
|
||||
|
@ -957,21 +1078,88 @@ export class SideBySideDiff extends React.Component<
|
|||
column: DiffColumn,
|
||||
isSelected: boolean
|
||||
) => {
|
||||
const point: ISelectionPoint = { row, column }
|
||||
const temporarySelection = { from: point, to: point, isSelected }
|
||||
const line = this.getDiffLineNumber(row, column)
|
||||
if (line === null) {
|
||||
return
|
||||
}
|
||||
const temporarySelection = { from: line, to: line, isSelected }
|
||||
this.setState({ temporarySelection })
|
||||
|
||||
document.addEventListener('mouseup', this.onEndSelection, { once: true })
|
||||
document.addEventListener('mousemove', this.onUpdateSelection)
|
||||
}
|
||||
|
||||
private onUpdateSelection = (row: number, column: DiffColumn) => {
|
||||
private onUpdateSelection = (ev: MouseEvent) => {
|
||||
const { temporarySelection } = this.state
|
||||
if (temporarySelection === undefined) {
|
||||
const list = this.virtualListRef.current
|
||||
if (!temporarySelection || !list) {
|
||||
return
|
||||
}
|
||||
|
||||
const to = { row, column }
|
||||
this.setState({ temporarySelection: { ...temporarySelection, to } })
|
||||
const listNode = ReactDOM.findDOMNode(list)
|
||||
if (!(listNode instanceof Element)) {
|
||||
return
|
||||
}
|
||||
|
||||
const rect = listNode.getBoundingClientRect()
|
||||
const offsetInList = ev.clientY - rect.top
|
||||
const offsetInListScroll = offsetInList + listNode.scrollTop
|
||||
|
||||
const rows = this.getCurrentDiffRows()
|
||||
const totalRows = rows.length
|
||||
|
||||
let rowOffset = 0
|
||||
|
||||
// I haven't found an easy way to calculate which row the mouse is over,
|
||||
// especially since react-virtualized's `getOffsetForRow` is buggy (see
|
||||
// https://github.com/bvaughn/react-virtualized/issues/1422).
|
||||
// Instead, the approach here is to iterate over all rows and sum their
|
||||
// heights to calculate the offset of each row. Once we find the row that
|
||||
// contains the mouse, we scroll to it and update the temporary selection.
|
||||
for (let index = 0; index < totalRows; index++) {
|
||||
// Use row height cache in order to do the math faster
|
||||
let height = listRowsHeightCache.getHeight(index, 0)
|
||||
if (height === undefined) {
|
||||
list.recomputeRowHeights(index)
|
||||
height = listRowsHeightCache.getHeight(index, 0) ?? DefaultRowHeight
|
||||
}
|
||||
|
||||
if (
|
||||
offsetInListScroll >= rowOffset &&
|
||||
offsetInListScroll < rowOffset + height
|
||||
) {
|
||||
const row = rows[index]
|
||||
let column = DiffColumn.Before
|
||||
|
||||
if (this.props.showSideBySideDiff) {
|
||||
column =
|
||||
ev.clientX <= rect.left + rect.width / 2
|
||||
? DiffColumn.Before
|
||||
: DiffColumn.After
|
||||
} else {
|
||||
// `column` is irrelevant in unified diff because there aren't rows of
|
||||
// type Modified (see `getModifiedRows`)
|
||||
}
|
||||
const diffLineNumber = this.getDiffRowLineNumber(row, column)
|
||||
|
||||
// Always scroll to the row that contains the mouse, to ease range-based
|
||||
// selection with it
|
||||
list.scrollToRow(index)
|
||||
|
||||
if (diffLineNumber !== null) {
|
||||
this.setState({
|
||||
temporarySelection: {
|
||||
...temporarySelection,
|
||||
to: diffLineNumber,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
rowOffset += height
|
||||
}
|
||||
}
|
||||
|
||||
private onEndSelection = () => {
|
||||
|
@ -984,20 +1172,11 @@ export class SideBySideDiff extends React.Component<
|
|||
|
||||
const { from: tmpFrom, to: tmpTo, isSelected } = temporarySelection
|
||||
|
||||
const fromRow = Math.min(tmpFrom.row, tmpTo.row)
|
||||
const toRow = Math.max(tmpFrom.row, tmpTo.row)
|
||||
const fromLine = Math.min(tmpFrom, tmpTo)
|
||||
const toLine = Math.max(tmpFrom, tmpTo)
|
||||
|
||||
for (let row = fromRow; row <= toRow; row++) {
|
||||
const lineBefore = this.getDiffLineNumber(row, tmpFrom.column)
|
||||
const lineAfter = this.getDiffLineNumber(row, tmpTo.column)
|
||||
|
||||
if (lineBefore !== null) {
|
||||
selection = selection.withLineSelection(lineBefore, isSelected)
|
||||
}
|
||||
|
||||
if (lineAfter !== null) {
|
||||
selection = selection.withLineSelection(lineAfter, isSelected)
|
||||
}
|
||||
for (let line = fromLine; line <= toLine; line++) {
|
||||
selection = selection.withLineSelection(line, isSelected)
|
||||
}
|
||||
|
||||
this.props.onIncludeChanged?.(selection)
|
||||
|
@ -1708,8 +1887,6 @@ function* enumerateColumnContents(
|
|||
|
||||
function isInSelection(
|
||||
diffLineNumber: number,
|
||||
row: number,
|
||||
column: DiffColumn,
|
||||
selection: DiffSelection | undefined,
|
||||
temporarySelection: ISelection | undefined
|
||||
) {
|
||||
|
@ -1719,7 +1896,10 @@ function isInSelection(
|
|||
return isInStoredSelection
|
||||
}
|
||||
|
||||
const isInTemporary = isInTemporarySelection(row, column, temporarySelection)
|
||||
const isInTemporary = isInTemporarySelection(
|
||||
diffLineNumber,
|
||||
temporarySelection
|
||||
)
|
||||
|
||||
if (temporarySelection.isSelected) {
|
||||
return isInStoredSelection || isInTemporary
|
||||
|
@ -1728,9 +1908,8 @@ function isInSelection(
|
|||
}
|
||||
}
|
||||
|
||||
export function isInTemporarySelection(
|
||||
row: number,
|
||||
column: DiffColumn,
|
||||
function isInTemporarySelection(
|
||||
diffLineNumber: number,
|
||||
selection: ISelection | undefined
|
||||
): selection is ISelection {
|
||||
if (selection === undefined) {
|
||||
|
@ -1738,9 +1917,8 @@ export function isInTemporarySelection(
|
|||
}
|
||||
|
||||
if (
|
||||
row >= Math.min(selection.from.row, selection.to.row) &&
|
||||
row <= Math.max(selection.to.row, selection.from.row) &&
|
||||
(column === selection.from.column || column === selection.to.column)
|
||||
diffLineNumber >= Math.min(selection.from, selection.to) &&
|
||||
diffLineNumber <= Math.max(selection.to, selection.from)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -2,30 +2,32 @@ import React from 'react'
|
|||
import { parseRepositoryIdentifier } from '../../lib/remote-parsing'
|
||||
import { ISubmoduleDiff } from '../../models/diff'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
import { TooltippedCommitSHA } from '../lib/tooltipped-commit-sha'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { SuggestedAction } from '../suggested-actions'
|
||||
import { Ref } from '../lib/ref'
|
||||
import { CopyButton } from '../copy-button'
|
||||
import { shortenSHA } from '../../models/commit'
|
||||
|
||||
type SubmoduleItemIcon =
|
||||
| {
|
||||
readonly octicon: typeof OcticonSymbol.info
|
||||
readonly octicon: typeof octicons.info
|
||||
readonly className: 'info-icon'
|
||||
}
|
||||
| {
|
||||
readonly octicon: typeof OcticonSymbol.diffModified
|
||||
readonly octicon: typeof octicons.diffModified
|
||||
readonly className: 'modified-icon'
|
||||
}
|
||||
| {
|
||||
readonly octicon: typeof OcticonSymbol.diffAdded
|
||||
readonly octicon: typeof octicons.diffAdded
|
||||
readonly className: 'added-icon'
|
||||
}
|
||||
| {
|
||||
readonly octicon: typeof OcticonSymbol.diffRemoved
|
||||
readonly octicon: typeof octicons.diffRemoved
|
||||
readonly className: 'removed-icon'
|
||||
}
|
||||
| {
|
||||
readonly octicon: typeof OcticonSymbol.fileDiff
|
||||
readonly octicon: typeof octicons.fileDiff
|
||||
readonly className: 'untracked-icon'
|
||||
}
|
||||
|
||||
|
@ -80,7 +82,7 @@ export class SubmoduleDiff extends React.Component<ISubmoduleDiffProps> {
|
|||
: ` (${repoIdentifier.hostname})`
|
||||
|
||||
return this.renderSubmoduleDiffItem(
|
||||
{ octicon: OcticonSymbol.info, className: 'info-icon' },
|
||||
{ octicon: octicons.info, className: 'info-icon' },
|
||||
<>
|
||||
This is a submodule based on the repository{' '}
|
||||
<LinkButton
|
||||
|
@ -105,27 +107,27 @@ export class SubmoduleDiff extends React.Component<ISubmoduleDiffProps> {
|
|||
|
||||
if (oldSHA !== null && newSHA !== null) {
|
||||
return this.renderSubmoduleDiffItem(
|
||||
{ octicon: OcticonSymbol.diffModified, className: 'modified-icon' },
|
||||
{ octicon: octicons.diffModified, className: 'modified-icon' },
|
||||
<>
|
||||
This submodule changed its commit from{' '}
|
||||
{this.renderTooltippedCommitSHA(oldSHA)} to{' '}
|
||||
{this.renderTooltippedCommitSHA(newSHA)}.{suffix}
|
||||
{this.renderCommitSHA(oldSHA, 'previous')} to{' '}
|
||||
{this.renderCommitSHA(newSHA, 'new')}.{suffix}
|
||||
</>
|
||||
)
|
||||
} else if (oldSHA === null && newSHA !== null) {
|
||||
return this.renderSubmoduleDiffItem(
|
||||
{ octicon: OcticonSymbol.diffAdded, className: 'added-icon' },
|
||||
{ octicon: octicons.diffAdded, className: 'added-icon' },
|
||||
<>
|
||||
This submodule {verb} added pointing at commit{' '}
|
||||
{this.renderTooltippedCommitSHA(newSHA)}.{suffix}
|
||||
{this.renderCommitSHA(newSHA)}.{suffix}
|
||||
</>
|
||||
)
|
||||
} else if (oldSHA !== null && newSHA === null) {
|
||||
return this.renderSubmoduleDiffItem(
|
||||
{ octicon: OcticonSymbol.diffRemoved, className: 'removed-icon' },
|
||||
{ octicon: octicons.diffRemoved, className: 'removed-icon' },
|
||||
<>
|
||||
This submodule {verb} removed while it was pointing at commit{' '}
|
||||
{this.renderTooltippedCommitSHA(oldSHA)}.{suffix}
|
||||
{this.renderCommitSHA(oldSHA)}.{suffix}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -133,8 +135,18 @@ export class SubmoduleDiff extends React.Component<ISubmoduleDiffProps> {
|
|||
return null
|
||||
}
|
||||
|
||||
private renderTooltippedCommitSHA(sha: string) {
|
||||
return <TooltippedCommitSHA commit={sha} asRef={true} />
|
||||
private renderCommitSHA(sha: string, which?: 'previous' | 'new') {
|
||||
const whichInfix = which === undefined ? '' : ` ${which}`
|
||||
|
||||
return (
|
||||
<>
|
||||
<Ref>{shortenSHA(sha)}</Ref>
|
||||
<CopyButton
|
||||
ariaLabel={`Copy the full${whichInfix} SHA`}
|
||||
copyContent={sha}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
private renderSubmodulesChangesInfo() {
|
||||
|
@ -152,7 +164,7 @@ export class SubmoduleDiff extends React.Component<ISubmoduleDiffProps> {
|
|||
: 'modified'
|
||||
|
||||
return this.renderSubmoduleDiffItem(
|
||||
{ octicon: OcticonSymbol.fileDiff, className: 'untracked-icon' },
|
||||
{ octicon: octicons.fileDiff, className: 'untracked-icon' },
|
||||
<>
|
||||
This submodule has {changes} changes. Those changes must be committed
|
||||
inside of the submodule before they can be part of the parent
|
||||
|
|
|
@ -52,18 +52,21 @@ import {
|
|||
DiffExpansionKind,
|
||||
expandWholeTextDiff,
|
||||
} from './text-diff-expansion'
|
||||
import { OcticonSymbolVariant } from '../octicons'
|
||||
import { createOcticonElement } from '../octicons/octicon'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { WhitespaceHintPopover } from './whitespace-hint-popover'
|
||||
import { PopoverAnchorPosition } from '../lib/popover'
|
||||
import { HiddenBidiCharsWarning } from './hidden-bidi-chars-warning'
|
||||
import { DiffContentsWarning } from './diff-contents-warning'
|
||||
|
||||
// This is a custom version of the no-newline octicon that's exactly as
|
||||
// tall as it needs to be (8px) which helps with aligning it on the line.
|
||||
export const narrowNoNewlineSymbol = {
|
||||
export const narrowNoNewlineSymbol: OcticonSymbolVariant = {
|
||||
w: 16,
|
||||
h: 8,
|
||||
d: 'm 16,1 0,3 c 0,0.55 -0.45,1 -1,1 l -3,0 0,2 -3,-3 3,-3 0,2 2,0 0,-2 2,0 z M 8,4 C 8,6.2 6.2,8 4,8 1.8,8 0,6.2 0,4 0,1.8 1.8,0 4,0 6.2,0 8,1.8 8,4 Z M 1.5,5.66 5.66,1.5 C 5.18,1.19 4.61,1 4,1 2.34,1 1,2.34 1,4 1,4.61 1.19,5.17 1.5,5.66 Z M 7,4 C 7,3.39 6.81,2.83 6.5,2.34 L 2.34,6.5 C 2.82,6.81 3.39,7 4,7 5.66,7 7,5.66 7,4 Z',
|
||||
p: [
|
||||
'm 16,1 0,3 c 0,0.55 -0.45,1 -1,1 l -3,0 0,2 -3,-3 3,-3 0,2 2,0 0,-2 2,0 z M 8,4 C 8,6.2 6.2,8 4,8 1.8,8 0,6.2 0,4 0,1.8 1.8,0 4,0 6.2,0 8,1.8 8,4 Z M 1.5,5.66 5.66,1.5 C 5.18,1.19 4.61,1 4,1 2.34,1 1,2.34 1,4 1,4.61 1.19,5.17 1.5,5.66 Z M 7,4 C 7,3.39 6.81,2.83 6.5,2.34 L 2.34,6.5 C 2.82,6.81 3.39,7 4,7 5.66,7 7,5.66 7,4 Z',
|
||||
],
|
||||
}
|
||||
|
||||
type ChangedFile = WorkingDirectoryFileChange | CommittedFileChange
|
||||
|
@ -101,7 +104,7 @@ function createNoNewlineIndicatorWidget() {
|
|||
const widget = document.createElement('span')
|
||||
const titleId = uuid()
|
||||
|
||||
const { w, h, d } = narrowNoNewlineSymbol
|
||||
const { w, h, p } = narrowNoNewlineSymbol
|
||||
|
||||
const xmlns = 'http://www.w3.org/2000/svg'
|
||||
const svgElem = document.createElementNS(xmlns, 'svg')
|
||||
|
@ -119,7 +122,7 @@ function createNoNewlineIndicatorWidget() {
|
|||
|
||||
const pathElem = document.createElementNS(xmlns, 'path')
|
||||
pathElem.setAttribute('role', 'presentation')
|
||||
pathElem.setAttribute('d', d)
|
||||
pathElem.setAttribute('d', p[0])
|
||||
pathElem.textContent = 'No newline at end of file'
|
||||
svgElem.appendChild(pathElem)
|
||||
|
||||
|
@ -1127,7 +1130,7 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
|
|||
marker.appendChild(hunkExpandUpHandle)
|
||||
|
||||
hunkExpandUpHandle.appendChild(
|
||||
createOcticonElement(OcticonSymbol.foldUp, 'hunk-expand-icon')
|
||||
createOcticonElement(octicons.foldUp, 'hunk-expand-icon')
|
||||
)
|
||||
|
||||
const hunkExpandDownHandle = document.createElement('button')
|
||||
|
@ -1143,7 +1146,7 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
|
|||
marker.appendChild(hunkExpandDownHandle)
|
||||
|
||||
hunkExpandDownHandle.appendChild(
|
||||
createOcticonElement(OcticonSymbol.foldDown, 'hunk-expand-icon')
|
||||
createOcticonElement(octicons.foldDown, 'hunk-expand-icon')
|
||||
)
|
||||
|
||||
const hunkExpandWholeHandle = document.createElement('button')
|
||||
|
@ -1160,7 +1163,7 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
|
|||
|
||||
hunkExpandWholeHandle.appendChild(
|
||||
createOcticonElement(
|
||||
OcticonSymbol.foldDown,
|
||||
octicons.foldDown,
|
||||
'hunk-expand-icon',
|
||||
'hunk-expand-down-icon'
|
||||
)
|
||||
|
@ -1168,7 +1171,7 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
|
|||
|
||||
hunkExpandWholeHandle.appendChild(
|
||||
createOcticonElement(
|
||||
OcticonSymbol.foldUp,
|
||||
octicons.foldUp,
|
||||
'hunk-expand-icon',
|
||||
'hunk-expand-up-icon'
|
||||
)
|
||||
|
@ -1176,7 +1179,7 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
|
|||
|
||||
hunkExpandWholeHandle.appendChild(
|
||||
createOcticonElement(
|
||||
OcticonSymbol.unfold,
|
||||
octicons.unfold,
|
||||
'hunk-expand-icon',
|
||||
'hunk-expand-short-icon'
|
||||
)
|
||||
|
@ -1556,7 +1559,7 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
|
|||
|
||||
return (
|
||||
<>
|
||||
{diff.hasHiddenBidiChars && <HiddenBidiCharsWarning />}
|
||||
<DiffContentsWarning diff={diff} />
|
||||
<CodeMirrorHost
|
||||
className="diff-code-mirror"
|
||||
value={doc}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { DragType, DropTarget, DropTargetType } from '../../models/drag-drop'
|
|||
import { GitHubRepository } from '../../models/github-repository'
|
||||
import { CommitListItem } from '../history/commit-list-item'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Account } from '../../models/account'
|
||||
|
||||
interface ICommitDragElementProps {
|
||||
|
@ -79,7 +79,7 @@ export class CommitDragElement extends React.Component<
|
|||
switch (currentDropTarget.type) {
|
||||
case DropTargetType.Branch:
|
||||
const copyToPlus = __DARWIN__ ? null : (
|
||||
<Octicon className="copy-to-icon" symbol={OcticonSymbol.plus} />
|
||||
<Octicon className="copy-to-icon" symbol={octicons.plus} />
|
||||
)
|
||||
toolTipContents = (
|
||||
<>
|
||||
|
|
|
@ -2,7 +2,7 @@ import classNames from 'classnames'
|
|||
import React from 'react'
|
||||
import { Button } from './lib/button'
|
||||
import { Octicon } from './octicons'
|
||||
import * as OcticonSymbol from './octicons/octicons.generated'
|
||||
import * as octicons from './octicons/octicons.generated'
|
||||
import { MenuPane } from './app-menu'
|
||||
import { ICheckboxMenuItem, MenuItem } from '../models/app-menu'
|
||||
import { ClickSource, SelectionSource } from './lib/list'
|
||||
|
@ -383,7 +383,7 @@ export class DropdownSelectButton extends React.Component<
|
|||
ariaHaspopup={true}
|
||||
ariaLabel={dropdownAriaLabel}
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.triangleDown} />
|
||||
<Octicon symbol={octicons.triangleDown} />
|
||||
</Button>
|
||||
</div>
|
||||
{this.renderSplitButtonOptions()}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { RelativeTime } from '../relative-time'
|
|||
import { CommitAttribution } from '../lib/commit-attribution'
|
||||
import { AvatarStack } from '../lib/avatar-stack'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Draggable } from '../lib/draggable'
|
||||
import { dragAndDropManager } from '../../lib/drag-and-drop-manager'
|
||||
import {
|
||||
|
@ -197,7 +197,7 @@ export class CommitListItem extends React.PureComponent<
|
|||
className="unpushed-indicator"
|
||||
tooltip={this.props.unpushedIndicatorTitle}
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.arrowUp} />
|
||||
<Octicon symbol={octicons.arrowUp} />
|
||||
</TooltippedContent>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react'
|
|||
import classNames from 'classnames'
|
||||
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { RichText } from '../lib/rich-text'
|
||||
import { Repository } from '../../models/repository'
|
||||
import { Commit } from '../../models/commit'
|
||||
|
@ -247,7 +247,7 @@ export class CommitSummary extends React.Component<
|
|||
|
||||
const expanded = this.props.isExpanded
|
||||
const onClick = expanded ? this.onCollapse : this.onExpand
|
||||
const icon = expanded ? OcticonSymbol.fold : OcticonSymbol.unfold
|
||||
const icon = expanded ? octicons.fold : octicons.unfold
|
||||
|
||||
return (
|
||||
<button onClick={onClick} className="expander">
|
||||
|
@ -393,7 +393,7 @@ export class CommitSummary extends React.Component<
|
|||
onMouseOver={this.onHighlightShasNotInDiff}
|
||||
onMouseOut={this.onRemoveHighlightOfShas}
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.info} />
|
||||
<Octicon symbol={octicons.info} />
|
||||
<LinkButton onClick={this.showUnreachableCommits}>
|
||||
{excludedCommitsCount} unreachable {commitsPluralized}
|
||||
</LinkButton>{' '}
|
||||
|
@ -431,7 +431,7 @@ export class CommitSummary extends React.Component<
|
|||
className="commit-summary-meta-item without-truncation"
|
||||
aria-label="SHA"
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.gitCommit} />
|
||||
<Octicon symbol={octicons.gitCommit} />
|
||||
<TooltippedCommitSHA
|
||||
className="selectable"
|
||||
commit={selectedCommits[0]}
|
||||
|
@ -559,10 +559,7 @@ export class CommitSummary extends React.Component<
|
|||
<>
|
||||
{filesAdded > 0 ? (
|
||||
<span>
|
||||
<Octicon
|
||||
className="files-added-icon"
|
||||
symbol={OcticonSymbol.diffAdded}
|
||||
/>
|
||||
<Octicon className="files-added-icon" symbol={octicons.diffAdded} />
|
||||
{filesAdded} added
|
||||
</span>
|
||||
) : null}
|
||||
|
@ -570,7 +567,7 @@ export class CommitSummary extends React.Component<
|
|||
<span>
|
||||
<Octicon
|
||||
className="files-modified-icon"
|
||||
symbol={OcticonSymbol.diffModified}
|
||||
symbol={octicons.diffModified}
|
||||
/>
|
||||
{filesModified} modified
|
||||
</span>
|
||||
|
@ -579,7 +576,7 @@ export class CommitSummary extends React.Component<
|
|||
<span>
|
||||
<Octicon
|
||||
className="files-deleted-icon"
|
||||
symbol={OcticonSymbol.diffRemoved}
|
||||
symbol={octicons.diffRemoved}
|
||||
/>
|
||||
{filesRemoved} deleted
|
||||
</span>
|
||||
|
@ -588,7 +585,7 @@ export class CommitSummary extends React.Component<
|
|||
<span>
|
||||
<Octicon
|
||||
className="files-renamed-icon"
|
||||
symbol={OcticonSymbol.diffRenamed}
|
||||
symbol={octicons.diffRenamed}
|
||||
/>
|
||||
{filesRenamed} renamed
|
||||
</span>
|
||||
|
@ -604,7 +601,7 @@ export class CommitSummary extends React.Component<
|
|||
fileCount > 0 && hasFileDescription ? filesLongDescription : undefined
|
||||
}
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.diff} />
|
||||
<Octicon symbol={octicons.diff} />
|
||||
{filesShortDescription}
|
||||
</TooltippedContent>
|
||||
)
|
||||
|
@ -657,7 +654,7 @@ export class CommitSummary extends React.Component<
|
|||
return (
|
||||
<li className="commit-summary-meta-item" title={tags.join('\n')}>
|
||||
<span>
|
||||
<Octicon symbol={OcticonSymbol.tag} />
|
||||
<Octicon symbol={octicons.tag} />
|
||||
</span>
|
||||
|
||||
<span className="tags selectable">{tags.join(', ')}</span>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { HighlightText } from '../lib/highlight-text'
|
||||
import { Branch, IAheadBehind } from '../../models/branch'
|
||||
import { IMatches } from '../../lib/fuzzy-find'
|
||||
|
@ -108,18 +108,18 @@ export class CompareBranchListItem extends React.Component<
|
|||
const { currentBranch, branch } = this.props
|
||||
const { aheadBehind } = this.state
|
||||
const isCurrentBranch = branch.name === currentBranch?.name
|
||||
const icon = isCurrentBranch ? OcticonSymbol.check : OcticonSymbol.gitBranch
|
||||
const icon = isCurrentBranch ? octicons.check : octicons.gitBranch
|
||||
|
||||
const aheadBehindElement = aheadBehind ? (
|
||||
<div className="branch-commit-counter">
|
||||
<span className="branch-commit-counter-item">
|
||||
{aheadBehind.behind}
|
||||
<Octicon className="icon" symbol={OcticonSymbol.arrowDown} />
|
||||
<Octicon className="icon" symbol={octicons.arrowDown} />
|
||||
</span>
|
||||
|
||||
<span className="branch-commit-counter-item">
|
||||
{aheadBehind.ahead}
|
||||
<Octicon className="icon" symbol={OcticonSymbol.arrowUp} />
|
||||
<Octicon className="icon" symbol={octicons.arrowUp} />
|
||||
</span>
|
||||
</div>
|
||||
) : null
|
||||
|
|
|
@ -19,7 +19,7 @@ import { IBranchListItem } from '../branches/group-branches'
|
|||
import { TabBar } from '../tab-bar'
|
||||
import { CompareBranchListItem } from './compare-branch-list-item'
|
||||
import { FancyTextBox } from '../lib/fancy-text-box'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { SelectionSource } from '../lib/filter-list'
|
||||
import { IMatches } from '../../lib/fuzzy-find'
|
||||
import { Ref } from '../lib/ref'
|
||||
|
@ -166,7 +166,7 @@ export class CompareSidebar extends React.Component<
|
|||
<div className="compare-form">
|
||||
<FancyTextBox
|
||||
ariaLabel="Branch filter"
|
||||
symbol={OcticonSymbol.gitBranch}
|
||||
symbol={octicons.gitBranch}
|
||||
displayClearButton={true}
|
||||
placeholder={placeholderText}
|
||||
onFocus={this.onTextBoxFocused}
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react'
|
|||
import classNames from 'classnames'
|
||||
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { RichText } from '../lib/rich-text'
|
||||
import { Repository } from '../../models/repository'
|
||||
import { Commit } from '../../models/commit'
|
||||
|
@ -240,9 +240,7 @@ export class ExpandableCommitSummary extends React.Component<
|
|||
}
|
||||
ariaControls="expandable-commit-summary"
|
||||
>
|
||||
<Octicon
|
||||
symbol={isExpanded ? OcticonSymbol.fold : OcticonSymbol.unfold}
|
||||
/>
|
||||
<Octicon symbol={isExpanded ? octicons.fold : octicons.unfold} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
@ -382,7 +380,7 @@ export class ExpandableCommitSummary extends React.Component<
|
|||
onMouseOver={this.onHighlightShasNotInDiff}
|
||||
onMouseOut={this.onRemoveHighlightOfShas}
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.info} />
|
||||
<Octicon symbol={octicons.info} />
|
||||
<LinkButton onClick={this.showUnreachableCommits}>
|
||||
{excludedCommitsCount} unreachable {commitsPluralized}
|
||||
</LinkButton>{' '}
|
||||
|
@ -459,7 +457,7 @@ export class ExpandableCommitSummary extends React.Component<
|
|||
|
||||
return (
|
||||
<div className="ecs-meta-item commit-ref">
|
||||
<Octicon symbol={OcticonSymbol.gitCommit} />
|
||||
<Octicon symbol={octicons.gitCommit} />
|
||||
<div className="ref selectable">{isExpanded ? sha : shortSha}</div>
|
||||
<CopyButton ariaLabel="Copy the full SHA" copyContent={sha} />
|
||||
</div>
|
||||
|
@ -569,7 +567,7 @@ export class ExpandableCommitSummary extends React.Component<
|
|||
|
||||
return (
|
||||
<div className="ecs-meta-item lines-added-deleted">
|
||||
{isExpanded ? <Octicon symbol={OcticonSymbol.diff} /> : null}
|
||||
{isExpanded ? <Octicon symbol={octicons.diff} /> : null}
|
||||
<div className="lines-added">
|
||||
{!isExpanded ? <>+{linesAdded}</> : <>{linesAdded} added lines</>}
|
||||
</div>
|
||||
|
@ -598,7 +596,7 @@ export class ExpandableCommitSummary extends React.Component<
|
|||
|
||||
return (
|
||||
<div className="ecs-meta-item tags selectable">
|
||||
<Octicon symbol={OcticonSymbol.tag} />
|
||||
<Octicon symbol={octicons.tag} />
|
||||
<span>{tags.join(', ')}</span>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -48,8 +48,10 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
|
|||
)
|
||||
}
|
||||
|
||||
private rowForFile(file: CommittedFileChange | null): number {
|
||||
return file ? this.props.files.findIndex(f => f.path === file.path) : -1
|
||||
private selectedRowsForFile(): ReadonlyArray<number> {
|
||||
const { selectedFile: file, files } = this.props
|
||||
const fileIndex = file ? files.findIndex(f => f.path === file.path) : -1
|
||||
return fileIndex >= 0 ? [fileIndex] : []
|
||||
}
|
||||
|
||||
private onRowContextMenu = (
|
||||
|
@ -73,7 +75,7 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
|
|||
rowRenderer={this.renderFile}
|
||||
rowCount={this.props.files.length}
|
||||
rowHeight={29}
|
||||
selectedRows={[this.rowForFile(this.props.selectedFile)]}
|
||||
selectedRows={this.selectedRowsForFile()}
|
||||
onSelectedRowChanged={this.onSelectedRowChanged}
|
||||
onRowDoubleClick={this.props.onRowDoubleClick}
|
||||
onRowContextMenu={this.onRowContextMenu}
|
||||
|
|
|
@ -173,6 +173,7 @@ export class SelectedCommits extends React.Component<
|
|||
diff={diff}
|
||||
readOnly={true}
|
||||
hideWhitespaceInDiff={this.props.hideWhitespaceInDiff}
|
||||
showDiffCheckMarks={false}
|
||||
showSideBySideDiff={this.props.showSideBySideDiff}
|
||||
onOpenBinaryFile={this.props.onOpenBinaryFile}
|
||||
onChangeImageDiffType={this.props.onChangeImageDiffType}
|
||||
|
|
|
@ -3,8 +3,8 @@ import classNames from 'classnames'
|
|||
import { ComputedAction } from '../../models/computed-action'
|
||||
import { assertNever } from '../../lib/fatal-error'
|
||||
|
||||
import { Octicon, OcticonSymbolType } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import { Octicon, OcticonSymbol } from '../octicons'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
|
||||
interface IActionStatusIconProps {
|
||||
/** The status to display to the user */
|
||||
|
@ -52,16 +52,16 @@ export class ActionStatusIcon extends React.Component<IActionStatusIconProps> {
|
|||
}
|
||||
}
|
||||
|
||||
function getSymbolForState(status: ComputedAction): OcticonSymbolType {
|
||||
function getSymbolForState(status: ComputedAction): OcticonSymbol {
|
||||
switch (status) {
|
||||
case ComputedAction.Loading:
|
||||
return OcticonSymbol.dotFill
|
||||
return octicons.dotFill
|
||||
case ComputedAction.Conflicts:
|
||||
return OcticonSymbol.alert
|
||||
return octicons.alert
|
||||
case ComputedAction.Invalid:
|
||||
return OcticonSymbol.x
|
||||
return octicons.x
|
||||
case ComputedAction.Clean:
|
||||
return OcticonSymbol.check
|
||||
return octicons.check
|
||||
default:
|
||||
return assertNever(status, `Unknown state: ${JSON.stringify(status)}`)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Loading } from './loading'
|
||||
import { Form } from './form'
|
||||
import { Button } from './button'
|
||||
|
@ -198,7 +198,7 @@ export class AuthenticationForm extends React.Component<
|
|||
autoFocus={true}
|
||||
>
|
||||
Sign in using your browser
|
||||
<Octicon symbol={OcticonSymbol.linkExternal} />
|
||||
<Octicon symbol={octicons.linkExternal} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import classNames from 'classnames'
|
|||
import React from 'react'
|
||||
import { Author, isKnownAuthor } from '../../../models/author'
|
||||
import { Octicon, syncClockwise } from '../../octicons'
|
||||
import * as OcticonSymbol from '../../octicons/octicons.generated'
|
||||
import * as octicons from '../../octicons/octicons.generated'
|
||||
import { getFullTextForAuthor, getDisplayTextForAuthor } from './author-text'
|
||||
|
||||
interface IAuthorHandleProps {
|
||||
|
@ -132,13 +132,11 @@ export class AuthorHandle extends React.Component<IAuthorHandleProps> {
|
|||
{!isKnownAuthor(author) && (
|
||||
<Octicon
|
||||
className={classNames('icon', { spin: author.state !== 'error' })}
|
||||
symbol={
|
||||
author.state === 'error' ? OcticonSymbol.stop : syncClockwise
|
||||
}
|
||||
symbol={author.state === 'error' ? octicons.stop : syncClockwise}
|
||||
/>
|
||||
)}
|
||||
<button onClick={this.onRemoveClick} tabIndex={-1}>
|
||||
<Octicon className="delete" symbol={OcticonSymbol.x} />
|
||||
<Octicon className="delete" symbol={octicons.x} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import * as React from 'react'
|
||||
import { IAvatarUser } from '../../models/avatar'
|
||||
import { shallowEquals } from '../../lib/equality'
|
||||
import { Octicon } from '../octicons'
|
||||
import { API, getDotComAPIEndpoint } from '../../lib/api'
|
||||
import { Octicon, OcticonSymbolVariant } from '../octicons'
|
||||
import { API, getDotComAPIEndpoint, getHTMLURL } from '../../lib/api'
|
||||
import { TooltippedContent } from './tooltipped-content'
|
||||
import { TooltipDirection } from './tooltip'
|
||||
import {
|
||||
isDotCom,
|
||||
isGHE,
|
||||
isGHES,
|
||||
supportsAvatarsAPI,
|
||||
} from '../../lib/endpoint-capabilities'
|
||||
import { Account } from '../../models/account'
|
||||
import { parseStealthEmail } from '../../lib/email'
|
||||
import { noop } from 'lodash'
|
||||
import { offsetFrom } from '../../lib/offset-from'
|
||||
import { ExpiringOperationCache } from './expiring-operation-cache'
|
||||
import { forceUnwrap } from '../../lib/fatal-error'
|
||||
|
@ -39,6 +39,81 @@ const avatarTokenCache = new ExpiringOperationCache<
|
|||
() => offsetFrom(0, 50, 'minutes')
|
||||
)
|
||||
|
||||
const getBotLogin = (user: IAvatarUser) => {
|
||||
const { endpoint } = user
|
||||
if (user.avatarURL !== undefined || endpoint === null) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const match = parseStealthEmail(user.email, endpoint)
|
||||
|
||||
return match?.login?.endsWith('[bot]') ? match.login : undefined
|
||||
}
|
||||
|
||||
const botAvatarCache = new ExpiringOperationCache<
|
||||
{ user: IAvatarUser; accounts: ReadonlyArray<Account> },
|
||||
IAvatarUser
|
||||
>(
|
||||
({ user }) => `${user.endpoint}:${user.email}`,
|
||||
async ({ user, accounts }) => {
|
||||
const { endpoint } = user
|
||||
if (user.avatarURL !== undefined || endpoint === null) {
|
||||
throw new Error('Avatar URL already resolved or endpoint is missing')
|
||||
}
|
||||
|
||||
const account = accounts.find(a => a.endpoint === user.endpoint)
|
||||
|
||||
if (!account) {
|
||||
throw new Error('No account found for endpoint')
|
||||
}
|
||||
|
||||
const login = getBotLogin(user)
|
||||
|
||||
if (!login) {
|
||||
throw new Error('Email does not appear to be a bot email')
|
||||
}
|
||||
|
||||
const api = new API(endpoint, account.token)
|
||||
const apiUser = await api.fetchUser(login)
|
||||
|
||||
if (!apiUser?.avatar_url) {
|
||||
throw new Error('No avatar url returned from API')
|
||||
}
|
||||
|
||||
return { ...user, avatarURL: apiUser.avatar_url }
|
||||
},
|
||||
({ user }) =>
|
||||
user.endpoint && isGHE(user.endpoint)
|
||||
? offsetFrom(0, 50, 'minutes')
|
||||
: Infinity
|
||||
)
|
||||
|
||||
const dotComBot = (login: string, id: number, integrationId: number) => {
|
||||
const avatarURL = `https://avatars.githubusercontent.com/in/${integrationId}?v=4`
|
||||
const endpoint = getDotComAPIEndpoint()
|
||||
const stealthHost = 'users.noreply.github.com'
|
||||
return [
|
||||
{
|
||||
email: `${id}+${login}@${stealthHost}`,
|
||||
name: login,
|
||||
avatarURL,
|
||||
endpoint,
|
||||
},
|
||||
{ email: `${login}@${stealthHost}`, name: login, avatarURL, endpoint },
|
||||
]
|
||||
}
|
||||
|
||||
const knownAvatars: ReadonlyArray<IAvatarUser> = [
|
||||
...dotComBot('dependabot[bot]', 49699333, 29110),
|
||||
...dotComBot('github-actions[bot]', 41898282, 15368),
|
||||
...dotComBot('github-pages[bot]', 52472962, 34598),
|
||||
]
|
||||
|
||||
// Preload some of the more popular bot avatars so we don't have to hit the API
|
||||
knownAvatars.forEach(user =>
|
||||
botAvatarCache.set({ user, accounts: [] }, user, Infinity)
|
||||
)
|
||||
|
||||
/**
|
||||
* This maps contains avatar URLs that have failed to load and
|
||||
* the last time they failed to load (in milliseconds since the epoc)
|
||||
|
@ -107,10 +182,33 @@ interface IAvatarState {
|
|||
* The octicon has been tweaked to add some padding and so that it scales nicely in
|
||||
* a square aspect ratio.
|
||||
*/
|
||||
const DefaultAvatarSymbol = {
|
||||
const DefaultAvatarSymbol: OcticonSymbolVariant = {
|
||||
w: 16,
|
||||
h: 16,
|
||||
d: 'M13 13.145a.844.844 0 0 1-.832.855H3.834A.846.846 0 0 1 3 13.142v-.856c0-2.257 3.333-3.429 3.333-3.429s.191-.35 0-.857c-.7-.531-.786-1.363-.833-3.429C5.644 2.503 7.056 2 8 2s2.356.502 2.5 2.571C10.453 6.637 10.367 7.47 9.667 8c-.191.506 0 .857 0 .857S13 10.03 13 12.286v.859z',
|
||||
p: [
|
||||
'M13 13.145a.844.844 0 0 1-.832.855H3.834A.846.846 0 0 1 3 13.142v-.856c0-2.257 3.333-3.429 3.333-3.429s.191-.35 0-.857c-.7-.531-.786-1.363-.833-3.429C5.644 2.503 7.056 2 8 2s2.356.502 2.5 2.571C10.453 6.637 10.367 7.47 9.667 8c-.191.506 0 .857 0 .857S13 10.03 13 12.286v.859z',
|
||||
],
|
||||
}
|
||||
|
||||
function getEmailAvatarUrl(ep: string) {
|
||||
if (isGHES(ep)) {
|
||||
// GHES Endpoint urls look something like https://github.example.com/api/v3
|
||||
// (note the lack of a trailing slash). We really should change our endpoint
|
||||
// URLs to always have a trailing slash but that's one heck of an
|
||||
// undertaking since we'd have to migrate all the existing endpoints in
|
||||
// our IndexedDB databases so for now we'll just assume we'll add a trailing
|
||||
// slash in the future and be future proof.
|
||||
return new URL(`enterprise/avatars/u/e`, ep.endsWith('/') ? ep : `${ep}/`)
|
||||
} else if (isGHE(ep)) {
|
||||
// getHTMLURL(ep) is a noop here since it currently only deals with
|
||||
// github.com and assumes all others confrom to the GHES url structure but
|
||||
// we're likely going to need to update that in the future to support
|
||||
// ghe.com specifically so we're calling it here for future proofing.
|
||||
return new URL('/avatars/u/e', getHTMLURL(ep))
|
||||
} else {
|
||||
// It's safe to fall back to GitHub.com, at worst we'll get identicons
|
||||
return new URL('https://avatars.githubusercontent.com/u/e')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -164,9 +262,7 @@ function getAvatarUrlCandidates(
|
|||
return []
|
||||
}
|
||||
|
||||
const emailAvatarUrl = isDotCom(ep)
|
||||
? new URL('https://avatars.githubusercontent.com/u/e')
|
||||
: new URL(isGHES(ep) ? '/enterprise/avatars/u/e' : '/avatars/u/e', ep)
|
||||
const emailAvatarUrl = getEmailAvatarUrl(ep)
|
||||
|
||||
emailAvatarUrl.searchParams.set('email', email)
|
||||
emailAvatarUrl.searchParams.set('s', `${size}`)
|
||||
|
@ -183,10 +279,14 @@ function getAvatarUrlCandidates(
|
|||
const getInitialStateForUser = (
|
||||
user: IAvatarUser | undefined,
|
||||
accounts: ReadonlyArray<Account>,
|
||||
size: number | undefined
|
||||
size: number | undefined,
|
||||
avatarToken?: string
|
||||
): Pick<IAvatarState, 'user' | 'candidates' | 'avatarToken'> => {
|
||||
if (user && !user.avatarURL) {
|
||||
user = botAvatarCache.tryGet({ user, accounts }) ?? user
|
||||
}
|
||||
const endpoint = user?.endpoint
|
||||
const avatarToken =
|
||||
avatarToken ??=
|
||||
endpoint && isGHE(endpoint)
|
||||
? avatarTokenCache.tryGet({ endpoint, accounts })
|
||||
: undefined
|
||||
|
@ -201,16 +301,21 @@ export class Avatar extends React.Component<IAvatarProps, IAvatarState> {
|
|||
props: IAvatarProps,
|
||||
state: IAvatarState
|
||||
) {
|
||||
const { user, size, accounts } = props
|
||||
// If the endpoint has changed we need to reset the avatar token so that
|
||||
// it'll be re-fetched for the new endpoint
|
||||
return shallowEquals(user, state.user)
|
||||
? null
|
||||
: getInitialStateForUser(user, accounts, size)
|
||||
// Explicitly exclude equality checks on avatarURL or else we'll end up
|
||||
// infinitely looping when the user gets resolved into a bot account
|
||||
if (
|
||||
props.user?.email !== state.user?.email ||
|
||||
props.user?.endpoint !== state.user?.endpoint ||
|
||||
props.user?.name !== state.user?.name
|
||||
) {
|
||||
return getInitialStateForUser(props.user, props.accounts, props.size)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/** Set to true when unmounting to avoid unnecessary state updates */
|
||||
private cancelAvatarTokenRequest = false
|
||||
private cancelAvatarRequests = false
|
||||
|
||||
public constructor(props: IAvatarProps) {
|
||||
super(props)
|
||||
|
@ -224,7 +329,8 @@ export class Avatar extends React.Component<IAvatarProps, IAvatarState> {
|
|||
}
|
||||
|
||||
private getTitle() {
|
||||
const { title, user, accounts } = this.props
|
||||
const { title, accounts } = this.props
|
||||
const { user } = this.state
|
||||
|
||||
if (title === null) {
|
||||
return undefined
|
||||
|
@ -265,8 +371,7 @@ export class Avatar extends React.Component<IAvatarProps, IAvatarState> {
|
|||
|
||||
public render() {
|
||||
const title = this.getTitle()
|
||||
const { user } = this.props
|
||||
const { imageError } = this.state
|
||||
const { imageError, user } = this.state
|
||||
const alt = user
|
||||
? `Avatar for ${user.name || user.email}`
|
||||
: `Avatar for unknown user`
|
||||
|
@ -325,7 +430,8 @@ export class Avatar extends React.Component<IAvatarProps, IAvatarState> {
|
|||
// the endpoint still matches
|
||||
// also need to keep track of whether we have an async fetch in flight or
|
||||
// not so we don't trigger multiple fetches for the same endpoint
|
||||
const { user, accounts } = this.props
|
||||
const { accounts } = this.props
|
||||
const { user } = this.state
|
||||
const endpoint = user?.endpoint
|
||||
|
||||
// We've already got a token or we don't have a user, nothing to do here
|
||||
|
@ -346,8 +452,8 @@ export class Avatar extends React.Component<IAvatarProps, IAvatarState> {
|
|||
|
||||
this.setState({
|
||||
avatarToken: avatarTokenCache.get({ endpoint, accounts }).then(token => {
|
||||
if (!this.cancelAvatarTokenRequest) {
|
||||
if (token && this.props.user?.endpoint === endpoint) {
|
||||
if (!this.cancelAvatarRequests) {
|
||||
if (token && this.state.user?.endpoint === endpoint) {
|
||||
this.resetAvatarCandidates(token)
|
||||
}
|
||||
}
|
||||
|
@ -357,30 +463,59 @@ export class Avatar extends React.Component<IAvatarProps, IAvatarState> {
|
|||
|
||||
public componentDidUpdate(prevProps: IAvatarProps, prevState: IAvatarState) {
|
||||
this.ensureAvatarToken()
|
||||
|
||||
const oldUser = prevState.user
|
||||
const newUser = this.state.user
|
||||
|
||||
if (
|
||||
oldUser?.endpoint !== newUser?.endpoint ||
|
||||
oldUser?.email !== newUser?.email
|
||||
) {
|
||||
this.resolveBotAvatar()
|
||||
}
|
||||
}
|
||||
|
||||
private resetAvatarCandidates(avatarToken?: string) {
|
||||
const { user, size, accounts } = this.props
|
||||
if (!avatarToken && user?.endpoint && isGHE(user.endpoint)) {
|
||||
avatarToken =
|
||||
avatarTokenCache.tryGet({ endpoint: user.endpoint, accounts }) ??
|
||||
avatarToken
|
||||
}
|
||||
const { user } = this.state
|
||||
const { size, accounts } = this.props
|
||||
|
||||
const candidates = getAvatarUrlCandidates(user, avatarToken, size)
|
||||
|
||||
this.setState({ candidates, avatarToken })
|
||||
this.setState(getInitialStateForUser(user, accounts, size, avatarToken))
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
window.addEventListener('online', this.onInternetConnected)
|
||||
pruneExpiredFailingAvatars()
|
||||
this.ensureAvatarToken()
|
||||
this.resolveBotAvatar()
|
||||
}
|
||||
|
||||
private resolveBotAvatar() {
|
||||
const { accounts } = this.props
|
||||
const { user } = this.state
|
||||
|
||||
if (user?.endpoint && !user.avatarURL && getBotLogin(user)) {
|
||||
botAvatarCache
|
||||
.get({ user, accounts })
|
||||
.then(resolved => {
|
||||
if (
|
||||
!this.cancelAvatarRequests &&
|
||||
user.endpoint === resolved.endpoint &&
|
||||
user.email === resolved.email &&
|
||||
user.name === resolved.name &&
|
||||
user.avatarURL !== resolved.avatarURL
|
||||
) {
|
||||
this.setState({ user: resolved }, () =>
|
||||
this.resetAvatarCandidates()
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch(noop)
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
window.removeEventListener('online', this.onInternetConnected)
|
||||
this.cancelAvatarTokenRequest = true
|
||||
this.cancelAvatarRequests = true
|
||||
}
|
||||
|
||||
private onInternetConnected = () => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Branch, BranchType } from '../../models/branch'
|
|||
|
||||
import { Row } from './row'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Ref } from './ref'
|
||||
import { IStashEntry } from '../../models/stash-entry'
|
||||
import { enableMoveStash } from '../../lib/feature-flag'
|
||||
|
@ -12,7 +12,7 @@ export function renderBranchHasRemoteWarning(branch: Branch) {
|
|||
if (branch.upstream != null) {
|
||||
return (
|
||||
<Row className="warning-helper-text">
|
||||
<Octicon symbol={OcticonSymbol.alert} />
|
||||
<Octicon symbol={octicons.alert} />
|
||||
<p>
|
||||
This branch is tracking <Ref>{branch.upstream}</Ref> and renaming this
|
||||
branch will not change the branch name on the remote.
|
||||
|
@ -39,7 +39,7 @@ export function renderBranchNameExistsOnRemoteWarning(
|
|||
|
||||
return (
|
||||
<Row className="warning-helper-text">
|
||||
<Octicon symbol={OcticonSymbol.alert} />
|
||||
<Octicon symbol={octicons.alert} />
|
||||
<p>
|
||||
A branch named <Ref>{sanitizedName}</Ref> already exists on the remote.
|
||||
</p>
|
||||
|
@ -53,7 +53,7 @@ export function renderStashWillBeLostWarning(stash: IStashEntry | null) {
|
|||
}
|
||||
return (
|
||||
<Row className="warning-helper-text">
|
||||
<Octicon symbol={OcticonSymbol.alert} />
|
||||
<Octicon symbol={octicons.alert} />
|
||||
<p>
|
||||
Your current stashed changes on this branch will no longer be visible in
|
||||
GitHub Desktop if the branch is renamed.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon } from '../../octicons'
|
||||
import * as OcticonSymbol from '../../octicons/octicons.generated'
|
||||
import * as octicons from '../../octicons/octicons.generated'
|
||||
import { LinkButton } from '../link-button'
|
||||
|
||||
export function renderUnmergedFilesSummary(conflictedFilesCount: number) {
|
||||
|
@ -16,7 +16,7 @@ export function renderAllResolved() {
|
|||
return (
|
||||
<div className="all-conflicts-resolved">
|
||||
<div className="green-circle">
|
||||
<Octicon symbol={OcticonSymbol.check} />
|
||||
<Octicon symbol={octicons.check} />
|
||||
</div>
|
||||
<div className="message">All conflicts resolved</div>
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Repository } from '../../../models/repository'
|
|||
import { Dispatcher } from '../../dispatcher'
|
||||
import { showContextualMenu } from '../../../lib/menu-item'
|
||||
import { Octicon } from '../../octicons'
|
||||
import * as OcticonSymbol from '../../octicons/octicons.generated'
|
||||
import * as octicons from '../../octicons/octicons.generated'
|
||||
import { PathText } from '../path-text'
|
||||
import { ManualConflictResolution } from '../../../models/manual-conflict-resolution'
|
||||
import {
|
||||
|
@ -141,7 +141,7 @@ const renderResolvedFile: React.FunctionComponent<{
|
|||
)
|
||||
return (
|
||||
<li key={props.path} className="unmerged-file-status-resolved">
|
||||
<Octicon symbol={OcticonSymbol.fileCode} className="file-octicon" />
|
||||
<Octicon symbol={octicons.fileCode} className="file-octicon" />
|
||||
<div className="column-left" id={props.path}>
|
||||
<PathText path={props.path} />
|
||||
<div className="file-conflicts-status">{fileStatusSummary}</div>
|
||||
|
@ -160,7 +160,7 @@ const renderResolvedFile: React.FunctionComponent<{
|
|||
</Button>
|
||||
)}
|
||||
<div className="green-circle">
|
||||
<Octicon symbol={OcticonSymbol.check} />
|
||||
<Octicon symbol={octicons.check} />
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
|
@ -212,7 +212,7 @@ const renderManualConflictedFile: React.FunctionComponent<{
|
|||
onClick={onDropdownClick}
|
||||
>
|
||||
Resolve
|
||||
<Octicon symbol={OcticonSymbol.triangleDown} />
|
||||
<Octicon symbol={octicons.triangleDown} />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
@ -227,7 +227,7 @@ function renderConflictedFileWrapper(
|
|||
): JSX.Element {
|
||||
return (
|
||||
<li key={path} className="unmerged-file-status-conflicts">
|
||||
<Octicon symbol={OcticonSymbol.fileCode} className="file-octicon" />
|
||||
<Octicon symbol={octicons.fileCode} className="file-octicon" />
|
||||
{content}
|
||||
</li>
|
||||
)
|
||||
|
@ -289,7 +289,7 @@ const renderConflictedFileWithConflictMarkers: React.FunctionComponent<{
|
|||
ariaHaspopup="menu"
|
||||
ariaExpanded={props.isFileResolutionOptionsMenuOpen}
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.triangleDown} />
|
||||
<Octicon symbol={octicons.triangleDown} />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon, OcticonSymbolType } from '../octicons'
|
||||
import { Octicon, OcticonSymbol } from '../octicons'
|
||||
import { TextBox, ITextBoxProps } from './text-box'
|
||||
import classNames from 'classnames'
|
||||
|
||||
interface IFancyTextBoxProps extends ITextBoxProps {
|
||||
/** Icon to render */
|
||||
readonly symbol: OcticonSymbolType
|
||||
readonly symbol: OcticonSymbol
|
||||
|
||||
/** Callback used to get a reference to internal TextBox */
|
||||
readonly onRef: (textbox: TextBox) => void
|
||||
|
|
|
@ -4,7 +4,7 @@ import { LinkButton } from './link-button'
|
|||
import { getDotComAPIEndpoint } from '../../lib/api'
|
||||
import { isAttributableEmailFor } from '../../lib/email'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { AriaLiveContainer } from '../accessibility/aria-live-container'
|
||||
|
||||
interface IGitEmailNotFoundWarningProps {
|
||||
|
@ -25,7 +25,7 @@ export class GitEmailNotFoundWarning extends React.Component<IGitEmailNotFoundWa
|
|||
<span className="warning-icon">⚠️</span>
|
||||
) : (
|
||||
<span className="green-circle">
|
||||
<Octicon className="check-icon" symbol={OcticonSymbol.check} />
|
||||
<Octicon className="check-icon" symbol={octicons.check} />
|
||||
</span>
|
||||
)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { Octicon } from '../../octicons'
|
||||
import * as OcticonSymbol from '../../octicons/octicons.generated'
|
||||
import * as octicons from '../../octicons/octicons.generated'
|
||||
import classNames from 'classnames'
|
||||
import { AriaLiveContainer } from '../../accessibility/aria-live-container'
|
||||
import { assertNever } from '../../../lib/fatal-error'
|
||||
|
@ -85,9 +85,9 @@ export class InputDescription extends React.Component<IInputDescriptionProps> {
|
|||
case InputDescriptionType.Caption:
|
||||
return null
|
||||
case InputDescriptionType.Warning:
|
||||
return <Octicon symbol={OcticonSymbol.alert} />
|
||||
return <Octicon symbol={octicons.alert} />
|
||||
case InputDescriptionType.Error:
|
||||
return <Octicon symbol={OcticonSymbol.stop} />
|
||||
return <Octicon symbol={octicons.stop} />
|
||||
default:
|
||||
return assertNever(type, `Unknown input type ${type}`)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react'
|
|||
import { ITextBoxProps, TextBox } from './text-box'
|
||||
import { Button } from './button'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
|
||||
interface IPasswordTextBoxState {
|
||||
/**
|
||||
|
@ -32,8 +32,8 @@ export class PasswordTextBox extends React.Component<
|
|||
|
||||
public render() {
|
||||
const buttonIcon = this.state.showPassword
|
||||
? OcticonSymbol.eye
|
||||
: OcticonSymbol.eyeClosed
|
||||
? octicons.eye
|
||||
: octicons.eyeClosed
|
||||
const type = this.state.showPassword ? 'text' : 'password'
|
||||
const props: ITextBoxProps = { ...this.props, ...{ type } }
|
||||
return (
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react'
|
|||
|
||||
import { AppFileStatus, AppFileStatusKind } from '../../models/status'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { PathText } from './path-text'
|
||||
|
||||
interface IPathLabelProps {
|
||||
|
@ -46,7 +46,7 @@ export class PathLabel extends React.Component<IPathLabelProps, {}> {
|
|||
return (
|
||||
<span {...props} aria-hidden={this.props.ariaHidden}>
|
||||
<PathText path={status.oldPath} availableWidth={segmentWidth} />
|
||||
<Octicon className="rename-arrow" symbol={OcticonSymbol.arrowRight} />
|
||||
<Octicon className="rename-arrow" symbol={octicons.arrowRight} />
|
||||
<PathText path={this.props.path} availableWidth={segmentWidth} />
|
||||
</span>
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react'
|
|||
import { Button } from './button'
|
||||
import { Popover, PopoverAnchorPosition, PopoverDecoration } from './popover'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import classNames from 'classnames'
|
||||
|
||||
const maxPopoverContentHeight = 500
|
||||
|
@ -74,7 +74,7 @@ export class PopoverDropdown extends React.Component<
|
|||
onClick={this.closePopover}
|
||||
aria-label="Close"
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.x} />
|
||||
<Octicon symbol={octicons.x} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="popover-dropdown-content">{this.props.children}</div>
|
||||
|
@ -95,7 +95,7 @@ export class PopoverDropdown extends React.Component<
|
|||
>
|
||||
<span className="popover-dropdown-button-label">{label}</span>
|
||||
<span className="button-content">{buttonContent}</span>
|
||||
<Octicon symbol={OcticonSymbol.triangleDown} />
|
||||
<Octicon symbol={octicons.triangleDown} />
|
||||
</Button>
|
||||
{this.renderPopover()}
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@ import classNames from 'classnames'
|
|||
import { createUniqueId, releaseUniqueId } from './id-pool'
|
||||
import { showContextualMenu } from '../../lib/menu-item'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { AriaLiveContainer } from '../accessibility/aria-live-container'
|
||||
|
||||
export interface ITextBoxProps {
|
||||
|
@ -328,7 +328,7 @@ export class TextBox extends React.Component<ITextBoxProps, ITextBoxState> {
|
|||
aria-label="Clear"
|
||||
onClick={this.clearSearchText}
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.x} />
|
||||
<Octicon symbol={octicons.x} />
|
||||
</button>
|
||||
)}
|
||||
{this.state.valueCleared && (
|
||||
|
|
|
@ -44,7 +44,7 @@ export function canStartOperation(
|
|||
return false
|
||||
}
|
||||
|
||||
return statusKind === ComputedAction.Clean
|
||||
return statusKind !== ComputedAction.Invalid
|
||||
}
|
||||
|
||||
export interface IBaseChooseBranchDialogProps {
|
||||
|
|
|
@ -116,17 +116,14 @@ export class MergeChooseBranchDialog extends React.Component<
|
|||
return
|
||||
}
|
||||
|
||||
// The clean status is the only one that needs the ahead/behind count. If
|
||||
// the status is conflicts or invalid, update the UI here and end the
|
||||
// function.
|
||||
if (
|
||||
mergeStatus.kind === ComputedAction.Conflicts ||
|
||||
mergeStatus.kind === ComputedAction.Invalid
|
||||
) {
|
||||
// Can't go forward if the merge status is invalid, no need to check commit count
|
||||
if (mergeStatus.kind === ComputedAction.Invalid) {
|
||||
this.setState({ mergeStatus })
|
||||
return
|
||||
}
|
||||
|
||||
// Commit count is used in the UI output as well as determining whether the
|
||||
// submit button is enabled
|
||||
const range = revSymmetricDifference('', branch.name)
|
||||
const aheadBehind = await getAheadBehind(this.props.repository, range)
|
||||
const commitCount = aheadBehind ? aheadBehind.behind : 0
|
||||
|
|
|
@ -3,7 +3,7 @@ import { formatRebaseValue } from '../../../lib/rebase'
|
|||
import { RichText } from '../../lib/rich-text'
|
||||
import { Dialog, DialogContent } from '../../dialog'
|
||||
import { Octicon } from '../../octicons'
|
||||
import * as OcticonSymbol from '../../octicons/octicons.generated'
|
||||
import * as octicons from '../../octicons/octicons.generated'
|
||||
import { IMultiCommitOperationProgress } from '../../../models/progress'
|
||||
|
||||
interface IProgressDialogProps {
|
||||
|
@ -39,7 +39,7 @@ export class ProgressDialog extends React.Component<IProgressDialogProps> {
|
|||
|
||||
<div className="details">
|
||||
<div className="green-circle">
|
||||
<Octicon symbol={OcticonSymbol.check} />
|
||||
<Octicon symbol={octicons.check} />
|
||||
</div>
|
||||
<div className="summary">
|
||||
<div className="message">
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { UiView } from '../ui-view'
|
||||
import { Button } from '../lib/button'
|
||||
import { Octicon, OcticonSymbolType } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import { Octicon, OcticonSymbol } from '../octicons'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import {
|
||||
WelcomeLeftTopImageUri,
|
||||
WelcomeLeftBottomImageUri,
|
||||
|
@ -351,7 +351,7 @@ export class NoRepositoriesView extends React.Component<
|
|||
private onShowClone = () => this.props.onClone()
|
||||
|
||||
private renderButtonGroupButton(
|
||||
symbol: OcticonSymbolType,
|
||||
symbol: OcticonSymbol,
|
||||
title: string,
|
||||
onClick: () => void,
|
||||
type?: 'submit',
|
||||
|
@ -375,7 +375,7 @@ export class NoRepositoriesView extends React.Component<
|
|||
|
||||
if (this.props.tutorialPaused) {
|
||||
return this.renderButtonGroupButton(
|
||||
OcticonSymbol.mortarBoard,
|
||||
octicons.mortarBoard,
|
||||
__DARWIN__
|
||||
? 'Return to In Progress Tutorial'
|
||||
: 'Return to in progress tutorial',
|
||||
|
@ -384,7 +384,7 @@ export class NoRepositoriesView extends React.Component<
|
|||
)
|
||||
} else {
|
||||
return this.renderButtonGroupButton(
|
||||
OcticonSymbol.mortarBoard,
|
||||
octicons.mortarBoard,
|
||||
__DARWIN__
|
||||
? 'Create a Tutorial Repository…'
|
||||
: 'Create a tutorial repository…',
|
||||
|
@ -396,7 +396,7 @@ export class NoRepositoriesView extends React.Component<
|
|||
|
||||
private renderCloneButton() {
|
||||
return this.renderButtonGroupButton(
|
||||
OcticonSymbol.repoClone,
|
||||
octicons.repoClone,
|
||||
__DARWIN__
|
||||
? 'Clone a Repository from the Internet…'
|
||||
: 'Clone a repository from the Internet…',
|
||||
|
@ -408,20 +408,20 @@ export class NoRepositoriesView extends React.Component<
|
|||
|
||||
private renderCreateRepositoryButton() {
|
||||
return this.renderButtonGroupButton(
|
||||
OcticonSymbol.plus,
|
||||
octicons.plus,
|
||||
__DARWIN__
|
||||
? 'Create a New Repository on your Hard Drive…'
|
||||
: 'Create a New Repository on your hard drive…',
|
||||
? 'Create a New Repository on your Local Drive…'
|
||||
: 'Create a New Repository on your local drive…',
|
||||
this.props.onCreate
|
||||
)
|
||||
}
|
||||
|
||||
private renderAddExistingRepositoryButton() {
|
||||
return this.renderButtonGroupButton(
|
||||
OcticonSymbol.fileDirectory,
|
||||
octicons.fileDirectory,
|
||||
__DARWIN__
|
||||
? 'Add an Existing Repository from your Hard Drive…'
|
||||
: 'Add an Existing Repository from your hard drive…',
|
||||
? 'Add an Existing Repository from your Local Drive…'
|
||||
: 'Add an Existing Repository from your local drive…',
|
||||
this.props.onAdd
|
||||
)
|
||||
}
|
||||
|
@ -437,7 +437,7 @@ export class NoRepositoriesView extends React.Component<
|
|||
</div>
|
||||
|
||||
<div className="drag-drop-info">
|
||||
<Octicon symbol={OcticonSymbol.lightBulb} />
|
||||
<Octicon symbol={octicons.lightBulb} />
|
||||
<div>
|
||||
<strong>ProTip!</strong> You can drag & drop an existing
|
||||
repository folder here to add it to Desktop
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
import { Account } from '../../models/account'
|
||||
import { API, IAPIWorkflowJobStep } from '../../lib/api'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { RepositoryWithGitHubRepository } from '../../models/repository'
|
||||
import { CICheckRunActionsJobStepList } from '../check-runs/ci-check-run-actions-job-step-list'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
|
@ -101,7 +101,7 @@ export class PullRequestChecksFailed extends React.Component<
|
|||
|
||||
const header = (
|
||||
<div className="ci-check-run-dialog-header">
|
||||
<Octicon symbol={OcticonSymbol.xCircleFill} />
|
||||
<Octicon symbol={octicons.xCircleFill} />
|
||||
<div className="title-container">
|
||||
<div className="summary">
|
||||
{failedChecks.length} {pluralChecks} failed in your pull request
|
||||
|
|
|
@ -2,9 +2,8 @@ import * as React from 'react'
|
|||
import { Dialog, DialogContent, DialogFooter } from '../dialog'
|
||||
import { PullRequest } from '../../models/pull-request'
|
||||
import { Dispatcher } from '../dispatcher'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import { OcticonSymbolType } from '../octicons/octicons.generated'
|
||||
import { Octicon, OcticonSymbol } from '../octicons'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { RepositoryWithGitHubRepository } from '../../models/repository'
|
||||
import { SandboxedMarkdown } from '../lib/sandboxed-markdown'
|
||||
import { LinkButton } from '../lib/link-button'
|
||||
|
@ -22,7 +21,7 @@ interface IPullRequestCommentLikeProps {
|
|||
readonly pullRequest: PullRequest
|
||||
readonly eventDate: Date
|
||||
readonly eventVerb: string
|
||||
readonly eventIconSymbol: OcticonSymbolType
|
||||
readonly eventIconSymbol: OcticonSymbol
|
||||
readonly eventIconClass: string
|
||||
readonly externalURL: string
|
||||
readonly user: IAPIIdentity
|
||||
|
@ -193,8 +192,8 @@ export abstract class PullRequestCommentLike extends React.Component<IPullReques
|
|||
className={cls}
|
||||
symbol={
|
||||
pullRequest.draft
|
||||
? OcticonSymbol.gitPullRequestDraft
|
||||
: OcticonSymbol.gitPullRequest
|
||||
? octicons.gitPullRequestDraft
|
||||
: octicons.gitPullRequest
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -2,8 +2,8 @@ import {
|
|||
ValidNotificationPullRequestReview,
|
||||
ValidNotificationPullRequestReviewState,
|
||||
} from '../../lib/valid-notification-pull-request-review'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import { OcticonSymbolType } from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { OcticonSymbol } from '../octicons'
|
||||
|
||||
/** Returns the user-facing verb for a given review's state. */
|
||||
export function getVerbForPullRequestReview(
|
||||
|
@ -20,7 +20,7 @@ export function getVerbForPullRequestReview(
|
|||
}
|
||||
|
||||
type ReviewStateIcon = {
|
||||
symbol: OcticonSymbolType
|
||||
symbol: OcticonSymbol
|
||||
className: string
|
||||
}
|
||||
|
||||
|
@ -31,17 +31,17 @@ export function getPullRequestReviewStateIcon(
|
|||
switch (state) {
|
||||
case 'APPROVED':
|
||||
return {
|
||||
symbol: OcticonSymbol.check,
|
||||
symbol: octicons.check,
|
||||
className: 'pr-review-approved',
|
||||
}
|
||||
case 'CHANGES_REQUESTED':
|
||||
return {
|
||||
symbol: OcticonSymbol.fileDiff,
|
||||
symbol: octicons.fileDiff,
|
||||
className: 'pr-review-changes-requested',
|
||||
}
|
||||
case 'COMMENTED':
|
||||
return {
|
||||
symbol: OcticonSymbol.eye,
|
||||
symbol: octicons.eye,
|
||||
className: 'pr-review-commented',
|
||||
}
|
||||
}
|
||||
|
|
13
app/src/ui/octicons/diff-check.ts
Normal file
13
app/src/ui/octicons/diff-check.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { OcticonSymbolVariant } from '.'
|
||||
|
||||
/**
|
||||
* An check mark produced by Gavin that is scaled for 12x12 as opposed to the
|
||||
* regular 16x16 and is thicker for better visibility.
|
||||
*/
|
||||
export const diffCheck: OcticonSymbolVariant = {
|
||||
w: 12,
|
||||
h: 12,
|
||||
p: [
|
||||
'M10.5303 2.96967C10.8232 3.26256 10.8232 3.73744 10.5303 4.03033L5.03033 9.53033C4.73744 9.82322 4.26256 9.82322 3.96967 9.53033L1.46967 7.03033C1.17678 6.73744 1.17678 6.26256 1.46967 5.96967C1.76256 5.67678 2.23744 5.67678 2.53033 5.96967L4.5 7.93934L9.46967 2.96967C9.76256 2.67678 10.2374 2.67678 10.5303 2.96967Z',
|
||||
],
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
export { OcticonSymbolType } from './octicons.generated'
|
||||
export * from './octicons.generated'
|
||||
export { Octicon } from './octicon'
|
||||
export { iconForRepository } from './repository'
|
||||
export { iconForStatus } from './status'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react'
|
||||
import { OcticonSymbolType } from './octicons.generated'
|
||||
import { OcticonSymbol, OcticonSymbolVariant } from '.'
|
||||
import classNames from 'classnames'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { createObservableRef } from '../lib/observable-ref'
|
||||
|
@ -7,11 +7,11 @@ import { Tooltip, TooltipDirection } from '../lib/tooltip'
|
|||
|
||||
interface IOcticonProps {
|
||||
/**
|
||||
* An instance of an object conforming to the OcticonSymbol
|
||||
* type. Supports custom paths as well as those provided
|
||||
* through the static properties of the OcticonSymbol class.
|
||||
* An instance of an object conforming to the OcticonSymbol type. Supports
|
||||
* custom paths as well as those provided through the static properties of
|
||||
* the OcticonSymbol class.
|
||||
*/
|
||||
readonly symbol: OcticonSymbolType
|
||||
readonly symbol: OcticonSymbol
|
||||
|
||||
/**
|
||||
* An optional classname that will be appended to the default
|
||||
|
@ -20,11 +20,13 @@ interface IOcticonProps {
|
|||
readonly className?: string
|
||||
|
||||
/**
|
||||
* An optional string to use as a tooltip for the icon
|
||||
* An optional string to use as a tooltip and aria-label for the icon
|
||||
*/
|
||||
readonly title?: JSX.Element | string
|
||||
readonly title?: string
|
||||
|
||||
readonly tooltipDirection?: TooltipDirection
|
||||
|
||||
readonly height?: number
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -34,14 +36,40 @@ interface IOcticonProps {
|
|||
* which is why the width and height properties specify the maximum and
|
||||
* not the minimum size.
|
||||
*
|
||||
* Usage: `<Octicon symbol={OcticonSymbol.mark_github} />`
|
||||
* Usage: `<Octicon symbol={octicons.markGithub} />`
|
||||
*/
|
||||
export class Octicon extends React.Component<IOcticonProps, {}> {
|
||||
private svgRef = createObservableRef<SVGSVGElement>()
|
||||
|
||||
public render() {
|
||||
const { symbol, title, tooltipDirection } = this.props
|
||||
const viewBox = `0 0 ${symbol.w} ${symbol.h}`
|
||||
const { symbol } = this.props
|
||||
|
||||
if (this.isSingleVariant(symbol)) {
|
||||
return this.renderIcon(symbol.p, symbol.h, symbol.w)
|
||||
} else {
|
||||
const height = this.props.height ?? 16
|
||||
const naturalHeight = this.closestNaturalHeight(
|
||||
Object.keys(symbol).map(h => parseInt(h, 10)) as Array<number>,
|
||||
height
|
||||
)
|
||||
|
||||
const scaledSymbol = symbol[naturalHeight]
|
||||
|
||||
if (scaledSymbol === undefined) {
|
||||
// Should never happen, but if it does the app should still be usable
|
||||
return null
|
||||
}
|
||||
|
||||
const naturalWidth = scaledSymbol.w
|
||||
const width = height * (naturalWidth / naturalHeight)
|
||||
|
||||
return this.renderIcon(scaledSymbol.p, height, width)
|
||||
}
|
||||
}
|
||||
|
||||
private renderIcon(paths: string[], height: number, width: number) {
|
||||
const { title, tooltipDirection } = this.props
|
||||
const viewBox = `0 0 ${width} ${height}`
|
||||
const className = classNames('octicon', this.props.className)
|
||||
|
||||
// Hide the octicon from screen readers when it's only being used
|
||||
|
@ -56,21 +84,59 @@ export class Octicon extends React.Component<IOcticonProps, {}> {
|
|||
return (
|
||||
<svg
|
||||
aria-hidden={ariaHidden}
|
||||
aria-label={title}
|
||||
className={className}
|
||||
version="1.1"
|
||||
viewBox={viewBox}
|
||||
ref={this.svgRef}
|
||||
tabIndex={-1}
|
||||
height={height}
|
||||
width={width}
|
||||
>
|
||||
{title !== undefined && (
|
||||
<Tooltip target={this.svgRef} direction={direction}>
|
||||
{title}
|
||||
</Tooltip>
|
||||
)}
|
||||
<path fillRule={symbol.fr} d={symbol.d} />
|
||||
|
||||
{paths.map((d, i) => (
|
||||
<path key={i} d={d} />
|
||||
))}
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given symbol is a single variant or a set of variants.
|
||||
*
|
||||
* @param symbol The symbol to check.
|
||||
* @returns True if the symbol is a single variant, false if it's a set.
|
||||
*/
|
||||
private isSingleVariant(
|
||||
symbol: OcticonSymbol
|
||||
): symbol is OcticonSymbolVariant {
|
||||
return (
|
||||
symbol instanceof Object &&
|
||||
symbol.hasOwnProperty('p') &&
|
||||
symbol.hasOwnProperty('h') &&
|
||||
symbol.hasOwnProperty('w')
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the closest natural height to the given height. Falls back to the
|
||||
* first height in the list if none are larger or equal than the given height.
|
||||
*
|
||||
* @param naturalHeights The list of natural heights to choose from.
|
||||
* @param height The height to find the closest natural height to.
|
||||
* @returns The closest natural height to the given height.
|
||||
*/
|
||||
private closestNaturalHeight(naturalHeights: Array<number>, height: number) {
|
||||
return naturalHeights.reduce(
|
||||
(acc, naturalHeight) => (naturalHeight <= height ? naturalHeight : acc),
|
||||
naturalHeights[0]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,7 +147,7 @@ export class Octicon extends React.Component<IOcticonProps, {}> {
|
|||
* @param id Optional identifier to set to the wrapper element.
|
||||
*/
|
||||
export function createOcticonElement(
|
||||
symbol: OcticonSymbolType,
|
||||
symbol: OcticonSymbol,
|
||||
className?: string,
|
||||
id?: string
|
||||
) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Repository } from '../../models/repository'
|
||||
import { CloningRepository } from '../../models/cloning-repository'
|
||||
|
||||
|
@ -7,24 +7,24 @@ import { CloningRepository } from '../../models/cloning-repository'
|
|||
*/
|
||||
export function iconForRepository(repository: Repository | CloningRepository) {
|
||||
if (repository instanceof CloningRepository) {
|
||||
return OcticonSymbol.desktopDownload
|
||||
return octicons.desktopDownload
|
||||
}
|
||||
|
||||
if (repository.missing) {
|
||||
return OcticonSymbol.alert
|
||||
return octicons.alert
|
||||
}
|
||||
|
||||
const gitHubRepo = repository.gitHubRepository
|
||||
if (!gitHubRepo) {
|
||||
return OcticonSymbol.deviceDesktop
|
||||
return octicons.deviceDesktop
|
||||
}
|
||||
|
||||
if (gitHubRepo.isPrivate) {
|
||||
return OcticonSymbol.lock
|
||||
return octicons.lock
|
||||
}
|
||||
if (gitHubRepo.fork) {
|
||||
return OcticonSymbol.repoForked
|
||||
return octicons.repoForked
|
||||
}
|
||||
|
||||
return OcticonSymbol.repo
|
||||
return octicons.repo
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ import {
|
|||
AppFileStatus,
|
||||
isConflictWithMarkers,
|
||||
} from '../../models/status'
|
||||
import * as OcticonSymbol from './octicons.generated'
|
||||
import { OcticonSymbolType } from '../octicons'
|
||||
import * as octicons from './octicons.generated'
|
||||
import { OcticonSymbol } from '../octicons'
|
||||
import { assertNever } from '../../lib/fatal-error'
|
||||
|
||||
/**
|
||||
|
@ -13,25 +13,25 @@ import { assertNever } from '../../lib/fatal-error'
|
|||
*
|
||||
* Used in file lists.
|
||||
*/
|
||||
export function iconForStatus(status: AppFileStatus): OcticonSymbolType {
|
||||
export function iconForStatus(status: AppFileStatus): OcticonSymbol {
|
||||
switch (status.kind) {
|
||||
case AppFileStatusKind.New:
|
||||
case AppFileStatusKind.Untracked:
|
||||
return OcticonSymbol.diffAdded
|
||||
return octicons.diffAdded
|
||||
case AppFileStatusKind.Modified:
|
||||
return OcticonSymbol.diffModified
|
||||
return octicons.diffModified
|
||||
case AppFileStatusKind.Deleted:
|
||||
return OcticonSymbol.diffRemoved
|
||||
return octicons.diffRemoved
|
||||
case AppFileStatusKind.Renamed:
|
||||
return OcticonSymbol.diffRenamed
|
||||
return octicons.diffRenamed
|
||||
case AppFileStatusKind.Conflicted:
|
||||
if (isConflictWithMarkers(status)) {
|
||||
const conflictsCount = status.conflictMarkerCount
|
||||
return conflictsCount > 0 ? OcticonSymbol.alert : OcticonSymbol.check
|
||||
return conflictsCount > 0 ? octicons.alert : octicons.check
|
||||
}
|
||||
return OcticonSymbol.alert
|
||||
return octicons.alert
|
||||
case AppFileStatusKind.Copied:
|
||||
return OcticonSymbol.diffAdded
|
||||
return octicons.diffAdded
|
||||
default:
|
||||
return assertNever(status, `Unknown file status ${status}`)
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { OcticonSymbolType } from './octicons.generated'
|
||||
import { OcticonSymbolVariant } from '.'
|
||||
|
||||
/**
|
||||
* An horizontally flipped (i.e `scaleX(-1)`) but otherwise unmodified version of
|
||||
* the `sync` octicon.
|
||||
*/
|
||||
export const syncClockwise: OcticonSymbolType = {
|
||||
export const syncClockwise: OcticonSymbolVariant = {
|
||||
w: 16,
|
||||
h: 16,
|
||||
d:
|
||||
p: [
|
||||
'M8 2.5c1.645 0 3.123.722 4.131 1.869l-1.204 1.204a.25.25 0 0 0 .177.427h3.646a.25.25 ' +
|
||||
'0 0 0 .25-.25V2.104a.25.25 0 0 0-.427-.177l-1.38 1.38A7.001 7.001 0 0 0 1.05 7.16a.75.75 ' +
|
||||
'0 1 0 1.49.178A5.501 5.501 0 0 1 8 2.5zm6.294 5.505a.75.75 0 0 0-.833.656 5.501 5.501 ' +
|
||||
'0 0 1-9.592 2.97l1.204-1.204A.25.25 0 0 0 4.896 10H1.25a.25.25 0 0 0-.25.25v3.646c0 ' +
|
||||
'.223.27.335.427.177l1.38-1.38A7.001 7.001 0 0 0 14.95 8.84a.75.75 0 0 0-.657-.834z',
|
||||
fr: 'evenodd',
|
||||
'0 0 0 .25-.25V2.104a.25.25 0 0 0-.427-.177l-1.38 1.38A7.001 7.001 0 0 0 1.05 7.16a.75.75 ' +
|
||||
'0 1 0 1.49.178A5.501 5.501 0 0 1 8 2.5zm6.294 5.505a.75.75 0 0 0-.833.656 5.501 5.501 ' +
|
||||
'0 0 1-9.592 2.97l1.204-1.204A.25.25 0 0 0 4.896 10H1.25a.25.25 0 0 0-.25.25v3.646c0 ' +
|
||||
'.223.27.335.427.177l1.38-1.38A7.001 7.001 0 0 0 14.95 8.84a.75.75 0 0 0-.657-.834z',
|
||||
],
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { DialogFooter, OkCancelButtonGroup, Dialog } from '../dialog'
|
|||
import { Dispatcher } from '../dispatcher'
|
||||
import { Ref } from '../lib/ref'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { OpenPullRequestDialogHeader } from './open-pull-request-header'
|
||||
import { PullRequestFilesChanged } from './pull-request-files-changed'
|
||||
import { PullRequestMergeStatus } from './pull-request-merge-status'
|
||||
|
@ -207,7 +207,7 @@ export class OpenPullRequestDialog extends React.Component<IOpenPullRequestDialo
|
|||
return (
|
||||
<div className="open-pull-request-message">
|
||||
<div>
|
||||
<Octicon symbol={OcticonSymbol.gitPullRequest} />
|
||||
<Octicon symbol={octicons.gitPullRequest} />
|
||||
<h3>There are no changes.</h3>
|
||||
{message}
|
||||
</div>
|
||||
|
@ -225,7 +225,7 @@ export class OpenPullRequestDialog extends React.Component<IOpenPullRequestDialo
|
|||
return (
|
||||
<div className="open-pull-request-message">
|
||||
<div>
|
||||
<Octicon symbol={OcticonSymbol.gitPullRequest} />
|
||||
<Octicon symbol={octicons.gitPullRequest} />
|
||||
<h3>Could not find a default branch to compare against.</h3>
|
||||
Select a base branch above.
|
||||
</div>
|
||||
|
@ -249,7 +249,7 @@ export class OpenPullRequestDialog extends React.Component<IOpenPullRequestDialo
|
|||
const okButton = (
|
||||
<>
|
||||
{currentBranchHasPullRequest && (
|
||||
<Octicon symbol={OcticonSymbol.linkExternal} />
|
||||
<Octicon symbol={octicons.linkExternal} />
|
||||
)}
|
||||
{__DARWIN__
|
||||
? `${viewCreate} Pull Request`
|
||||
|
|
|
@ -301,6 +301,7 @@ export class PullRequestFilesChanged extends React.Component<
|
|||
readOnly={true}
|
||||
hideWhitespaceInDiff={hideWhitespaceInDiff}
|
||||
showSideBySideDiff={showSideBySideDiff}
|
||||
showDiffCheckMarks={false}
|
||||
onOpenBinaryFile={this.onOpenBinaryFile}
|
||||
onChangeImageDiffType={this.onChangeImageDiffType}
|
||||
onHideWhitespaceInDiffChanged={this.onHideWhitespaceInDiffChanged}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { assertNever } from '../../lib/fatal-error'
|
|||
import { ComputedAction } from '../../models/computed-action'
|
||||
import { MergeTreeResult } from '../../models/merge'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
|
||||
interface IPullRequestMergeStatusProps {
|
||||
/** The result of merging the pull request branch into the base branch */
|
||||
|
@ -39,7 +39,7 @@ export class PullRequestMergeStatus extends React.Component<IPullRequestMergeSta
|
|||
return (
|
||||
<span className="pr-merge-status-clean">
|
||||
<strong>
|
||||
<Octicon symbol={OcticonSymbol.check} /> Able to merge.
|
||||
<Octicon symbol={octicons.check} /> Able to merge.
|
||||
</strong>{' '}
|
||||
These branches can be automatically merged.
|
||||
</span>
|
||||
|
@ -48,7 +48,7 @@ export class PullRequestMergeStatus extends React.Component<IPullRequestMergeSta
|
|||
return (
|
||||
<span className="pr-merge-status-conflicts">
|
||||
<strong>
|
||||
<Octicon symbol={OcticonSymbol.x} /> Can't automatically merge.
|
||||
<Octicon symbol={octicons.x} /> Can't automatically merge.
|
||||
</strong>{' '}
|
||||
Don’t worry, you can still create the pull request.
|
||||
</span>
|
||||
|
|
|
@ -29,7 +29,7 @@ import {
|
|||
defaultUncommittedChangesStrategy,
|
||||
} from '../../models/uncommitted-changes-strategy'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import {
|
||||
isConfigFileLockError,
|
||||
parseConfigLockFilePathFromError,
|
||||
|
@ -242,35 +242,35 @@ export class Preferences extends React.Component<
|
|||
type={TabBarType.Vertical}
|
||||
>
|
||||
<span>
|
||||
<Octicon className="icon" symbol={OcticonSymbol.home} />
|
||||
<Octicon className="icon" symbol={octicons.home} />
|
||||
Accounts
|
||||
</span>
|
||||
<span>
|
||||
<Octicon className="icon" symbol={OcticonSymbol.person} />
|
||||
<Octicon className="icon" symbol={octicons.person} />
|
||||
Integrations
|
||||
</span>
|
||||
<span>
|
||||
<Octicon className="icon" symbol={OcticonSymbol.gitCommit} />
|
||||
<Octicon className="icon" symbol={octicons.gitCommit} />
|
||||
Git
|
||||
</span>
|
||||
<span>
|
||||
<Octicon className="icon" symbol={OcticonSymbol.paintbrush} />
|
||||
<Octicon className="icon" symbol={octicons.paintbrush} />
|
||||
Appearance
|
||||
</span>
|
||||
<span>
|
||||
<Octicon className="icon" symbol={OcticonSymbol.accessibility} />
|
||||
<Octicon className="icon" symbol={octicons.accessibility} />
|
||||
Accessibility
|
||||
</span>
|
||||
<span>
|
||||
<Octicon className="icon" symbol={OcticonSymbol.bell} />
|
||||
<Octicon className="icon" symbol={octicons.bell} />
|
||||
Notifications
|
||||
</span>
|
||||
<span>
|
||||
<Octicon className="icon" symbol={OcticonSymbol.question} />
|
||||
<Octicon className="icon" symbol={octicons.question} />
|
||||
Prompts
|
||||
</span>
|
||||
<span>
|
||||
<Octicon className="icon" symbol={OcticonSymbol.gear} />
|
||||
<Octicon className="icon" symbol={octicons.gear} />
|
||||
Advanced
|
||||
</span>
|
||||
</TabBar>
|
||||
|
|
|
@ -9,7 +9,7 @@ import { merge } from '../../lib/merge'
|
|||
import { caseInsensitiveCompare } from '../../lib/compare'
|
||||
import { sanitizedRepositoryName } from '../add-repository/sanitized-repository-name'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { RepositoryPublicationSettings } from '../../models/publish-settings'
|
||||
|
||||
interface IPublishRepositoryProps {
|
||||
|
@ -184,7 +184,7 @@ export class PublishRepository extends React.Component<
|
|||
|
||||
return (
|
||||
<Row className="warning-helper-text">
|
||||
<Octicon symbol={OcticonSymbol.alert} />
|
||||
<Octicon symbol={octicons.alert} />
|
||||
Will be created as {sanitizedName}
|
||||
</Row>
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Dispatcher } from './dispatcher'
|
|||
import { Button } from './lib/button'
|
||||
import { SandboxedMarkdown } from './lib/sandboxed-markdown'
|
||||
import { Octicon } from './octicons'
|
||||
import * as OcticonSymbol from './octicons/octicons.generated'
|
||||
import * as octicons from './octicons/octicons.generated'
|
||||
import classNames from 'classnames'
|
||||
|
||||
/**
|
||||
|
@ -160,11 +160,11 @@ export class PullRequestQuickView extends React.Component<
|
|||
private renderHeader = (): JSX.Element => {
|
||||
return (
|
||||
<header className="header">
|
||||
<Octicon symbol={OcticonSymbol.listUnordered} />
|
||||
<Octicon symbol={octicons.listUnordered} />
|
||||
<div className="action-needed">Review requested</div>
|
||||
<Button className="button-with-icon" onClick={this.onViewOnGitHub}>
|
||||
View on GitHub
|
||||
<Octicon symbol={OcticonSymbol.linkExternal} />
|
||||
<Octicon symbol={octicons.linkExternal} />
|
||||
</Button>
|
||||
</header>
|
||||
)
|
||||
|
@ -176,9 +176,7 @@ export class PullRequestQuickView extends React.Component<
|
|||
<Octicon
|
||||
className="icon"
|
||||
symbol={
|
||||
isDraft
|
||||
? OcticonSymbol.gitPullRequestDraft
|
||||
: OcticonSymbol.gitPullRequest
|
||||
isDraft ? octicons.gitPullRequestDraft : octicons.gitPullRequest
|
||||
}
|
||||
/>
|
||||
<span className="state">{isDraft ? 'Draft' : 'Open'}</span>
|
||||
|
|
|
@ -15,7 +15,7 @@ import { ILocalRepositoryState, Repository } from '../../models/repository'
|
|||
import { Dispatcher } from '../dispatcher'
|
||||
import { Button } from '../lib/button'
|
||||
import { Octicon } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { showContextualMenu } from '../../lib/menu-item'
|
||||
import { IMenuItem } from '../../lib/menu-item'
|
||||
import { PopupType } from '../../models/popup'
|
||||
|
@ -282,7 +282,7 @@ export class RepositoriesList extends React.Component<
|
|||
ariaExpanded={this.state.newRepositoryMenuExpanded}
|
||||
>
|
||||
Add
|
||||
<Octicon symbol={OcticonSymbol.triangleDown} />
|
||||
<Octicon symbol={octicons.triangleDown} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react'
|
|||
|
||||
import { Repository } from '../../models/repository'
|
||||
import { Octicon, iconForRepository } from '../octicons'
|
||||
import * as OcticonSymbol from '../octicons/octicons.generated'
|
||||
import * as octicons from '../octicons/octicons.generated'
|
||||
import { Repositoryish } from './group-repositories'
|
||||
import { HighlightText } from '../lib/highlight-text'
|
||||
import { IMatches } from '../../lib/fuzzy-find'
|
||||
|
@ -141,8 +141,8 @@ const renderAheadBehindIndicator = (aheadBehind: IAheadBehind) => {
|
|||
tagName="div"
|
||||
tooltip={aheadBehindTooltip}
|
||||
>
|
||||
{ahead > 0 && <Octicon symbol={OcticonSymbol.arrowUp} />}
|
||||
{behind > 0 && <Octicon symbol={OcticonSymbol.arrowDown} />}
|
||||
{ahead > 0 && <Octicon symbol={octicons.arrowUp} />}
|
||||
{behind > 0 && <Octicon symbol={octicons.arrowDown} />}
|
||||
</TooltippedContent>
|
||||
)
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ const renderChangesIndicator = () => {
|
|||
className="change-indicator-wrapper"
|
||||
tooltip="There are uncommitted changes in this repository"
|
||||
>
|
||||
<Octicon symbol={OcticonSymbol.dotFill} />
|
||||
<Octicon symbol={octicons.dotFill} />
|
||||
</TooltippedContent>
|
||||
)
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue