1
0
mirror of https://github.com/fazo96/ipfs-boards synced 2025-03-12 21:48:39 +01:00

eslint fix

This commit is contained in:
Enrico Fasoli 2019-03-10 19:53:18 +01:00
parent bc32ede2b5
commit 4e880f8864
No known key found for this signature in database
GPG Key ID: 1238873C5F27DB4D
28 changed files with 668 additions and 653 deletions

View File

@ -1,23 +1,23 @@
export const ADD_POST = 'ADD_POST' export const ADD_POST = 'ADD_POST';
export const EDIT_POST = 'EDIT_POST' export const EDIT_POST = 'EDIT_POST';
export const HIDE_POST = 'HIDE_POST' export const HIDE_POST = 'HIDE_POST';
export const UPDATE_BOARD_METADATA = 'UPDATE_BOARD_METADATA' export const UPDATE_BOARD_METADATA = 'UPDATE_BOARD_METADATA';
export const ADD_COMMENT = 'ADD_COMMENT' export const ADD_COMMENT = 'ADD_COMMENT';
export const EDIT_COMMENT = 'EDIT_COMMENT' export const EDIT_COMMENT = 'EDIT_COMMENT';
export const HIDE_COMMENT = 'HIDE_COMMENT' export const HIDE_COMMENT = 'HIDE_COMMENT';
export const OPEN_BOARD = 'OPEN_BOARD' export const OPEN_BOARD = 'OPEN_BOARD';
export const OPENED_BOARD = 'OPENED_BOARD' export const OPENED_BOARD = 'OPENED_BOARD';
export const CLOSE_BOARD = 'CLOSE_BOARD' export const CLOSE_BOARD = 'CLOSE_BOARD';
export const UPDATE_BOARD = 'UPDATE_BOARD' export const UPDATE_BOARD = 'UPDATE_BOARD';
export const ORBITDB_WRITE = 'ORBITDB_WRITE' export const ORBITDB_WRITE = 'ORBITDB_WRITE';
export const ORBITDB_REPLICATE = 'ORBITDB_REPLICATE' export const ORBITDB_REPLICATE = 'ORBITDB_REPLICATE';
export const ORBITDB_REPLICATE_PROGRESS = 'ORBITDB_REPLICATE_PROGRESS' export const ORBITDB_REPLICATE_PROGRESS = 'ORBITDB_REPLICATE_PROGRESS';
export const ORBITDB_REPLICATED = 'ORBITDB_REPLICATED' export const ORBITDB_REPLICATED = 'ORBITDB_REPLICATED';
export const ERROR = 'ERROR' export const ERROR = 'ERROR';

View File

@ -1,35 +1,35 @@
import { import {
OPEN_BOARD, OPEN_BOARD,
CLOSE_BOARD, CLOSE_BOARD,
OPENED_BOARD, OPENED_BOARD,
UPDATE_BOARD_METADATA UPDATE_BOARD_METADATA,
} from './actionTypes' } from './actionTypes';
export function openBoard(board) { export function openBoard(board) {
return { return {
type: OPEN_BOARD, type: OPEN_BOARD,
board board,
} };
} }
export function createdBoard(board) { export function createdBoard(board) {
return { return {
type: OPENED_BOARD, type: OPENED_BOARD,
board board,
} };
} }
export function updateBoardMetadata(address, metadata) { export function updateBoardMetadata(address, metadata) {
return { return {
type: UPDATE_BOARD_METADATA, type: UPDATE_BOARD_METADATA,
address, address,
metadata metadata,
} };
} }
export function closeBoard(address) { export function closeBoard(address) {
return { return {
type: CLOSE_BOARD, type: CLOSE_BOARD,
address address,
} };
} }

View File

@ -1,32 +1,32 @@
import { HIDE_COMMENT, ADD_COMMENT, EDIT_COMMENT } from './actionTypes' import { HIDE_COMMENT, ADD_COMMENT, EDIT_COMMENT } from './actionTypes';
export function addComment(address, postId, comment, replyTo = 'post') { export function addComment(address, postId, comment, replyTo = 'post') {
return { return {
type: ADD_COMMENT, type: ADD_COMMENT,
address, address,
postId, postId,
comment, comment,
replyTo replyTo,
} };
} }
export function editComment(address, postId, commentId, comment, replyTo = 'post') { export function editComment(address, postId, commentId, comment, replyTo = 'post') {
return { return {
type: EDIT_COMMENT, type: EDIT_COMMENT,
address, address,
postId, postId,
commentId, commentId,
comment, comment,
replyTo replyTo,
} };
} }
export function hideComment(address, postId, commentId, replyTo = 'post') { export function hideComment(address, postId, commentId, replyTo = 'post') {
return { return {
type: HIDE_COMMENT, type: HIDE_COMMENT,
address, address,
postId, postId,
commentId, commentId,
replyTo replyTo,
} };
} }

View File

@ -1,26 +1,26 @@
import { ADD_POST, EDIT_POST, HIDE_POST } from './actionTypes' import { ADD_POST, EDIT_POST, HIDE_POST } from './actionTypes';
export function addPost(address, post) { export function addPost(address, post) {
return { return {
type: ADD_POST, type: ADD_POST,
post, post,
address address,
} };
} }
export function editPost(address, postId, post) { export function editPost(address, postId, post) {
return { return {
type: EDIT_POST, type: EDIT_POST,
address, address,
postId, postId,
post, post,
} };
} }
export function hidePost(address, postId) { export function hidePost(address, postId) {
return { return {
type: HIDE_POST, type: HIDE_POST,
address, address,
postId postId,
} };
} }

View File

