Skip to content
This repository has been archived by the owner on Jan 20, 2024. It is now read-only.

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
tdemin committed Jan 5, 2020
2 parents 6cd4477 + 70f5f77 commit f80141f
Show file tree
Hide file tree
Showing 16 changed files with 320 additions and 131 deletions.
19 changes: 7 additions & 12 deletions .drone.yml
Expand Up @@ -11,6 +11,8 @@ steps:
- yarn install
- yarn lint
when:
event:
- push
branch:
exclude:
- master
Expand All @@ -24,16 +26,9 @@ steps:
repo: registry.git.tdem.in/amber_web
registry: registry.git.tdem.in
dockerfile: Dockerfile
tags:
- ${DRONE_TAG}
- latest
when:
branch:
exclude:
- develop
- backend
- feature/*

trigger:
event:
- push
- tag
- promote
- rollback
event:
- tag
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "amber_web",
"version": "0.0.7",
"version": "0.0.8",
"private": true,
"dependencies": {
"@types/jest": "^24.X",
Expand Down
9 changes: 9 additions & 0 deletions src/actions/auth.ts
Expand Up @@ -46,6 +46,15 @@ export const logout = () => (dispatch: Dispatch) => {
});
};

/** Redux action creator. Performs a logout locally without making an API call.
* */
export const localLogout = () => {
resetToken();
return {
type: Actions.LoggedOut,
} as AuthAction;
};

/**
* Signup function. Performs an HTTP POST request, calls the provided functions
* on success/fail.
Expand Down
2 changes: 1 addition & 1 deletion src/const.ts
@@ -1,7 +1,7 @@
export const baseURI: string =
process.env.REACT_APP_APIURI || "https://amber.h.tdem.in/api/v0";

export const appVersion: string = "0.0.7";
export const appVersion: string = "0.0.8";
export const appFullName: string = "Amber Web";
export const appName: string = "amber_web";
export const appAuthor: string = "Timur Demin";
Expand Down
29 changes: 24 additions & 5 deletions src/main.tsx
Expand Up @@ -2,14 +2,19 @@ import React from "react";
import { connect } from "react-redux";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

import req from "./axios";

import { Store } from "./typings/store";
import { Dispatch } from "./typings/react";
import { HTTPSuccessCode } from "./typings/api";

import LoginForm from "./views/loginForm";
import SignupForm from "./views/signupForm";
import MainView from "./views/mainView";
import EditorView from "./views/editorView";
import AboutView from "./views/aboutView";

import { setToken, resetToken } from "./actions/auth";
import { setToken, resetToken, localLogout } from "./actions/auth";

import "./views/styles/common.scss";

Expand All @@ -18,7 +23,11 @@ const mapStateToProps = (state: Store) => ({
username: state.auth.username,
});

interface Props {
const mapDispatchToProps = (dispatch: Dispatch) => ({
logout: () => dispatch(localLogout()),
});

interface Props extends ReturnType<typeof mapDispatchToProps> {
token?: string;
username?: string;
}
Expand Down Expand Up @@ -47,26 +56,36 @@ class App extends React.Component<Props, Props> {
});
}
};
componentDidMount = () => {
if (this.props.token) {
req.head("/session").then((res) => {
if (res.status !== HTTPSuccessCode) {
this.props.logout();
}
});
}
};
render = () => {
const token = this.state.token as string;
const loggedIn = token.length !== 0;
const loggedIn = (this.state.token as string).length !== 0;
return (
<Router>
{!loggedIn && (
<Switch>
<Route path="/signup" exact component={SignupForm} />
<Route path="/" exact component={LoginForm} />
<Route path="/about" exact component={AboutView} />
</Switch>
)}
{loggedIn && (
<Switch>
<Route path="/" exact component={MainView} />
<Route path="/task/:id" exact component={EditorView} />
<Route path="/about" exact component={AboutView} />
</Switch>
)}
</Router>
);
};
}

export default connect(mapStateToProps)(App);
export default connect(mapStateToProps, mapDispatchToProps)(App);
2 changes: 2 additions & 0 deletions src/typings/api.ts
Expand Up @@ -12,3 +12,5 @@ export type VersionData = {
version: string;
signup: boolean;
};

export const HTTPSuccessCode = 200;
6 changes: 6 additions & 0 deletions src/typings/bulma.ts
Expand Up @@ -31,3 +31,9 @@ export interface BulmaFieldProps {
/** `<div className="control">` */
control?: React.ReactNode;
}

