loading instances

This commit is contained in:
Tao Bojlen 2018-08-27 23:31:27 +02:00
parent 4e0adc7579
commit 6d05ceadba
13 changed files with 139 additions and 36 deletions

View File

@ -54,11 +54,13 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
'rest_framework',
'silk',
'corsheaders',
'scraper.apps.ScraperConfig',
'apiv1.apps.Apiv1Config',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',

View File

@ -4,3 +4,9 @@ DEBUG = True
ALLOWED_HOSTS += ['localhost']
CORS_ORIGIN_WHITELIST = [
'localhost:3000',
'localhost:8000',
'127.0.0.1',
]

View File

@ -1,3 +1,7 @@
from .base import *
ALLOWED_HOSTS += ['fediverse.space']
ALLOWED_HOSTS += ['fediverse.space']
CORS_ORIGIN_WHITELIST = [
'fediverse.space',
]

View File

@ -6,12 +6,14 @@
"@blueprintjs/core": "^3.4.0",
"@blueprintjs/icons": "^3.1.0",
"@blueprintjs/select": "^3.1.0",
"classnames": "^2.2.6",
"cross-fetch": "^2.2.2",
"normalize.css": "^8.0.0",
"react": "^16.4.2",
"react-dom": "^16.4.2",
"react-redux": "^5.0.7",
"react-scripts-ts": "2.17.0",
"react-virtualized": "^9.20.1",
"redux": "^4.0.0",
"redux-thunk": "^2.3.0"
},
@ -22,11 +24,13 @@
"eject": "react-scripts-ts eject"
},
"devDependencies": {
"@types/classnames": "^2.2.6",
"@types/jest": "^23.3.1",
"@types/node": "^10.9.2",
"@types/react": "^16.4.12",
"@types/react-dom": "^16.0.7",
"@types/react-redux": "^6.0.6",
"@types/react-virtualized": "^9.18.7",
"typescript": "^3.0.1"
}
}

View File

@ -19,7 +19,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>fediverse.space</title>
</head>
<body>
<noscript>

View File

