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 OPENED_BOARD = 'OPENED_BOARD'
export const CLOSE_BOARD = 'CLOSE_BOARD'
export const UPDATE_BOARD = 'UPDATE_BOARD'

View File

@ -1,5 +1,6 @@
import {
OPEN_BOARD,
CLOSE_BOARD,
OPENED_BOARD,
UPDATE_BOARD_METADATA
} from './actionTypes'
@ -25,3 +26,10 @@ export function updateBoardMetadata(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)}
/>
</Form.Field>
<Form.Group fluid widths="equal">
<Form.Group widths="equal">
<Form.Field>
<label>Website</label>
<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 BoardsItem from './BoardsItem'
export default function Boards({ stats, boards, createBoard }) {
export default function Boards({ stats, boards, createBoard, closeBoard }) {
return <Grid container divided colums={2}>
<Grid.Column width={8}>
<Header size='large' style={{marginTop:'.5em'}}>
@ -65,7 +65,7 @@ export default function Boards({ stats, boards, createBoard }) {
</Grid.Column>
<Grid.Column width={8} style={{paddingTop:'3em'}}>
<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}
</Card.Group>
</Grid.Column>

View File

@ -1,9 +1,9 @@
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 { shortenAddress } from '../utils/orbitdb'
export default function BoardsItem({ address, metadata, name }) {
export default function BoardsItem({ address, metadata, name, closeBoard }) {
return <Card fluid>
<Card.Content>
<Card.Header>
@ -29,8 +29,15 @@ export default function BoardsItem({ address, metadata, name }) {
</List.Item>
</List>
</Card.Content>
<Card.Content extra>
<Button as={Link} to={shortenAddress(address)} basic fluid>View</Button>
<Card.Content>
<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>
}

View File

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

View File

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

View File

@ -1,5 +1,6 @@
import {
OPENED_BOARD,
CLOSE_BOARD,
UPDATE_BOARD,
ORBITDB_REPLICATE_PROGRESS,
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) {
let address
switch (action.type) {
case OPENED_BOARD:
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:
address = action.address
let { posts, metadata } = action
@ -45,6 +52,11 @@ export default function BoardsReducer(state = getInitialState(), action) {
replicating: false,
lastReplicated: action.time
})})
case CLOSE_BOARD:
address = action.address
return Object.assign({}, state, {
boards: deleteBoard(state.boards, address)
})
default:
return state;
}

View File

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

View File

@ -1,15 +1,29 @@
import { takeEvery } from 'redux-saga/effects'
import { OPEN_BOARD, OPENED_BOARD, ADD_POST, ORBITDB_REPLICATED, ORBITDB_WRITE, UPDATE_BOARD_METADATA } from '../actions/actionTypes'
import { openBoard, updateBoard, goToBoard, updateBoardMetadata } from './boards'
import { takeEvery, put, call } from 'redux-saga/effects'
import {
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 { openPreviousBoards, saveSaga } from './persistence'
export default function* saga(){
yield takeEvery(OPEN_BOARD, openBoard)
yield takeEvery(OPENED_BOARD, goToBoard)
yield takeEvery(OPENED_BOARD, saveSaga)
yield takeEvery(CLOSE_BOARD, closeBoard)
yield takeEvery(ADD_POST, addPost)
yield takeEvery(UPDATE_BOARD_METADATA, updateBoardMetadata)
yield takeEvery(ORBITDB_WRITE, 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 dbs = {}
const stats = {}
if (ipfs) {
if (ipfs && ipfs.isOnline()) {
stats.ipfsLoaded = true
const peers = await ipfs.swarm.peers()
const id = await ipfs.id()
@ -26,7 +26,7 @@ export async function getStats() {
} else {
stats.ipfsLoaded = false
}
if (orbitDb) {
if (stats.ipfsLoaded && orbitDb) {
stats.orbitDbLoaded = true
stats.pubKey = await orbitDb.key.getPublic('hex')
Object.values(window.dbs || {}).forEach(db => {

View File

@ -6,3 +6,9 @@ export function getBoardAddress(hash, name) {
export function shortenAddress(address) {
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 {}
}
}