1
0
mirror of https://github.com/fazo96/ipfs-boards synced 2025-01-09 12:19:49 +01:00

implemented persistence with boards opened before

This commit is contained in:
Enrico Fasoli 2018-02-09 17:49:39 +01:00
parent 73eaff3b87
commit e4b0a16cf0
No known key found for this signature in database
GPG Key ID: 1238873C5F27DB4D
15 changed files with 115 additions and 21 deletions

View File

@ -4,6 +4,7 @@ export const UPDATE_BOARD_METADATA = 'UPDATE_BOARD_METADATA'
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 UPDATE_BOARD = 'UPDATE_BOARD' export const UPDATE_BOARD = 'UPDATE_BOARD'

View File

@ -1,5 +1,6 @@
import { import {
OPEN_BOARD, OPEN_BOARD,
CLOSE_BOARD,
OPENED_BOARD, OPENED_BOARD,
UPDATE_BOARD_METADATA UPDATE_BOARD_METADATA
} from './actionTypes' } from './actionTypes'
@ -24,4 +25,11 @@ export function updateBoardMetadata(address, metadata) {
address, address,
metadata metadata
} }
}
export function closeBoard(address) {
return {
type: CLOSE_BOARD,
address
}
} }

View File

@ -37,7 +37,7 @@ export default class BoardEditorForm extends Component {
onChange={this.updateTitle.bind(this)} onChange={this.updateTitle.bind(this)}
/> />
</Form.Field> </Form.Field>
<Form.Group fluid widths="equal"> <Form.Group widths="equal">
<Form.Field> <Form.Field>
<label>Website</label> <label>Website</label>
<input <input

View File

@ -3,7 +3,7 @@ import { List, Icon, Segment, Divider, Grid, Header, Button, Card } from 'semant
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import BoardsItem from './BoardsItem' import BoardsItem from './BoardsItem'
export default function Boards({ stats, boards, createBoard }) { export default function Boards({ stats, boards, createBoard, closeBoard }) {
return <Grid container divided colums={2}> return <Grid container divided colums={2}>
<Grid.Column width={8}> <Grid.Column width={8}>
<Header size='large' style={{marginTop:'.5em'}}> <Header size='large' style={{marginTop:'.5em'}}>
@ -65,7 +65,7 @@ export default function Boards({ stats, boards, createBoard }) {
</Grid.Column> </Grid.Column>
<Grid.Column width={8} style={{paddingTop:'3em'}}> <Grid.Column width={8} style={{paddingTop:'3em'}}>
<Card.Group className="centered"> <Card.Group className="centered">
{Object.values(boards).map(board => <BoardsItem key={board.address} {...board} />)} {Object.values(boards).map(board => <BoardsItem key={board.address} closeBoard={closeBoard} {...board} />)}
{Object.keys(boards).length === 0 ? <Segment>No boards opened</Segment> : null} {Object.keys(boards).length === 0 ? <Segment>No boards opened</Segment> : null}
</Card.Group> </Card.Group>
</Grid.Column> </Grid.Column>

View File

@ -1,9 +1,9 @@
import React from 'react' import React from 'react'
import { List, Button, Card } from 'semantic-ui-react' import { Icon, List, Button, Card } from 'semantic-ui-react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { shortenAddress } from '../utils/orbitdb' import { shortenAddress } from '../utils/orbitdb'
export default function BoardsItem({ address, metadata, name }) { export default function BoardsItem({ address, metadata, name, closeBoard }) {
return <Card fluid> return <Card fluid>
<Card.Content> <Card.Content>
<Card.Header> <Card.Header>
@ -29,8 +29,15 @@ export default function BoardsItem({ address, metadata, name }) {
</List.Item> </List.Item>
</List> </List>
</Card.Content> </Card.Content>
<Card.Content extra> <Card.Content>
<Button as={Link} to={shortenAddress(address)} basic fluid>View</Button> <div className="ui two buttons">
<Button onClick={() => closeBoard(address)} basic>
<Icon name="close"/> Close
</Button>
<Button as={Link} to={shortenAddress(address)} basic>
<Icon name="list"/> View
</Button>
</div>
</Card.Content> </Card.Content>
</Card> </Card>
} }

View File

@ -43,7 +43,7 @@ export default class OpenBoardForm extends Component {
<Button as={Link} to={'/'}> <Button as={Link} to={'/'}>
<Icon name="arrow left"/> Back <Icon name="arrow left"/> Back
</Button> </Button>
<Button type="submit" onClick={() => openBoard({ address })}> <Button type="submit" onClick={() => openBoard({ address, redirect: true })}>
<Icon name="plus"/> Open <Icon name="plus"/> Open
</Button> </Button>
</div> </div>

View File

@ -3,11 +3,16 @@ import { connect } from 'react-redux'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import BoardsComponent from '../components/Boards' import BoardsComponent from '../components/Boards'
import WithStats from './WithStats' import WithStats from './WithStats'
import { closeBoard } from '../actions/board'
const WrappedComponent = WithStats(BoardsComponent) const WrappedComponent = WithStats(BoardsComponent)
function Boards({ boards, createBoard }) { function Boards({ boards, createBoard, closeBoard }) {
return <WrappedComponent boards={boards} createBoard={createBoard} /> return <WrappedComponent
boards={boards}
createBoard={createBoard}
closeBoard={closeBoard}
/>
} }
function mapStateToProps(state){ function mapStateToProps(state){
@ -18,7 +23,8 @@ function mapStateToProps(state){
function mapDispatchToProps(dispatch){ function mapDispatchToProps(dispatch){
return { return {
createBoard: () => dispatch(push('/b/new')) createBoard: () => dispatch(push('/b/new')),
closeBoard: address => dispatch(closeBoard(address)),
} }
} }

View File

@ -12,7 +12,7 @@ function mapStateToProps(state){
function mapDispatchToProps(dispatch){ function mapDispatchToProps(dispatch){
return { return {
openBoard: address => dispatch(openBoard({ address })) openBoard: address => dispatch(openBoard({ address, redirect: false }))
} }
} }

View File

@ -1,5 +1,6 @@
import { import {
OPENED_BOARD, OPENED_BOARD,
CLOSE_BOARD,
UPDATE_BOARD, UPDATE_BOARD,
ORBITDB_REPLICATE_PROGRESS, ORBITDB_REPLICATE_PROGRESS,
ORBITDB_REPLICATED, ORBITDB_REPLICATED,
@ -18,12 +19,18 @@ function updateBoard(existingBoards, address, value) {
}) })
} }
function deleteBoard(existingBoards, address) {
const boards = Object.assign({}, existingBoards)
delete boards[address]
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, action.board) }) 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 let { posts, metadata } = action
@ -45,6 +52,11 @@ export default function BoardsReducer(state = getInitialState(), action) {
replicating: false, replicating: false,
lastReplicated: action.time lastReplicated: action.time
})}) })})
case CLOSE_BOARD:
address = action.address
return Object.assign({}, state, {
boards: deleteBoard(state.boards, address)
})
default: default:
return state; return state;
} }