@ -10,6 +10,7 @@ import { fetchInstances } from './redux/actions';
import { IAppState, IInstance } from './redux/types';
interface IAppProps {
currentInstance: IInstance;
instances?: IInstance[],
isLoadingInstances: boolean,
fetchInstances: () => void;
@ -19,6 +20,8 @@ class AppImpl extends React.Component<IAppProps> {
let body = this.welcomeState();
if (this.props.isLoadingInstances) {
body = this.loadingState();
} else if (!!this.props.instances) {
body = this.renderGraph()
}
// TODO: show the number of instances up front
return (
@ -46,14 +49,26 @@ class AppImpl extends React.Component<IAppProps> {
<NonIdealState
className="fediverse-welcome"
icon={<Spinner />}
title="Welcome to fediverse.space!"
title="Loading..."
/>
)
}
private renderGraph = () => {
return (
<NonIdealState
className="fediverse-welcome"
icon={IconNames.SEARCH_AROUND}
title="Graph. TODO"
description={"Selected " + this.props.currentInstance.name}
/>
)
}
}
const mapStateToProps = (state: IAppState) => ({
currentInstance: state.currentInstance,
instances: state.data.instances,
isLoadingInstances: state.data.isLoadingInstances,
})

View File

@ -15,41 +15,48 @@ interface IInstanceSearchProps {
}
class InstanceSearchImpl extends React.Component<IInstanceSearchProps> {
public render() {
// TODO: make prettier when no instances loaded
if (!this.props.instances) {
return (
<Suggest
items={[]}
inputValueRenderer={this.inputValueRenderer}
itemRenderer={this.itemRenderer}
onItemSelect={this.onItemSelect}
popoverProps={{minimal: true}}
/>
)
}
return (
<Suggest
items={this.props.instances!.map(i => i.name)}
inputValueRenderer={this.inputValueRenderer}
itemRenderer={this.itemRenderer}
items={this.props.instances || []}
onItemSelect={this.onItemSelect}
popoverProps={{minimal: true}}
// itemListRenderer={this.itemListRenderer}
/>
)
}
private inputValueRenderer = (item: string) => {
return item;
private inputValueRenderer = (item: IInstance): string => {
return item.name;
}
private itemRenderer = (item: string, itemProps: IItemRendererProps) => {
return <MenuItem text={item} />;
private itemRenderer = (item: IInstance, itemProps: IItemRendererProps): JSX.Element => {
return <MenuItem label={item.name} key={item.name} />;
}
private onItemSelect = (item: string) => {
return;
private onItemSelect = (item: IInstance, event?: React.SyntheticEvent<HTMLElement>) => {
this.props.selectInstance(item.name);
}
// private itemListRenderer = (itemListProps: IItemListRendererProps<IInstance>): JSX.Element => {
// return (
// <List
// height={350}
// rowHeight={30}
// rowCount={(this.props.instances && this.props.instances.length) || 0}
// // tslint:disable-next-line
// rowRenderer={this.rowRenderer}
// width={214}
// />
// )
// }
// private rowRenderer = (listRowProps: ListRowProps) => {
// const instanceName = (this.props.instances && this.props.instances[listRowProps.index].name) || "";
// return <MenuItem label={instanceName} key={instanceName} />;
// }
}
const mapStateToProps = (state: IAppState) => ({

View File

@ -1,11 +1,20 @@
import * as React from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { Alignment, Button, Icon, Navbar } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { selectInstance } from '../redux/actions';
import { IAppState, IInstance } from '../redux/types';
import { InstanceSearch } from './InstanceSearch';
export class Nav extends React.Component {
interface INavProps {
instances?: IInstance[];
selectInstance: (instance: string) => void;
}
class NavImpl extends React.Component<INavProps> {
public render() {
return (
<Navbar>
@ -29,3 +38,11 @@ export class Nav extends React.Component {
)
}
}
const mapStateToProps = (state: IAppState) => ({
instances: state.data.instances,
})
const mapDispatchToProps = (dispatch: Dispatch) => ({
selectInstance: (instanceName: string) => dispatch(selectInstance(instanceName)),
})
export const Nav = connect(mapStateToProps, mapDispatchToProps)(NavImpl)

View File

@ -9,4 +9,18 @@ html, body {
.fediverse-welcome {
margin-top: 50px;
}
.fediverse-instance-search-popover {
max-height: 350px;
min-width: 214px;
overflow: scroll;
overflow-x: hidden;
text-align: left;
}
.fediverse-instance-search-popover-empty-state {
padding: 10px;
width: 214px;
height: 60px;
}

View File

@ -7,7 +7,7 @@ import './index.css';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import { applyMiddleware, compose, createStore } from 'redux';
import thunk from 'redux-thunk';
import { FocusStyleManager } from '@blueprintjs/core';
@ -20,10 +20,11 @@ import registerServiceWorker from './registerServiceWorker';
FocusStyleManager.onlyShowFocusOnTabs();
// Initialize redux
const store = createStore(
rootReducer,
// @ts-ignore
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(rootReducer, /* preloadedState, */ composeEnhancers(
applyMiddleware(thunk)
);
));
ReactDOM.render(
<Provider store={store}>

View File

@ -1,10 +1,8 @@
import fetch from 'cross-fetch';
import { Dispatch } from 'redux';
import { getFromApi } from '../util';
import { ActionType, IInstance } from './types';
const API_ROOT = "https://fediverse.space/api/v1"
export const selectInstance = (instance: string) => {
return {
payload: {
@ -33,8 +31,10 @@ export const fetchInstances = () => {
// TODO: handle errors
return (dispatch: Dispatch) => {
dispatch(requestInstances());
return fetch(`${API_ROOT}/instances/`)
.then(response => response.json())
return getFromApi("instances")
.then(response => {
return response.json();
})
.then(instances => dispatch(receiveInstances(instances))
);
}

11
frontend/src/util.ts Normal file
View File

@ -0,0 +1,11 @@
import fetch from 'cross-fetch';
// const API_ROOT = "https://fediverse.space/api/v1/"
const API_ROOT = "http://localhost:8000/api/v1/"
export const getFromApi = (path: string): Promise<any> => {
const domain = API_ROOT.endsWith("/") ? API_ROOT : API_ROOT + "/";
path = path.endsWith("/") ? path : path + "/";
path += "?format=json"
return fetch(domain + path);
}

View File

@ -46,6 +46,10 @@
classnames "^2.2"
tslib "^1.9.0"
"@types/classnames@^2.2.6":
version "2.2.6"
resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.6.tgz#dbe8a666156d556ed018e15a4c65f08937c3f628"
"@types/dom4@^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/dom4/-/dom4-2.0.1.tgz#506d5781b9bcab81bd9a878b198aec7dee2a6033"
@ -82,6 +86,13 @@
"@types/react" "*"
redux "^4.0.0"
"@types/react-virtualized@^9.18.7":
version "9.18.7"
resolved "https://registry.yarnpkg.com/@types/react-virtualized/-/react-virtualized-9.18.7.tgz#8703d8904236819facff90b8b320f29233160c90"
dependencies:
"@types/prop-types" "*"
"@types/react" "*"
"@types/react@*", "@types/react@^16.4.12":
version "16.4.12"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.12.tgz#c554005770b06c7cbcd6b0b19721e180deab7a02"
@ -1468,7 +1479,7 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
classnames@^2.2:
classnames@^2.2, classnames@^2.2.3, classnames@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
@ -2139,7 +2150,7 @@ dom-converter@~0.1:
dependencies:
utila "~0.3"
dom-helpers@^3.3.1:
"dom-helpers@^2.4.0 || ^3.0.0", dom-helpers@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
@ -4368,7 +4379,7 @@ longest@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.3.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
dependencies:
@ -5807,6 +5818,17 @@ react-transition-group@^2.2.1:
prop-types "^15.6.2"
react-lifecycles-compat "^3.0.4"
react-virtualized@^9.20.1:
version "9.20.1"
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.20.1.tgz#02dc08fe9070386b8c48e2ac56bce7af0208d22d"
dependencies:
babel-runtime "^6.26.0"
classnames "^2.2.3"
dom-helpers "^2.4.0 || ^3.0.0"
loose-envify "^1.3.0"
prop-types "^15.6.0"
react-lifecycles-compat "^3.0.4"
react@^16.4.2:
version "16.4.2"
resolved "https://registry.yarnpkg.com/react/-/react-16.4.2.tgz#2cd90154e3a9d9dd8da2991149fdca3c260e129f"