diff --git a/components/AppBar.js b/components/AppBar.js
index ca669ed..2882a54 100644
--- a/components/AppBar.js
+++ b/components/AppBar.js
@@ -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() {
IPFS Boards
+
)
diff --git a/components/Board.js b/components/Board.js
new file mode 100644
index 0000000..cc5796e
--- /dev/null
+++ b/components/Board.js
@@ -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 (
+
+ {posts.map(p =>
+
+
+
+
+ )}
+ {posts.length === 0 &&
+ }
+ title="No Posts Yet"
+ subheader="Why don't you break the ice?"
+ />
+ }
+
+
+ )
+}
\ No newline at end of file
diff --git a/components/Status.js b/components/Status.js
new file mode 100644
index 0000000..4c2dda0
--- /dev/null
+++ b/components/Status.js
@@ -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
+ }
+}
+
+export default Status
\ No newline at end of file
diff --git a/components/system.js b/components/system.js
index 6e34c3e..4ca2a8f 100644
--- a/components/system.js
+++ b/components/system.js
@@ -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
+}
\ No newline at end of file
diff --git a/pages/b/[board]/index.js b/pages/b/[board]/index.js
index f2bf794..6125756 100644
--- a/pages/b/[board]/index.js
+++ b/pages/b/[board]/index.js
@@ -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 (
-
- {posts.map(p =>
-
-
-
-
- )}
- {posts.length === 0 &&
-
- }
- Router.push(`/b/${boardId}/p/new`)}>
-
- )
+ return
}
}
-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
diff --git a/pages/b/[board]/p/new.js b/pages/b/[board]/p/new.js
index bb4f0cd..c9d0208 100644
--- a/pages/b/[board]/p/new.js
+++ b/pages/b/[board]/p/new.js
@@ -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
+ return
+ }
+ title="Post Something"
+ subheader={
+ Your post will be published to the {boardId} board
+ }
+ />
setTitle(e.target.value)}
autoFocus
+ fullWidth
/>
-
}
+
+CreatePost.getInitialProps = ({ query }) => {
+ return { boardId: query.board }
+}