@ -1,20 +1,20 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Switch, Route, withRouter } from 'react-router-dom' import { Switch, Route, withRouter } from 'react-router-dom';
import Boards from '../containers/Boards' import Boards from '../containers/Boards';
import OpenBoard from '../containers/OpenBoard' import OpenBoard from '../containers/OpenBoard';
import WithBoard from '../containers/WithBoard' import WithBoard from '../containers/WithBoard';
import BoardPage from '../components/BoardPage' import BoardPage from './BoardPage';
class App extends Component { class App extends Component {
render() { render() {
return ( return (
<Switch> <Switch>
<Route path='/b/new' component={OpenBoard} /> <Route path="/b/new" component={OpenBoard} />
<Route path='/b/:hash/:name/' component={withRouter(WithBoard(BoardPage))} /> <Route path="/b/:hash/:name/" component={withRouter(WithBoard(BoardPage))} />
<Route path='/' component={Boards} /> <Route path="/" component={Boards} />
</Switch> </Switch>
) );
} }
} }
export default withRouter(App) export default withRouter(App);

View File

@ -1,21 +1,23 @@
import React from 'react' import React from 'react';
import { connect } from 'react-redux' import { connect } from 'react-redux';
import BoardComponent from '../components/Board' import BoardComponent from '../components/Board';
import { getBoardAddress } from '../utils/orbitdb' import { getBoardAddress } from '../utils/orbitdb';
function Board({ stats, location, match, boards }) { function Board({
const { hash, name } = match.params stats, location, match, boards,
const address = getBoardAddress(hash, name) }) {
const boardStats = stats.dbs[address] || {} const { hash, name } = match.params;
return <BoardComponent stats={boardStats} {...boards[address]} /> const address = getBoardAddress(hash, name);
const boardStats = stats.dbs[address] || {};
return <BoardComponent stats={boardStats} {...boards[address]} />;
} }
function mapStateToProps(state){ function mapStateToProps(state) {
return { return {
boards: state.boards.boards boards: state.boards.boards,
} };
} }
export default connect( export default connect(
mapStateToProps mapStateToProps,
)(Board) )(Board);

View File

@ -1,35 +1,39 @@
import React from 'react' import React from 'react';
import { connect } from 'react-redux' import { connect } from 'react-redux';
import BoardEditorForm from '../components/BoardEditorForm' import BoardEditorForm from '../components/BoardEditorForm';
import { updateBoardMetadata } from '../actions/board' import { updateBoardMetadata } from '../actions/board';
import { getBoardAddress } from '../utils/orbitdb' import { getBoardAddress } from '../utils/orbitdb';
function BoardEditor({ boards, boardEditor, match, updateBoardMetadata }) { function BoardEditor({
const { hash, name } = match.params boards, boardEditor, match, updateBoardMetadata,
const address = getBoardAddress(hash, name) }) {
const board = boards[address] const { hash, name } = match.params;
return <BoardEditorForm const address = getBoardAddress(hash, name);
board={board} const board = boards[address];
address={address} return (
updateBoardMetadata={updateBoardMetadata} <BoardEditorForm
{...board.metadata} board={board}
address={address}
updateBoardMetadata={updateBoardMetadata}
{...board.metadata}
/> />
);
} }
function mapStateToProps(state){ function mapStateToProps(state) {
return { return {
boards: state.boards.boards boards: state.boards.boards,
} };
} }
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return { return {
updateBoardMetadata: (address, metadata) => dispatch(updateBoardMetadata(address, metadata)) updateBoardMetadata: (address, metadata) => dispatch(updateBoardMetadata(address, metadata)),
} };
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps,
)(BoardEditor) )(BoardEditor);

View File

@ -1,34 +1,36 @@
import React from 'react' import React from 'react';
import { connect } from 'react-redux' import { connect } from 'react-redux';
import { push } from 'connected-react-router' import { push } from 'connected-react-router';
import BoardsComponent from '../components/Boards' import BoardsComponent from '../components/Boards';
import WithStats from './WithStats' import WithStats from './WithStats';
import { closeBoard } from '../actions/board' import { closeBoard } from '../actions/board';
const WrappedComponent = WithStats(BoardsComponent) const WrappedComponent = WithStats(BoardsComponent);
function Boards({ boards, createBoard, closeBoard }) { function Boards({ boards, createBoard, closeBoard }) {
return <WrappedComponent return (
boards={boards} <WrappedComponent
createBoard={createBoard} boards={boards}
closeBoard={closeBoard} createBoard={createBoard}
closeBoard={closeBoard}
/> />
);
} }
function mapStateToProps(state){ function mapStateToProps(state) {
return { return {
boards: state.boards.boards boards: state.boards.boards,
} };
} }
function mapDispatchToProps(dispatch){ function mapDispatchToProps(dispatch) {
return { return {
createBoard: () => dispatch(push('/b/new')), createBoard: () => dispatch(push('/b/new')),
closeBoard: address => dispatch(closeBoard(address)), closeBoard: address => dispatch(closeBoard(address)),
} };
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps,
)(Boards) )(Boards);

View File

@ -1,25 +1,25 @@
import React from 'react' import React from 'react';
import { connect } from 'react-redux' import { connect } from 'react-redux';
import OpenBoardForm from '../components/OpenBoardForm' import OpenBoardForm from '../components/OpenBoardForm';
import { openBoard } from '../actions/board' import { openBoard } from '../actions/board';
function OpenBoard(props) { function OpenBoard(props) {
return <OpenBoardForm {...props} /> return <OpenBoardForm {...props} />;
} }
function mapStateToProps(state){ function mapStateToProps(state) {
return { return {
opening: state.openBoard.opening opening: state.openBoard.opening,
} };
} }
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return { return {
openBoard: board => dispatch(openBoard(board)) openBoard: board => dispatch(openBoard(board)),
} };
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps,
)(OpenBoard) )(OpenBoard);

View File

