diff --git a/app/actions/api.ts b/app/actions/api.ts index eebbb1fa..04725133 100644 --- a/app/actions/api.ts +++ b/app/actions/api.ts @@ -59,6 +59,7 @@ export function fetchWorkingDatasetDetails (): ApiActionThunk { } response = await whenOk(fetchWorkingStatus())(response) response = await whenOk(fetchBody(-1))(response) + response = await whenOk(fetchStats())(response) response = await whenOk(fetchWorkingHistory(-1))(response) return response @@ -172,6 +173,7 @@ export function fetchCommitDetail (): ApiActionThunk { response = await fetchCommitDataset()(dispatch, getState) response = await whenOk(fetchCommitStatus())(response) response = await whenOk(fetchCommitBody(-1))(response) + response = await whenOk(fetchCommitStats())(response) return response } @@ -349,6 +351,55 @@ export function fetchCommitBody (page: number = 1, pageSize: number = bodyPageSi } } +export function fetchStats (): +ApiActionThunk { + return async (dispatch, getState) => { + const { selections, workingDataset } = getState() + + const response = await dispatch({ + type: 'stats', + [CALL_API]: { + endpoint: 'stats', + method: 'GET', + segments: { + peername: selections.peername, + name: selections.name + }, + query: { + fsi: !!workingDataset.fsiPath + } + } + }) + + return response + } +} + +export function fetchCommitStats (): +ApiActionThunk { + return async (dispatch, getState) => { + const { selections } = getState() + + const response = await dispatch({ + type: 'commitstats', + [CALL_API]: { + endpoint: 'stats', + method: 'GET', + segments: { + peername: selections.peername, + name: selections.name, + path: selections.commit + }, + query: { + fsi: false + } + } + }) + + return response + } +} + export function saveWorkingDataset (): ApiActionThunk { return async (dispatch, getState) => { const { workingDataset, mutations } = getState() diff --git a/app/components/Body.tsx b/app/components/Body.tsx index a15de24c..766f1975 100644 --- a/app/components/Body.tsx +++ b/app/components/Body.tsx @@ -7,7 +7,7 @@ import { ApiAction } from '../store/api' import Spinner from './chrome/Spinner' import { PageInfo, WorkingDataset } from '../models/store' import { Action } from 'redux' -import { DetailsType, Details } from '../models/details' +import { DetailsType, Details, StatsDetails } from '../models/details' export interface BodyProps { workingDataset: WorkingDataset @@ -62,8 +62,8 @@ const Body: React.FunctionComponent = (props) => { format, fetchCommitBody, details, - setDetailsBar - // stats + setDetailsBar, + stats } = props const onFetch = history ? fetchCommitBody : fetchBody @@ -73,12 +73,23 @@ const Body: React.FunctionComponent = (props) => { // if there's no value or format, don't show anything yet const showSpinner = !(value && format) + const makeStatsDetails = (stats: {[key: string]: any}, title: string, index: number): StatsDetails => { + return { + type: DetailsType.StatsDetails, + title: title, + index: index, + stats: stats + } + } + const handleToggleDetailsBar = (index: number) => { + if (!stats || stats.length === 0) return + const statsHeaders = headers as string[] if (details.type === DetailsType.NoDetails) { - setDetailsBar({ type: DetailsType.StatDetails, stat: { 'wooo': 'yay' }, index }) + setDetailsBar(makeStatsDetails(stats[index], statsHeaders[index], index)) return } - if (details.type === DetailsType.StatDetails) { + if (details.type === DetailsType.StatsDetails) { // if the index is the same, then the user has clicked // on the header twice. The second time, we should // remove the detailsbar @@ -86,7 +97,7 @@ const Body: React.FunctionComponent = (props) => { setDetailsBar({ type: DetailsType.NoDetails }) return } - setDetailsBar({ type: DetailsType.StatDetails, stat: { 'wooo': 'yay' }, index }) + setDetailsBar(makeStatsDetails(stats[index], statsHeaders[index], index)) } } diff --git a/app/components/DetailsBar.tsx b/app/components/DetailsBar.tsx index b2acbb51..7c141814 100644 --- a/app/components/DetailsBar.tsx +++ b/app/components/DetailsBar.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { Details } from '../models/details' +import { Details, DetailsType, StatsDetails } from '../models/details' export interface DetailsBarProps { details: Details @@ -9,8 +9,22 @@ const DetailsBar: React.FunctionComponent = (props) => { details } = props console.log(details) + + const renderStatsDetails = () => { + const statsDetails = details as StatsDetails + return ( +
+

{statsDetails.title}

+

{JSON.stringify(statsDetails.stats)}

+
+ ) + } + return

Details

+
+ {details.type === DetailsType.StatsDetails && renderStatsDetails()} +
} diff --git a/app/containers/BodyContainer.tsx b/app/containers/BodyContainer.tsx index b5d77a3d..34253358 100644 --- a/app/containers/BodyContainer.tsx +++ b/app/containers/BodyContainer.tsx @@ -10,6 +10,7 @@ const mapStateToProps = (state: Store) => { const { workingDataset, commitDetails, selections, ui } = state const history = selections.activeTab === 'history' const dataset = history ? commitDetails : workingDataset + const stats = history ? commitDetails.stats : workingDataset.stats const { pageInfo, value } = dataset.components.body const details = ui.detailsBar @@ -35,7 +36,8 @@ const mapStateToProps = (state: Store) => { format, history, details, - workingDataset + workingDataset, + stats } } diff --git a/app/models/details.ts b/app/models/details.ts index 106d562a..4f4cc4c0 100644 --- a/app/models/details.ts +++ b/app/models/details.ts @@ -1,18 +1,19 @@ export enum DetailsType { NoDetails, - StatDetails + StatsDetails } -interface StatDetails { - type: DetailsType.StatDetails +export interface StatsDetails { + type: DetailsType.StatsDetails // eventually replace with a Stats interface index: number - details: Details + title: string + stats: {[key: string]: any} } -interface NoDetails { +export interface NoDetails { type: DetailsType.NoDetails } -export type Details = StatDetails | NoDetails +export type Details = StatsDetails | NoDetails diff --git a/app/models/store.ts b/app/models/store.ts index 7ed5a770..eaa291dc 100644 --- a/app/models/store.ts +++ b/app/models/store.ts @@ -149,6 +149,7 @@ export interface CommitDetails { value: Structure } } + stats: Array<{[key: string]: any}> } export interface HistoryItem { diff --git a/app/reducers/commitDetail.ts b/app/reducers/commitDetail.ts index 4fede233..40631c38 100644 --- a/app/reducers/commitDetail.ts +++ b/app/reducers/commitDetail.ts @@ -25,12 +25,14 @@ const initialState: CommitDetails = { structure: { value: {} } - } + }, + stats: [] } const [COMMITDATASET_REQ, COMMITDATASET_SUCC, COMMITDATASET_FAIL] = apiActionTypes('commitdataset') const [COMMITSTATUS_REQ, COMMITSTATUS_SUCC, COMMITSTATUS_FAIL] = apiActionTypes('commitstatus') const [COMMITBODY_REQ, COMMITBODY_SUCC, COMMITBODY_FAIL] = apiActionTypes('commitbody') +const [COMMITSTATS_REQ, COMMITSTATS_SUCC, COMMITSTATS_FAIL] = apiActionTypes('commitstats') const commitDetailsReducer: Reducer = (state = initialState, action: AnyAction): CommitDetails => { switch (action.type) { @@ -114,6 +116,25 @@ const commitDetailsReducer: Reducer = (state = initialState, action: AnyAction): } } + case COMMITSTATS_REQ: + if (state.peername === action.segments.peername && state.name === action.segments.name) { + return state + } + return { + ...state, + stats: [] + } + case COMMITSTATS_SUCC: + return { + ...state, + stats: action.payload.data + } + case COMMITSTATS_FAIL: + return { + ...state, + stats: [] + } + default: return state } diff --git a/app/reducers/ui.ts b/app/reducers/ui.ts index d88c57bc..12e6e489 100644 --- a/app/reducers/ui.ts +++ b/app/reducers/ui.ts @@ -6,6 +6,12 @@ import { apiActionTypes } from '../utils/actionType' import { SAVE_SUCC, SAVE_FAIL } from '../reducers/mutations' import { ModalType } from '../models/modals' import { DetailsType } from '../models/details' +import { + SELECTIONS_SET_SELECTED_LISTITEM, + SELECTIONS_SET_WORKING_DATASET, + SELECTIONS_CLEAR, + SELECTIONS_SET_ACTIVE_TAB +} from './selections' export const UI_TOGGLE_DATASET_LIST = 'UI_TOGGLE_DATASET_LIST' export const UI_SET_SIDEBAR_WIDTH = 'UI_SET_SIDEBAR_WIDTH' @@ -128,6 +134,16 @@ export default (state = initialState, action: AnyAction) => { detailsBar: action.details } + // if we change screens + case SELECTIONS_SET_WORKING_DATASET: + case SELECTIONS_CLEAR: + case SELECTIONS_SET_ACTIVE_TAB: + case SELECTIONS_SET_SELECTED_LISTITEM: + return { + ...state, + detailsBar: { type: DetailsType.NoDetails } + } + // listen for SAVE_SUCC and SAVE_FAIL to set the toast case SAVE_SUCC: return { diff --git a/app/reducers/workingDataset.ts b/app/reducers/workingDataset.ts index a0f4d1ee..89ebf874 100644 --- a/app/reducers/workingDataset.ts +++ b/app/reducers/workingDataset.ts @@ -43,7 +43,8 @@ const initialState: WorkingDataset = { pageSize: 0 }, value: [] - } + }, + stats: [] } export const [DATASET_REQ, DATASET_SUCC, DATASET_FAIL] = apiActionTypes('dataset') @@ -51,6 +52,7 @@ export const [HISTORY_REQ, HISTORY_SUCC, HISTORY_FAIL] = apiActionTypes('history export const [DATASET_STATUS_REQ, DATASET_STATUS_SUCC, DATASET_STATUS_FAIL] = apiActionTypes('status') const [DATASET_BODY_REQ, DATASET_BODY_SUCC, DATASET_BODY_FAIL] = apiActionTypes('body') const [, RESETOTHERCOMPONENTS_SUCC, RESETOTHERCOMPONENTS_FAIL] = apiActionTypes('resetOtherComponents') +const [STATS_REQ, STATS_SUCC, STATS_FAIL] = apiActionTypes('stats') export const RESET_BODY = 'RESET_BODY' @@ -242,6 +244,26 @@ const workingDatasetsReducer: Reducer = (state = initialState, action: AnyAction return initialState } return state + + case STATS_REQ: + if (state.peername === action.segments.peername && state.name === action.segments.name) { + return state + } + return { + ...state, + stats: [] + } + case STATS_SUCC: + return { + ...state, + stats: action.payload.data + } + case STATS_FAIL: + return { + ...state, + stats: [] + } + default: return state } diff --git a/app/scss/_dataset.scss b/app/scss/_dataset.scss index 5ac7b087..c818fca7 100644 --- a/app/scss/_dataset.scss +++ b/app/scss/_dataset.scss @@ -606,4 +606,9 @@ $header-font-size: .9rem; -moz-transition-duration: 0.6s; -o-transition-duration: 0.6s; transition-duration: 0.6s; +} + +.details-bar-content { + overflow-y: scroll; + overflow-wrap: break-word; } \ No newline at end of file