Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update discussions app to support 3box threads #2003

Open
wants to merge 2 commits into
base: experimental-oe-fixes
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions apps/discussions/app/ipfs/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import axios from 'axios'
import ipfsClient from 'ipfs-http-client'

export const ipfs = ipfsClient({
host: 'ipfs.autark.xyz',
port: '5001',
protocol: 'https',
})
const environments = {
development: { host: 'localhost', port: '5001', protocol: 'http' },
production: { host: 'ipfs.autark.xyz', port: '5001', protocol: 'https' },
staging: { host: 'ipfs.autark.xyz', port: '5001', protocol: 'https' },
}

const config = environments[process.env.NODE_ENV]

export const ipfs = ipfsClient(config)

export const ipfsGet = async hash => {
const endpoint = `${config.protocol}://${config.host}:8080/ipfs/${hash}`
try {
const { data } = await axios.get(endpoint)
return data
} catch (err) {
console.error('Error getting data from IPFS', err)
}
}
23 changes: 18 additions & 5 deletions apps/discussions/app/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import '@babel/polyfill'
import { of } from 'rxjs'
import AragonApi from '@aragon/api'

import { handleHide, handlePost, handleRevise, initialState } from './state'
import {
updateThread,
deleteThread,
handleHide,
handlePost,
handleRevise,
initialState
} from './state'

const INITIALIZATION_TRIGGER = Symbol('INITIALIZATION_TRIGGER')