@ -1,32 +1,34 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { connect } from 'react-redux' import { connect } from 'react-redux';
import PostForm from '../components/PostForm' import PostForm from '../components/PostForm';
import { addPost } from '../actions/post' import { addPost } from '../actions/post';
import { getBoardAddress } from '../utils/orbitdb'; import { getBoardAddress } from '../utils/orbitdb';
class PostEditor extends Component { class PostEditor extends Component {
render() { render() {
const { post, addPost, match, boards } = this.props const {
const address = getBoardAddress(match.params.hash, match.params.name) post, addPost, match, boards,
const board = boards[address] } = this.props;
return <PostForm post={post} board={board} onSave={p => addPost(address, p)} /> const address = getBoardAddress(match.params.hash, match.params.name);
} const board = boards[address];
return <PostForm post={post} board={board} onSave={p => addPost(address, p)} />;
}
} }
function mapStateToProps(state){ function mapStateToProps(state) {
return { return {
post: state.postEditor.post, post: state.postEditor.post,
boards: state.boards.boards boards: state.boards.boards,
} };
} }
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return { return {
addPost: (address, post) => dispatch(addPost(address, post)) addPost: (address, post) => dispatch(addPost(address, post)),
} };
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps,
)(PostEditor) )(PostEditor);

View File

@ -1,53 +1,50 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { connect } from 'react-redux' import { connect } from 'react-redux';
import { openBoard } from '../actions/board' import { openBoard } from '../actions/board';
import { getBoardAddress } from '../utils/orbitdb' import { getBoardAddress } from '../utils/orbitdb';
function mapStateToProps(state){ function mapStateToProps(state) {
return { return {
boards: state.boards.boards boards: state.boards.boards,
} };
} }
function mapDispatchToProps(dispatch){ function mapDispatchToProps(dispatch) {
return { return {
openBoard: address => dispatch(openBoard({ address, redirect: false })) openBoard: address => dispatch(openBoard({ address, redirect: false })),
} };
} }
export default function WithBoard(WrappedComponent) { export default function WithBoard(WrappedComponent) {
class ToExport extends Component { class ToExport extends Component {
componentDidMount() {
componentDidMount() { const { boards, match } = this.props;
const { boards, match } = this.props const address = getBoardAddress(match.params.hash, match.params.name);
const address = getBoardAddress(match.params.hash, match.params.name) if (!boards[address]) {
if (!boards[address]) { this.props.openBoard(address);
this.props.openBoard(address) }
}
}
componentWillReceiveProps({ match, boards }) {
const address = getBoardAddress(match.params.hash, match.params.name)
if (!boards[address]) {
this.props.openBoard(address)
}
}
render() {
const { boards, match } = this.props
const address = getBoardAddress(match.params.hash, match.params.name)
const board = boards[address]
if (board) {
return <WrappedComponent {...board} {...this.props} />
} else {
return "Opening this board"
}
}
} }
return connect( componentWillReceiveProps({ match, boards }) {
mapStateToProps, const address = getBoardAddress(match.params.hash, match.params.name);
mapDispatchToProps if (!boards[address]) {
)(ToExport) this.props.openBoard(address);
}
}
render() {
const { boards, match } = this.props;
const address = getBoardAddress(match.params.hash, match.params.name);
const board = boards[address];
if (board) {
return <WrappedComponent {...board} {...this.props} />;
}
return 'Opening this board';
}
}
return connect(
mapStateToProps,
mapDispatchToProps,
)(ToExport);
} }

View File

@ -1,43 +1,43 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { getStats } from '../utils/ipfs' import { getStats } from '../utils/ipfs';
export default function(WrappedComponent) { export default function (WrappedComponent) {
return class extends Component { return class extends Component {
constructor(props) { constructor(props) {
super(props) super(props);
this.state = { this.state = {
stats: { stats: {
id: '?', id: '?',
peers: [], peers: [],
pubKey: '?', pubKey: '?',
dbs: {} dbs: {},
}, },
timeout: null timeout: null,
} };
}
async refresh(loop = true) {
const newStats = await getStats()
const stats = Object.assign({}, this.state.stats, newStats)
this.setState({ stats }, loop ? this.refreshDelayed.bind(this) : undefined)
}
refreshDelayed() {
this.timeout = setTimeout(() => {
this.refresh()
}, 2000)
}
componentDidMount() {
this.refresh()
}
componentWillUnmount() {
if (this.timeout) clearTimeout(this.timeout)
}
render() {
return <WrappedComponent stats={this.state.stats} {...this.props} />
}
} }
async refresh(loop = true) {
const newStats = await getStats();
const stats = Object.assign({}, this.state.stats, newStats);
this.setState({ stats }, loop ? this.refreshDelayed.bind(this) : undefined);
}
refreshDelayed() {
this.timeout = setTimeout(() => {
this.refresh();
}, 2000);
}
componentDidMount() {
this.refresh();
}
componentWillUnmount() {
if (this.timeout) clearTimeout(this.timeout);
}
render() {
return <WrappedComponent stats={this.state.stats} {...this.props} />;
}
};
} }

View File

