mirror of
https://github.com/fazo96/ipfs-boards
synced 2025-01-24 14:44:19 +01:00
implement commenting and posting
This commit is contained in:
parent
2670ab2298
commit
1711807308
43
components/AddComment.js
Normal file
43
components/AddComment.js
Normal file
@ -0,0 +1,43 @@
|
||||
import React from 'react'
|
||||
import { TextField, InputAdornment, IconButton } from '@material-ui/core'
|
||||
import SendIcon from '@material-ui/icons/Send'
|
||||
import { openBoard } from './system'
|
||||
|
||||
class AddComment extends React.PureComponent {
|
||||
state = { text: '' }
|
||||
|
||||
render() {
|
||||
const { text } = this.state
|
||||
return (
|
||||
<TextField
|
||||
variant="outlined"
|
||||
label="Comment"
|
||||
placeholder="What's on your mind?"
|
||||
value={text}
|
||||
onChange={event => this.setState({ text: event.target.value })}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton onClick={this.submit} disabled={!text}>
|
||||
<SendIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
submit = async () => {
|
||||
const { boardId, postId, parentId, afterSend } = this.props
|
||||
const { text } = this.state
|
||||
const board = await openBoard(boardId)
|
||||
const comment = { text }
|
||||
await board.commentPost(postId, comment, parentId)
|
||||
if (afterSend) afterSend()
|
||||
}
|
||||
}
|
||||
|
||||
export default AddComment
|
||||
|
||||
|
@ -37,7 +37,7 @@ export default function Board({ boardId, posts }){
|
||||
<CardHeader
|
||||
avatar={<Avatar><EmptyIcon /></Avatar>}
|
||||
title="No Posts Yet"
|
||||
subheader="Why don't you break the ice?"
|
||||
subheader="Don't panic. Your device will keep looking for new posts in the network."
|
||||
/>
|
||||
</Card>}
|
||||
<Button
|
||||
|
82
components/Comments.js
Normal file
82
components/Comments.js
Normal file
@ -0,0 +1,82 @@
|
||||
import React from 'react'
|
||||
import { List, ListItem, ListItemText, ListItemSecondaryAction, IconButton, Button } from '@material-ui/core'
|
||||
import AddComment from './AddComment'
|
||||
import CommentIcon from '@material-ui/icons/Comment'
|
||||
import { openBoard } from './system'
|
||||
|
||||
class Comments extends React.PureComponent {
|
||||
state = {
|
||||
comments: [],
|
||||
replying: false
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.refreshComments()
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { boardId, postId, parentId } = this.props
|
||||
if (prevProps.boardId !== boardId || prevProps.postId !== postId || prevProps.parentId !== parentId) {
|
||||
this.refreshComments()
|
||||
}
|
||||
}
|
||||
|
||||
async refreshComments() {
|
||||
const { boardId, postId, parentId } = this.props
|
||||
const board = await openBoard(boardId)
|
||||
const comments = await board.getComments(postId, parentId)
|
||||
this.setState({ comments })
|
||||
}
|
||||
|
||||
toggleReplying = () => this.setState({ replying: !this.state.replying })
|
||||
|
||||
afterCommenting = () => {
|
||||
this.setState({ replying: false })
|
||||
this.refreshComments()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { boardId, postId, parentId, ...others } = this.props
|
||||
const { comments = [], replying } = this.state
|
||||
return (
|
||||
<List {...others}>
|
||||
{comments.length === 0 && !parentId && (
|
||||
<ListItem>
|
||||
<ListItemText>No comments yet</ListItemText>
|
||||
</ListItem>
|
||||
)}
|
||||
{replying && (
|
||||
<AddComment
|
||||
boardId={boardId}
|
||||
postId={postId}
|
||||
parentId={parentId}
|
||||
afterSend={this.afterCommenting}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={this.toggleReplying}
|
||||
>
|
||||
<CommentIcon style={{ marginRight: '8px' }} />
|
||||
{parentId ? 'Reply' : 'Add Comment'}
|
||||
</Button>
|
||||
{comments.map(c => (
|
||||
<React.Fragment>
|
||||
<ListItem>
|
||||
<ListItemText>{c.text}</ListItemText>
|
||||
</ListItem>
|
||||
<Comments
|
||||
boardId={boardId}
|
||||
postId={postId}
|
||||
parentId={c.multihash}
|
||||
style={{ marginLeft: '32px' }}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</List>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Comments
|
||||
|
27
components/Container.js
Normal file
27
components/Container.js
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
container: {
|
||||
padding: theme.spacing(2),
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
content: {
|
||||
maxWidth: '600px',
|
||||
flexGrow: 2
|
||||
}
|
||||
}))
|
||||
|
||||
const Container = ({ children }) => {
|
||||
const styles = useStyles()
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Container
|
37
components/Post.js
Normal file
37
components/Post.js
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react'
|
||||
import { Fab, Card, CardContent, CardHeader, List, ListItem, ListItemAvatar, ListItemText } from '@material-ui/core'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import Comments from './Comments'
|
||||
import AddIcon from '@material-ui/icons/Add'
|
||||
import ProfileIcon from '@material-ui/icons/AccountCircle'
|
||||
|
||||
const Post = ({ post = {}, boardId }) => {
|
||||
const found = Boolean(post.multihash)
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Card>
|
||||
<CardHeader
|
||||
title={found ? post.title || 'Untitled Post' : 'Not Found'}
|
||||
subheader="Last Activity X Time Ago"
|
||||
/>
|
||||
<CardContent>
|
||||
{post.text || post.multihash || '(This post is empty)'}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<List>
|
||||
<ListItem>
|
||||
<ListItemAvatar><ProfileIcon /></ListItemAvatar>
|
||||
<ListItemText
|
||||
primary="Username"
|
||||
secondary="Discovered X Time Ago"
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
{boardId && post.multihash && (
|
||||
<Comments boardId={boardId} postId={post.multihash} />
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default Post
|
@ -28,11 +28,12 @@ class Status extends React.PureComponent {
|
||||
let statusText = 'Pre-Rendered'
|
||||
if (!info.isServer) {
|
||||
statusText = 'Offline'
|
||||
if (info.ipfsLoading) statusText = 'Starting IPFS'
|
||||
if (info.ipfsReady) {
|
||||
statusText = 'Starting DB'
|
||||
Icon = ConnectedIcon
|
||||
statusText = `${info.ipfsPeers.length} Peers`
|
||||
}
|
||||
if (info.orbitDbReady) statusText = `${info.ipfsPeers.length} Peers`
|
||||
if (info.orbitDbLoading) statusText = 'Starting DB'
|
||||
}
|
||||
|
||||
return <Button color="inherit">
|
||||
|
@ -21,12 +21,43 @@ export function getGlobalData() {
|
||||
return scope.ipfsBoards
|
||||
}
|
||||
|
||||
async function getIPFSOptions() {
|
||||
const common = {
|
||||
libp2p: {
|
||||
config: {
|
||||
pubsub: { enabled: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isServer()) {
|
||||
return {
|
||||
relay: { enabled: true, hop: { enabled: true, active: true } },
|
||||
...common
|
||||
}
|
||||
} else {
|
||||
const serverInfo = await getServerInfo()
|
||||
let additionalOptions = {}
|
||||
if (serverInfo) {
|
||||
additionalOptions = {
|
||||
config: {
|
||||
Bootstrap: [ ...serverInfo.multiaddrs ]
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
...common,
|
||||
...additionalOptions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getIPFS() {
|
||||
const data = getGlobalData()
|
||||
if (data.ipfs) return data.ipfs
|
||||
const IPFS = await import(/* webpackChunkName: "ipfs" */ 'ipfs')
|
||||
const options = await getIPFSOptions()
|
||||
if (!data.ipfsPromise) {
|
||||
const IPFS = await import(/* webpackChunkName: "ipfs" */ 'ipfs')
|
||||
data.ipfsPromise = IPFS.create()
|
||||
data.ipfsPromise = IPFS.create(options)
|
||||
}
|
||||
data.ipfs = await data.ipfsPromise
|
||||
delete data.ipfsPromise
|
||||
@ -34,19 +65,24 @@ export async function getIPFS() {
|
||||
}
|
||||
|
||||
export async function getOrbitDB() {
|
||||
const data = getGlobalData()
|
||||
if (data.orbitDb) return data.orbitDb
|
||||
const ipfs = await getIPFS()
|
||||
if (!data.orbitDbPromise) {
|
||||
try {
|
||||
const data = getGlobalData()
|
||||
if (data.orbitDb) return data.orbitDb
|
||||
const ipfs = await getIPFS()
|
||||
const OrbitDB = await import(/* webpackChunkName: "orbit-db" */ 'orbit-db').then(m => m.default)
|
||||
const BoardStore = await import(/* webpackChunkName: "orbit-db-discussion-board" */ 'orbit-db-discussion-board').then(m => m.default)
|
||||
OrbitDB.addDatabaseType(BoardStore.type, BoardStore)
|
||||
data.orbitDbPromise = OrbitDB.createInstance(ipfs)
|
||||
if (!data.orbitDbPromise) {
|
||||
OrbitDB.addDatabaseType(BoardStore.type, BoardStore)
|
||||
data.orbitDbPromise = OrbitDB.createInstance(ipfs)
|
||||
}
|
||||
data.orbitDb = await data.orbitDbPromise
|
||||
delete data.orbitDbPromise
|
||||
if (!data.boards) data.boards = {}
|
||||
return data.orbitDb
|
||||
} catch (error) {
|
||||
console.log('FATAL: COULD NOT LOAD ORBITDB', error)
|
||||
throw error
|
||||
}
|
||||
data.orbitDb = await data.orbitDbPromise
|
||||
delete data.orbitDbPromise
|
||||
if (!data.boards) data.boards = {}
|
||||
return data.orbitDb
|
||||
}
|
||||
|
||||
export async function openBoard(id) {
|
||||
@ -106,13 +142,43 @@ export function getInfo() {
|
||||
|
||||
export async function refreshInfo() {
|
||||
const data = getGlobalData()
|
||||
const ipfsReady = Boolean(data.ipfs)
|
||||
const multiaddrs = ipfsReady ? data.ipfs.libp2p.peerInfo.multiaddrs.toArray().map(m => m.toJSON()) : []
|
||||
data.info = {
|
||||
isServer: isServer(),
|
||||
ipfsReady: Boolean(data.ipfs),
|
||||
ipfsReady,
|
||||
ipfsLoading: Boolean(data.ipfsPromise),
|
||||
orbitDbReady: Boolean(data.orbitDb),
|
||||
orbitDbPromise: Boolean(data.orbitDbPromise),
|
||||
openBoards: Object.keys(data.boards || {}),
|
||||
ipfsPeers: await getIPFSPeers(),
|
||||
pubsub: await getPubsubInfo()
|
||||
pubsub: await getPubsubInfo(),
|
||||
multiaddrs
|
||||
}
|
||||
return data.info
|
||||
}
|
||||
|
||||
export async function getServerInfo() {
|
||||
const response = await fetch('/api/status')
|
||||
if (response.status === 200) {
|
||||
return response.json()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export async function connectoToIPFSMultiaddr(multiaddr) {
|
||||
const ipfs = await getIPFS()
|
||||
try {
|
||||
// console.log(`Connecting to ${multiaddr}...`)
|
||||
await ipfs.swarm.connect(multiaddr)
|
||||
console.log(`Connected to ${multiaddr}!`)
|
||||
} catch (error) {
|
||||
// console.log(`Connection to ${multiaddr} failed:`, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
export async function connectIPFSToBackend() {
|
||||
const serverInfo = await getServerInfo()
|
||||
const addresses = serverInfo.multiaddrs
|
||||
return Promise.race(addresses.map(connectoToIPFSMultiaddr))
|
||||
}
|
999
package-lock.json
generated
999
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@
|
||||
"@material-ui/core": "^4.5.1",
|
||||
"@material-ui/icons": "^4.5.1",
|
||||
"@material-ui/styles": "^4.5.0",
|
||||
"ipfs": "~0.38.0",
|
||||
"ipfs": "~0.39.0",
|
||||
"next": "9.1.1",
|
||||
"orbit-db": "~0.22.0",
|
||||
"orbit-db-discussion-board": "https://github.com/fazo96/orbit-db-discussion-board.git",
|
||||
|
@ -5,6 +5,8 @@ import { ThemeProvider } from '@material-ui/styles';
|
||||
import AppBar from '../components/AppBar'
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import theme from '../components/theme';
|
||||
import Container from '../components/Container'
|
||||
import { connectIPFSToBackend } from '../components/system';
|
||||
|
||||
export default class MyApp extends App {
|
||||
componentDidMount() {
|
||||
@ -13,6 +15,8 @@ export default class MyApp extends App {
|
||||
if (jssStyles) {
|
||||
jssStyles.parentNode.removeChild(jssStyles);
|
||||
}
|
||||
// Connect IPFS to backend server to improve connectivity
|
||||
connectIPFSToBackend().catch(error => console.log('connectIPFSToBackend failed', error))
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -27,7 +31,9 @@ export default class MyApp extends App {
|
||||
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
|
||||
<CssBaseline />
|
||||
<AppBar />
|
||||
<Component {...pageProps} />
|
||||
<Container>
|
||||
<Component {...pageProps} />
|
||||
</Container>
|
||||
</ThemeProvider>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
8
pages/api/status.js
Normal file
8
pages/api/status.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { refreshInfo } from '../../components/system'
|
||||
|
||||
export default async (req, res) => {
|
||||
const info = await refreshInfo()
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
res.statusCode = 200
|
||||
res.end(JSON.stringify(info))
|
||||
}
|
@ -1,33 +1,46 @@
|
||||
|
||||
import React from 'react'
|
||||
import { Fab, Card, CardContent, CardContentText, CardHeader, List, ListItem, ListItemAvatar, ListItemText } from '@material-ui/core'
|
||||
import AddIcon from '@material-ui/icons/Add'
|
||||
import ProfileIcon from '@material-ui/icons/AccountCircle'
|
||||
import { openBoard } from '../../../../components/system'
|
||||
import Post from '../../../../components/Post'
|
||||
|
||||
const Post = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Card>
|
||||
<CardHeader
|
||||
title="First Post"
|
||||
subheader="Last Activity X Time Ago"
|
||||
/>
|
||||
<CardContent>
|
||||
<CardContentText>Lorem Ipsum...</CardContentText>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<List>
|
||||
<ListItem>
|
||||
<ListItemAvatar><ProfileIcon /></ListItemAvatar>
|
||||
<ListItemText
|
||||
primary="Username"
|
||||
secondary="Discovered X Time Ago"
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
<Fab color="primary"><AddIcon /></Fab>
|
||||
</React.Fragment>
|
||||
)
|
||||
const findPost = (posts, id) => {
|
||||
const results = posts.filter(p => p.multihash === id)
|
||||
if (results.length > 0) return results[0]
|
||||
return undefined
|
||||
}
|
||||
|
||||
export default Post
|
||||
class PostPage extends React.PureComponent {
|
||||
state = {
|
||||
post: undefined
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.refreshPost()
|
||||
}
|
||||
|
||||
async refreshPost() {
|
||||
const { boardId, postId } = this.props
|
||||
const board = await openBoard(boardId)
|
||||
const post = findPost(board.posts, postId)
|
||||
this.setState({ post })
|
||||
}
|
||||
|
||||
render() {
|
||||
const { post: postProp, boardId } = this.props
|
||||
const { post } = this.state
|
||||
return <Post
|
||||
post={post || postProp}
|
||||
boardId={boardId}
|
||||
/>
|
||||
}
|
||||
}
|
||||
|
||||
PostPage.getInitialProps = async ({ query }) => {
|
||||
const board = await openBoard(query.board)
|
||||
return {
|
||||
post: findPost(board.posts, query.post),
|
||||
boardId: query.board,
|
||||
postId: query.post
|
||||
}
|
||||
}
|
||||
|
||||
export default PostPage
|
||||
|
@ -2,10 +2,25 @@ import React, { useState } from 'react'
|
||||
import { openBoard } from '../../../../components/system'
|
||||
import Router from 'next/router'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { Card, CardHeader, CardContent, TextField, Button, Avatar } from '@material-ui/core'
|
||||
import { Typography, Box, Card, CardHeader, Divider, CardActions, CardContent, TextField, Button, Avatar, Tabs, Tab } from '@material-ui/core'
|
||||
import SendIcon from '@material-ui/icons/Send'
|
||||
import AddIcon from '@material-ui/icons/Add'
|
||||
|
||||
function TabPanel(props) {
|
||||
const { children, value, index, ...other } = props
|
||||
|
||||
return (
|
||||
<Typography
|
||||
component="div"
|
||||
role="tabpanel"
|
||||
hidden={value !== index}
|
||||
{...other}
|
||||
>
|
||||
<Box p={3}>{children}</Box>
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
card: {
|
||||
maxWidth: 600,
|
||||
@ -22,7 +37,18 @@ const useStyles = makeStyles(theme => ({
|
||||
},
|
||||
buttonIcon: {
|
||||
marginLeft: theme.spacing(1)
|
||||
}
|
||||
},
|
||||
tabContainer: {
|
||||
flexGrow: 1,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
display: 'flex',
|
||||
},
|
||||
tabPanel: {
|
||||
flexGrow: 1
|
||||
},
|
||||
tabs: {
|
||||
borderRight: `1px solid ${theme.palette.divider}`,
|
||||
},
|
||||
}))
|
||||
|
||||
async function createPost(boardId, postData) {
|
||||
@ -33,7 +59,21 @@ async function createPost(boardId, postData) {
|
||||
|
||||
export default function CreatePost({ boardId }) {
|
||||
const classes = useStyles()
|
||||
|
||||
// State
|
||||
const [title, setTitle] = useState('')
|
||||
const [cid, setCID] = useState('')
|
||||
const [content, setContent] = useState('')
|
||||
const [tab, setTab] = useState(0)
|
||||
|
||||
// Actions
|
||||
const submitPost = () => {
|
||||
const payload = { title }
|
||||
if (tab === 0) payload.text = content
|
||||
if (tab === 2) payload.multihash = cid
|
||||
return createPost(boardId, payload)
|
||||
}
|
||||
|
||||
return <Card className={classes.card}>
|
||||
<CardHeader
|
||||
avatar={<Avatar><AddIcon /></Avatar>}
|
||||
@ -53,25 +93,68 @@ export default function CreatePost({ boardId }) {
|
||||
autoFocus
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
className={classes.field}
|
||||
variant="filled"
|
||||
label="CID"
|
||||
placeholder="Paste the IPFS CID or your post content (WIP)"
|
||||
value={title}
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
fullWidth
|
||||
/>
|
||||
<Button variant="contained" color="primary" onClick={() => createPost(boardId, { title })}>
|
||||
Submit <SendIcon className={classes.buttonIcon} />
|
||||
</Button>
|
||||
<Button className={classes.secondaryButton} onClick={() => Router.push(`/b/${boardId}`)}>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<div className={classes.tabContainer}>
|
||||
<Tabs
|
||||
orientation="vertical"
|
||||
value={tab}
|
||||
onChange={(event, value) => setTab(value)}
|
||||
className={classes.tabs}
|
||||
>
|
||||
<Tab label="Text" />
|
||||
<Tab label="Media" />
|
||||
<Tab label="IPFS CID" />
|
||||
</Tabs>
|
||||
<TabPanel value={tab} index={0} className={classes.tabPanel}>
|
||||
<TextField
|
||||
multiline
|
||||
fullWidth
|
||||
placeholder="What's on your mind?"
|
||||
rows={3}
|
||||
variant="outlined"
|
||||
label="Post"
|
||||
value={content}
|
||||
onChange={event => setContent(event.target.value)}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel value={tab} index={1} className={classes.tabPanel}>
|
||||
WIP
|
||||
</TabPanel>
|
||||
<TabPanel value={tab} index={2} className={classes.tabPanel}>
|
||||
<TextField
|
||||
className={classes.field}
|
||||
variant="filled"
|
||||
label="CID"
|
||||
placeholder="Paste the IPFS CID or your post content (WIP)"
|
||||
value={cid}
|
||||
onChange={e => setCID(e.target.value)}
|
||||
helperText="Enter the CID or Multihash of existing IPFS content"
|
||||
fullWidth
|
||||
/>
|
||||
</TabPanel>
|
||||
</div>
|
||||
<Divider />
|
||||
<CardActions>
|
||||
<Button
|
||||
className={classes.secondaryButton}
|
||||
onClick={() => Router.push(`/b/${boardId}`)}
|
||||
style={{ marginLeft: 'auto' }}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</CardContent>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={submitPost}
|
||||
>
|
||||
Submit <SendIcon className={classes.buttonIcon} />
|
||||
</Button>
|
||||
|
||||
</CardActions>
|
||||
</Card>
|
||||
}
|
||||
|
||||
CreatePost.getInitialProps = ({ query }) => {
|
||||
return { boardId: query.board }
|
||||
}
|
||||
}
|
@ -1,19 +1,44 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Card, CardContent, TextField, Button } from '@material-ui/core'
|
||||
import { Card, CardContent, TextField, Button, Typography, Divider} from '@material-ui/core'
|
||||
import OpenIcon from '@material-ui/icons/Add'
|
||||
import Router from 'next/router'
|
||||
import { makeStyles } from '@material-ui/styles'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
button: {
|
||||
marginTop: theme.spacing(2),
|
||||
marginLeft: 'auto',
|
||||
}
|
||||
}))
|
||||
|
||||
export default function OpenBoard() {
|
||||
const [name, setName] = useState('')
|
||||
const styles = useStyles()
|
||||
return <Card>
|
||||
<CardContent>
|
||||
<Typography variant="h5">
|
||||
Open a Board
|
||||
</Typography>
|
||||
<Typography variant="subtitle1">
|
||||
IPFS Boards is a work in progress. Thank you for testing the app!
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<CardContent>
|
||||
<TextField
|
||||
label="Board"
|
||||
placeholder="Type a name..."
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
autoFocus
|
||||
fullWidth
|
||||
/>
|
||||
<Button variant="contained" color="primary">
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={styles.button}
|
||||
disabled={!name}
|
||||
onClick={() => Router.push(`/b/${name}`)}
|
||||
>
|
||||
<OpenIcon /> Open
|
||||
</Button>
|
||||
</CardContent>
|
||||
|
@ -1,12 +1,21 @@
|
||||
import React from 'react'
|
||||
import { Fab, Card, CardActions, CardHeader, Button } from '@material-ui/core'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import AddIcon from '@material-ui/icons/Add'
|
||||
import ViewIcon from '@material-ui/icons/Visibility'
|
||||
import DeleteIcon from '@material-ui/icons/Delete'
|
||||
import Link from 'next/link'
|
||||
import Router from 'next/router'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
fab: {
|
||||
float: 'right',
|
||||
marginTop: theme.spacing(2)
|
||||
},
|
||||
}))
|
||||
|
||||
const Home = () => {
|
||||
const styles = useStyles()
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Card>
|
||||
@ -24,7 +33,9 @@ const Home = () => {
|
||||
</CardActions>
|
||||
</Card>
|
||||
<Link href="/b/open">
|
||||
<Fab color="primary"><AddIcon /></Fab>
|
||||
<Fab color="primary" className={styles.fab}>
|
||||
<AddIcon />
|
||||
</Fab>
|
||||
</Link>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user