Skip to content

Commit

Permalink
image home removeFiles
Browse files Browse the repository at this point in the history
  • Loading branch information
cancerberoSgx committed Nov 15, 2018
1 parent d37ac18 commit 63d88b4
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 114 deletions.
256 changes: 143 additions & 113 deletions samples/interactive-execute-context/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { sampleCommandTemplates } from 'imagemagick-browser';
import pmap from 'p-map';
import * as React from 'react';
import { style } from 'typestyle';
import { arrayToCli, buildImageSrc, buildInputFile, cliToArray, ExecutionContext, extractInfo, getBuiltInImages, getInputFilesFromHtmlInputElement, MagickFile, MagickInputFile } from 'wasm-imagemagick';
import { sampleCommandTemplates } from 'imagemagick-browser'
import pmap from 'p-map'
import * as React from 'react'
import { style } from 'typestyle'
import {
arrayToCli, buildImageSrc, buildInputFile, cliToArray, ExecutionContext, extractInfo, getBuiltInImages,
getInputFilesFromHtmlInputElement, MagickFile, MagickInputFile,
} from 'wasm-imagemagick'

export interface AppProps {
context: ExecutionContext
Expand All @@ -27,8 +30,8 @@ export interface AppState {
export class App extends React.Component<AppProps, AppState> {

state: AppState = {
commandString: '',
commandArray: '[]',
commandString: 'identify rose:',
commandArray: '["identify", "rose:"]',
jsonError: '',
files: [],
imgSrcs: [],
Expand All @@ -51,20 +54,20 @@ export class App extends React.Component<AppProps, AppState> {
width: '400px',
height: '160px',
}),
stdioTextarea: style({
width: '100%',
height: '160px',
}),
imagesList: style({
height: '500px',
overflowY: 'scroll',
textAlign: 'left',
}),
executionBad: style({
backgroundColor: '#ff8888',
}),
executionGood: style({
backgroundColor: '#88ff88',
}),
h5: style({
margin: 0
}),
}

render(): React.ReactNode {
Expand All @@ -78,132 +81,159 @@ export class App extends React.Component<AppProps, AppState> {

<div><button onClick={this.addBuiltInImages.bind(this)} disabled={this.state.builtInImagesAdded}>Add built-in images</button></div>


<ul className={(this.state.showImagesAndInfo || '') && this.styles.imagesList}>{this.state.files.map((f, i) =>
<li>
<table>
<thead><tr><th>Name</th><th>Image</th><th>Info</th></tr></thead>
<tbody>
<tr>
<td>{f.name}</td>
<td>{(this.state.showImagesAndInfo || '') &&
<img alt={f.name} src={this.state.imgSrcs[i]}></img>}</td>
<td>{(this.state.showImagesAndInfo || '') &&
<textarea className={this.styles.infoTextarea} value={JSON.stringify(this.state.filesInfo[i][0].image, null, 2)}></textarea>}</td>
</tr>
</tbody>
</table>
</li>)}
</ul>
<div><button onClick={this.removeAllImages.bind(this)} disabled={this.state.files.length === 0}>Remove all images</button></div>

<table className={(this.state.showImagesAndInfo || '') && this.styles.imagesList}>
{
(this.state.showImagesAndInfo || '') && <thead><tr>
<th>Name</th>
<th>Image</th>
<th>Info</th>
</tr></thead>
}
<tbody>
{this.state.files.map((f, i) =>
// <li>
// <table>

<tr>
<td>
<button data-image={f.name} onClick={this.removeImage.bind(this)}>remove</button>
{f.name}
</td>
<td>{(this.state.showImagesAndInfo || '') &&
<img alt={f.name} src={this.state.imgSrcs[i]}></img>}</td>
<td>{(this.state.showImagesAndInfo || '') &&
<textarea className={this.styles.infoTextarea} value={JSON.stringify(this.state.filesInfo[i][0].image, null, 2)}></textarea>}</td>
</tr>
)}
</tbody>
</table>
</div>
<div>
<h4>Command</h4>
<p>Write a command using one supported syntax type:</p>
<div>Command (String syntax):
<textarea className={this.styles.textarea} onChange={this.commandStringChange.bind(this)} value={this.state.commandString}></textarea>
</div>
<div>Command (Array syntax):
<textarea className={this.styles.textarea} onChange={this.commandArrayChange.bind(this)} value={this.state.commandArray}></textarea>
{(this.state.jsonError || '') && <div>Execution error: {this.state.jsonError} <br />See browser console for more information.</div>}
</div>
<div>
Or select one example
<select onChange={this.selectExampleChange.bind(this)}>
{sampleCommandTemplates.map(t =>
<option>{t.name}</option>)}
</select>
</div>
</div>
<div>
<button onClick={this.execute.bind(this)}>Execute</button>
</div>

<div>Command (String syntax):
<textarea className={this.styles.textarea} onChange={this.commandStringChange.bind(this)} value={this.state.commandString}></textarea>
</div>
<div>Command (Array syntax):
<textarea className={this.styles.textarea} onChange={this.commandArrayChange.bind(this)} value={this.state.commandArray}></textarea>
{(this.state.jsonError || '') && <div>Execution error: {this.state.jsonError} <br />See browser console for more information.</div>}
</div>
<div>
Or select one example
<select onChange={this.selectExample.bind(this)}>
{sampleCommandTemplates.map(t =>
<option>{t.name}</option>)}
</select>

</div>
<div>
<button onClick={this.execute.bind(this)}>Execute</button>
</div>
{(this.state.outputFiles.length || '') &&
<div>
<ul>{this.state.outputFiles.map((f, i) =>
<p>Output Files (#{this.state.outputFiles.length}) </p>
{(this.state.outputFiles.length || '') && <ul>{this.state.outputFiles.map((f, i) =>
<li><div>{f.name}</div>
<img src={this.state.outputFileSrcs[i]}></img>
</li>,
)}
</ul>
</div>}
<h5><span className={this.state.exitCode ? this.styles.executionBad : this.styles.executionGood}>Exit code: {this.state.exitCode + ''}</span></h5>
<h5>stdout:</h5>
<textarea className={this.styles.stdioTextarea} value={this.state.stdout}></textarea>
<h5>stderr:</h5>
<textarea className={this.styles.stdioTextarea} value={this.state.stderr}></textarea>
</div>)
}

</ul>}
</div>
<h5 className={this.styles.h5}><span className={this.state.exitCode ? this.styles.executionBad : this.styles.executionGood}>Exit code: {this.state.exitCode + ''}</span></h5>
<h5 className={this.styles.h5}>stdout:</h5>
<textarea className={this.styles.textarea} value={this.state.stdout}></textarea>
<h5 className={this.styles.h5}>stderr:</h5>
<textarea className={this.styles.textarea} value={this.state.stderr}></textarea>
</div>)
}

private defaultImage = 'fn.png'
async componentDidMount() {
if (!this.state.files.find(f => f.name === this.defaultImage)) {
await this.addInputFiles([await buildInputFile(this.defaultImage)])
}
}

private selectExampleCounter = 0
private defaultImage = 'fn.png'
protected async selectExample(e: React.ChangeEvent<HTMLSelectElement>) {
const template = sampleCommandTemplates[e.target.selectedIndex]
const img = this.state.files.find(i => i.name === this.defaultImage)
const info = await extractInfo(img)
const context = { ...template.defaultTemplateContext, imageWidth: info[0].image.geometry.width, imageHeight: info[0].image.geometry.height }
const command = template.template(context)[0].map(s => s === '$INPUT' ? this.defaultImage : s === '$OUTPUT' ? `output_${this.selectExampleCounter++}.gif` : s)
this.setState({ ...this.state, commandArray: JSON.stringify(command), commandString: arrayToCli(command) })
}

await this.addInputFiles([await buildInputFile(this.defaultImage)])
}
}

removeImage(e: React.MouseEvent<HTMLButtonElement>) {
const name = e.currentTarget.getAttribute('data-image')
this.props.context.removeFiles([name])
this.state.files = this.state.files.filter(f => f.name !== name)
this.setState({...this.state, files: this.state.files.filter(f => f.name !== name) })
}

async removeAllImages(e: React.MouseEvent<HTMLButtonElement>) {
const all = await this.props.context.getAllFiles()
this.props.context.removeFiles(all.map(f => f.name))
this.state.builtInImagesAdded = false
this.setState({...this.state, files: [] })
}

protected commandStringChange(e: React.ChangeEvent<HTMLTextAreaElement>) {
const commandArray = JSON.stringify(cliToArray(e.target.value)) // TODO: validate
this.setState({ ...this.state, commandString: e.target.value, commandArray })
}

this.setState({...this.state, commandString: e.target.value, commandArray })
}
protected async addBuiltInImages() {
if (!this.state.builtInImagesAdded) {
const builtIn = await getBuiltInImages()
await this.addInputFiles(builtIn)
this.setState({ ...this.state, builtInImagesAdded: true })
}
}