@ -1,13 +1,13 @@
import 'react-hot-loader/patch' import 'react-hot-loader/patch';
import React from 'react' import React from 'react';
import { render } from 'react-dom' import { render } from 'react-dom';
import { AppContainer } from 'react-hot-loader' import { AppContainer } from 'react-hot-loader';
import configureStore, { history } from './store/configureStore' import { Provider } from 'react-redux';
import App from './components/App' import { ConnectedRouter } from 'connected-react-router';
import registerServiceWorker from './registerServiceWorker' import configureStore, { history } from './store/configureStore';
import { Provider } from 'react-redux' import App from './components/App';
import { ConnectedRouter } from 'connected-react-router' import registerServiceWorker from './registerServiceWorker';
import { start } from './orbitdb' import { start } from './orbitdb';
const store = configureStore(); const store = configureStore();
@ -19,12 +19,12 @@ render(
</ConnectedRouter> </ConnectedRouter>
</Provider> </Provider>
</AppContainer>, </AppContainer>,
document.getElementById('root') document.getElementById('root'),
); );
if (module.hot) { if (module.hot) {
module.hot.accept('./components/App', () => { module.hot.accept('./components/App', () => {
const NewApp = require('./components/App').default const NewApp = require('./components/App').default;
render( render(
<AppContainer> <AppContainer>
<Provider store={store}> <Provider store={store}>
@ -33,10 +33,10 @@ if (module.hot) {
</ConnectedRouter> </ConnectedRouter>
</Provider> </Provider>
</AppContainer>, </AppContainer>,
document.getElementById('root') document.getElementById('root'),
) );
}) });
} }
registerServiceWorker() registerServiceWorker();
start(store.dispatch) start(store.dispatch);

View File

@ -1,15 +1,15 @@
import IPFS from 'ipfs' import IPFS from 'ipfs';
import OrbitDB from 'orbit-db' import OrbitDB from 'orbit-db';
import BoardStore from 'orbit-db-discussion-board' import BoardStore from 'orbit-db-discussion-board';
import multihashes from 'multihashes' import multihashes from 'multihashes';
export function isValidID(id) { export function isValidID(id) {
try { try {
if (typeof id === 'string' && multihashes.fromB58String(id)) return true if (typeof id === 'string' && multihashes.fromB58String(id)) return true;
} catch (error) { } catch (error) {
return false return false;
} }
return false return false;
} }
export async function start() { export async function start() {
@ -17,96 +17,96 @@ export async function start() {
window.ipfs = new IPFS({ window.ipfs = new IPFS({
repo: 'ipfs-v6-boards-v0', repo: 'ipfs-v6-boards-v0',
EXPERIMENTAL: { EXPERIMENTAL: {
pubsub: true pubsub: true,
} },
});
await new Promise((resolve) => {
window.ipfs.on('ready', () => resolve());
}); });
await new Promise(resolve => {
window.ipfs.on('ready', () => resolve())
})
} }
if (!window.orbitDb) { if (!window.orbitDb) {
OrbitDB.addDatabaseType(BoardStore.type, BoardStore) OrbitDB.addDatabaseType(BoardStore.type, BoardStore);
window.orbitDb = new OrbitDB(window.ipfs) window.orbitDb = new OrbitDB(window.ipfs);
} }
} }
export async function open(address, metadata) { export async function open(address, metadata) {
if (window.dbs && window.dbs[address]) return window.dbs[address] if (window.dbs && window.dbs[address]) return window.dbs[address];
await start() await start();
const options = { const options = {
type: BoardStore.type, type: BoardStore.type,
create: true, create: true,
write: ['*'] write: ['*'],
} };
const db = await window.orbitDb.open(address, options) const db = await window.orbitDb.open(address, options);
await db.load() await db.load();
if (metadata) { if (metadata) {
await db.updateMetadata(metadata) await db.updateMetadata(metadata);
} }
if (!window.dbs) window.dbs = {} if (!window.dbs) window.dbs = {};
window.dbs[db.address.toString()] = db window.dbs[db.address.toString()] = db;
return db return db;
} }
export function connectDb(db, dispatch) { export function connectDb(db, dispatch) {
db.events.on('write', (dbname, hash, entry) => { db.events.on('write', (dbname, hash, entry) => {
dispatch({ dispatch({
type: 'ORBITDB_WRITE', type: 'ORBITDB_WRITE',
time: Date.now(), time: Date.now(),
address: db.address.toString(), address: db.address.toString(),
hash, hash,
entry entry,
}) });
}) });
db.events.on('replicated', address => { db.events.on('replicated', (address) => {
dispatch({ dispatch({
type: 'ORBITDB_REPLICATED', type: 'ORBITDB_REPLICATED',
time: Date.now(), time: Date.now(),
address: db.address.toString() address: db.address.toString(),
}) });
}) });
db.events.on('replicate.progress', (address, hash, entry, progress, have) => { db.events.on('replicate.progress', (address, hash, entry, progress, have) => {
dispatch({ dispatch({
type: 'ORBITDB_REPLICATE_PROGRESS', type: 'ORBITDB_REPLICATE_PROGRESS',
address: db.address.toString(), address: db.address.toString(),
hash, hash,
entry, entry,
progress, progress,
have, have,
time: Date.now(), time: Date.now(),
replicationInfo: Object.assign({}, db._replicationInfo) replicationInfo: Object.assign({}, db._replicationInfo),
}) });
}) });
db.events.on('replicate', address => { db.events.on('replicate', (address) => {
dispatch({ dispatch({
type: 'ORBITDB_REPLICATE', type: 'ORBITDB_REPLICATE',
time: Date.now(), time: Date.now(),
address: db.address.toString() address: db.address.toString(),
}) });
}) });
db.events.on('close', address => { db.events.on('close', (address) => {
dispatch({ dispatch({
type: 'ORBITDB_CLOSE', type: 'ORBITDB_CLOSE',
time: Date.now(), time: Date.now(),
address: db.address.toString() address: db.address.toString(),
}) });
}) });
db.events.on('load', address => { db.events.on('load', (address) => {
dispatch({ dispatch({
type: 'ORBITDB_LOAD', type: 'ORBITDB_LOAD',
time: Date.now(), time: Date.now(),
address: db.address.toString() address: db.address.toString(),
}) });
}) });
db.events.on('load.progress', (address, hash, entry, progress, total) => { db.events.on('load.progress', (address, hash, entry, progress, total) => {
dispatch({ dispatch({
type: 'ORBITDB_LOAD_PROGRESS', type: 'ORBITDB_LOAD_PROGRESS',
time: Date.now(), time: Date.now(),
address: db.address.toString(), address: db.address.toString(),
hash, hash,
entry, entry,
progress, progress,
total total,
}) });
}) });
} }