/** List of properties used by the `<section>` component in Bulma. */
export interface BulmaSectionProps {
isMedium?: boolean;
isLarge?: boolean;
}
25 changes: 25 additions & 0 deletions src/typings/react.ts
@@ -1,4 +1,9 @@
import React from "react";
import { Dispatch as DispatchFunction } from "redux";
import { ThunkDispatch } from "redux-thunk";
import { RouteComponentProps } from "react-router";

import { AnyAction } from "./actions";

/** Generic type for React children. */
export type ReactDescendant =
Expand All @@ -8,3 +13,23 @@ export type ReactDescendant =
| string
| React.ReactNode
| React.ReactNode[];

/** Dispatching function, possibly using Redux-Thunk middleware. */
export type Dispatch = DispatchFunction & ThunkDispatch<any, any, AnyAction>;

/** Component properties passed by by React-Redux. */
export type ReduxProps = {
dispatch: Dispatch;
};

/** Route component props, including dispatching function. */
export type RCPWithDispatch = RouteComponentProps & {
dispatch: Dispatch;
};

type AnyFunction = (...args: any) => any;

/** Route component props, extended by mapDispatchToProps return value. Pass
* `typeof func` as the generic parameter. */
export type RCPWithDispProps<func extends AnyFunction> = RouteComponentProps &
ReturnType<func>;
60 changes: 60 additions & 0 deletions src/views/aboutView.tsx
@@ -0,0 +1,60 @@
import React from "react";
import { RouteComponentProps } from "react-router-dom";

import Container from "./components/bulma/container";
import Level from "./components/bulma/level";
import Button from "./components/bulma/button";

import { getServerVersion } from "../actions/misc";
import strings from "./assets/locales";
import { appHomePage, amberHomePage } from "../const";

interface State {
serverVersion: string;
}

class AboutView extends React.Component<RouteComponentProps, State> {
state = {
serverVersion: "unknown",
};
componentDidMount = async () => {
let versionData = await getServerVersion();
this.setState({ serverVersion: versionData.version });
};
render = () => (
<Container>
<Level level className="navbar">
<Level levelItem levelLeft>
<Button
value={strings.btns_goBack}
onClick={this.props.history.goBack}
/>
</Level>
</Level>
<Container>
<p>
{`${strings.app_fullVersionString(
this.state.serverVersion
)}. ${strings.about_licenseInfo}`}
</p>
<p>
{strings.about_linksParagraph}
<ul>
<li>
<a href={appHomePage}>
{strings.about_amberWebHomePage}
</a>
</li>
<li>
<a href={amberHomePage}>
{strings.about_amberHomePage}
</a>
</li>
</ul>
</p>
</Container>
</Container>
);
}

export default AboutView;
9 changes: 8 additions & 1 deletion src/views/assets/locales.ts
@@ -1,3 +1,5 @@
/* eslint-disable max-len */

import LocalizedStrings from "react-localization";

import { appVersion, appFullName, appAuthor, amberFullName } from "../../const";
Expand Down Expand Up @@ -35,6 +37,11 @@ export default new LocalizedStrings({
task_toggleBtnCompleted: "Completed",
task_toggleBtnPending: "Pending",
app_versionString: `${appFullName} v${appVersion} by ${appAuthor}`,
amber_versionString: `${amberFullName} v`,
app_fullVersionString: (version: string) =>
`${appFullName} v${appVersion} by ${appAuthor}, running on top of ${amberFullName} v${version}`,
about_licenseInfo: "This app is free software, licensed MIT.",
about_amberWebHomePage: "Get Amber Web source code.",
about_amberHomePage: "Get Amber source code.",
about_linksParagraph: "Links:",
},
});
30 changes: 10 additions & 20 deletions src/views/components/footer.tsx
@@ -1,28 +1,18 @@
import React, { useState } from "react";
import React from "react";

import Level from "./bulma/level";
import Link from "./link";

import { getServerVersion } from "../../actions/misc";
import { appHomePage, amberHomePage } from "../../const";
import strings from "../assets/locales";

export const Footer: React.FC = () => {
const [version, setVersion] = useState("unknown");
getServerVersion().then((r) => setVersion(r.version));
return (
<footer>
<Level level>
<Level levelItem className="footer_links">
<a className="text link" href={appHomePage}>
{`${strings.app_versionString}`}
</a>
<a className="text link" href={amberHomePage}>
{`${strings.amber_versionString}${version}`}
</a>
</Level>
export const Footer: React.FC = () => (
<footer>
<Level level>
<Level levelItem className="footer_links">
<Link to="/about">{strings.app_versionString}</Link>
</Level>
</footer>
);
};
</Level>
</footer>
);

export default React.memo(Footer);

0 comments on commit f80141f

Please sign in to comment.