1
0
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:
Enrico Fasoli 2019-10-27 12:10:03 +01:00
parent d5ea93722a
commit 2670ab2298
No known key found for this signature in database
GPG Key ID: 1238873C5F27DB4D
6 changed files with 233 additions and 45 deletions

View File

@ -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
View 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
View 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

View File

@ -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
}

View File

@ -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

View File

@ -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 }
}