View File

@ -1,63 +1,69 @@
import { import {
OPENED_BOARD, OPENED_BOARD,
CLOSE_BOARD, CLOSE_BOARD,
UPDATE_BOARD, UPDATE_BOARD,
ORBITDB_REPLICATE_PROGRESS, ORBITDB_REPLICATE_PROGRESS,
ORBITDB_REPLICATED, ORBITDB_REPLICATED,
ORBITDB_REPLICATE ORBITDB_REPLICATE,
} from '../actions/actionTypes' } from '../actions/actionTypes';
function getInitialState() { function getInitialState() {
return { return {
boards: {} boards: {},
} };
} }
function updateBoard(existingBoards, address, value) { function updateBoard(existingBoards, address, value) {
return Object.assign({}, existingBoards, { return Object.assign({}, existingBoards, {
[address]: Object.assign({}, existingBoards[address] || {}, value) [address]: Object.assign({}, existingBoards[address] || {}, value),
}) });
} }
function deleteBoard(existingBoards, address) { function deleteBoard(existingBoards, address) {
const boards = Object.assign({}, existingBoards) const boards = Object.assign({}, existingBoards);
delete boards[address] delete boards[address];
return boards return boards;
} }
export default function BoardsReducer(state = getInitialState(), action) { export default function BoardsReducer(state = getInitialState(), action) {
let address let address;
switch (action.type) { switch (action.type) {
case OPENED_BOARD: case OPENED_BOARD:
address = action.board.address address = action.board.address;
return Object.assign({}, state, { boards: updateBoard(state.boards, address, Object.assign({}, action.board, { open: true })) }) return Object.assign({}, state, { boards: updateBoard(state.boards, address, Object.assign({}, action.board, { open: true })) });
case UPDATE_BOARD: case UPDATE_BOARD:
address = action.address address = action.address;
let { posts, metadata } = action const { posts, metadata } = action;
return Object.assign({}, state, { boards: updateBoard(state.boards, address, { posts, metadata })}) return Object.assign({}, state, { boards: updateBoard(state.boards, address, { posts, metadata }) });
case ORBITDB_REPLICATE: case ORBITDB_REPLICATE:
address = action.address address = action.address;
return Object.assign({}, state, { boards: updateBoard(state.boards, address, { return Object.assign({}, state, {
replicating: true boards: updateBoard(state.boards, address, {
})}) replicating: true,
case ORBITDB_REPLICATE_PROGRESS: }),
address = action.address });
return Object.assign({}, state, { boards: updateBoard(state.boards, address, { case ORBITDB_REPLICATE_PROGRESS:
replicating: true, address = action.address;
replicationInfo: action.replicationInfo return Object.assign({}, state, {
})}) boards: updateBoard(state.boards, address, {
case ORBITDB_REPLICATED: replicating: true,
address = action.address replicationInfo: action.replicationInfo,
return Object.assign({}, state, { boards: updateBoard(state.boards, address, { }),
replicating: false, });
lastReplicated: action.time case ORBITDB_REPLICATED:
})}) address = action.address;
case CLOSE_BOARD: return Object.assign({}, state, {
address = action.address boards: updateBoard(state.boards, address, {
return Object.assign({}, state, { replicating: false,
boards: deleteBoard(state.boards, address) lastReplicated: action.time,
}) }),
default: });
return state; case CLOSE_BOARD:
} address = action.address;
return Object.assign({}, state, {
boards: deleteBoard(state.boards, address),
});
default:
return state;
}
} }

View File

@ -1,12 +1,12 @@
import { combineReducers } from 'redux' import { combineReducers } from 'redux';
import { connectRouter} from 'connected-react-router' import { connectRouter } from 'connected-react-router';
import postReducer from './post' import postReducer from './post';
import boardsReducer from './boards' import boardsReducer from './boards';
import openBoardReducer from './openboard' import openBoardReducer from './openboard';
export default history => combineReducers({ export default history => combineReducers({
router: connectRouter(history), router: connectRouter(history),
postEditor: postReducer, postEditor: postReducer,
boards: boardsReducer, boards: boardsReducer,
openBoard: openBoardReducer openBoard: openBoardReducer,
}) });

View File

@ -1,21 +1,21 @@
import { import {
OPEN_BOARD, OPEN_BOARD,
OPENED_BOARD OPENED_BOARD,
} from '../actions/actionTypes' } from '../actions/actionTypes';
function getInitialState() { function getInitialState() {
return { return {
opening: false opening: false,
} };
} }
export default function openBoardReducer(state = getInitialState(), action) { export default function openBoardReducer(state = getInitialState(), action) {
switch (action.type) { switch (action.type) {
case OPEN_BOARD: case OPEN_BOARD:
return Object.assign({}, state, { opening: true }) return Object.assign({}, state, { opening: true });
case OPENED_BOARD: case OPENED_BOARD:
return Object.assign({}, state, { opening: false }) return Object.assign({}, state, { opening: false });
default: default:
return state return state;
} }
} }

View File

@ -1,19 +1,19 @@
import { ADD_POST } from '../actions/actionTypes' import { ADD_POST } from '../actions/actionTypes';
function getInitialState(){ function getInitialState() {
return { return {
post: { post: {
title: '', title: '',
content: '' content: '',
} },
} };
} }
export default function(state = getInitialState(), action) { export default function (state = getInitialState(), action) {
switch (action.type) { switch (action.type) {
case ADD_POST: case ADD_POST:
return Object.assign({}, state, { post: action.post }) return Object.assign({}, state, { post: action.post });
default: default:
return state return state;
} }
} }

View File

