Skip to content

Commit

Permalink
Merge pull request #2383 from desktop/kactus-image-diffs
Browse files Browse the repository at this point in the history
Kactus' image diffs
  • Loading branch information
shiftkey committed Aug 17, 2017
2 parents 618f678 + 62552fc commit 82b1d23
Show file tree
Hide file tree
Showing 28 changed files with 940 additions and 112 deletions.
18 changes: 18 additions & 0 deletions app/src/lib/app-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ export enum SelectionType {
MissingRepository,
}

/** The image diff type. */
export enum ImageDiffType {
/** Show the old and new images side by side. */
TwoUp,

/** Swipe between the old and new image. */
Swipe,

/** Onion skin. */
OnionSkin,

/** Highlight differences. */
Difference,
}

export type PossibleSelections =
| {
type: SelectionType.Repository
Expand Down Expand Up @@ -138,6 +153,9 @@ export interface IAppState {

/** The external editor to use when opening repositories */
readonly selectedExternalEditor: ExternalEditor

/** What type of visual diff mode we should use to compare images */
readonly imageDiffType: ImageDiffType
}

export enum PopupType {
Expand Down
20 changes: 20 additions & 0 deletions app/src/lib/dispatcher/app-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
SelectionType,
ICheckoutProgress,
Progress,
ImageDiffType,
} from '../app-state'
import { Account } from '../../models/account'
import { Repository } from '../../models/repository'
Expand Down Expand Up @@ -105,6 +106,9 @@ const confirmRepoRemovalKey: string = 'confirmRepoRemoval'
const externalEditorDefault = ExternalEditor.Atom
const externalEditorKey: string = 'externalEditor'

const imageDiffTypeDefault = ImageDiffType.TwoUp
const imageDiffTypeKey = 'image-diff-type'

export class AppStore {
private emitter = new Emitter()

Expand Down Expand Up @@ -172,6 +176,7 @@ export class AppStore {
private windowZoomFactor: number = 1
private isUpdateAvailableBannerVisible: boolean = false
private confirmRepoRemoval: boolean = confirmRepoRemovalDefault
private imageDiffType: ImageDiffType = imageDiffTypeDefault

private selectedExternalEditor: ExternalEditor = externalEditorDefault

Expand Down Expand Up @@ -478,6 +483,7 @@ export class AppStore {
isUpdateAvailableBannerVisible: this.isUpdateAvailableBannerVisible,
confirmRepoRemoval: this.confirmRepoRemoval,
selectedExternalEditor: this.selectedExternalEditor,
imageDiffType: this.imageDiffType,
}
}

Expand Down Expand Up @@ -866,6 +872,12 @@ export class AppStore {

updateExternalEditorMenuItem(this.selectedExternalEditor)

const imageDiffTypeValue = localStorage.getItem(imageDiffTypeKey)
this.imageDiffType =
imageDiffTypeValue === null
? imageDiffTypeDefault
: parseInt(imageDiffTypeValue)

this.emitUpdateNow()

this.accountsStore.refresh()
Expand Down Expand Up @@ -2243,6 +2255,14 @@ export class AppStore {
return Promise.resolve()
}

public _changeImageDiffType(type: ImageDiffType): Promise<void> {
this.imageDiffType = type
localStorage.setItem(imageDiffTypeKey, JSON.stringify(this.imageDiffType))
this.emitUpdate()

return Promise.resolve()
}

public _setUpdateBannerVisibility(visibility: boolean) {
this.isUpdateAvailableBannerVisible = visibility

Expand Down
6 changes: 6 additions & 0 deletions app/src/lib/dispatcher/dispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
PopupType,
Foldout,
FoldoutType,
ImageDiffType,
} from '../app-state'
import { AppStore } from './app-store'
import { CloningRepository } from './cloning-repositories-store'
Expand Down Expand Up @@ -952,4 +953,9 @@ export class Dispatcher {
return assertNever(retryAction, `Unknown retry action: ${retryAction}`)
}
}

/** Change the selected image diff type. */
public changeImageDiffType(type: ImageDiffType): Promise<void> {
return this.appStore._changeImageDiffType(type)
}
}
13 changes: 13 additions & 0 deletions app/src/lib/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,16 @@ declare namespace Electron {
): AppleActionOnDoubleClickPref
}
}

// https://wicg.github.io/ResizeObserver/#resizeobserverentry
interface IResizeObserverEntry {
readonly target: HTMLElement
readonly contentRect: ClientRect
}

