Skip to content
This repository has been archived by the owner on Feb 2, 2021. It is now read-only.

Commit

Permalink
feat: allow to name cats
Browse files Browse the repository at this point in the history
  • Loading branch information
coderbyheart committed Jul 30, 2019
1 parent 8226c55 commit e93f095
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 95 deletions.
40 changes: 0 additions & 40 deletions src/Avatar/updateAvatar.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/Cat/Cat.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
top: 0;
left: 50%;
margin-left: -38.5px;
margin-top: -38.5px;
margin-top: -53.5px;
z-index: 9000;
}
h2 {
margin-top: 25px;
margin: 10px;
}
}
103 changes: 61 additions & 42 deletions src/Cat/Cat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,23 @@ import { WarningRounded as WarningIcon } from '@material-ui/icons'

import './Cat.scss'
import { AvatarPicker } from '../Avatar/AvatarPicker'
import { updateAvatar } from '../Avatar/updateAvatar'
import { uploadAvatar } from './uploadAvatar'
import { Editable } from '../Editable/Editable'
import { updateThingAttributes } from './updateThingAttributes'

const ShowCat = ({
catId,
iot,
iotData,
avatarUploader,
onAvatarChange,
onNameChange,
identityId,
credentials,
}: {
iot: Iot
iotData: IotData
avatarUploader: (args: { blob: Blob }) => Promise<{ url: string }>
onAvatarChange: (args: { avatar: Blob }) => void
onNameChange: (args: { name: string }) => void
catId: string
identityId: string
credentials: {
Expand All @@ -36,6 +40,7 @@ const ShowCat = ({
const [cat, setCat] = useState({
name: catId,
avatar: 'https://placekitten.com/75/75',
version: 0
})
const [reported, setReported] = useState({} as {
[key: string]: { v: any; ts: string }
Expand Down Expand Up @@ -91,16 +96,17 @@ const ShowCat = ({
}),
])

.then(([{ thingName, attributes }, connection, reported]) => {
.then(([{ thingName, attributes, version }, _, reported]) => {
setLoading(false)
console.log('Inital reported state', catId, reported)
setReported(reported)
if (thingName) {
setCat({
name: thingName,
name: (attributes && attributes.name) || thingName,
avatar:
(attributes && attributes.avatar) ||
'https://placekitten.com/75/75',
version: version || 0,
})
}
})
Expand Down Expand Up @@ -129,8 +135,9 @@ const ShowCat = ({
<Card>
<CardHeader className={'cat'}>
<AvatarPicker
key={`${cat.version}`}
onChange={blob => {
// Display image directly
// Display image directly
const reader = new FileReader()
reader.onload = (e: any) => {
setCat({
Expand All @@ -139,24 +146,20 @@ const ShowCat = ({
})
}
reader.readAsDataURL(blob)

// Upload
avatarUploader({ blob })
.then(({ url }) => {
setCat({
...cat,
avatar: url,
})
})
.catch(error => {
console.error(error)
})
onAvatarChange({ avatar: blob })
}}
>
<img src={cat.avatar} alt={cat.name} className={'avatar'} />
</AvatarPicker>

<h2>{cat.name}</h2>
<h2>
<Editable
key={`${cat.version}`}
text={cat.name}
onChange={v => {
onNameChange({ name: v })
}}
/>
</h2>
</CardHeader>
<CardBody>
<dl>
Expand Down Expand Up @@ -192,34 +195,50 @@ const ShowCat = ({

export const Cat = ({ catId }: { catId: string }) => (
<CredentialsContext.Consumer>
{credentials => {
const s3 = new S3({
credentials,
region: process.env.REACT_APP_REGION,
})
return (
<IdentityIdContext.Consumer>
{identityId => (
<IotContext.Consumer>
{({ iot, iotData }) => (
{credentials => (
<IdentityIdContext.Consumer>
{identityId => (
<IotContext.Consumer>
{({ iot, iotData }) => {
const s3 = new S3({
credentials,
region: process.env.REACT_APP_REGION,
})
const avatarUploader = uploadAvatar({
s3,
bucketName: `${process.env.REACT_APP_AVATAR_BUCKET_NAME}`,
})
const attributeUpdater = updateThingAttributes({
iot,
thingName: catId,
})
return (
<ShowCat
catId={catId}
iot={iot}
iotData={iotData}
identityId={identityId}
credentials={credentials}
avatarUploader={updateAvatar({
s3,
iot,
bucketName: `${process.env.REACT_APP_AVATAR_BUCKET_NAME}`,
thingName: catId,
})}
onAvatarChange={({ avatar }) => {
avatarUploader({
avatar,
})
.then(({ url }) => attributeUpdater({ avatar: url }))
.catch(err => {
console.error(err)
})
}}
onNameChange={({ name }) => {
attributeUpdater({ name }).catch(err => {
console.error(err)
})
}}
/>
)}
</IotContext.Consumer>
)}
</IdentityIdContext.Consumer>
)
}}
)
}}
</IotContext.Consumer>
)}
</IdentityIdContext.Consumer>
)}
</CredentialsContext.Consumer>
)
19 changes: 19 additions & 0 deletions src/Cat/updateThingAttributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Iot } from 'aws-sdk'

export const updateThingAttributes = ({
iot,
thingName,
}: {
iot: Iot
thingName: string
}) => async (attributes: { [key: string]: string }): Promise<void> => {
await iot
.updateThing({
thingName,
attributePayload: {
attributes: attributes,
merge: true,
},
})
.promise()
}
24 changes: 24 additions & 0 deletions src/Cat/uploadAvatar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { S3 } from 'aws-sdk'
import { v4 } from 'uuid'

export const uploadAvatar = ({
s3,
bucketName,
}: {
s3: S3
bucketName: string
}) => async ({ avatar }: { avatar: Blob }): Promise<{ url: string }> => {
const id = v4()
const url = `https://${bucketName}.s3.amazonaws.com/${id}.jpg`
await s3
.putObject({
Bucket: `${bucketName}`,
Key: `${id}.jpg`,
ContentType: avatar.type,
ContentLength: avatar.size,
Body: avatar,
})
.promise()

return { url }
}
18 changes: 7 additions & 11 deletions src/Cats/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ import { Error } from '../Error/Error'

const ListCats = ({ iot }: { iot: Iot }) => {
const [loading, setLoading] = useState(true)
const [cats, setCats] = useState([] as { name: string }[])
const [cats, setCats] = useState([] as { id: string, name: string }[])
const [error, setError] = useState()
useEffect(() => {
iot
.listThings()
.promise()
.then(({ things }) => {
setCats(
(things || []).map(({ thingName }) => ({
name: thingName || 'unknown',
(things || []).map(({ thingName, attributes }) => ({
id: thingName || 'unknown',
name: (attributes && attributes.name) || thingName || 'unknown',
})),
)
setLoading(false)
Expand All @@ -32,16 +33,11 @@ const ListCats = ({ iot }: { iot: Iot }) => {
<Card>
<CardHeader>Cats</CardHeader>
<Table>
<thead>
<tr>
<th>name</th>
</tr>
</thead>
<tbody>
{cats.map(({ name }) => (
<tr key={name}>
{cats.map(({ id, name }) => (
<tr key={id}>
<td>
<a href={`/cat/${name}`}>{name}</a>
<a href={`/cat/${id}`}>{name}</a>
</td>
</tr>
))}
Expand Down
29 changes: 29 additions & 0 deletions src/Editable/Editable.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.editable {
width: 100%;
&.error {
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
border-color: red;
}
}


@keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}

20%, 80% {
transform: translate3d(2px, 0, 0);
}

30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}

40%, 60% {
transform: translate3d(4px, 0, 0);
}
}
48 changes: 48 additions & 0 deletions src/Editable/Editable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useState } from 'react'
import classNames from 'classnames'

import './Editable.scss'

export const Editable = ({
text: originalText,
onChange,
}: {
text: string
onChange: (updatedText: string) => void
}) => {
const [editing, setEditing] = useState(false)
const [input, updateInput] = useState(originalText)
const [text, updateText] = useState(originalText)
if (editing) {
return (
<input
className={classNames('editable', { error: !input.length })}
type="text"
value={input}
autoFocus
onBlur={() => {
if (!input.length) {
return
}
setEditing(false)
if (input !== text) {
updateText(input)
onChange(input)
}
}}
onChange={({ target: { value } }) => {
updateInput(value)
}}
/>
)
}
return (
<span
onClick={() => {
setEditing(true)
}}
>
{text}
</span>
)
}

0 comments on commit e93f095

Please sign in to comment.