await this.addInputFiles(builtIn)
this.setState({...this.state, builtInImagesAdded: true })
}
}
protected async execute() {
const result = await this.props.context.execute(this.state.commandString)
this.state.outputFiles = result.outputFiles
this.state.stderr = result.stderr.join('\n')
this.state.stdout = result.stdout.join('\n')
this.state.exitCode = result.exitCode
await this.updateImages()
}

this.state.outputFiles = result.outputFiles
this.state.stderr = result.stderr.join('\n')
this.state.stdout = result.stdout.join('\n')
this.state.exitCode = result.exitCode
await this.updateImages()
}
protected commandArrayChange(e: React.ChangeEvent<HTMLTextAreaElement>) {
const commandArray = e.target.value
let jsonError = ''
let commandString = this.state.commandString
let jsonError = ''
let commandString = this.state.commandString
try {
commandString = arrayToCli(JSON.parse(e.target.value))
} catch (error) {
jsonError = error + ''
}
this.setState({ ...this.state, commandString, commandArray, jsonError })
}

commandString = arrayToCli(JSON.parse(e.target.value))
} catch (error) {
jsonError = error + ''
}
this.setState({...this.state, commandString, commandArray, jsonError })
}
protected async addImagesInputChanged(e: React.ChangeEvent<HTMLInputElement>) {
const inputFiles = await getInputFilesFromHtmlInputElement(e.target)
this.addInputFiles(inputFiles)
}

