loading instances
This commit is contained in:
parent
4e0adc7579
commit
6d05ceadba
|
@ -54,11 +54,13 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'silk',
|
'silk',
|
||||||
|
'corsheaders',
|
||||||
'scraper.apps.ScraperConfig',
|
'scraper.apps.ScraperConfig',
|
||||||
'apiv1.apps.Apiv1Config',
|
'apiv1.apps.Apiv1Config',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
'corsheaders.middleware.CorsMiddleware',
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
|
|
@ -4,3 +4,9 @@ DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS += ['localhost']
|
ALLOWED_HOSTS += ['localhost']
|
||||||
|
|
||||||
|
CORS_ORIGIN_WHITELIST = [
|
||||||
|
'localhost:3000',
|
||||||
|
'localhost:8000',
|
||||||
|
'127.0.0.1',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
from .base import *
|
from .base import *
|
||||||
|
|
||||||
ALLOWED_HOSTS += ['fediverse.space']
|
ALLOWED_HOSTS += ['fediverse.space']
|
||||||
|
|
||||||
|
CORS_ORIGIN_WHITELIST = [
|
||||||
|
'fediverse.space',
|
||||||
|
]
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
"@blueprintjs/core": "^3.4.0",
|
"@blueprintjs/core": "^3.4.0",
|
||||||
"@blueprintjs/icons": "^3.1.0",
|
"@blueprintjs/icons": "^3.1.0",
|
||||||
"@blueprintjs/select": "^3.1.0",
|
"@blueprintjs/select": "^3.1.0",
|
||||||
|
"classnames": "^2.2.6",
|
||||||
"cross-fetch": "^2.2.2",
|
"cross-fetch": "^2.2.2",
|
||||||
"normalize.css": "^8.0.0",
|
"normalize.css": "^8.0.0",
|
||||||
"react": "^16.4.2",
|
"react": "^16.4.2",
|
||||||
"react-dom": "^16.4.2",
|
"react-dom": "^16.4.2",
|
||||||
"react-redux": "^5.0.7",
|
"react-redux": "^5.0.7",
|
||||||
"react-scripts-ts": "2.17.0",
|
"react-scripts-ts": "2.17.0",
|
||||||
|
"react-virtualized": "^9.20.1",
|
||||||
"redux": "^4.0.0",
|
"redux": "^4.0.0",
|
||||||
"redux-thunk": "^2.3.0"
|
"redux-thunk": "^2.3.0"
|
||||||
},
|
},
|
||||||
|
@ -22,11 +24,13 @@
|
||||||
"eject": "react-scripts-ts eject"
|
"eject": "react-scripts-ts eject"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/classnames": "^2.2.6",
|
||||||
"@types/jest": "^23.3.1",
|
"@types/jest": "^23.3.1",
|
||||||
"@types/node": "^10.9.2",
|
"@types/node": "^10.9.2",
|
||||||
"@types/react": "^16.4.12",
|
"@types/react": "^16.4.12",
|
||||||
"@types/react-dom": "^16.0.7",
|
"@types/react-dom": "^16.0.7",
|
||||||
"@types/react-redux": "^6.0.6",
|
"@types/react-redux": "^6.0.6",
|
||||||
|
"@types/react-virtualized": "^9.18.7",
|
||||||
"typescript": "^3.0.1"
|
"typescript": "^3.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
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`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>React App</title>
|
<title>fediverse.space</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { fetchInstances } from './redux/actions';
|
||||||
import { IAppState, IInstance } from './redux/types';
|
import { IAppState, IInstance } from './redux/types';
|
||||||
|
|
||||||
interface IAppProps {
|
interface IAppProps {
|
||||||
|
currentInstance: IInstance;
|
||||||
instances?: IInstance[],
|
instances?: IInstance[],
|
||||||
isLoadingInstances: boolean,
|
isLoadingInstances: boolean,
|
||||||
fetchInstances: () => void;
|
fetchInstances: () => void;
|
||||||
|
@ -19,6 +20,8 @@ class AppImpl extends React.Component<IAppProps> {
|
||||||
let body = this.welcomeState();
|
let body = this.welcomeState();
|
||||||
if (this.props.isLoadingInstances) {
|
if (this.props.isLoadingInstances) {
|
||||||
body = this.loadingState();
|
body = this.loadingState();
|
||||||
|
} else if (!!this.props.instances) {
|
||||||
|
body = this.renderGraph()
|
||||||
}
|
}
|
||||||
// TODO: show the number of instances up front
|
// TODO: show the number of instances up front
|
||||||
return (
|
return (
|
||||||
|
@ -46,14 +49,26 @@ class AppImpl extends React.Component<IAppProps> {
|
||||||
<NonIdealState
|
<NonIdealState
|
||||||
className="fediverse-welcome"
|
className="fediverse-welcome"
|
||||||
icon={<Spinner />}
|
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) => ({
|
const mapStateToProps = (state: IAppState) => ({
|
||||||
|
currentInstance: state.currentInstance,
|
||||||
instances: state.data.instances,
|
instances: state.data.instances,
|
||||||
isLoadingInstances: state.data.isLoadingInstances,
|
isLoadingInstances: state.data.isLoadingInstances,
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,41 +15,48 @@ interface IInstanceSearchProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
class InstanceSearchImpl extends React.Component<IInstanceSearchProps> {
|
class InstanceSearchImpl extends React.Component<IInstanceSearchProps> {
|
||||||
|
|
||||||
public render() {
|
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 (
|
return (
|
||||||
<Suggest
|
<Suggest
|
||||||
items={this.props.instances!.map(i => i.name)}
|
|
||||||
inputValueRenderer={this.inputValueRenderer}
|
inputValueRenderer={this.inputValueRenderer}
|
||||||
itemRenderer={this.itemRenderer}
|
itemRenderer={this.itemRenderer}
|
||||||
|
items={this.props.instances || []}
|
||||||
onItemSelect={this.onItemSelect}
|
onItemSelect={this.onItemSelect}
|
||||||
popoverProps={{minimal: true}}
|
// itemListRenderer={this.itemListRenderer}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inputValueRenderer = (item: string) => {
|
private inputValueRenderer = (item: IInstance): string => {
|
||||||
return item;
|
return item.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private itemRenderer = (item: string, itemProps: IItemRendererProps) => {
|
private itemRenderer = (item: IInstance, itemProps: IItemRendererProps): JSX.Element => {
|
||||||
return <MenuItem text={item} />;
|
return <MenuItem label={item.name} key={item.name} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onItemSelect = (item: string) => {
|
private onItemSelect = (item: IInstance, event?: React.SyntheticEvent<HTMLElement>) => {
|
||||||
return;
|
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) => ({
|
const mapStateToProps = (state: IAppState) => ({
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
import { Alignment, Button, Icon, Navbar } from '@blueprintjs/core';
|
import { Alignment, Button, Icon, Navbar } from '@blueprintjs/core';
|
||||||
import { IconNames } from '@blueprintjs/icons';
|
import { IconNames } from '@blueprintjs/icons';
|
||||||
|
|
||||||
|
import { selectInstance } from '../redux/actions';
|
||||||
|
import { IAppState, IInstance } from '../redux/types';
|
||||||
import { InstanceSearch } from './InstanceSearch';
|
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() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<Navbar>
|
<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)
|
|
@ -9,4 +9,18 @@ html, body {
|
||||||
|
|
||||||
.fediverse-welcome {
|
.fediverse-welcome {
|
||||||
margin-top: 50px;
|
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;
|
||||||
}
|
}
|
|
@ -7,7 +7,7 @@ import './index.css';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ReactDOM from 'react-dom';
|
import * as ReactDOM from 'react-dom';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { applyMiddleware, createStore } from 'redux';
|
import { applyMiddleware, compose, createStore } from 'redux';
|
||||||
import thunk from 'redux-thunk';
|
import thunk from 'redux-thunk';
|
||||||
|
|
||||||
import { FocusStyleManager } from '@blueprintjs/core';
|
import { FocusStyleManager } from '@blueprintjs/core';
|
||||||
|
@ -20,10 +20,11 @@ import registerServiceWorker from './registerServiceWorker';
|
||||||
FocusStyleManager.onlyShowFocusOnTabs();
|
FocusStyleManager.onlyShowFocusOnTabs();
|
||||||
|
|
||||||
// Initialize redux
|
// Initialize redux
|
||||||
const store = createStore(
|
// @ts-ignore
|
||||||
rootReducer,
|
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||||
|
const store = createStore(rootReducer, /* preloadedState, */ composeEnhancers(
|
||||||
applyMiddleware(thunk)
|
applyMiddleware(thunk)
|
||||||
);
|
));
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import fetch from 'cross-fetch';
|
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
import { getFromApi } from '../util';
|
||||||
import { ActionType, IInstance } from './types';
|
import { ActionType, IInstance } from './types';
|
||||||
|
|
||||||
const API_ROOT = "https://fediverse.space/api/v1"
|
|
||||||
|
|
||||||
export const selectInstance = (instance: string) => {
|
export const selectInstance = (instance: string) => {
|
||||||
return {
|
return {
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -33,8 +31,10 @@ export const fetchInstances = () => {
|
||||||
// TODO: handle errors
|
// TODO: handle errors
|
||||||
return (dispatch: Dispatch) => {
|
return (dispatch: Dispatch) => {
|
||||||
dispatch(requestInstances());
|
dispatch(requestInstances());
|
||||||
return fetch(`${API_ROOT}/instances/`)
|
return getFromApi("instances")
|
||||||
.then(response => response.json())
|
.then(response => {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
.then(instances => dispatch(receiveInstances(instances))
|
.then(instances => dispatch(receiveInstances(instances))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
11
frontend/src/util.ts
Normal file
11
frontend/src/util.ts
Normal 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);
|
||||||
|
}
|
|
@ -46,6 +46,10 @@
|
||||||
classnames "^2.2"
|
classnames "^2.2"
|
||||||
tslib "^1.9.0"
|
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":
|
"@types/dom4@^2.0.0":
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/dom4/-/dom4-2.0.1.tgz#506d5781b9bcab81bd9a878b198aec7dee2a6033"
|
resolved "https://registry.yarnpkg.com/@types/dom4/-/dom4-2.0.1.tgz#506d5781b9bcab81bd9a878b198aec7dee2a6033"
|
||||||
|
@ -82,6 +86,13 @@
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
redux "^4.0.0"
|
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":
|
"@types/react@*", "@types/react@^16.4.12":
|
||||||
version "16.4.12"
|
version "16.4.12"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.12.tgz#c554005770b06c7cbcd6b0b19721e180deab7a02"
|
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"
|
isobject "^3.0.0"
|
||||||
static-extend "^0.1.1"
|
static-extend "^0.1.1"
|
||||||
|
|
||||||
classnames@^2.2:
|
classnames@^2.2, classnames@^2.2.3, classnames@^2.2.6:
|
||||||
version "2.2.6"
|
version "2.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
|
||||||
|
|
||||||
|
@ -2139,7 +2150,7 @@ dom-converter@~0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
utila "~0.3"
|
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"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
|
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"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
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"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5807,6 +5818,17 @@ react-transition-group@^2.2.1:
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
react-lifecycles-compat "^3.0.4"
|
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:
|
react@^16.4.2:
|
||||||
version "16.4.2"
|
version "16.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-16.4.2.tgz#2cd90154e3a9d9dd8da2991149fdca3c260e129f"
|
resolved "https://registry.yarnpkg.com/react/-/react-16.4.2.tgz#2cd90154e3a9d9dd8da2991149fdca3c260e129f"
|
||||||
|
|
Loading…
Reference in a new issue