From 868aa20cf50f46e382e2588ed179c7ad50bfdeea Mon Sep 17 00:00:00 2001 From: ZeroSum24 Date: Tue, 15 Sep 2020 19:51:17 +0100 Subject: [PATCH 1/5] Template file for dashboard actions --- src/actions/dashboard.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/actions/dashboard.js diff --git a/src/actions/dashboard.js b/src/actions/dashboard.js new file mode 100644 index 0000000..e69de29 From cd888215c936a685c81e39fa48d350c5adddbf56 Mon Sep 17 00:00:00 2001 From: ZeroSum24 Date: Tue, 15 Sep 2020 21:34:32 +0100 Subject: [PATCH 2/5] Dashboard data integration: redux setup, prepopulation through function trigger in login; data calculation for distinct values --- src/actions/dashboard.js | 129 ++++++++++++++++++++ src/actions/profile.js | 17 +++ src/actions/search.js | 2 +- src/actions/user.js | 2 + src/pages/dashboard/Dashboard.js | 22 +++- src/pages/dashboard/components/DeviceRow.js | 7 ++ src/pages/dashboard/components/TableRow.js | 25 ++-- src/reducers/dashboard.js | 38 ++++++ src/reducers/index.js | 2 + 9 files changed, 231 insertions(+), 13 deletions(-) create mode 100644 src/reducers/dashboard.js diff --git a/src/actions/dashboard.js b/src/actions/dashboard.js index e69de29..79eb45e 100644 --- a/src/actions/dashboard.js +++ b/src/actions/dashboard.js @@ -0,0 +1,129 @@ +import {getAllUsers, retrieveAllAvailableFirmware, retrieveAllBounties} from "../blockchain/contracts"; +import {containsIgnoreCase} from "./search"; +import {getUserDevices} from "./profile"; +export const DASHBOARD_DATA = 'DASHBOARD_DATA'; + + +function updateDashboardData(payload) { + return { + type: DASHBOARD_DATA, + payload + }; +} + + +export function retrieveDashboardData(currentUserAddress) { + return async (dispatch) => { + + let firmwareResults = await retrieveAllAvailableFirmware(); + let userFirmware = (firmwareResults).filter(fw => isUserFirmware(currentUserAddress, fw)); + // search users on user reputation blockchain for user inclusion + let userAddresses = await getAllUsers(); + // search bounties on the bounty blockchain for bounty inclusion + let bountyResults = await retrieveAllBounties(); + let userBounties = (bountyResults).filter(b => isUserBounty(currentUserAddress, b)); + + // getting user devices from local storage + let userDevices = await getUserDevices(); + + + // retrieve data values + let communityData = communityContributions(firmwareResults, userAddresses, bountyResults); + let bountiesData = bountiesClaimed(userBounties); + let firmwareData = firmwareStats(userFirmware) + let deviceData = deviceStats(userDevices) + + // trigger the reducer data update + dispatch(updateDashboardData({community: communityData, bounties: bountiesData, + firmware: firmwareData, devices: deviceData})) + + } +} +/** + * + * @returns {function(*): Promise<{totalFirmware: number, totalBounties: number, userAmount: number}>} + */ +function communityContributions(firmwares, users, bounties) { + + // amount of users; total bounties; total firmware + return { + userAmount: users.length, + totalBounties: bounties.length, + totalFirmware: firmwares.length, + monthlyContributions: 0 + } +} + +/** + * + * @param userBounties + * @returns {{overallClaimed: number, amountSubmitted: number, monthlyClaims: number}} + */ +function bountiesClaimed(userBounties) { + + let claimedBounties = userBounties.filter(b => isBountyClaimed(true, b)); + let claimedPercent = 100 * (claimedBounties.length / userBounties.length); + // TODO check above works with rounding as a percentage + + // Amount Submitted; Overall Claimed; Monthly Claims + return { + amountSubmitted: userBounties.length, + overallClaimed: claimedPercent, + monthlyClaims: 0 + } +} + +/** + * + * @param userFirmwares + * @returns {{firmwareAmount: number, overallDownloads: number, monthlyDownloads: number}} + */ +function firmwareStats(userFirmwares) { + + // TODO replace this firmware downloads value (consider using FirmwareWithThumbs) -- look for Michael + let firmwareDownloads = 0; + + // Amount Submitted; Overall Claimed; Monthly Claims + return { + firmwareAmount: userFirmwares.length, + overallDownloads: firmwareDownloads, + monthlyDownloads: 0 + } +} + +/** + * Retrieves the device statistics for display in the dashboard. + * @param devices - list of {Device} objects + * @returns {{deviceDetails: {onMap: number, totalDevices: number}, inactive: {number: number, percent: number}, active: {number: number, percent: number}, unknown: {number: number, percent: number}}} + */ +function deviceStats(devices) { + + // TODO retrieve the values around the device status for each + // (likely have to get Michael to provide class (and method to produce) to wrap the device in status + location + + return { + active: {number: 0, percent: 0}, + unknown: {number: 0, percent: 0}, + inactive: {number: 0, percent: 0}, + deviceDetails: {onMap: 0, totalDevices: 0} + } +} + +function isUserBounty(term, bounty) { + return containsIgnoreCase(bounty.bountySetter, term); +} + +function isBountyClaimed(term, bounty) { + // TODO replace with some kind of claimed flag (see Michael for getting this setup) -- Michael!!!! :L + return containsIgnoreCase(bounty.block_num, term); +} + +/** + * Return true if the users firmware contains the developers name + * @param term + * @param firmware + * @returns {boolean} + */ +function isUserFirmware(term, firmware) { + return containsIgnoreCase(firmware.developer, term); +} diff --git a/src/actions/profile.js b/src/actions/profile.js index d0f8165..b71e488 100644 --- a/src/actions/profile.js +++ b/src/actions/profile.js @@ -61,6 +61,23 @@ export function initUserDevices() { } +/** + * Reads available user devices from the browser cache + */ +export function getUserDevices() { + return async (dispatch) => { + let devices = []; + if (localStorage[deviceLocalStorageKey]) { + try{ + devices = JSON.parse(localStorage['devices']); + } catch (e) { + devices = []; + } + } + return devices; + } +} + /** * Add to the device list. * TODO implement UI binding and functionality diff --git a/src/actions/search.js b/src/actions/search.js index 081359a..a9e9fb7 100644 --- a/src/actions/search.js +++ b/src/actions/search.js @@ -31,7 +31,7 @@ function searchFailure(payload) { }; } -function containsIgnoreCase(string, term) { +export function containsIgnoreCase(string, term) { if (string == null || term == null) { return false; } diff --git a/src/actions/user.js b/src/actions/user.js index c8a27f5..9a3002f 100644 --- a/src/actions/user.js +++ b/src/actions/user.js @@ -4,6 +4,7 @@ import Box from "3box"; import {setProfilePassword, setUserProfile} from "./profile"; import { setBounties, setFirmware } from './model'; import { getPG } from '../filecoin/client'; +import {retrieveDashboardData} from "./dashboard"; export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; export const LOGIN_FAILURE = 'LOGIN_FAILURE'; @@ -120,6 +121,7 @@ export function enableUserEthereum() { let userPassword = await space.private.get('password'); dispatch(setProfilePassword({userPassword: userPassword})); dispatch(setUserProfile({userAddress: ethereumAddress})); + dispatch(retrieveDashboardData(ethereumAddress)) // Accounts now exposed dispatch(ethereumAuthSuccess({ diff --git a/src/pages/dashboard/Dashboard.js b/src/pages/dashboard/Dashboard.js index b088690..74f013d 100644 --- a/src/pages/dashboard/Dashboard.js +++ b/src/pages/dashboard/Dashboard.js @@ -3,6 +3,7 @@ import React from 'react'; import s from './Dashboard.module.scss'; import DeviceRow from "./components/DeviceRow"; import TableRow from "./components/TableRow"; +import {connect} from "react-redux"; class Dashboard extends React.Component { @@ -12,6 +13,14 @@ class Dashboard extends React.Component { } render() { + const { + devicesStats, + communityStats, + firmwareStats, + bountiesStats, + } = this.props; + + console.log('Dashboard', this.props) return (

Dashboard   @@ -19,11 +28,18 @@ class Dashboard extends React.Component { Hub Overview

- - + +
); } } -export default Dashboard; +const mapStateToProps = state => ({ + communityStats: state.dashboard.community, + bountiesStats: state.dashboard.bounties, + firmwareStats: state.dashboard.firmware, + devicesStats: state.dashboard.devices +}); + +export default connect(mapStateToProps)(Dashboard); diff --git a/src/pages/dashboard/components/DeviceRow.js b/src/pages/dashboard/components/DeviceRow.js index 1eddfc1..0d578fe 100644 --- a/src/pages/dashboard/components/DeviceRow.js +++ b/src/pages/dashboard/components/DeviceRow.js @@ -13,7 +13,14 @@ import StatusLines from "./DeviceComponents/StatusLines/StatusLines"; class DeviceRow extends React.Component { + constructor(props) { + super(props); + } + render() { + // TODO this object can then be used to replace the 0 values where appropriate + // - should have the same fields as the return value from the function deviceStats in src/actions/dashboard.js + const deviceStats = this.props.devicesStats; return ( diff --git a/src/pages/dashboard/components/TableRow.js b/src/pages/dashboard/components/TableRow.js index b2b1290..6256ee8 100644 --- a/src/pages/dashboard/components/TableRow.js +++ b/src/pages/dashboard/components/TableRow.js @@ -12,6 +12,13 @@ class TableRow extends React.Component { } render() { + + const { + communityStats, + firmwareStats, + bountiesStats, + } = this.props; + return ( @@ -26,15 +33,15 @@ class TableRow extends React.Component {
Amount of Users
-

0

+

{communityStats.userAmount}

Total Bounties
-

0

+

{communityStats.totalBounties}

Total Firmware
-

0

+

{communityStats.totalFirmware}

@@ -58,15 +65,15 @@ class TableRow extends React.Component {
Amount Submitted
-

0

+

{bountiesStats.amountSubmitted}

Overall Claimed
-

0%

+

{bountiesStats.overallClaimed}%

Monthly Claims
-

0

+

{bountiesStats.monthlyClaims}

@@ -93,15 +100,15 @@ class TableRow extends React.Component {
My Firmware
-

0

+

{firmwareStats.firmwareAmount}

Overall Downloads
-

0

+

{firmwareStats.overallDownloads}

Monthly Downloads
-

0

+

{firmwareStats.monthlyDownloads}

diff --git a/src/reducers/dashboard.js b/src/reducers/dashboard.js new file mode 100644 index 0000000..d0b3a69 --- /dev/null +++ b/src/reducers/dashboard.js @@ -0,0 +1,38 @@ +import { DASHBOARD_DATA } from '../actions/dashboard'; + +export default function dashboard(state = { + community: { + userAmount: 0, + totalBounties: 0, + totalFirmware: 0, + monthlyContributions: 0 + }, + bounties: { + amountSubmitted: 0, + overallClaimed: 0, + monthlyClaims: 0 + }, + firmware: { + firmwareAmount: 0, + overallDownloads: 0, + monthlyDownloads: 0 + }, + devices: { + active: {number: 0, percent: 0}, + unknown: {number: 0, percent: 0}, + inactive: {number: 0, percent: 0}, + deviceDetails: {onMap: 0, totalDevices: 0} + } +}, action) { + switch (action.type) { + case DASHBOARD_DATA: + return Object.assign({}, state, { + community: action.payload.community, + bounties: action.payload.bounties, + firmware: action.payload.firmware, + devices: action.payload.devices, + }); + default: + return state; + } +} \ No newline at end of file diff --git a/src/reducers/index.js b/src/reducers/index.js index ace8f6e..7fe053b 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -1,6 +1,7 @@ import { combineReducers } from 'redux'; import auth from './auth'; import alerts from './alerts'; +import dashboard from "./dashboard"; import ethereum from "./ethereum"; import navigation from './navigation'; import register from './register'; @@ -12,6 +13,7 @@ import views from './views' export default combineReducers({ alerts, auth, + dashboard, ethereum, navigation, register, From 0b4db989205a46c9dff29785281e342e87b7eae7 Mon Sep 17 00:00:00 2001 From: ZeroSum24 Date: Tue, 15 Sep 2020 21:36:39 +0100 Subject: [PATCH 3/5] Comments added --- src/actions/dashboard.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/actions/dashboard.js b/src/actions/dashboard.js index 79eb45e..4cb89af 100644 --- a/src/actions/dashboard.js +++ b/src/actions/dashboard.js @@ -11,7 +11,13 @@ function updateDashboardData(payload) { }; } +// TODO must consider where to store, and how to calculate, last months values as compared to this ones for various fields +/** + * High-level function for triggering the Dashboard data retrival. + * @param currentUserAddress + * @returns {function(*): Promise} + */ export function retrieveDashboardData(currentUserAddress) { return async (dispatch) => { From 74702f0f327a923d8abedf0f9e03cc9392532906 Mon Sep 17 00:00:00 2001 From: Mahbub Iftekhar Date: Wed, 16 Sep 2020 19:36:03 +0100 Subject: [PATCH 4/5] Work so far, calculations done --- src/actions/dashboard.js | 54 +++++++++++-------- .../StatusLines/StatusLines.js | 2 + src/pages/dashboard/components/DeviceRow.js | 9 ++-- src/reducers/dashboard.js | 22 ++++---- 4 files changed, 52 insertions(+), 35 deletions(-) diff --git a/src/actions/dashboard.js b/src/actions/dashboard.js index 4cb89af..f81f0ec 100644 --- a/src/actions/dashboard.js +++ b/src/actions/dashboard.js @@ -35,13 +35,16 @@ export function retrieveDashboardData(currentUserAddress) { // retrieve data values let communityData = communityContributions(firmwareResults, userAddresses, bountyResults); - let bountiesData = bountiesClaimed(userBounties); - let firmwareData = firmwareStats(userFirmware) - let deviceData = deviceStats(userDevices) + let bountiesData = bountiesClaimed (userBounties); + let firmwareData = firmwareStats (userFirmware) + let deviceData = deviceStats (userDevices) // trigger the reducer data update - dispatch(updateDashboardData({community: communityData, bounties: bountiesData, - firmware: firmwareData, devices: deviceData})) + dispatch(updateDashboardData({community: communityData, + bounties: bountiesData, + firmware: firmwareData, + devices: deviceData + })) } } @@ -53,10 +56,10 @@ function communityContributions(firmwares, users, bounties) { // amount of users; total bounties; total firmware return { - userAmount: users.length, - totalBounties: bounties.length, - totalFirmware: firmwares.length, - monthlyContributions: 0 + userAmount : users.length, + totalBounties : bounties.length, + totalFirmware : firmwares.length, + monthlyContributions : 0 } } @@ -73,9 +76,9 @@ function bountiesClaimed(userBounties) { // Amount Submitted; Overall Claimed; Monthly Claims return { - amountSubmitted: userBounties.length, - overallClaimed: claimedPercent, - monthlyClaims: 0 + amountSubmitted : userBounties.length, + overallClaimed : claimedPercent, + monthlyClaims : 0 } } @@ -91,9 +94,9 @@ function firmwareStats(userFirmwares) { // Amount Submitted; Overall Claimed; Monthly Claims return { - firmwareAmount: userFirmwares.length, - overallDownloads: firmwareDownloads, - monthlyDownloads: 0 + firmwareAmount : userFirmwares.length, + overallDownloads : firmwareDownloads, + monthlyDownloads : 0 } } @@ -103,15 +106,24 @@ function firmwareStats(userFirmwares) { * @returns {{deviceDetails: {onMap: number, totalDevices: number}, inactive: {number: number, percent: number}, active: {number: number, percent: number}, unknown: {number: number, percent: number}}} */ function deviceStats(devices) { - + //Mahbub // TODO retrieve the values around the device status for each - // (likely have to get Michael to provide class (and method to produce) to wrap the device in status + location + // (likely have to get Michael to provide class (and method to produce) to wrap the device in status + location + let numberOfDevices = devices.length; + + let activeDevices = 12; + let inactiveDevices = 0; + let unknownDevices = (numberOfDevices-activeDevices-inactiveDevices); + + let activeDevicesPercentage = 100 * (activeDevices / numberOfDevices) + let inactiveDevicesPercentage = 100 * (inactiveDevices / numberOfDevices) + let unknownDevicesPercentage = 100 * (unknownDevices / numberOfDevices) return { - active: {number: 0, percent: 0}, - unknown: {number: 0, percent: 0}, - inactive: {number: 0, percent: 0}, - deviceDetails: {onMap: 0, totalDevices: 0} + active: {number: activeDevices, percent : activeDevicesPercentage}, + unknown: {number: unknownDevices, percent : unknownDevicesPercentage}, + inactive: {number: inactiveDevices, percent : inactiveDevicesPercentage}, + deviceDetails: {onMap : 0, totalDevices: numberOfDevices} } } diff --git a/src/pages/dashboard/components/DeviceComponents/StatusLines/StatusLines.js b/src/pages/dashboard/components/DeviceComponents/StatusLines/StatusLines.js index 994ca2b..5bbcfb3 100644 --- a/src/pages/dashboard/components/DeviceComponents/StatusLines/StatusLines.js +++ b/src/pages/dashboard/components/DeviceComponents/StatusLines/StatusLines.js @@ -16,6 +16,8 @@ class StatusLines extends React.Component { render() { + const deviceStats = this.props.devicesStats; + return (
diff --git a/src/pages/dashboard/components/DeviceRow.js b/src/pages/dashboard/components/DeviceRow.js index 0d578fe..60eff7d 100644 --- a/src/pages/dashboard/components/DeviceRow.js +++ b/src/pages/dashboard/components/DeviceRow.js @@ -45,12 +45,12 @@ class DeviceRow extends React.Component {
Amount of devices which are active, unknown or inactive on the map.
- +
Map Distribution

Tracking: Active

-   0 device added, 0 devices total +   {deviceStats.deviceDetails.onMap} device added, {deviceStats.deviceDetails.totalDevices} devices total

@@ -60,7 +60,6 @@ class DeviceRow extends React.Component {
- @@ -68,4 +67,8 @@ class DeviceRow extends React.Component { } } +const mapStateToProps = state => ({ + devicesStats: state.dashboard.devices +}); + export default DeviceRow; diff --git a/src/reducers/dashboard.js b/src/reducers/dashboard.js index d0b3a69..6e842d9 100644 --- a/src/reducers/dashboard.js +++ b/src/reducers/dashboard.js @@ -2,26 +2,26 @@ import { DASHBOARD_DATA } from '../actions/dashboard'; export default function dashboard(state = { community: { - userAmount: 0, - totalBounties: 0, - totalFirmware: 0, + userAmount: 0, + totalBounties: 0, + totalFirmware: 0, monthlyContributions: 0 }, bounties: { amountSubmitted: 0, - overallClaimed: 0, - monthlyClaims: 0 + overallClaimed: 0, + monthlyClaims: 0 }, firmware: { - firmwareAmount: 0, + firmwareAmount: 0, overallDownloads: 0, monthlyDownloads: 0 }, devices: { - active: {number: 0, percent: 0}, - unknown: {number: 0, percent: 0}, - inactive: {number: 0, percent: 0}, - deviceDetails: {onMap: 0, totalDevices: 0} + active: {number: 0, percent: 0}, + unknown: {number: 0, percent: 0}, + inactive: {number: 0, percent: 0}, + deviceDetails: {onMap: 0, totalDevices: 0} } }, action) { switch (action.type) { @@ -35,4 +35,4 @@ export default function dashboard(state = { default: return state; } -} \ No newline at end of file +} From 5c7edc2926ebd64e1ee360c7a87acce06011da2a Mon Sep 17 00:00:00 2001 From: Mahbub Iftekhar Date: Wed, 16 Sep 2020 20:07:50 +0100 Subject: [PATCH 5/5] More done for UI changes --- src/actions/dashboard.js | 16 ++++++++-------- .../DeviceComponents/StatusLines/StatusLines.js | 12 ++++++------ src/pages/dashboard/components/DeviceRow.js | 4 ---- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/actions/dashboard.js b/src/actions/dashboard.js index f81f0ec..fe18949 100644 --- a/src/actions/dashboard.js +++ b/src/actions/dashboard.js @@ -106,24 +106,24 @@ function firmwareStats(userFirmwares) { * @returns {{deviceDetails: {onMap: number, totalDevices: number}, inactive: {number: number, percent: number}, active: {number: number, percent: number}, unknown: {number: number, percent: number}}} */ function deviceStats(devices) { - //Mahbub // TODO retrieve the values around the device status for each // (likely have to get Michael to provide class (and method to produce) to wrap the device in status + location let numberOfDevices = devices.length; - let activeDevices = 12; - let inactiveDevices = 0; - let unknownDevices = (numberOfDevices-activeDevices-inactiveDevices); + let activeDevices = 12; //: TODO Michael + let inactiveDevices = 14; //: TODO Michael + let numberOfDeviceOnMap = 15; //: TODO Michael + let unknownDevices = (numberOfDevices-activeDevices-inactiveDevices); let activeDevicesPercentage = 100 * (activeDevices / numberOfDevices) let inactiveDevicesPercentage = 100 * (inactiveDevices / numberOfDevices) let unknownDevicesPercentage = 100 * (unknownDevices / numberOfDevices) return { - active: {number: activeDevices, percent : activeDevicesPercentage}, - unknown: {number: unknownDevices, percent : unknownDevicesPercentage}, - inactive: {number: inactiveDevices, percent : inactiveDevicesPercentage}, - deviceDetails: {onMap : 0, totalDevices: numberOfDevices} + active: {number: activeDevices , percent : activeDevicesPercentage}, + unknown: {number: unknownDevices , percent : unknownDevicesPercentage}, + inactive: {number: inactiveDevices , percent : inactiveDevicesPercentage}, + deviceDetails: {onMap : numberOfDeviceOnMap , totalDevices: numberOfDevices} } } diff --git a/src/pages/dashboard/components/DeviceComponents/StatusLines/StatusLines.js b/src/pages/dashboard/components/DeviceComponents/StatusLines/StatusLines.js index 5bbcfb3..3670c3c 100644 --- a/src/pages/dashboard/components/DeviceComponents/StatusLines/StatusLines.js +++ b/src/pages/dashboard/components/DeviceComponents/StatusLines/StatusLines.js @@ -23,33 +23,33 @@ class StatusLines extends React.Component {

Active

- +
- % + %

Unknown

- +
- % + %

Inactive

- +
- % + %
diff --git a/src/pages/dashboard/components/DeviceRow.js b/src/pages/dashboard/components/DeviceRow.js index 60eff7d..0794c11 100644 --- a/src/pages/dashboard/components/DeviceRow.js +++ b/src/pages/dashboard/components/DeviceRow.js @@ -67,8 +67,4 @@ class DeviceRow extends React.Component { } } -const mapStateToProps = state => ({ - devicesStats: state.dashboard.devices -}); - export default DeviceRow;