mirror of
https://github.com/fazo96/ipfs-boards
synced 2025-01-24 14:44:19 +01:00
implemented some functionality and status info
This commit is contained in:
parent
d5ea93722a
commit
2670ab2298
@ -2,6 +2,7 @@ import React from 'react'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { AppBar, Toolbar, Typography, IconButton } from '@material-ui/core'
|
||||
import ProfileIcon from '@material-ui/icons/AccountCircle'
|
||||
import Status from './Status'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
title: {
|
||||
@ -16,6 +17,7 @@ export default function BoardsAppBar() {
|
||||
<Toolbar>
|
||||
<Typography variant="h6" className={styles.title}>IPFS Boards</Typography>
|
||||
<IconButton color="inherit"><ProfileIcon /></IconButton>
|
||||
<Status />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
)
|
||||
|
53
components/Board.js
Normal file
53
components/Board.js
Normal file
@ -0,0 +1,53 @@
|
||||
import React from 'react'
|
||||
import { Card, CardActions, CardHeader, Button, Avatar } 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 EmptyIcon from '@material-ui/icons/HourglassEmpty'
|
||||
import Router from 'next/router'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
card: {
|
||||
margin: theme.spacing(2),
|
||||
},
|
||||
buttonIcon: {
|
||||
marginRight: theme.spacing(1)
|
||||
},
|
||||
button: {
|
||||
marginLeft: theme.spacing(2)
|
||||
}
|
||||
}))
|
||||
|
||||
export default function Board({ boardId, posts }){
|
||||
const classes = useStyles()
|
||||
return (
|
||||
<React.Fragment>
|
||||
{posts.map(p => <Card key={p.multihash} className={classes.card}>
|
||||
<CardHeader
|
||||
title={p.title}
|
||||
subheader="Last Activity X Time Ago"
|
||||
/>
|
||||
<CardActions>
|
||||
<Button variant="contained" color="primary" onClick={() => Router.push(`/b/${boardId}/p/${p.multihash}`)}>
|
||||
<ViewIcon /> View
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>)}
|
||||
{posts.length === 0 && <Card className={classes.card}>
|
||||
<CardHeader
|
||||
avatar={<Avatar><EmptyIcon /></Avatar>}
|
||||
title="No Posts Yet"
|
||||
subheader="Why don't you break the ice?"
|
||||
/>
|
||||
</Card>}
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => Router.push(`/b/${boardId}/p/new`)}
|
||||
className={classes.button}
|
||||
>
|
||||
<AddIcon className={classes.buttonIcon} /> Post Something
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
44
components/Status.js
Normal file
44
components/Status.js
Normal file
@ -0,0 +1,44 @@
|
||||
import React from 'react'
|
||||
import { Button } from '@material-ui/core'
|
||||
import { refreshInfo, getInfo } from './system'
|
||||
import SSRIcon from '@material-ui/icons/SignalCellularNull'
|
||||
import ConnectedIcon from '@material-ui/icons/SignalCellular1Bar'
|
||||
|
||||
class Status extends React.PureComponent {
|
||||
state = { info: getInfo() || {} }
|
||||
|
||||
componentDidMount() {
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.state.timeout)
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
const info = await refreshInfo()
|
||||
this.setState({
|
||||
info, timeout: setTimeout(this.refresh.bind(this), 3000)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { info } = this.state
|
||||
let Icon = SSRIcon
|
||||
let statusText = 'Pre-Rendered'
|
||||
if (!info.isServer) {
|
||||
statusText = 'Offline'
|
||||
if (info.ipfsReady) {
|
||||
statusText = 'Starting DB'
|
||||
Icon = ConnectedIcon
|
||||
}
|
||||
if (info.orbitDbReady) statusText = `${info.ipfsPeers.length} Peers`
|
||||
}
|
||||
|
||||
return <Button color="inherit">
|
||||
{statusText} <Icon />
|
||||
</Button>
|
||||
}
|
||||
}
|
||||
|
||||
export default Status
|
@ -6,6 +6,15 @@ export function getGlobalScope() {
|
||||
}
|
||||
}
|
||||
|
||||
export function isServer() {
|
||||
try {
|
||||
window.document
|
||||
return false
|
||||
} catch (error) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export function getGlobalData() {
|
||||
const scope = getGlobalScope()
|
||||
if (!scope.ipfsBoards) scope.ipfsBoards = {}
|
||||
@ -15,26 +24,35 @@ export function getGlobalData() {
|
||||
export async function getIPFS() {
|
||||
const data = getGlobalData()
|
||||
if (data.ipfs) return data.ipfs
|
||||
const IPFS = await import('ipfs')
|
||||
data.ipfs = await IPFS.create()
|
||||
if (!data.ipfsPromise) {
|
||||
const IPFS = await import(/* webpackChunkName: "ipfs" */ 'ipfs')
|
||||
data.ipfsPromise = IPFS.create()
|
||||
}
|
||||
data.ipfs = await data.ipfsPromise
|
||||
delete data.ipfsPromise
|
||||
return data.ipfs
|
||||
}
|
||||
|
||||
export async function getOrbitDB() {
|
||||
const data = getGlobalData()
|
||||
if (data.orbitDb) return data.orbitDb
|
||||
const OrbitDB = await import('orbit-db').then(m => m.default)
|
||||
const BoardStore = await import('orbit-db-discussion-board').then(m => m.default)
|
||||
OrbitDB.addDatabaseType(BoardStore.type, BoardStore)
|
||||
data.orbitDb = await OrbitDB.createInstance(await getIPFS())
|
||||
data.boards = {}
|
||||
const ipfs = await getIPFS()
|
||||
if (!data.orbitDbPromise) {
|
||||
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)
|
||||
}
|
||||
data.orbitDb = await data.orbitDbPromise
|
||||
delete data.orbitDbPromise
|
||||
if (!data.boards) data.boards = {}
|
||||
return data.orbitDb
|
||||
}
|
||||
|
||||
export async function openBoard(id) {
|
||||
const data = getGlobalData()
|
||||
if (data.boards && data.boards[id]) return data.boards[id]
|
||||
const BoardStore = await import('orbit-db-discussion-board').then(m => m.default)
|
||||
const BoardStore = await import(/* webpackChunkName: "orbit-db-discussion-board" */ 'orbit-db-discussion-board').then(m => m.default)
|
||||
const options = {
|
||||
type: BoardStore.type,
|
||||
create: true,
|
||||
@ -64,3 +82,37 @@ export function getLocalStorage() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getIPFSPeers() {
|
||||
const data = getGlobalData()
|
||||
return data.ipfs ? (await data.ipfs.swarm.peers()).map(x => x.peer._idB58String) : []
|
||||
}
|
||||
|
||||
export async function getPubsubInfo() {
|
||||
const data = getGlobalData()
|
||||
if (!data.ipfs) return {}
|
||||
const rooms = await data.ipfs.pubsub.ls()
|
||||
const pubsubInfo = {}
|
||||
for (const room of rooms) {
|
||||
pubsubInfo[room] = await data.ipfs.pubsub.peers(room)
|
||||
}
|
||||
return pubsubInfo
|
||||
}
|
||||
|
||||
export function getInfo() {
|
||||
const data = getGlobalData()
|
||||
return data.info
|
||||
}
|
||||
|
||||
export async function refreshInfo() {
|
||||
const data = getGlobalData()
|
||||
data.info = {
|
||||
isServer: isServer(),
|
||||
ipfsReady: Boolean(data.ipfs),
|
||||
orbitDbReady: Boolean(data.orbitDb),
|
||||
openBoards: Object.keys(data.boards || {}),
|
||||
ipfsPeers: await getIPFSPeers(),
|
||||
pubsub: await getPubsubInfo()
|
||||
}
|
||||
return data.info
|
||||
}
|
@ -1,11 +1,8 @@
|
||||
import React from 'react'
|
||||
import { Fab, Card, CardActions, CardHeader, Button } from '@material-ui/core'
|
||||
import AddIcon from '@material-ui/icons/Add'
|
||||
import ViewIcon from '@material-ui/icons/Visibility'
|
||||
import { openBoard } from '../../../components/system'
|
||||
import Router from 'next/router'
|
||||
import { openBoard, refreshInfo } from '../../../components/system'
|
||||
import Board from '../../../components/Board'
|
||||
|
||||
class Board extends React.PureComponent {
|
||||
class BoardPage extends React.PureComponent {
|
||||
state = { posts: [] }
|
||||
|
||||
componentDidMount() {
|
||||
@ -14,41 +11,25 @@ class Board extends React.PureComponent {
|
||||
|
||||
async refreshPosts() {
|
||||
const { boardId } = this.props
|
||||
const board = await openBoard(boardId)
|
||||
this.setState({ posts: board.posts })
|
||||
if (boardId) {
|
||||
const board = await openBoard(boardId)
|
||||
this.setState({ posts: board.posts })
|
||||
} else {
|
||||
throw new Error('Missing boardId')
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { boardId } = this.props
|
||||
const posts = this.state.posts || this.props.posts
|
||||
return (
|
||||
<React.Fragment>
|
||||
{posts.map(p => <Card key={p.multihash}>
|
||||
<CardHeader
|
||||
title={p.title}
|
||||
subheader="Last Activity X Time Ago"
|
||||
/>
|
||||
<CardActions>
|
||||
<Button variant="contained" color="primary" onClick={() => Router.push(`/b/${boardId}/p/${p.multihash}`)}>
|
||||
<ViewIcon /> View
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>)}
|
||||
{posts.length === 0 && <Card>
|
||||
<CardHeader
|
||||
title="No Posts Yet"
|
||||
subheader="Why don't you break the ice?"
|
||||
/>
|
||||
</Card>}
|
||||
<Fab color="primary" onClick={() => Router.push(`/b/${boardId}/p/new`)}><AddIcon /></Fab>
|
||||
</React.Fragment>
|
||||
)
|
||||
return <Board boardId={boardId} posts={posts} />
|
||||
}
|
||||
}
|
||||
|
||||
Board.getInitialProps = async ({ query }) => {
|
||||
BoardPage.getInitialProps = async ({ query }) => {
|
||||
await refreshInfo()
|
||||
const board = await openBoard(query.board)
|
||||
return { posts: await board.posts, boardId: query.board }
|
||||
}
|
||||
|
||||
export default Board
|
||||
export default BoardPage
|
||||
|
@ -1,21 +1,77 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Card, CardContent, TextField, Button } from '@material-ui/core'
|
||||
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 SendIcon from '@material-ui/icons/Send'
|
||||
import AddIcon from '@material-ui/icons/Add'
|
||||
|
||||
export default function CreatePost() {
|
||||
const useStyles = makeStyles(theme => ({
|
||||
card: {
|
||||
maxWidth: 600,
|
||||
margin: theme.spacing(2),
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto'
|
||||
},
|
||||
field: {
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
secondaryButton: {
|
||||
marginLeft: theme.spacing(2)
|
||||
},
|
||||
buttonIcon: {
|
||||
marginLeft: theme.spacing(1)
|
||||
}
|
||||
}))
|
||||
|
||||
async function createPost(boardId, postData) {
|
||||
const board = await openBoard(boardId)
|
||||
await board.addPost(postData)
|
||||
Router.push(`/b/${boardId}`)
|
||||
}
|
||||
|
||||
export default function CreatePost({ boardId }) {
|
||||
const classes = useStyles()
|
||||
const [title, setTitle] = useState('')
|
||||
return <Card>
|
||||
return <Card className={classes.card}>
|
||||
<CardHeader
|
||||
avatar={<Avatar><AddIcon /></Avatar>}
|
||||
title="Post Something"
|
||||
subheader={<React.Fragment>
|
||||
Your post will be published to the <b>{boardId}</b> board
|
||||
</React.Fragment>}
|
||||
/>
|
||||
<CardContent>
|
||||
<TextField
|
||||
className={classes.field}
|
||||
variant="outlined"
|
||||
label="Title"
|
||||
placeholder="What is your Post about?"
|
||||
value={title}
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
autoFocus
|
||||
fullWidth
|
||||
/>
|
||||
<Button variant="contained" color="primary">
|
||||
<SendIcon /> Submit
|
||||
<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}`)}>
|
||||
Cancel
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
}
|
||||
|
||||
CreatePost.getInitialProps = ({ query }) => {
|
||||
return { boardId: query.board }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user