this.addInputFiles(inputFiles)
}
protected async addInputFiles(files: MagickInputFile[]) {
this.props.context.addFiles(files)
this.props.context.addFiles(files)
await this.updateImages()
}

}
protected async updateImages() {
const files = await this.props.context.getAllFiles()
const imgSrcs = this.state.showImagesAndInfo ? await pmap(files, f => buildImageSrc(f, true)) : this.state.imgSrcs
const filesInfo = this.state.showImagesAndInfo ? await pmap(files, f => extractInfo(f)) : this.state.filesInfo
const outputFileSrcs = await pmap(this.state.outputFiles, f => buildImageSrc(f, true))
this.setState({ ...this.state, files, imgSrcs, outputFileSrcs, filesInfo })
}

const imgSrcs = this.state.showImagesAndInfo ? await pmap(files, f => buildImageSrc(f, true)) : this.state.imgSrcs
const filesInfo = this.state.showImagesAndInfo ? await pmap(files, f => extractInfo(f)) : this.state.filesInfo
const outputFileSrcs = await pmap(this.state.outputFiles, f => buildImageSrc(f, true))
this.setState({...this.state, files, imgSrcs, outputFileSrcs, filesInfo })
}
protected async showImagesAndInfoChange(e: React.ChangeEvent<HTMLInputElement>) {
this.state.showImagesAndInfo = e.target.checked
this.state.showImagesAndInfo = e.target.checked
await this.updateImages()
}

}
}

private selectExampleCounter = 0
protected async selectExampleChange(e: React.ChangeEvent<HTMLSelectElement>) {
const template = sampleCommandTemplates[e.target.selectedIndex]
const img = this.state.files.find(i => i.name === this.defaultImage)
const info = await extractInfo(img)
const context = {...template.defaultTemplateContext, imageWidth: info[0].image.geometry.width, imageHeight: info[0].image.geometry.height }
const command = template.template(context)[0].map(s => s === '$INPUT' ? this.defaultImage : s === '$OUTPUT' ? `output${this.selectExampleCounter++}.gif` : s)
this.setState({...this.state, commandArray: JSON.stringify(command), commandString: arrayToCli(command) })
}
}
7 changes: 6 additions & 1 deletion src/executionContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface ExecutionContext {
// /** add built-in images like `rose:` to this execution context so they are present in getAllFiles() */
addBuiltInImages(): Promise<void>
getFile(name: string): Promise<MagickInputFile>
removeFiles(names: string[]): MagickInputFile[]
}

class ExecutionContextImpl implements ExecutionContext {
Expand Down Expand Up @@ -50,9 +51,13 @@ class ExecutionContextImpl implements ExecutionContext {
return this.imageHome.addBuiltInImages()
}

removeFiles(names: string[]): MagickInputFile[] {
return this.imageHome.remove(names)
}

static create(inheritFrom?: ExecutionContext) {
if (inheritFrom && !(inheritFrom as ExecutionContextImpl).imageHome) {
throw new Error('Dont know how to inherith from other ExecutionContext implementation than this one')
throw new Error('Dont know how to inherit from other ExecutionContext implementation than this one')
}
return new ExecutionContextImpl(inheritFrom && (inheritFrom as ExecutionContextImpl).imageHome)
}
Expand Down
12 changes: 12 additions & 0 deletions src/imageHome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { MagickInputFile, MagickFile, asInputFile, getBuiltInImages } from '.'
import pMap from 'p-map'

export interface ImageHome {
remove(names: string[]): MagickInputFile[];
get(name: string): Promise<MagickInputFile>
register(file: MagickFile, name?: string): void
isRegistered(name: string): boolean
Expand All @@ -20,6 +21,17 @@ class ImageHomeImpl implements ImageHome {
return this.images[name]
}

remove(names: string[]): MagickInputFile[] {
const result = []
Object.keys(this.images).forEach(name=>{
if(names.indexOf(name)!==-1) {
result.push(this.images[name])
delete this.images[name]
}
})
return result
}

async getAll(): Promise<MagickInputFile[]> {
return await Promise.all(Object.keys(this.images).map(k => this.images[k]))
}
Expand Down

0 comments on commit 63d88b4

Please sign in to comment.