diff --git a/frontend/package.json b/frontend/package.json index 72ec83f..4be6db8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,9 +3,17 @@ "version": "0.1.0", "private": true, "dependencies": { + "@blueprintjs/core": "^3.4.0", + "@blueprintjs/icons": "^3.1.0", + "@blueprintjs/select": "^3.1.0", + "cross-fetch": "^2.2.2", + "normalize.css": "^8.0.0", "react": "^16.4.2", "react-dom": "^16.4.2", - "react-scripts-ts": "2.17.0" + "react-redux": "^5.0.7", + "react-scripts-ts": "2.17.0", + "redux": "^4.0.0", + "redux-thunk": "^2.3.0" }, "scripts": { "start": "react-scripts-ts start", @@ -18,6 +26,7 @@ "@types/node": "^10.9.2", "@types/react": "^16.4.12", "@types/react-dom": "^16.0.7", + "@types/react-redux": "^6.0.6", "typescript": "^3.0.1" } } diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index c5c6e8a..0000000 --- a/frontend/src/App.css +++ /dev/null @@ -1,28 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - animation: App-logo-spin infinite 20s linear; - height: 80px; -} - -.App-header { - background-color: #222; - height: 150px; - padding: 20px; - color: white; -} - -.App-title { - font-size: 1.5em; -} - -.App-intro { - font-size: large; -} - -@keyframes App-logo-spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } -} diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index e0f09ab..95469a2 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import App from './App'; + +import { App } from './App'; it('renders without crashing', () => { const div = document.createElement('div'); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c1bc27c..8de5bb0 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,22 +1,63 @@ import * as React from 'react'; -import './App.css'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; -import logo from './logo.svg'; +import { Button, Intent, NonIdealState, Spinner } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; -class App extends React.Component { +import { Nav } from './components/Nav'; +import { fetchInstances } from './redux/actions'; +import { IAppState, IInstance } from './redux/types'; + +interface IAppProps { + instances?: IInstance[], + isLoadingInstances: boolean, + fetchInstances: () => void; +} +class AppImpl extends React.Component { public render() { + let body = this.welcomeState(); + if (this.props.isLoadingInstances) { + body = this.loadingState(); + } + // TODO: show the number of instances up front return ( -
-
- logo -

Welcome to React

-
-

- To get started, edit src/App.tsx and save to reload. -

+
+
); } + + private welcomeState = () => { + return ( + } + /> + ) + } + + private loadingState = () => { + return ( + } + title="Welcome to fediverse.space!" + /> + ) + } + } -export default App; +const mapStateToProps = (state: IAppState) => ({ + instances: state.data.instances, + isLoadingInstances: state.data.isLoadingInstances, +}) +const mapDispatchToProps = (dispatch: Dispatch) => ({ + fetchInstances: () => dispatch(fetchInstances() as any) +}) +export const App = connect(mapStateToProps, mapDispatchToProps)(AppImpl) diff --git a/frontend/src/components/InstanceSearch.tsx b/frontend/src/components/InstanceSearch.tsx new file mode 100644 index 0000000..b2a4032 --- /dev/null +++ b/frontend/src/components/InstanceSearch.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; + +import { MenuItem } from '@blueprintjs/core'; +import { IItemRendererProps, Suggest } from '@blueprintjs/select'; + +import { selectInstance } from '../redux/actions'; +import { IAppState, IInstance } from '../redux/types'; + +interface IInstanceSearchProps { + currentInstance: IInstance; + instances?: IInstance[]; + selectInstance: (instanceName: string) => void; +} + +class InstanceSearchImpl extends React.Component { + public render() { + // TODO: make prettier when no instances loaded + if (!this.props.instances) { + return ( + + ) + } + return ( + i.name)} + inputValueRenderer={this.inputValueRenderer} + itemRenderer={this.itemRenderer} + onItemSelect={this.onItemSelect} + popoverProps={{minimal: true}} + /> + ) + } + + private inputValueRenderer = (item: string) => { + return item; + } + + private itemRenderer = (item: string, itemProps: IItemRendererProps) => { + return ; + } + + private onItemSelect = (item: string) => { + return; + } +} + +const mapStateToProps = (state: IAppState) => ({ + currentInstance: state.currentInstance, + instances: state.data.instances, +}) +const mapDispatchToProps = (dispatch: Dispatch) => ({ + selectInstance: (instanceName: string) => dispatch(selectInstance(instanceName)), +}) +export const InstanceSearch = connect(mapStateToProps, mapDispatchToProps)(InstanceSearchImpl) diff --git a/frontend/src/components/Nav.tsx b/frontend/src/components/Nav.tsx new file mode 100644 index 0000000..509664d --- /dev/null +++ b/frontend/src/components/Nav.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; + +import { Alignment, Button, Icon, Navbar } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; + +import { InstanceSearch } from './InstanceSearch'; + +export class Nav extends React.Component { + public render() { + return ( + + + fediverse.space +