@ -9,13 +9,13 @@
// This link also includes instructions on opting out of this behavior. // This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean( const isLocalhost = Boolean(
window.location.hostname === 'localhost' || window.location.hostname === 'localhost'
// [::1] is the IPv6 localhost address. // [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' || || window.location.hostname === '[::1]'
// 127.0.0.1/8 is considered localhost for IPv4. // 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match( || window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/,
) ),
); );
export default function register() { export default function register() {
@ -40,8 +40,8 @@ export default function register() {
// service worker/PWA documentation. // service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => { navigator.serviceWorker.ready.then(() => {
console.log( console.log(
'This web app is being served cache-first by a service ' + 'This web app is being served cache-first by a service '
'worker. To learn more, visit https://goo.gl/SC7cgQ' + 'worker. To learn more, visit https://goo.gl/SC7cgQ',
); );
}); });
} else { } else {
@ -55,7 +55,7 @@ export default function register() {
function registerValidSW(swUrl) { function registerValidSW(swUrl) {
navigator.serviceWorker navigator.serviceWorker
.register(swUrl) .register(swUrl)
.then(registration => { .then((registration) => {
registration.onupdatefound = () => { registration.onupdatefound = () => {
const installingWorker = registration.installing; const installingWorker = registration.installing;
installingWorker.onstatechange = () => { installingWorker.onstatechange = () => {
@ -76,7 +76,7 @@ function registerValidSW(swUrl) {
}; };
}; };
}) })
.catch(error => { .catch((error) => {
console.error('Error during service worker registration:', error); console.error('Error during service worker registration:', error);
}); });
} }
@ -84,14 +84,14 @@ function registerValidSW(swUrl) {
function checkValidServiceWorker(swUrl) { function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page. // Check if the service worker can be found. If it can't reload the page.
fetch(swUrl) fetch(swUrl)
.then(response => { .then((response) => {
// Ensure service worker exists, and that we really are getting a JS file. // Ensure service worker exists, and that we really are getting a JS file.
if ( if (
response.status === 404 || response.status === 404
response.headers.get('content-type').indexOf('javascript') === -1 || response.headers.get('content-type').indexOf('javascript') === -1
) { ) {
// No service worker found. Probably a different app. Reload the page. // No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => { registration.unregister().then(() => {
window.location.reload(); window.location.reload();
}); });
@ -103,14 +103,14 @@ function checkValidServiceWorker(swUrl) {
}) })
.catch(() => { .catch(() => {
console.log( console.log(
'No internet connection found. App is running in offline mode.' 'No internet connection found. App is running in offline mode.',
); );
}); });
} }
export function unregister() { export function unregister() {
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => { navigator.serviceWorker.ready.then((registration) => {
registration.unregister(); registration.unregister();
}); });
} }

View File

@ -1,79 +1,81 @@
import { put, call, fork, take } from 'redux-saga/effects' import {
import { eventChannel } from 'redux-saga' put, call, fork, take,
import { push } from 'connected-react-router' } from 'redux-saga/effects';
import { open, connectDb } from '../orbitdb' import { eventChannel } from 'redux-saga';
import { createdBoard } from '../actions/board' import { push } from 'connected-react-router';
import { shortenAddress, closeBoard as closeOrbitDBBoard } from '../utils/orbitdb' import { open, connectDb } from '../orbitdb';
import { UPDATE_BOARD } from '../actions/actionTypes' import { createdBoard } from '../actions/board';
import { saveSaga } from './persistence' import { shortenAddress, closeBoard as closeOrbitDBBoard } from '../utils/orbitdb';
import { UPDATE_BOARD } from '../actions/actionTypes';
import { saveSaga } from './persistence';
export function* goToBoard({ board }){ export function* goToBoard({ board }) {
if (board.redirect) { if (board.redirect) {
yield put(push(shortenAddress(board.address))) yield put(push(shortenAddress(board.address)));
} }
} }
export function* updateBoard({ address }){ export function* updateBoard({ address }) {
const db = window.dbs[address] const db = window.dbs[address];
yield put({ yield put({
type: UPDATE_BOARD, type: UPDATE_BOARD,
address, address,
posts: db.posts, posts: db.posts,
metadata: Object.assign({}, db._index._index.metadata) // TODO: fix in lib and use db.metadata metadata: Object.assign({}, db._index._index.metadata), // TODO: fix in lib and use db.metadata
}) });
} }
export function* closeBoard({ address }){ export function* closeBoard({ address }) {
yield call(closeOrbitDBBoard, address) yield call(closeOrbitDBBoard, address);
yield saveSaga() yield saveSaga();
} }
export function* updateBoardMetadata({ address, metadata }){ export function* updateBoardMetadata({ address, metadata }) {
const db = window.dbs[address] const db = window.dbs[address];
if (db) { if (db) {
yield call([db, db.updateMetadata], [metadata]) yield call([db, db.updateMetadata], [metadata]);
yield goToBoard({ board: { address } }); yield goToBoard({ board: { address } });
} else { } else {
yield put({ type: 'ERROR', error: address + ' not found' }) yield put({ type: 'ERROR', error: `${address} not found` });
} }
} }
export function* openBoard({ board }) { export function* openBoard({ board }) {
let db let db;
try {
const metadata = board.title ? { title: board.title } : null;
db = yield call(open, board.address, metadata);
} catch (error) {
yield put({ type: 'ERROR', error });
}
if (db) {
const address = db.address.toString();
const dbInfo = { address };
dbInfo.posts = db.posts;
dbInfo.metadata = Object.assign({}, db._index._index.metadata); // TODO: fix in lib and use db.metadata
dbInfo.name = db.dbname;
try { try {
const metadata = board.title ? { title: board.title } : null const channel = yield call(createDbEventChannel, db);
db = yield call(open, board.address, metadata) yield fork(watchDb, channel);
yield put(createdBoard(Object.assign({ redirect: !!board.redirect }, board, dbInfo)));
} catch (error) { } catch (error) {
yield put({ type: 'ERROR', error }) yield put({ type: 'ERROR', error });
}
if (db) {
const address = db.address.toString()
const dbInfo = { address }
dbInfo.posts = db.posts
dbInfo.metadata = Object.assign({}, db._index._index.metadata) // TODO: fix in lib and use db.metadata
dbInfo.name = db.dbname
try {
const channel = yield call(createDbEventChannel, db)
yield fork(watchDb, channel)
yield put(createdBoard(Object.assign({ redirect: !!board.redirect }, board, dbInfo)))
} catch (error) {
yield put({ type: 'ERROR', error })
}
} }
}
} }
function* watchDb(channel) { function* watchDb(channel) {
// Dispatches action coming from the channel, terminates when ORBITDB_CLOSE arrives // Dispatches action coming from the channel, terminates when ORBITDB_CLOSE arrives
let action let action;
while(!action || action.type !== 'ORBITDB_CLOSE') { while (!action || action.type !== 'ORBITDB_CLOSE') {
action = yield take(channel) action = yield take(channel);
yield put(action) yield put(action);
} }
} }
function createDbEventChannel(db) { function createDbEventChannel(db) {
return eventChannel(emitter => { return eventChannel((emitter) => {
connectDb(db, emitter) connectDb(db, emitter);
return () => db.close() return () => db.close();
}) });
} }

View File

@ -1,28 +1,30 @@
import { takeEvery, put, call } from 'redux-saga/effects' import { takeEvery, put, call } from 'redux-saga/effects';
import { import {
OPEN_BOARD, OPEN_BOARD,
OPENED_BOARD, OPENED_BOARD,
CLOSE_BOARD, CLOSE_BOARD,
ADD_POST, ADD_POST,
ORBITDB_REPLICATED, ORBITDB_REPLICATED,
ORBITDB_WRITE, ORBITDB_WRITE,
UPDATE_BOARD_METADATA UPDATE_BOARD_METADATA,
} from '../actions/actionTypes' } from '../actions/actionTypes';
import { openBoard, updateBoard, goToBoard, updateBoardMetadata, closeBoard } from './boards' import {
import { addPost } from './posts' openBoard, updateBoard, goToBoard, updateBoardMetadata, closeBoard,
import { openPreviousBoards, saveSaga } from './persistence' } from './boards';
import { addPost } from './posts';
import { openPreviousBoards, saveSaga } from './persistence';
export default function* saga(){ export default function* saga() {
yield takeEvery(OPEN_BOARD, openBoard) yield takeEvery(OPEN_BOARD, openBoard);
yield takeEvery(OPENED_BOARD, goToBoard) yield takeEvery(OPENED_BOARD, goToBoard);
yield takeEvery(OPENED_BOARD, saveSaga) yield takeEvery(OPENED_BOARD, saveSaga);
yield takeEvery(CLOSE_BOARD, closeBoard) yield takeEvery(CLOSE_BOARD, closeBoard);
yield takeEvery(ADD_POST, addPost) yield takeEvery(ADD_POST, addPost);
yield takeEvery(UPDATE_BOARD_METADATA, updateBoardMetadata) yield takeEvery(UPDATE_BOARD_METADATA, updateBoardMetadata);
yield takeEvery(ORBITDB_WRITE, updateBoard) yield takeEvery(ORBITDB_WRITE, updateBoard);
yield takeEvery(ORBITDB_REPLICATED, updateBoard) yield takeEvery(ORBITDB_REPLICATED, updateBoard);
yield openPreviousBoards() yield openPreviousBoards();
} }

View File

@ -1,16 +1,16 @@
import { call, put } from 'redux-saga/effects' import { call, put } from 'redux-saga/effects';
import { openBoard } from '../actions/board' import { openBoard } from '../actions/board';
import { save, load } from '../utils/persistence' import { save, load } from '../utils/persistence';
export function* openPreviousBoards() { export function* openPreviousBoards() {
const data = yield call(load) const data = yield call(load);
if (Array.isArray(data.addresses)) { if (Array.isArray(data.addresses)) {
for (const address of data.addresses) { for (const address of data.addresses) {
yield put(openBoard({ address, redirect: false })) yield put(openBoard({ address, redirect: false }));
}
} }
}
} }
export function* saveSaga() { export function* saveSaga() {
yield call(save) yield call(save);
} }

View File

@ -1,18 +1,18 @@
import { call } from 'redux-saga/effects' import { call } from 'redux-saga/effects';
import { goToBoard } from './boards'; import { goToBoard } from './boards';
export function* addPost({ address, post }) { export function* addPost({ address, post }) {
const db = window.dbs[address] const db = window.dbs[address];
const { title, text } = post const { title, text } = post;
yield call([db, db.addPost], { title, text }) yield call([db, db.addPost], { title, text });
yield goToBoard({ board: { address, redirect: true } }); yield goToBoard({ board: { address, redirect: true } });
// TODO: goto post // TODO: goto post
} }
export function* editPost({ address, postId, post }) { export function* editPost({ address, postId, post }) {
const db = window.dbs[address] const db = window.dbs[address];
const { title, text } = post const { title, text } = post;
yield call([db, db.updatePost], postId, { title, text }) yield call([db, db.updatePost], postId, { title, text });
yield goToBoard({ board: { address, redirect: true } }); yield goToBoard({ board: { address, redirect: true } });
// TODO: goto post // TODO: goto post
} }

View File

@ -1,9 +1,9 @@
import {createStore, compose, applyMiddleware} from 'redux'; import { createStore, compose, applyMiddleware } from 'redux';
import reduxImmutableStateInvariant from 'redux-immutable-state-invariant'; import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
import createSagaMiddleware from 'redux-saga'; import createSagaMiddleware from 'redux-saga';
import saga from '../sagas';
import createHistory from 'history/createHashHistory'; import createHistory from 'history/createHashHistory';
import { routerMiddleware } from 'connected-react-router'; import { routerMiddleware } from 'connected-react-router';
import saga from '../sagas';
import createRootReducer from '../reducers'; import createRootReducer from '../reducers';
const sagaMiddleware = createSagaMiddleware(); const sagaMiddleware = createSagaMiddleware();
@ -17,9 +17,8 @@ function configureStoreProd(initialState) {
]; ];
const store = createStore(createRootReducer(history), initialState, compose( const store = createStore(createRootReducer(history), initialState, compose(
applyMiddleware(...middlewares) applyMiddleware(...middlewares),
) ));
);
sagaMiddleware.run(saga); sagaMiddleware.run(saga);
@ -36,9 +35,8 @@ function configureStoreDev(initialState) {
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // add support for Redux dev tools const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // add support for Redux dev tools
const store = createStore(createRootReducer(history), initialState, composeEnhancers( const store = createStore(createRootReducer(history), initialState, composeEnhancers(
applyMiddleware(...middlewares) applyMiddleware(...middlewares),
) ));
);
let sagaTask = sagaMiddleware.run(saga); let sagaTask = sagaMiddleware.run(saga);

View File

@ -1,55 +1,55 @@
export async function ipfsPut(content) { export async function ipfsPut(content) {
const obj = { const obj = {
content: Buffer.from(content), content: Buffer.from(content),
path: '/' path: '/',
} };
const response = await window.ipfs.files.add(obj) const response = await window.ipfs.files.add(obj);
return response[0].hash return response[0].hash;
} }
export async function readText(multihash) { export async function readText(multihash) {
const buffer = await window.ipfs.object.get(multihash) const buffer = await window.ipfs.object.get(multihash);
return buffer.toString('utf-8') return buffer.toString('utf-8');
} }
export async function getStats() { export async function getStats() {
const ipfs = window.ipfs; const ipfs = window.ipfs;
const orbitDb = window.orbitDb const orbitDb = window.orbitDb;
const dbs = {} const dbs = {};
const stats = {} const stats = {};
if (ipfs && ipfs.isOnline()) { if (ipfs && ipfs.isOnline()) {
stats.ipfsLoaded = true stats.ipfsLoaded = true;
const peers = await ipfs.swarm.peers() const peers = await ipfs.swarm.peers();
const id = await ipfs.id() const id = await ipfs.id();
stats.peers = peers.map(p => p.peer.id._idB58String) stats.peers = peers.map(p => p.peer.id._idB58String);
stats.id = id.id stats.id = id.id;
} else { } else {
stats.ipfsLoaded = false stats.ipfsLoaded = false;
} }
if (stats.ipfsLoaded && orbitDb) { if (stats.ipfsLoaded && orbitDb) {
stats.orbitDbLoaded = true stats.orbitDbLoaded = true;
stats.pubKey = await orbitDb.key.getPublic('hex') stats.pubKey = await orbitDb.key.getPublic('hex');
Object.values(window.dbs || {}).forEach(db => { Object.values(window.dbs || {}).forEach((db) => {
let writeable = db.access.write.indexOf('*') >= 0 || db.access.write.indexOf(stats.pubKey) >= 0 const writeable = db.access.write.indexOf('*') >= 0 || db.access.write.indexOf(stats.pubKey) >= 0;
const dbInfo = { const dbInfo = {
opLogLength: db._oplog.length, opLogLength: db._oplog.length,
access: { access: {
admin: db.access.admin, admin: db.access.admin,
read: db.access.read, read: db.access.read,
write: db.access.write, write: db.access.write,
writeable writeable,
}, },
peers: [] peers: [],
} };
const subscription = orbitDb._pubsub._subscriptions[db.address] const subscription = orbitDb._pubsub._subscriptions[db.address];
if (subscription && subscription.room) { if (subscription && subscription.room) {
dbInfo.peers = [...(subscription.room._peers || [])] dbInfo.peers = [...(subscription.room._peers || [])];
} }
dbs[db.address] = dbInfo dbs[db.address] = dbInfo;
}) });
} else { } else {
stats.orbitDbLoaded = false stats.orbitDbLoaded = false;
} }
stats.dbs = dbs stats.dbs = dbs;
return stats return stats;
} }

View File

@ -1,14 +1,14 @@
export function getBoardAddress(hash, name) { export function getBoardAddress(hash, name) {
return '/orbitdb/' + hash + '/' + name return `/orbitdb/${hash}/${name}`;
} }
export function shortenAddress(address) { export function shortenAddress(address) {
return address.replace(/^\/orbitdb/, '/b') return address.replace(/^\/orbitdb/, '/b');
} }
export function closeBoard(address) { export function closeBoard(address) {
const db = window.dbs[address] const db = window.dbs[address];
delete window.dbs[address] delete window.dbs[address];
if (db && db.close) db.close() if (db && db.close) db.close();
} }

View File

@ -1,16 +1,16 @@
export function save(){ export function save() {
const obj = { const obj = {
addresses: Object.keys(window.dbs || {}) addresses: Object.keys(window.dbs || {}),
} };
localStorage.setItem('ipfs-boards-v0', JSON.stringify(obj)) localStorage.setItem('ipfs-boards-v0', JSON.stringify(obj));
} }
export function load(){ export function load() {
const str = localStorage.getItem('ipfs-boards-v0') const str = localStorage.getItem('ipfs-boards-v0');
try { try {
return JSON.parse(str) || {}; return JSON.parse(str) || {};
} catch (error) { } catch (error) {
return {} return {};
} }
} }