declare class ResizeObserver {
public constructor(cb: (entries: ReadonlyArray<IResizeObserverEntry>) => void)

public disconnect(): void
public observe(e: HTMLElement): void
}
1 change: 1 addition & 0 deletions app/src/ui/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1444,6 +1444,7 @@ export class App extends React.Component<IAppProps, IAppState> {
issuesStore={this.props.appStore.issuesStore}
gitHubUserStore={this.props.appStore.gitHubUserStore}
onViewCommitOnGitHub={this.onViewCommitOnGitHub}
imageDiffType={this.state.imageDiffType}
/>
)
} else if (selectedState.type === SelectionType.CloningRepository) {
Expand Down
3 changes: 3 additions & 0 deletions app/src/ui/changes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react'
import { Diff } from '../diff'
import { ChangedFileDetails } from './changed-file-details'
import { ImageDiffType } from '../../lib/app-state'
import { DiffSelection, IDiff } from '../../models/diff'
import { WorkingDirectoryFileChange } from '../../models/status'
import { Repository } from '../../models/repository'
Expand All @@ -15,6 +16,7 @@ interface IChangesProps {
readonly file: WorkingDirectoryFileChange
readonly diff: IDiff
readonly dispatcher: Dispatcher
readonly imageDiffType: ImageDiffType
}

export class Changes extends React.Component<IChangesProps, {}> {
Expand Down Expand Up @@ -42,6 +44,7 @@ export class Changes extends React.Component<IChangesProps, {}> {
<div className="diff-wrapper">
<Diff
repository={this.props.repository}
imageDiffType={this.props.imageDiffType}
file={file}
readOnly={false}
onIncludeChanged={this.onDiffLineIncludeChanged}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react'

import { Image } from '../../models/diff'
import { renderImage } from './render-image'
import { Image } from '../../../models/diff'
import { DiffImage } from './diff-image'

interface IDeletedImageDiffProps {
readonly previous: Image
Expand All @@ -15,8 +15,10 @@ export class DeletedImageDiff extends React.Component<
public render() {
return (
<div className="panel image" id="diff">
<div className="image-header">this image will be removed</div>
{renderImage(this.props.previous)}
<div className="image-diff-previous">
<div className="image-diff-header">Deleted</div>
<DiffImage image={this.props.previous} />
</div>
</div>
)
}
Expand Down
28 changes: 28 additions & 0 deletions app/src/ui/diff/image-diffs/diff-image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from 'react'

import { Image } from '../../../models/diff'

interface IImageProps {
readonly image: Image

readonly style?: React.CSSProperties

readonly onElementLoad?: (img: HTMLImageElement) => void
}

export class DiffImage extends React.Component<IImageProps, {}> {
public render() {
const image = this.props.image
const imageSource = `data:${image.mediaType};base64,${image.contents}`

return (
<img src={imageSource} style={this.props.style} onLoad={this.onLoad} />
)
}

private onLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {
if (this.props.onElementLoad) {
this.props.onElementLoad(e.currentTarget)
}
}
}
47 changes: 47 additions & 0 deletions app/src/ui/diff/image-diffs/difference-blend.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as React from 'react'
import { DiffImage } from './diff-image'
import { ICommonImageDiffProperties } from './modified-image-diff'

export class DifferenceBlend extends React.Component<
ICommonImageDiffProperties,
{}
> {
public render() {
const style: React.CSSProperties = {
height: this.props.maxSize.height,
width: this.props.maxSize.width,
}

const maxSize: React.CSSProperties = {
maxHeight: this.props.maxSize.height,
maxWidth: this.props.maxSize.width,
}

return (
<div className="image-diff-difference" ref={this.props.onContainerRef}>
<div className="sizing-container">
<div className="image-container" style={style}>
<div className="image-diff-previous">
<DiffImage
image={this.props.previous}
onElementLoad={this.props.onPreviousImageLoad}
style={maxSize}
/>
</div>

<div className="image-diff-current">
<DiffImage
image={this.props.current}
onElementLoad={this.props.onCurrentImageLoad}
style={{
...maxSize,
mixBlendMode: 'difference',
}}
/>
</div>
</div>
</div>
</div>
)
}
}
3 changes: 3 additions & 0 deletions app/src/ui/diff/image-diffs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { ModifiedImageDiff } from './modified-image-diff'
export { NewImageDiff } from './new-image-diff'
export { DeletedImageDiff } from './deleted-image-diff'

0 comments on commit 82b1d23

Please sign in to comment.