Expand All @@ -13,16 +20,22 @@ api.store(
let newState
switch (event.event) {
case INITIALIZATION_TRIGGER:
newState = { ...initialState, syncing: false }
newState = { ...initialState, isSyncing: false }
return newState
case 'SYNC_STATUS_SYNCING':
newState = { ...initialState, syncing: true }
newState = { ...initialState, isSyncing: true }
return newState
case 'SYNC_STATUS_SYNCED':
newState = { ...initialState, syncing: false }
newState = { ...initialState, isSyncing: false }
return newState
case 'ACCOUNTS_TRIGGER':
newState = { ...initialState, syncing: false }
newState = { ...initialState, isSyncing: false }
return newState
case 'UpdateThread':
newState = await updateThread(state, event.returnValues)
return newState
case 'DeleteThread':
newState = await deleteThread(state, event.returnValues)
return newState
case 'Post':
newState = await handlePost(state, event)
Expand Down
1 change: 1 addition & 0 deletions apps/discussions/app/state/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './initialState'
export * from './updateState'
export * from './threads'
3 changes: 2 additions & 1 deletion apps/discussions/app/state/initialState.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const initialState = {
threads: [],
discussions: {},
syncing: true,
isSyncing: true,
}
44 changes: 44 additions & 0 deletions apps/discussions/app/state/threads.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ipfsGet } from '../ipfs'

export const updateThread = async (state, { thread, metadata }) => {
const { threads = [] } = state
try {
const {
name,
title,
description,
creationDate,
context,
author,
} = await ipfsGet(metadata)
const index = threads.findIndex(t => t.address === thread)
if (index > -1) threads[index].description = description
else {
threads.push({
address: thread,
name,
title,
description,
context,
creationDate: new Date(creationDate),
author,
})
}
return {
...state,
threads
}
} catch (e) {
console.error(e)
return state
}
}

export const deleteThread = async (state, { thread }) => {
const { threads = [] } = state
const filteredThreads = threads.filter(t => t.address !== thread)
return {
...state,
threads: filteredThreads
}
}
9 changes: 7 additions & 2 deletions apps/discussions/arapp.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
{
"roles": [
{
"name": "Is doing nothing, just needed to technically be an AragonApp",
"id": "EMPTY_ROLE",
"name": "Register role",
"id": "REGISTER_ROLE",
"params": []
},
{
"name": "Moderator role",
"id": "MODERATOR_ROLE",
"params": []
}
],
Expand Down
42 changes: 41 additions & 1 deletion apps/discussions/contracts/DiscussionApp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,16 @@ contract DiscussionApp is IForwarder, AragonApp {
);
event Hide(address indexed author, uint256 discussionThreadId, uint256 postId, uint256 hiddenAt);
event CreateDiscussionThread(uint256 actionId, bytes _evmScript);
event UpdateThread(string thread, string metadata);
event DeleteThread(string thread);

bytes32 public constant EMPTY_ROLE = keccak256("EMPTY_ROLE");
bytes32 public constant REGISTER_ROLE = keccak256("REGISTER_ROLE");
bytes32 public constant MODERATOR_ROLE = keccak256("MODERATOR_ROLE");

string private constant ERROR_CAN_NOT_FORWARD = "DISCUSSIONS_CAN_NOT_FORWARD";
string private constant ERROR_ALREADY_EXISTS = "You cannot create an existing thread.";
string private constant ERROR_EDIT_NOT_AUTHOR = "You cannot edit a thread you did not author.";
string private constant ERROR_DELETE_NOT_AUTHOR = "You cannot delete a thread you did not author.";

struct DiscussionPost {
address author;
Expand All @@ -38,6 +44,8 @@ contract DiscussionApp is IForwarder, AragonApp {

mapping(uint256 => DiscussionPost[]) public discussionThreadPosts;

mapping(string => address) private threadAuthors;

function initialize() external onlyInit {
discussionThreadId = 0;
initialized();
Expand Down Expand Up @@ -96,6 +104,38 @@ contract DiscussionApp is IForwarder, AragonApp {
);
}

/**
* @notice Create new thread at `thread`
* @param thread The address of the 3Box thread
* @param metadata The IPFS hash of the metadata for the thread
*/
function registerThread(string thread, string metadata)
auth(REGISTER_ROLE) external {
require(threadAuthors[thread] == 0, ERROR_ALREADY_EXISTS);
threadAuthors[thread] = msg.sender;
emit UpdateThread(thread, metadata);
}

/**
* @notice Edit thread at `thread`
* @param thread The address of the 3Box thread
* @param newMetadata The IPFS hash of the new metadata for the thread
*/
function editThread(string thread, string newMetadata)
auth(REGISTER_ROLE) external {
require(threadAuthors[thread] == msg.sender, ERROR_EDIT_NOT_AUTHOR);
emit UpdateThread(thread, newMetadata);
}

/**
* @notice Delete thread at `thread`
* @param thread The address of the 3Box thread
*/
function deleteThread(string thread) auth(REGISTER_ROLE) external {
require(threadAuthors[thread] == msg.sender, ERROR_DELETE_NOT_AUTHOR);
emit DeleteThread(thread);
}

// Forwarding fns

/**
Expand Down
2 changes: 1 addition & 1 deletion apps/discussions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"clean:start": "aragon clean && rm -rf ~/.aragon && npm run start:http:template",
"start": "npm run start:ipfs",
"start:ipfs": "aragon run",
"start:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:http": "NODE_ENV=development aragon run --http localhost:8001 --http-served-from ./dist",
"start:ipfs:template": "npm run start:ipfs -- --template Template --template-init @ARAGON_ENS",
"start:http:template": "rm -rf build && npm run start:http -- --template Template --template-init @ARAGON_ENS",
"start:app": "cd app && npm start && cd ..",
Expand Down
11 changes: 10 additions & 1 deletion apps/discussions/public/meta/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"lerna": "^3.10.8",
"truffle-privatekey-provider": "1.1.0",
"wait-on": "^3.2.0",
"web3": "^1.2.4"
"web3": "1.2.7"
},
"scripts": {
"bootstrap": "lerna bootstrap --hoist",
Expand Down Expand Up @@ -74,7 +74,7 @@
"start:dev-template": "cd templates/dev && npm run publish:aragen && npm run start:template:aragen",
"start:address": "cd apps/address-book && npm start",
"start:allocations": "cd apps/allocations && npm start",
"start:dev": "DEV=true apps/shared/test-helpers/ganache-cli.sh",
"start:dev": "NODE_ENV=development DEV=true apps/shared/test-helpers/ganache-cli.sh",
"start:no:client": "NO_CLIENT=true apps/shared/test-helpers/ganache-cli.sh",
"start:dot": "cd apps/dot-voting && npm start",
"start:projects": "cd apps/projects && npm start",
Expand Down
2 changes: 1 addition & 1 deletion templates/dev/contracts/BaseCache.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract BaseCache is BaseTemplate {
ENS(_deployedSetupContracts[1]),
MiniMeTokenFactory(_deployedSetupContracts[2]),
IFIFSResolvingRegistrar(_deployedSetupContracts[3])
) {}
) public {}

function _cacheBase(
ACL _acl,
Expand Down
8 changes: 5 additions & 3 deletions templates/dev/contracts/BaseOEApps.sol
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,13 @@ contract BaseOEApps is BaseCache, TokenCache {
/* DISCUSSIONS */

function _installDiscussionsApp(Kernel _dao) internal returns (DiscussionApp) {
return DiscussionApp(_installNonDefaultApp(_dao, DISCUSSIONS_APP_ID));
bytes memory initializeData = abi.encodeWithSelector(DiscussionApp(0).initialize.selector);
return DiscussionApp(_installNonDefaultApp(_dao, DISCUSSIONS_APP_ID, initializeData));
}

function _createDiscussionsPermissions(ACL _acl, DiscussionApp _discussions, address _grantee, address _manager) internal {
_acl.createPermission(_grantee, _discussions, _discussions.EMPTY_ROLE(), _manager);
function _createDiscussionsPermissions(ACL _acl, DiscussionApp _discussions, address _moderator, address _register, address _manager) internal {
_acl.createPermission(_moderator, _discussions, _discussions.MODERATOR_ROLE(), _manager);
_acl.createPermission(_register, _discussions, _discussions.REGISTER_ROLE(), _manager);
}

/* PROJECTS */
Expand Down
13 changes: 8 additions & 5 deletions templates/dev/contracts/DevTemplate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -158,18 +158,21 @@ contract DevTemplate is BaseOEApps {
)
internal
{
if (_useDiscussions) {
DiscussionApp discussions = _installDiscussionsApp(_dao);
_createDiscussionsPermissions(_acl, discussions, ANY_ENTITY, _voting);
}

MiniMeToken token = _popTokenCache(msg.sender);
AddressBook addressBook = _installAddressBookApp(_dao);
Allocations allocations = _installAllocationsApp(_dao, _vault, _allocationsPeriod == 0 ? DEFAULT_PERIOD : _allocationsPeriod);
DotVoting dotVoting = _installDotVotingApp(_dao, token, _dotVotingSettings);
Projects projects = _installProjectsApp(_dao, _vault);
Rewards rewards = _installRewardsApp(_dao, _vault);

if (_useDiscussions) {
DiscussionApp discussions = _installDiscussionsApp(_dao);
// _createDiscussionsPermissions(_acl, discussions, msg.sender, _tokenManager, _voting); // Stack too deep
_acl.createPermission(msg.sender, discussions, discussions.MODERATOR_ROLE(), _voting);
_acl.createPermission(_tokenManager, discussions, discussions.REGISTER_ROLE(), _voting);
}


_setupOEPermissions(
_acl,
_tokenManager,
Expand Down