View File

@ -3,11 +3,14 @@ import { eventChannel } from 'redux-saga'
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import { open, connectDb } from '../orbitdb' import { open, connectDb } from '../orbitdb'
import { createdBoard } from '../actions/board' import { createdBoard } from '../actions/board'
import { shortenAddress } from '../utils/orbitdb' import { shortenAddress, closeBoard as closeOrbitDBBoard } from '../utils/orbitdb'
import { UPDATE_BOARD } from '../actions/actionTypes' import { UPDATE_BOARD } from '../actions/actionTypes'
import { saveSaga } from './persistence'
export function* goToBoard({ board }){ export function* goToBoard({ board }){
yield put(push(shortenAddress(board.address))) if (board.redirect) {
yield put(push(shortenAddress(board.address)))
}
} }
export function* updateBoard({ address }){ export function* updateBoard({ address }){
@ -20,6 +23,11 @@ export function* updateBoard({ address }){
}) })
} }
export function* closeBoard({ address }){
yield call(closeOrbitDBBoard, address)
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) {
@ -47,7 +55,7 @@ export function* openBoard({ board }) {
try { try {
const channel = yield call(createDbEventChannel, db) const channel = yield call(createDbEventChannel, db)
yield fork(watchDb, channel) yield fork(watchDb, channel)
yield put(createdBoard(Object.assign({}, board, dbInfo))) yield put(createdBoard(Object.assign({ redirect: !!board.redirect }, board, dbInfo)))
} catch (error) { } catch (error) {
yield put({ type: 'ERROR', error }) yield put({ type: 'ERROR', error })
} }

View File

@ -1,15 +1,29 @@
import { takeEvery } from 'redux-saga/effects' import { takeEvery, put, call } from 'redux-saga/effects'
import { OPEN_BOARD, OPENED_BOARD, ADD_POST, ORBITDB_REPLICATED, ORBITDB_WRITE, UPDATE_BOARD_METADATA } from '../actions/actionTypes' import {
import { openBoard, updateBoard, goToBoard, updateBoardMetadata } from './boards' OPEN_BOARD,
OPENED_BOARD,
CLOSE_BOARD,
ADD_POST,
ORBITDB_REPLICATED,
ORBITDB_WRITE,
UPDATE_BOARD_METADATA
} from '../actions/actionTypes'
import { openBoard, updateBoard, goToBoard, updateBoardMetadata, closeBoard } from './boards'
import { openBoard as openBoardAction } from '../actions/board'
import { addPost } from './posts' 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(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()
} }

16
src/sagas/persistence.js Normal file
View File

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

View File

@ -17,7 +17,7 @@ export async function getStats() {
const orbitDb = window.orbitDb const orbitDb = window.orbitDb
const dbs = {} const dbs = {}
const stats = {} const stats = {}
if (ipfs) { 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()
@ -26,7 +26,7 @@ export async function getStats() {
} else { } else {
stats.ipfsLoaded = false stats.ipfsLoaded = false
} }
if (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 => {

View File

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

16
src/utils/persistence.js Normal file
View File

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