Skip to content

Commit

Permalink
Merge pull request #11 from mysterycommand/feat/finishing-touches
Browse files Browse the repository at this point in the history
Finishing touches
  • Loading branch information
mysterycommand authored Apr 27, 2019
2 parents a9f7aca + bd0e702 commit eb1e754
Show file tree
Hide file tree
Showing 22 changed files with 452 additions and 62 deletions.
89 changes: 51 additions & 38 deletions src/components/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,32 @@
import React, { Component, MouseEvent } from 'react';
import { AppConfig, Person, UserSession } from 'blockstack';

import './style.css';
import Workspace from '../workspace';
import Splash from '../splash';
import { ToolType } from '../tools';

import './style.css';

const appConfig = new AppConfig(['store_write', 'publish_data']);
const userSession = new UserSession({ appConfig });

type AppState = {
appConfig: AppConfig;
userSession: UserSession;
currentTool: ToolType;
isFetching: boolean;
isSaving: boolean;
};

class App extends Component<{}, AppState> {
private canvasRef = React.createRef<HTMLCanvasElement>();

constructor(props: {}) {
super(props);

const appConfig = new AppConfig(['store_write', 'publish_data']);
const userSession = new UserSession({ appConfig });

this.state = {
appConfig,
userSession,
};
}
public state = {
currentTool: ToolType.Paint,
isFetching: false,
isSaving: false,
};

signIn = (event: MouseEvent<HTMLButtonElement>) => {
public signIn = (event: MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
const { userSession } = this.state;

/**
* @see: https://github.com/zone117x/blockstack-monero/blob/master/src/main.ts#L40-L46
Expand All @@ -39,50 +38,60 @@ class App extends Component<{}, AppState> {
);
};

signOut = (event: MouseEvent<HTMLButtonElement>) => {
public signOut = (event: MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
const { userSession } = this.state;

userSession.signUserOut();
const { origin, pathname } = window.location;
window.location.assign(`${origin}${pathname}`);
};

savePainting = (event: MouseEvent<HTMLButtonElement>) => {
public savePainting = (event: MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
const { userSession } = this.state;

const { current } = this.canvasRef;

if (!current) {
return;
}

userSession.putFile(
'painting.json',
JSON.stringify({
data: current.toDataURL('image/png'),
createdAt: Date.now(),
}),
{
encrypt: false,
},
);
this.setState({ isSaving: true });
userSession
.putFile(
'painting.json',
JSON.stringify({
data: current.toDataURL('image/png'),
createdAt: Date.now(),
}),
{
encrypt: false,
},
)
.then(() => {
this.setState({ isSaving: false });
});
};

fetchPainting = () => {
const { userSession } = this.state;
public fetchPainting = () => {
const { current } = this.canvasRef;

return !current
this.setState({ isFetching: true });
return (!current
? Promise.resolve(JSON.stringify({}))
: userSession.getFile('painting.json', {
decrypt: false,
});
})
).then(file => {
this.setState({ isFetching: false });
return file;
});
};

UNSAFE_componentWillMount() {
const { userSession } = this.state;
public setCurrentTool = (currentTool: ToolType) => {
this.setState({ currentTool });
};

public UNSAFE_componentWillMount() {
if (userSession.isSignInPending()) {
userSession.handlePendingSignIn().then((/* userData */) => {
const { origin, pathname } = window.location;
Expand All @@ -91,17 +100,21 @@ class App extends Component<{}, AppState> {
}
}

render() {
const { userSession } = this.state;
public render() {
const { currentTool, isFetching, isSaving } = this.state;

return (
<div className="app">
{userSession.isUserSignedIn() ? (
<Workspace
currentTool={currentTool}
canvasRef={this.canvasRef}
fetchPainting={this.fetchPainting}
isFetching={isFetching}
isSaving={isSaving}
person={new Person(userSession.loadUserData().profile)}
savePainting={this.savePainting}
setCurrentTool={this.setCurrentTool}
signOut={this.signOut}
/>
) : (
Expand Down
12 changes: 8 additions & 4 deletions src/components/button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import React, { FC, DOMAttributes } from 'react';
import React, { FC, ButtonHTMLAttributes } from 'react';

import Loader from '../loader';

import './style.css';

type ButtonProps = DOMAttributes<HTMLButtonElement> & {};
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {};

const Button: FC<ButtonProps> = ({ children, onClick }) => (
<button className="button" onClick={onClick}>
const Button: FC<ButtonProps> = ({ children, disabled, onClick }) => (
<button className="button" onClick={onClick} disabled={disabled}>
{children}
{disabled && <Loader />}
</button>
);

Expand Down
23 changes: 23 additions & 0 deletions src/components/button/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,27 @@
font-size: calc(10px + 2vmin);
color: white;
margin-top: 1em;

position: relative;
}

.button:disabled {
color: transparent;
}

.button > .loader {
display: none;

transform: scale(0.5);

margin-top: -32px;
margin-left: -32px;

position: absolute;
top: 50%;
left: 50%;
}

.button:disabled > .loader {
display: block;
}
43 changes: 36 additions & 7 deletions src/components/canvas/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { Component, MouseEvent, RefObject, TouchEvent } from 'react';

import './style.css';
import { ToolType } from '../tools';

type CanvasProps = {
canvasRef: RefObject<HTMLCanvasElement>;
currentTool: ToolType;
fetchPainting: () => Promise<string | ArrayBuffer>;
};

Expand Down Expand Up @@ -47,6 +49,12 @@ class Canvas extends Component<CanvasProps, CanvasState> {
return;
}

/**
* casting because TypeScript can't follow these being set and so thinks
* that they're the 'never' type
*
* @see: https://github.com/Microsoft/TypeScript/issues/11498
*/
const ctx = canvasContext as CanvasRenderingContext2D;

const img = new Image(current.width, current.height);
Expand All @@ -62,6 +70,27 @@ class Canvas extends Component<CanvasProps, CanvasState> {
});
}

public componentDidUpdate() {
const { currentTool } = this.props;
const { canvasContext } = this.state;

if (!canvasContext) {
return;
}

/**
* casting because TypeScript can't follow these being set and so thinks
* that they're the 'never' type
*
* @see: https://github.com/Microsoft/TypeScript/issues/11498
*/
const ctx = canvasContext as CanvasRenderingContext2D;
ctx.strokeStyle = currentTool === ToolType.Paint ? 'black' : 'white';
ctx.lineWidth = currentTool === ToolType.Paint ? 4 : 24;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
}

public render() {
const { canvasRef } = this.props;

Expand All @@ -87,7 +116,8 @@ class Canvas extends Component<CanvasProps, CanvasState> {

private handleMouse = (event: MouseEvent<HTMLCanvasElement>) => {
event.preventDefault();
this.setState({ isPainting: event.buttons !== 0, prevX: -1, prevY: -1 });
const isPainting = event.buttons !== 0;
this.setState({ isPainting, prevX: -1, prevY: -1 });
};

private mousePaint = (event: MouseEvent<HTMLCanvasElement>) => {
Expand All @@ -103,11 +133,8 @@ class Canvas extends Component<CanvasProps, CanvasState> {
* @see: https://www.chromestatus.com/features/5093566007214080
*/
// event.preventDefault();
this.setState({
isPainting: event.touches.length !== 0,
prevX: -1,
prevY: -1,
});
const isPainting = event.touches.length !== 0;
this.setState({ isPainting, prevX: -1, prevY: -1 });
};

private touchPaint = (event: TouchEvent<HTMLCanvasElement>) => {
Expand Down Expand Up @@ -164,7 +191,9 @@ class Canvas extends Component<CanvasProps, CanvasState> {
const fillX = (clientX - x) * (current.width / width);
const fillY = (clientY - y) * (current.height / height);

if (prevX !== -1 && prevY !== -1) {
if (prevX === -1 && prevY === -1) {
ctx.beginPath();
} else {
ctx.moveTo(prevX, prevY);
ctx.lineTo(fillX, fillY);
ctx.stroke();
Expand Down
7 changes: 6 additions & 1 deletion src/components/canvas/tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom';

import Canvas from '.';
import { ToolType } from '../tools';

it('renders without crashing', () => {
const div = document.createElement('div');
Expand All @@ -10,7 +11,11 @@ it('renders without crashing', () => {
const mockFetch = () => Promise.resolve(JSON.stringify({}));

ReactDOM.render(
<Canvas canvasRef={mockCanvasRef} fetchPainting={mockFetch} />,
<Canvas
canvasRef={mockCanvasRef}
currentTool={ToolType.Paint}
fetchPainting={mockFetch}
/>,
div,
);
ReactDOM.unmountComponentAtNode(div);
Expand Down
19 changes: 19 additions & 0 deletions src/components/icons/erase/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { FC } from 'react';
import '../style.css';

type EraseProps = {};

const Erase: FC<EraseProps> = () => (
<svg
className="icon erase"
width="24"
height="24"
viewBox="0 0 24 24"
preserveAspectRatio="xMidYMid meet"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M19 20H5a1 1 0 0 0 0 2h14a1 1 0 0 0 0-2zm-.009-17.942h-.09l-4.17.38a2 2 0 0 0-1.21.57l-9 9a1.92 1.92 0 0 0 .07 2.71l2.74 2.74a2 2 0 0 0 2.66.07l9-9a2 2 0 0 0 .57-1.21l.43-4.17a1 1 0 0 0-1-1.09zm-10.27 14l-2.73-2.73 2-1.95 2.68 2.68-1.95 2zm8.9-8.91l-5.63 5.59-2.7-2.7 5.6-5.6 3-.28-.27 2.99z" />
</svg>
);

export default Erase;
12 changes: 12 additions & 0 deletions src/components/icons/erase/tests.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

import React from 'react';
import ReactDOM from 'react-dom';

import Erase from '.';

it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<Erase />, div);
ReactDOM.unmountComponentAtNode(div);
});

19 changes: 19 additions & 0 deletions src/components/icons/paint/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { FC } from 'react';
import '../style.css';

type PaintProps = {};

const Paint: FC<PaintProps> = () => (
<svg
className="icon paint"
width="24"
height="24"
viewBox="0 0 24 24"
preserveAspectRatio="xMidYMid meet"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M19 20H5a1 1 0 0 0 0 2h14a1 1 0 0 0 0-2zM5 18h.09l4.17-.38a2 2 0 0 0 1.21-.57l9-9a1.92 1.92 0 0 0-.07-2.71L16.66 2.6A2 2 0 0 0 14 2.53l-9 9a2 2 0 0 0-.57 1.21L4 16.91A1 1 0 0 0 5 18zM15.27 4L18 6.73l-2 1.95L13.32 6l1.95-2zm-8.9 8.91L12 7.32l2.7 2.7-5.6 5.6-3 .28.27-2.99z" />
</svg>
);

export default Paint;
12 changes: 12 additions & 0 deletions src/components/icons/paint/tests.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

import React from 'react';
import ReactDOM from 'react-dom';

import Paint from '.';

it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<Paint />, div);
ReactDOM.unmountComponentAtNode(div);
});

3 changes: 3 additions & 0 deletions src/components/icons/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.icon {
fill: white;
}
Loading

0 comments on commit eb1e754

Please sign in to comment.